thread.cpp revision 1c94c0a63ba68be1a7b2c640e70d7a06464e4fca
/* $Id$ */
/** @file
* innotek Portable Runtime - Threads, common routines.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP RTLOGGROUP_THREAD
#include <iprt/semaphore.h>
#ifdef IN_RING0
# include <iprt/spinlock.h>
#endif
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#ifdef IN_RING0
#else
# define RT_THREAD_LOCK_TMP(Tmp)
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The AVL thread containing the threads. */
static PAVLPVNODECORE g_ThreadTree;
#ifdef IN_RING3
/** The RW lock protecting the tree. */
#else
/** The spinlocks protecting the tree. */
#endif
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static PRTTHREADINT rtThreadAlloc(RTTHREADTYPE enmType, unsigned fFlags, unsigned fIntFlags, const char *pszName);
/** @page pg_rt_thread IPRT Thread Internals
*
* IPRT provides interface to whatever native threading that the host provides,
* preferably using a CRT level interface to better integrate with other libraries.
*
* Internally IPRT keeps track of threads by means of the RTTHREADINT structure.
* All the RTTHREADINT structures are kept in a AVL tree which is protected by a
* three places in the code. The main thread is 'adopted' by IPRT on RTR3Init()
* by rtThreadAdopt(). When creating a new thread there the child and the parent
* race inserting the thread, this is rtThreadMain() and RTThreadCreate.
*
* RTTHREADINT objects are using reference counting as a mean of sticking around
* till no-one needs them any longer. Waitable threads is created with one extra
* reference so they won't go away until they are waited on. This introduces a
* major problem if we use the host thread identifier as key in the AVL tree - the
* host may reuse the thread identifier before the thread was waited on. So, on
* most platforms we are using the RTTHREADINT pointer as key and not the
* thread id. RTThreadSelf() then have to be implemented using a pointer stored
* in thread local storage (TLS).
*
* In Ring-0 we only try keep track of kernel threads created by RTCreateThread
* at the moment. There we really only need the 'join' feature, but doing things
* the same way allow us to name threads and similar stuff.
*/
/**
* Initializes the thread database.
*
* @returns iprt status code.
*/
int rtThreadInit(void)
{
#ifdef IN_RING3
int rc = VINF_ALREADY_INITIALIZED;
if (g_ThreadRWSem == NIL_RTSEMRW)
{
/*
* We assume the caller is the 1st thread, which we'll call 'main'.
* But first, we'll create the semaphore.
*/
if (RT_SUCCESS(rc))
{
rc = rtThreadNativeInit();
#ifdef IN_RING3
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
#endif
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
/* failed, clear out */
}
}
/*
* Create the spinlock and to native init.
*/
if (RT_SUCCESS(rc))
{
rc = rtThreadNativeInit();
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
/* failed, clear out */
}
#else
# error "!IN_RING0 && !IN_RING3"
#endif
return rc;
}
/**
* Terminates the thread database.
*/
void rtThreadTerm(void)
{
#ifdef IN_RING3
/* we don't cleanup here yet */
/* just destroy the spinlock and assume the thread is fine... */
if (g_ThreadTree != NULL)
#endif
}
#ifdef IN_RING3
inline void rtThreadLockRW(void)
{
if (g_ThreadRWSem == NIL_RTSEMRW)
rtThreadInit();
}
inline void rtThreadLockRD(void)
{
if (g_ThreadRWSem == NIL_RTSEMRW)
rtThreadInit();
}
inline void rtThreadUnLockRW(void)
{
}
inline void rtThreadUnLockRD(void)
{
}
#endif /* IN_RING3 */
/**
* Adopts the calling thread.
* No locks are taken or released by this function.
*/
{
/*
* Allocate and insert the thread.
*/
int rc = VERR_NO_MEMORY;
if (pThread)
{
if (RT_SUCCESS(rc))
{
}
}
return rc;
}
/**
* Adopts a non-IPRT thread.
*
* @returns IPRT status code.
* @param enmType The thread type.
* @param fFlags The thread flags. RTTHREADFLAGS_WAITABLE is not currently allowed.
* @param pszName The thread name. Optional.
* @param pThread Where to store the thread handle. Optional.
*/
RTDECL(int) RTThreadAdopt(RTTHREADTYPE enmType, unsigned fFlags, const char *pszName, PRTTHREAD pThread)
{
int rc = VINF_SUCCESS;
if (Thread == NIL_RTTHREAD)
{
/* generate a name if none was given. */
char szName[RTTHREAD_NAME_LEN];
{
static uint32_t s_i32AlienId = 0;
}
/* try adopt it */
Thread = RTThreadSelf();
Log(("RTThreadAdopt: %RTthrd %RTnthrd '%s' enmType=%d fFlags=%#x rc=%Rrc\n",
}
else
Log(("RTThreadAdopt: %RTthrd %RTnthrd '%s' enmType=%d fFlags=%#x - already adopted!\n",
if (pThread)
return rc;
}
/**
* Allocates a per thread data structure and initializes the basic fields.
*
* @returns Pointer to per thread data structure.
* This is reference once.
* @returns NULL on failure.
* @param enmType The thread type.
* @param fFlags The thread flags.
* @param fIntFlags The internal thread flags.
* @param pszName Pointer to the thread name.
*/
PRTTHREADINT rtThreadAlloc(RTTHREADTYPE enmType, unsigned fFlags, unsigned fIntFlags, const char *pszName)
{
if (pThread)
{
if (cchName >= RTTHREAD_NAME_LEN)
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
return pThread;
}
}
return NULL;
}
/**
* Insert the per thread data structure into the tree.
*
* This can be called from both the thread it self and the parent,
* thus it must handle insertion failures in a nice manner.
*
* @param pThread Pointer to thread structure allocated by rtThreadAlloc().
* @param NativeThread The native thread id.
*/
{
/*
* Before inserting we must check if there is a thread with this id
* in the tree already. We're racing parent and child on insert here
* so that the handle is valid in both ends when they return / start.
*
* If it's not ourself we find, it's a dead alien thread and we will
* unlink it from the tree. Alien threads will be released at this point.
*/
if (pThreadOther != pThread)
{
/* remove dead alien if any */
if (pThreadOther)
{
}
/* insert the thread */
AssertReleaseMsg(fRc, ("Lock problem? %p (%RTnthrd) %s\n", pThread, NativeThread, pThread->szName));
}
}
/**
* Removes the thread from the AVL tree, call owns the tree lock
* and has cleared the RTTHREADINT_FLAG_IN_TREE bit.
*
* @param pThread The thread to remove.
*/
{
AssertMsg(pThread2 == pThread, ("%p(%s) != %p (%p/%s)\n", pThread2, pThread2 ? pThread2->szName : "<null>",
}
/**
* Removes the thread from the AVL tree.
*
* @param pThread The thread to remove.
*/
{
}
/**
* Checks if a thread is alive or not.
*
* @returns true if the thread is alive (or we don't really know).
* @returns false if the thread has surely terminate.
*/
{
}
/**
* Gets a thread by it's native ID.
*
* @returns pointer to the thread structure.
* @returns NULL if not a thread IPRT knows.
* @param NativeThread The native thread id.
*/
{
/*
* Simple tree lookup.
*/
return pThread;
}
/**
* Gets the per thread data structure for a thread handle.
*
* @returns Pointer to the per thread data structure for Thread.
* The caller must release the thread using rtThreadRelease().
* @returns NULL if Thread was not found.
* @param Thread Thread id which structure is to be returned.
*/
{
if ( Thread != NIL_RTTHREAD
{
{
return pThread;
}
}
return NULL;
}
/**
* Release a per thread data structure.
*
* @returns New reference count.
* @param pThread The thread structure to release.
*/
{
{
if (!cRefs)
}
else
cRefs = 0;
return cRefs;
}
/**
* Destroys the per thread data.
*
* @param pThread The thread to destroy.
*/
{
/*
* Mark it dead and remove it from the tree.
*/
/*
* Free resources.
*/
{
}
}
/**
* Terminates the thread.
* Called by the thread wrapper function when the thread terminates.
*
* @param pThread The thread structure.
* @param rc The thread result code.
*/
{
#ifdef IPRT_WITH_GENERIC_TLS
/*
* Destroy TLS entries.
*/
#endif /* IPRT_WITH_GENERIC_TLS */
/*
* Set the rc, mark it terminated and signal anyone waiting.
*/
/*
* Remove the thread from the tree so that there will be no
* key clashes in the AVL tree and release our reference to ourself.
*/
}
/**
* The common thread main function.
* This is called by rtThreadNativeMain().
*
* @returns The status code of the thread.
* pThread is dereference by the thread before returning!
* @param pThread The thread structure.
* @param NativeThread The native thread id.
* @param pszThreadName The name of the thread (purely a dummy for backtrace).
*/
{
Log(("rtThreadMain: Starting: pThread=%p NativeThread=%RTnthrd Name=%s pfnThread=%p pvUser=%p\n",
/*
* Change the priority.
*/
#ifdef IN_RING3
AssertMsgRC(rc, ("Failed to set priority of thread %p (%RTnthrd / %s) to enmType=%d enmPriority=%d rc=%Vrc\n",
#else
#endif
/*
* Call thread function and terminate when it returns.
*/
Log(("rtThreadMain: Terminating: rc=%d pThread=%p NativeThread=%RTnthrd Name=%s pfnThread=%p pvUser=%p\n",
return rc;
}
/**
* Create a new thread.
*
* @returns iprt status code.
* @param pThread Where to store the thread handle to the new thread. (optional)
* @param pfnThread The thread function.
* @param pvUser User argument.
* @param cbStack The size of the stack for the new thread.
* Use 0 for the default stack size.
* @param enmType The thread type. Used for deciding scheduling attributes
* of the thread.
* @param fFlags Flags of the RTTHREADFLAGS type (ORed together).
* @param pszName Thread name.
*/
{
LogFlow(("RTThreadCreate: pThread=%p pfnThread=%p pvUser=%p cbStack=%#x enmType=%d fFlags=%#x pszName=%p:{%s}\n",
/*
* Validate input.
*/
{
return VERR_INVALID_PARAMETER;
}
{
return VERR_INVALID_PARAMETER;
}
{
AssertMsgFailed(("pszName=%s (max len is %d because of logging)\n", pszName, RTTHREAD_NAME_LEN - 1));
return VERR_INVALID_PARAMETER;
}
if (fFlags & ~RTTHREADFLAGS_MASK)
{
return VERR_INVALID_PARAMETER;
}
/*
* Allocate thread argument.
*/
int rc;
if (pThreadInt)
{
if (RT_SUCCESS(rc))
{
if (pThread)
*pThread = pThreadInt;
return VINF_SUCCESS;
}
}
else
return rc;
}
/**
* Gets the native thread id of a IPRT thread.
*
* @returns The native thread id.
* @param Thread The IPRT thread.
*/
{
if (pThread)
{
return NativeThread;
}
return NIL_RTNATIVETHREAD;
}
/**
* Gets the IPRT thread of a native thread.
*
* @returns The IPRT thread handle
* @returns NIL_RTTHREAD if not a thread known to IPRT.
*/
{
if (pThread)
return pThread;
return NIL_RTTHREAD;
}
/**
* Gets the name of the current thread thread.
*
* @returns Pointer to readonly name string.
* @returns NULL on failure.
*/
RTDECL(const char *) RTThreadSelfName(void)
{
if (Thread != NIL_RTTHREAD)
{
if (pThread)
{
return szName;
}
}
return NULL;
}
/**
* Gets the name of a thread.
*
* @returns Pointer to readonly name string.
* @returns NULL on failure.
* @param Thread Thread handle of the thread to query the name of.
*/
{
if (Thread == NIL_RTTHREAD)
return NULL;
if (pThread)
{
return szName;
}
return NULL;
}
/**
* Sets the name of a thread.
*
* @returns iprt status code.
* @param Thread Thread handle of the thread to query the name of.
* @param pszName The thread name.
*/
{
/*
* Validate input.
*/
if (cchName >= RTTHREAD_NAME_LEN)
{
return VERR_INVALID_PARAMETER;
}
if (!pThread)
return VERR_INVALID_HANDLE;
/*
* Update the name.
*/
return VINF_SUCCESS;
}
/**
* Signal the user event.
*
* @returns iprt status code.
*/
{
int rc;
if (pThread)
{
}
else
return rc;
}
/**
* Wait for the user event, resume on interruption.
*
* @returns iprt status code.
* @param Thread The thread to wait for.
* @param cMillies The number of milliseconds to wait. Use RT_INDEFINITE_WAIT for
* an indefinite wait.
*/
{
int rc;
if (pThread)
{
}
else
return rc;
}
/**
* Wait for the user event, return on interruption.
*
* @returns iprt status code.
* @param Thread The thread to wait for.
* @param cMillies The number of milliseconds to wait. Use RT_INDEFINITE_WAIT for
* an indefinite wait.
*/
{
int rc;
if (pThread)
{
}
else
return rc;
}
/**
* Reset the user event.
*
* @returns iprt status code.
* @param Thread The thread to reset.
*/
{
int rc;
if (pThread)
{
}
else
return rc;
}
/**
* Wait for the thread to terminate.
*
* @returns iprt status code.
* @param Thread The thread to wait for.
* @param cMillies The number of milliseconds to wait. Use RT_INDEFINITE_WAIT for
* an indefinite wait.
* @param prc Where to store the return code of the thread. Optional.
* @param fAutoResume Whether or not to resume the wait on VERR_INTERRUPTED.
*/
{
int rc = VERR_INVALID_HANDLE;
if (Thread != NIL_RTTHREAD)
{
if (pThread)
{
{
if (fAutoResume)
else
if (RT_SUCCESS(rc))
{
if (prc)
/*
* If the thread is marked as waitable, we'll do one additional
* release in order to free up the thread structure (see how we
* init cRef in rtThreadAlloc()).
*/
}
}
else
{
}
}
}
return rc;
}
/**
* Wait for the thread to terminate, resume on interruption.
*
* @returns iprt status code.
* Will not return VERR_INTERRUPTED.
* @param Thread The thread to wait for.
* @param cMillies The number of milliseconds to wait. Use RT_INDEFINITE_WAIT for
* an indefinite wait.
* @param prc Where to store the return code of the thread. Optional.
*/
{
return rc;
}
/**
* Wait for the thread to terminate, return on interruption.
*
* @returns iprt status code.
* @param Thread The thread to wait for.
* @param cMillies The number of milliseconds to wait. Use RT_INDEFINITE_WAIT for
* an indefinite wait.
* @param prc Where to store the return code of the thread. Optional.
*/
{
}
/**
* Changes the type of the specified thread.
*
* @returns iprt status code.
* @param Thread The thread which type should be changed.
* @param enmType The new thread type.
*/
{
/*
* Validate input.
*/
int rc;
if ( enmType > RTTHREADTYPE_INVALID
&& enmType < RTTHREADTYPE_END)
{
if (pThread)
{
if (rtThreadIsAlive(pThread))
{
/*
* Do the job.
*/
if (RT_SUCCESS(rc))
if (RT_FAILURE(rc))
}
else
}
else
}
else
{
}
return rc;
}
/**
* Gets the type of the specified thread.
*
* @returns The thread type.
* @returns RTTHREADTYPE_INVALID if the thread handle is invalid.
* @param Thread The thread in question.
*/
{
if (pThread)
{
}
return enmType;
}
#ifdef IN_RING3
/**
* Recalculates scheduling attributes for the the default process
* priority using the specified priority type for the calling thread.
*
* The scheduling attributes are targeted at threads and they are protected
* by the thread read-write semaphore, that's why RTProc is forwarding the
* operation to RTThread.
*
* @returns iprt status code.
*/
{
return rc;
}
/**
* Thread enumerator - sets the priority of one thread.
*
* @returns 0 to continue.
* @returns !0 to stop. In our case a VERR_ code.
* @param pNode The thread node.
* @param pvUser The new priority.
*/
{
if (!rtThreadIsAlive(pThread))
return VINF_SUCCESS;
return VINF_SUCCESS;
return rc;
}
/**
* Attempts to alter the priority of the current process.
*
* The scheduling attributes are targeted at threads and they are protected
* by the thread read-write semaphore, that's why RTProc is forwarding the
* operation to RTThread. This operation also involves updating all thread
* which is much faster done from RTThread.
*
* @returns iprt status code.
* @param enmPriority The new priority.
*/
{
/*
* First validate that we're allowed by the OS to use all the
* scheduling attributes defined by the specified process priority.
*/
if (RT_SUCCESS(rc))
{
/*
* Update the priority of existing thread.
*/
if (RT_SUCCESS(rc))
else
{
/*
* Failed, restore the priority.
*/
}
}
return rc;
}
/**
* Bitch about a deadlock.
*
* @param pThread This thread.
* @param pCur The thread we're deadlocking with.
* @param enmState The sleep state.
* @param u64Block The block data. A pointer or handle.
* @param pszFile Where we are gonna block.
* @param uLine Where we are gonna block.
* @param uId Where we are gonna block.
*/
static void rtThreadDeadlock(PRTTHREADINT pThread, PRTTHREADINT pCur, RTTHREADSTATE enmState, uint64_t u64Block,
{
/*
* Print the threads and locks involved.
*/
unsigned iSeenThread = 0;
{
/*
* Print info on pCur. Determin next while doing so.
*/
AssertMsg2(" #%d: %RTthrd/%RTnthrd %s: %s(%u) %RTptr\n",
{
case RTTHREADSTATE_CRITSECT:
{
{
AssertMsg2("Impossible!!!\n");
break;
}
{
AssertMsg2(" Waiting on CRITSECT %p: Entered %s(%u) %RTptr\n",
}
else
break;
}
default:
break;
}
/*
* Check for cycle.
*/
break;
for (unsigned i = 0; i < ELEMENTS(apSeenThreads); i++)
if (apSeenThreads[i] == pCur)
{
AssertMsg2(" Cycle!\n");
break;
}
/*
* Advance to the next thread.
*/
}
}
/**
* Change the thread state to blocking and do deadlock detection.
*
* This is a RT_STRICT method for debugging locks and detecting deadlocks.
*
* @param pThread This thread.
* @param enmState The sleep state.
* @param u64Block The block data. A pointer or handle.
* @param pszFile Where we are blocking.
* @param uLine Where we are blocking.
* @param uId Where we are blocking.
*/
{
{
/** @todo This has to be serialized! The deadlock detection isn't 100% safe!!! */
/*
* Do deadlock detection.
*
* Since we're missing proper serialization, we don't declare it a
* deadlock until we've got three runs with the same list length.
* While this isn't perfect, it should avoid out the most obvious
* races on SMP boxes.
*/
unsigned cPrevLength = ~0U;
unsigned cEqualRuns = 0;
unsigned iParanoia = 256;
do
{
unsigned cLength = 0;
for (;;)
{
/*
* Get the next thread.
*/
for (;;)
{
{
case RTTHREADSTATE_CRITSECT:
{
continue;
break;
}
default:
break;
}
break;
}
if (!pCur)
return;
/*
* If we've got back to the blocking thread id we've got a deadlock.
* If we've got a chain of more than 256 items, there is some kind of cycle
* in the list, which means that there is already a deadlock somewhere.
*/
break;
cLength++;
}
/* compare with previous list run. */
if (cLength != cPrevLength)
{
cEqualRuns = 0;
}
else
cEqualRuns++;
/*
* Ok, if we ever get here, it's most likely a genuine deadlock.
*/
}
}
/**
* Unblocks a thread.
*
* This function is paired with rtThreadBlocking.
*
* @param pThread The current thread.
* @param enmCurState The current state, used to check for nested blocking.
* The new state will be running.
*/
{
}
#endif /* IN_RING3 */
#ifdef IPRT_WITH_GENERIC_TLS
/**
* Thread enumerator - clears a TLS entry.
*
* @returns 0.
* @param pNode The thread node.
* @param pvUser The TLS index.
*/
{
return 0;
}
/**
* Helper for the generic TLS implementation that clears a given TLS
* entry on all threads.
*
* @param iTls The TLS entry. (valid)
*/
{
RTAvlPVDoWithAll(&g_ThreadTree, true /* fFromLeft*/, rtThreadClearTlsEntryCallback, (void *)(uintptr_t)iTls);
}
#endif /* IPRT_WITH_GENERIC_TLS */