public static uint Update(uint maxInstr, int *pReturnCode) { tThread *pThread; tThread *pPrevThread; uint status; pThread = pAllThreads; // Set the initial thread to the RUNNING state. pThread->state = THREADSTATE_RUNNING; // Set the initial CurrentThread pCurrentThread = pThread; for (;;) { uint minSleepTime = 0xffffffff; int threadExitValue; status = JIT_Execute.Execute(pThread, maxInstr); switch (status) { case Thread.THREAD_STATUS_EXIT: threadExitValue = pThread->threadExitValue; Sys.log_f(1, "Thread ID#%d exited. Return value: %d\n", (int)pThread->threadID, (int)threadExitValue); // Remove the current thread from the running threads list. // Note that this list may have changed since before the call to JitOps.JIT_Execute(). { if (pAllThreads == pThread) { pAllThreads = pAllThreads->pNextThread; } else { tThread *pThread1 = pAllThreads; while (pThread1->pNextThread != pThread) { pThread1 = pThread1->pNextThread; } pThread1->pNextThread = pThread1->pNextThread->pNextThread; } } // Delete the current thread Thread.Delete(pThread); // If there are no more threads left running, then exit application (by returning) // Threads that are unstarted or background do not stop the exit // [Steve edit] Threads that are suspended also do not stop the exit. This is because you'd just // wait forever for them if they did. Note that 'exit' doesn't mean tearing down the process // like in a regular .NET runtime case. The application state is still there and we can make // further calls into it to create new threads. { tThread *pThread2 = pAllThreads; uint canExit = 1; while (pThread2 != null) { if ( ((pThread2->state & THREADSTATE_BACKGROUND) == 0) && ((pThread2->state & (~THREADSTATE_BACKGROUND)) != THREADSTATE_UNSTARTED) && ((pThread2->state & (~THREADSTATE_BACKGROUND)) != THREADSTATE_SUSPENDED)) { canExit = 0; break; } pThread2 = pThread2->pNextThread; } if (canExit != 0) { if (pReturnCode != null) { *pReturnCode = threadExitValue; } return(THREADSTATE_STOPPED); } } pThread = pAllThreads; // This is not really correct, but it'll work for the time being break; case THREAD_STATUS_RUNNING: case THREAD_STATUS_LOCK_EXIT: // Nothing to do break; case THREAD_STATUS_ASYNC: pThread->pAsync->startTime = Sys.msTime(); break; } // Move on to the next thread. // Find the next thread that isn't sleeping or blocked on IO pPrevThread = pThread; for (;;) { pThread = pThread->pNextThread; if (pThread == null) { // That was the thread -- return! return(THREADSTATE_RUNNING); } // Set the CurrentThread correctly pCurrentThread = pThread; if ((pThread->state & (~THREADSTATE_BACKGROUND)) != 0) { // Thread is not running continue; } if (pThread->pAsync != null) { // Discover if whatever is being waited for is finished tAsyncCall *pAsync = pThread->pAsync; if (pAsync->sleepTime >= 0) { // This is a sleep ulong nowTime = Sys.msTime(); int msSleepRemaining = pAsync->sleepTime - (int)(nowTime - pAsync->startTime); if (msSleepRemaining <= 0) { // Sleep is finished break; } // Sleep is not finished, so continue to next thread if ((uint)msSleepRemaining < minSleepTime) { minSleepTime = (uint)msSleepRemaining; } } else { // This is blocking IO, or a lock tMethodState *pMethodState = pThread->pCurrentMethodState; byte * pThis; uint thisOfs; uint unblocked; if (MetaData.METHOD_ISSTATIC(pMethodState->pMethod)) { pThis = null; thisOfs = 0; } else { pThis = *(byte **)pMethodState->pParamsLocals; thisOfs = 4; } unblocked = ((fnInternalCallCheck)H.ToObj(pAsync->checkFn))(null, pThis, pMethodState->pParamsLocals + thisOfs, pMethodState->pEvalStack, pAsync); if (unblocked != 0) { // The IO has unblocked, and the return value is ready. // So delete the async object. // TODO: The async->state object needs to be deleted somehow (maybe) Mem.free(pAsync); // And remove it from the thread pThread->pAsync = null; break; } minSleepTime = 5; } } else { // Thread is ready to run break; } if (pThread == pPrevThread) { // When it gets here, it means that all threads are currently blocked. //printf("All blocked; sleep(%d)\n", minSleepTime); Sys.SleepMS(minSleepTime); } } } }