📄 thread-impl.c
字号:
TUNLOCK( cur); /* -------------------------------------------------- tLock */ /* we shouldn't get here, this is a last safeguard */ EXIT(0); } } if ( cur == firstThread ) { /* * 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) */ TLOCK( cur); /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tLock */ /* 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 = 0; TUNLOCK( cur); /* ---------------------------------------------------- tLock */ /* * Put us into a permanent freeze to avoid shut down of the whole process (it's * not clear if this is common pthread behavior or just a implementation * linux-threads "feature") */ sem_wait( &cur->sem); } else { /* flag that we soon will get a new cache entry (would be annoying to * create a new thread in the meantime) */ pendingExits++; }}/* * Thread is being finalized - free any held resource. */voidjthread_destroy (jthread_t cur){ DBG( JTHREAD, TMSG_SHORT( "finalize ", cur))}void jthread_sleep (jlong timeout) { struct timespec time; time.tv_sec = timeout / 1000; time.tv_nsec = (timeout % 1000) * 1000000; nanosleep (&time, NULL);}/*********************************************************************** * 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] */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); }}/******************************************************************************* * the suspend/resume mechanism *//* * 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 ){ jthread_t cur = jthread_current(); DBG( JTHREADDETAIL, dprintf("suspend signal handler: %p\n", cur)) /* signals are global things and might come from everywhere, anytime */ if ( !cur || !cur->active ) return; 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; /* notify the critSect owner that we are now suspending in the handler */ sem_post( &critSem); /* freeze until we get a subsequent SIG_RESUME */ while( cur->suspendState == SS_SUSPENDED ) sigsuspend( &suspendSet); DBG( JTHREADDETAIL, dprintf("sigsuspend return: %p\n", cur)) cur->stackCur = 0; cur->suspendState = 0; /* notify the critSect owner we are leaving the handler */ sem_post( &critSem); }}/* * The resume signal handler, which we mainly need to get the implicit sigreturn * call (i.e. to unblock a preceeding sigsuspend). */voidresume_signal_handler ( int sig ){ /* 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){ int stat; jthread_t cur = jthread_current(); jthread_t t; int iLockRoot; //int nSuspends; /* don't allow any new thread to be created or recycled until this is done */ TLOCK( cur); /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tLock */ DBG( JTHREAD, dprintf("enter crit section[%d] from: %p [tid:%4ld, java:%p)\n", critSection, cur, cur->tid, cur->data.jlThread)) if ( ++critSection == 1 ){ //nSuspends = 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) */ if ( (t != cur) && (t->suspendState == 0) && (t->active) ) { DBG( JTHREADDETAIL, dprintf("signal suspend: %p (susp: %d blk: %d)\n", t, t->suspendState, t->blockState)) t->suspendState = SS_PENDING_SUSPEND; if ( (stat = pthread_kill( t->tid, SIG_SUSPEND)) ){ DBG( JTHREAD, dprintf("error sending SUSPEND signal to %p: %d\n", t, stat)) } else { //nSuspends++; /* 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 */ sem_wait( &critSem); } } }#ifdef NEVER /* wait until all signals we've sent out have been handled */ while ( nSuspends ){ sem_wait( &critSem); nSuspends--; }#endif } TUNLOCK( cur); /* ------------------------------------------------------ tLock */ DBG( JTHREAD, dprintf("critical section (%d) established\n", critSection))}/* * Resume all temporarily suspended threads. Just take action if this * is the outmost exit */voidjthread_unsuspendall (void){ jthread_t cur = jthread_current(); jthread_t t; int stat; int iLockRoot; //int nResumes; if ( !critSection ) return; if ( --critSection == 0 ){ //nResumes = 0; /* No need to sync, there's nobody else running. It's just a matter of * defensive programming (and we use our fast locks) */ TLOCK( cur); /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tLock */ for ( t=activeThreads; t; t = t->next ){ if ( t->suspendState & (SS_PENDING_SUSPEND | SS_SUSPENDED) ){ //nResumes++; DBG( JTHREAD, dprintf("signal resume: %p (sus: %d blk: %d)\n", t, t->suspendState, t->blockState)) t->suspendState = SS_PENDING_RESUME; stat = pthread_kill( t->tid, SIG_RESUME); if ( stat ) { DBG( JTHREAD, dprintf("error sending RESUME signal to %p: %d\n", t, stat)) } /* ack wait workaround, see TentercritSect remarks */ sem_wait( &critSem); } }#ifdef NEVER /* wait until all signals we've sent out have been handled */ while ( nResumes ){ sem_wait( &critSem); nResumes--; }#endif TUNLOCK( cur); /*----------------------------------------------------- tLock */ } DBG( JTHREAD, dprintf("exit crit section (%d)\n", critSection))}/******************************************************************************* * GC related stuff *//* * Walk stacks of all threads, except of the current one (doesn't * make much sense since that's the GC itself, and it's task is to * get rid of garbage, not to pin it down - besides the fact that * its stack is a moving target). * * The call chain looks like this * * startGC * Kaffe_ThreadInterface.GcWalkThreads == TwalkThreads * walkMemory * gcFunctions[..].walk == walkThread * */voidjthread_walkLiveThreads (void(*func)(jthread_t)){ jthread_t t; DBG( JTHREAD, dprintf("start walking threads\n")) for ( t = activeThreads; t != NULL; t = t->next) { func(t); } DBG( JTHREAD, dprintf("end walking threads\n"))}voidjthread_set_blocking (int fd, int blocking){ int r; /* This code has been copied from jthreadedFileDescriptor in unix-jthreads/jthread.c */ if (!blocking) { /* Make non-blocking */ if ((r = fcntl(fd, F_GETFL, 0)) < 0) { perror("F_GETFL"); return; } /* * Apparently, this can fail, for instance when we stdout is * redirected to /dev/null. (On FreeBSD) */ fcntl(fd, F_SETFL, r | O_NONBLOCK #if defined(O_ASYNC) | O_ASYNC#elif defined(FASYNC) | FASYNC#endif ); } else { /* clear nonblocking flag */ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK); }}#if 0/* * Walk the thread's internal context. The stack is actually "walked down" * [base+size..base]. * ? how useful is this for the GC thread itself ? */voidTwalkThread ( Hjava_lang_Thread* thread ){ nativeThread *t; nativeThread *nt = NATIVE_THREAD( thread); void *base; long size; if ( !nt || !nt->active || !nt->stackMin ){ return; } DBG( vm_thread, TMSG_LONG( "walking ", nt)); if ( !nt->suspendState && !nt->blockState ){ t = GET_CURRENT_THREAD(&t); if ( t == nt ){ /* Ok, we do walk the gcMan for now, but just up to this point */ nt->stackCur = &t; } else { /* everything else should be blocked or suspended by now */ DBG( vm_thread, ("walking a running thread %p\n", nt)); //tDump(); //ABORT(); return; } } else if ( nt->suspendState == SS_PENDING_SUSPEND ){ DBG( vm_thread, ("pending suspend, walk whole stack\n")); /* * Assuming the very next thing after a context switch to this thread * would be calling the signal handler, we accept that case. Unfortunately, * we don't have a stackCur, yet */#if defined(STACK_GROWS_UP) nt->stackCur = nt->stackMax;#else nt->stackCur = nt->stackMin;#endif } if ( ((uintp) nt->stackCur < (uintp) nt->stackMin) || (((uintp) nt->stackCur > (uintp) nt->stackMax)) ) { DBG( vm_thread, ("inconsistent stack\n")); tDump(); ABORT(); }#if defined(STACK_GROWS_UP) base = nt->stackMin; size = (unsigned long)nt->stackCur - (unsigned long) base;#else base = nt->stackCur; size = (uintp)nt->stackMax - (uintp) base;#endif walkConservative( base, size);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -