📄 thread-impl.c
字号:
*/ if (creation_succeeded != 0) { switch(creation_succeeded) { case EAGAIN: DBG( JTHREAD, dprintf( "too many threads (%d)\n", nSysThreads)); break; case EINVAL: DBG( JTHREAD, dprintf( "invalid value for nt.attr\n")); break; case EPERM: DBG( JTHREAD, dprintf( "no permission to set scheduling\n")); break; default: break; } repsem_destroy( &nt->sem); KGC_rmRef(threadCollector, nt); nt->active = 0; activeThreads = nt->next; unprotectThreadList(cur); return NULL; } /* wait until the thread specific data has been set, and the new thread * is in a suspendable state */ repsem_wait( &nt->sem); /* The key is installed. We can now let the signals coming. */ unprotectThreadList(cur); } return (nt);}/*********************************************************************** * thread exit & cleanup *//* * Native thread cleanup. This is just called in case a native thread * is not cached */staticvoid tDispose ( jthread_t nt ){ /* We must lock the GC to prevent any garbage collection in this * function. */ jthread_lockGC(); /* Remove the static reference so the thread context may be freed. */ KGC_rmRef(threadCollector, nt); KaffeVM_unlinkNativeAndJavaThread(); pthread_detach( nt->tid); pthread_mutex_destroy (&nt->suspendLock); repsem_destroy( &nt->sem); /* The context is not freed explictly as it may cause troubles * with the locking system which is invoked by the GC in that case. * The thread context will be automatically freed by the GC in its * thread context. */ jthread_unlockGC();}/** * Function to be called (by threads.c firstStartThread) when the thread leaves * the user thread function. If the calling thread is the main thread then it * suspend the thread until all other threads has exited. */voidjthread_exit ( void ){ jthread_t cur = jthread_current(); jthread_t t; /* * We are leaving the thread user func, which means we are not * subject to GC, anymore (has to be marked here because the first thread * never goes through tRun) */ cur->active = 0; DBG( JTHREAD, TMSG_SHORT( "exit ", cur)); DBG( JTHREAD, dprintf("exit with %d non daemons (%x)\n", nonDaemons, cur->daemon)); if ( !cur->daemon ) { /* the last non daemon should shut down the process */ protectThreadList(cur); if ( --nonDaemons == 0 ) { DBG( JTHREAD, dprintf("exit on last nonDaemon\n")); if (runOnExit != NULL) { unprotectThreadList(cur); runOnExit(); protectThreadList(cur); } /* * be a nice citizen, try to cancel all other threads before we * bail out, to give them a chance to run their cleanup handlers */ for ( t=cache; t != NULL; t = t->next ){ t->status = THREAD_KILL; repsem_post(&t->sem); } t = activeThreads; while (t != NULL) { /* We must not kill the current thread and the main thread */ if ( t != cur && t != firstThread && t->active) { /* Mark the thread as to be killed. */ t->status = THREAD_KILL; /* Send an interrupt event to the remote thread to wake up. * This may not work in any cases. However that way we prevent a * predictable deadlock on some threads implementation. */ jthread_interrupt(t); unprotectThreadList(cur); pthread_join(t->tid, NULL); protectThreadList(cur); t = activeThreads; } else t = t->next; }#if defined(KAFFE_VMDEBUG) if ( deadlockWatchdog ){ pthread_cancel( deadlockWatchdog); }#endif if ( (cur != firstThread) && (firstThread->active == 0) ) { /* if the firstThread has already been frozen, * it's not in the cache list. We must wake it up because * this thread is the last one alive and it is exiting. */ repsem_post (&firstThread->sem); } /* This is not the main thread so we may kill it. */ if (cur != firstThread) { unprotectThreadList(cur); pthread_exit( NULL); /* we shouldn't get here, this is a last safeguard */ KAFFEVM_EXIT(0); } } unprotectThreadList(cur); } /* * We don't cache this one, but we have to remove it from the active list. Note * that the firstThread always has to be the last entry in the activeThreads list * (we just add new entries at the head) */ protectThreadList(cur); if ( cur == firstThread && nonDaemons != 0) { /* if we would be the head, we would have been the last, too (and already exited) */ assert( cur != activeThreads); for ( t=activeThreads; (t != NULL) && (t->next != cur); t=t->next ); assert( t != NULL); t->next = NULL; unprotectThreadList(cur); /* Put the main thread in a frozen state waiting for the other * real threads to terminate. The main thread gets the control back * after that. */ while (repsem_wait( &cur->sem) != 0); } else if (cur != firstThread) { /* flag that we soon will get a new cache entry (would be annoying to * create a new thread in the meantime) */ pendingExits++; unprotectThreadList(cur); } else unprotectThreadList(cur);}/* * Thread is being finalized - free any held resource. */voidjthread_destroy (jthread_t cur){ DBG( JTHREAD, TMSG_SHORT( "finalize ", cur));}void jthread_sleep (jlong timeout) {#ifdef __INTERIX unsigned int seconds = timeout / 1000; useconds_t microseconds = (timeout % 1000) * 1000; if (0 == sleep(seconds)) usleep(microseconds);#else struct timespec ts; ts.tv_sec = timeout / 1000; ts.tv_nsec = (timeout % 1000) * 1000000; nanosleep (&ts, NULL);#endif}/*********************************************************************** * scheduling and query *//* * Change the scheduler priority of a running thread. Since we aren't * called directly, we can assume that 'prio' is within [MIN_PRIORITY..MAX_PRIORITY] */#if defined(SCHEDUL_POLICY)voidjthread_setpriority (jthread_t cur, jint prio){ struct sched_param sp; if ( cur ) { sp.sched_priority = priorities[prio]; DBG( JTHREAD, dprintf("set priority: %p [tid: %4ld, java: %p) to %d (%d)\n", cur, cur->tid, cur->data.jlThread, prio, priorities[prio])) pthread_setschedparam( cur->tid, SCHEDULE_POLICY, &sp); }}#elsevoidjthread_setpriority (jthread_t cur UNUSED, jint prio UNUSED){}#endif/** * yield. * */void jthread_yield (void){#if defined(HAVE_PTHREAD_YIELD) pthread_yield();#elif defined(HAVE_SCHED_YIELD) sched_yield();#endif}/****************************************************************************** * the GC lock */static pthread_mutex_t GClock = PTHREAD_MUTEX_INITIALIZER;void jthread_lockGC(void){ pthread_mutex_lock(&GClock);}void jthread_unlockGC(void){ pthread_mutex_unlock(&GClock);}/******************************************************************************* * the suspend/resume mechanism *//** * This function make the enters in deep suspend mode. It is generally called * suspend_signal_handler() and the locking mechanism when a thread has called * jthread_suspendall(). It temporarily changes the state of the signal mask * for the current thread. * * @param releaseMutex If true, the function is requested to change the signal * mask and to release the suspendLock mutex only after this. In that case we * keep in sync with jthread_suspendall() */void KaffePThread_WaitForResume(int releaseMutex, unsigned int state){ volatile jthread_t cur = jthread_current(); int s; sigset_t oldset; if (releaseMutex) { pthread_sigmask(SIG_BLOCK, &suspendSet, &oldset); pthread_mutex_unlock(&cur->suspendLock); /* * In that particular case we have to release the mutex on the thread list * because it may cause deadlocks in the GC thread. It does not hurt as we * do not access the thread list but we will have to reacquire it before * returning. */ if (cur->blockState & BS_THREAD) pthread_mutex_unlock(&activeThreadsLock); } /* freeze until we get a subsequent sigResume */ while( cur->suspendState == SS_SUSPENDED ) { sigwait( &suspendSet, &s); /* Post something even if it is not the right thread. */ if (cur->suspendState == SS_SUSPENDED) repsem_post(&critSem); } DBG( JTHREADDETAIL, dprintf("sigwait return: %p\n", cur)); /* If the thread needs to be put back in a block state * we must not reset the stack pointer. */ if (state == 0) cur->stackCur = NULL; cur->suspendState = 0; cur->blockState |= state; /* notify the critSect owner we are leaving the handler */ repsem_post( &critSem); if (releaseMutex) { if (cur->blockState & BS_THREAD) pthread_mutex_lock(&activeThreadsLock); pthread_sigmask(SIG_SETMASK, &oldset, NULL); }}/** * The suspend signal handler, which we need to implement critical sections. * It is used for two purposes: (a) to block all active threads which might * get rescheduled during a critical section (we can't rely on priority-fifo * scheduling, it might be SCHED_OTHER), and (b) to get a GC-limit of the current * thread stack so that we don't have to scan the whole thing. Since this * assumes that we execute on the threads normal stack, make sure we have no * SA_ONSTACK / signalstack() in effect */voidsuspend_signal_handler (int sig UNUSED){ volatile jthread_t cur = jthread_current(); DBG( JTHREAD, dprintf("suspend signal handler: %p\n", cur)); /* signals are global things and might come from everywhere, anytime */ if ( !cur || !cur->active ) return; KaffePThread_AckAndWaitForResume(cur, 0);}void KaffePThread_AckAndWaitForResume(volatile jthread_t cur, unsigned int state){ if ( cur->suspendState == SS_PENDING_SUSPEND ){ JTHREAD_JMPBUF env; /* * Note: We're not gonna do a longjmp to this place, we just want * to do something that will save all of the registers onto the stack. */ JTHREAD_SETJMP(env); /* assuming we are executing on the thread stack, we record our current pos */ cur->stackCur = (void*)&env; cur->suspendState = SS_SUSPENDED; cur->blockState &= ~state; /* notify the critSect owner that we are now suspending in the handler */ repsem_post( &critSem); KaffePThread_WaitForResume(false, state); }}/** * The resume signal handler, which we mainly need to get the implicit sigreturn * call (i.e. to unblock a preceeding sigwait). */voidresume_signal_handler ( int sig UNUSED ){ /* we don't do anything, here - all the action is in the suspend handler */}/** * Temporarily suspend all threads but the current one. This is * a dangerous operation, but it is more safe than to rely on * fixed priority scheduling (which most desktop OSes don't provide). * Make sure no suspended thread blocks on a resource (mutexed non-reentrant * lib func) we use within the critical section, or we have a classical deadlock. * Critical section can be nested, but that's usually not a good idea (increases * chances for deadlocks or trojan threads) */voidjthread_suspendall (void){ jthread_t cur = jthread_current(); volatile jthread_t t; if (!jthreadInitialized) return; /* don't allow any new thread to be created or recycled until this is done */ protectThreadList(cur); DBG( JTHREAD, dprintf("enter crit section[%d] from: %p [tid:%4ld, java:%p)\n", critSection, cur, (unsigned long)cur->tid, cur->data.jlThread)); if ( ++critSection == 1 ){#if !defined(KAFFE_BOEHM_GC) int status; int val; int numPending = 0; repsem_getvalue(&critSem, &val); assert(val == 0); for ( t=activeThreads; t; t = t->next ){ /* * make sure we don't suspend ourselves, and we don't expect suspend * signals handled by threads which are blocked on someting else * than the thread lock (which we soon release) */ pthread_mutex_lock(&t->suspendLock); if ( (t != cur) && (t->suspendState == 0) && (t->active != 0) ) { DBG( JTHREAD, dprintf("signal suspend: %p (susp: %d blk: %d)\n", t, t->suspendState, t->blockState)); t->suspendState = SS_PENDING_SUSPEND; if ((t->blockState & (BS_SYSCALL|BS_CV|BS_MUTEX|BS_CV_TO)) != 0) { /* The thread is already stopped. */ assert(t->stackCur != NULL); t->suspendState = SS_SUSPENDED; } else { if ((status = pthread_kill( t->tid, sigSuspend)) != 0) { dprintf("Internal error: error sending SUSPEND signal to %p: %d (%s)\n", t, status, strerror(status)); KAFFEVM_ABORT(); } else { /* BAD: Empirical workaround for lost signals (with accumulative syncing) * It shouldn't be necessary (posix sems are accumulative), and it * is bad, performancewise (at least n*2 context switches per suspend) * but it works more reliably on linux 2.2.x */ numPending++; } } } pthread_mutex_unlock(&t->suspendLock); } /* Now that all signals has been sent we may wait for all concerned * threads to handle them. */ while (numPending > 0) { repsem_wait( &critSem); numPending--; }#else /* * Here, we must use the special Boehm's stop world routine. * However we continue to update our own thread state flag. */ GC_disable();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -