📄 thread-impl.c
字号:
int maxpr, int minpr, void *(*_allocator)(size_t), void (*_deallocator)(void*), void *(*_reallocator)(void*,size_t), void (*_destructor1)(void*), void (*_onstop)(void), void (*_ondeadlock)(void)){ DBG(JTHREAD, dprintf("initialized\n")) thread_malloc = _allocator; thread_free = _deallocator; pthread_key_create( &ntKey, NULL); sem_init( &critSem, 0, 0); priorities = (int *)_allocator ((maxpr+1) * sizeof(int)); tMapPriorities(maxpr+1); tInitSignalHandlers(); sigfillset( &suspendSet); sigdelset( &suspendSet, SIG_RESUME); tSetupFirstNative(); DBG( JTHREAD, tStartDeadlockWatchdog() )}jthread_tjthread_createfirst(size_t mainThreadStackSize, unsigned char pri, void* jlThread){ jthread_t nt; int oldCancelType; nt = jthread_current(); /* we can't use nt->attr, because it wasn't used to create this thread */ pthread_attr_init( &nt->attr); nt->tid = pthread_self(); nt->data.jlThread = jlThread; nt->suspendState = 0; nt->active = 1; nt->func = NULL; nt->next = NULL; nt->daemon = false; /* Get stack boundaries. Note that this is just an approximation * which should cover all gc-relevant stack locations */#if defined(STACK_GROWS_UP) nt->stackMin = (void*) (uintp)(&nt - 0x100); nt->stackMax = (void*) ((uintp) nt->stackMin + mainThreadStackSize);#else nt->stackMax = (void*) (uintp)(&nt + 0x100); nt->stackMin = (void*) ((uintp) nt->stackMax - mainThreadStackSize);#endif DBG( JTHREAD, TMSG_SHORT( "create first ", nt)) /* init our cv and mux fields for locking */ tInitLock( nt); /* Link native and Java thread objects * We already are executing in the right thread, so we can set the specific * data straight away */ pthread_setspecific( ntKey, nt); pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, &oldCancelType); /* if we aren't the first one, we are in trouble */ assert( activeThreads == 0); activeThreads = firstThread = nt; nonDaemons=1; nSysThreads=1; return (nt);}void jthread_interrupt(jthread_t tid){ /* We need to send some signal to interrupt syscalls. */ pthread_kill(tid->tid, SIG_RESUME);}bool jthread_attach_current_thread (bool daemon){ jthread_t nt; /* create the jthread* thingy */ nt = thread_malloc( sizeof(struct _jthread) ); nt->func = 0; nt->suspendState = 0; nt->stackMin = (void *)((uintp)&nt - 0x8000); nt->stackMax = (void *)((uintp)&nt + 0x400); nt->stackCur = 0; nt->daemon = daemon; /* link everything together */ nt->tid = pthread_self(); pthread_setspecific( ntKey, nt); /* and done */ return true;}/* * This is our thread function wrapper, which we need because of two * reasons. First, there is no way to set thread specific data from * outside the current thread (and it should be propperly set before we * enter the *real* thread func). Second, this is a better place to handle * the recycle looping than Texit (would be strange to loop in there) */staticvoid* tRun ( void* p ){ jthread_t cur = (jthread_t)p; jthread_t t; size_t ss; int oldCancelType; int iLockRoot; /* get the stack boundaries */ pthread_attr_getstacksize( &cur->attr, &ss);#if defined(STACK_GROWS_UP) cur->stackMin = &cur; cur->stackMax = (void*) ((unsigned long)cur->stackMin + ss);#else cur->stackMax = &cur; cur->stackMin = (void*) ((unsigned long)cur->stackMax - ss);#endif pthread_setspecific( ntKey, cur); pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, &oldCancelType); cur->tid = pthread_self(); /* we are reasonably operational now, flag our creator that it's safe to give * up the thread lock */ sem_post( &cur->sem); while ( 1 ) { DBG( JTHREAD, TMSG_LONG( "calling user func of: ", cur)) /* Now call our thread function, which happens to be firstStartThread(), * which will call TExit before it returns */ cur->func(cur->data.jlThread); DBG( JTHREAD, TMSG_LONG( "exiting user func of: ", cur)) TLOCK( cur); /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tLock */ /* remove from active list */ if ( cur == activeThreads ){ activeThreads = cur->next; } else { for ( t=activeThreads; t->next && (t->next != cur); t=t->next ); assert( t->next != 0 ); t->next = cur->next; } /* unlink Java and native thread */ cur->data.jlThread = 0; cur->suspendState = 0; /* link into cache list (if still within limit) */ if ( ++nCached < MAX_CACHED_THREADS ){ cur->next = cache; cache = cur; DBG( JTHREAD, TMSG_SHORT( "cached thread ", cur)) } pendingExits--; TUNLOCK( cur); /* ---------------------------------------------------- tLock */ if ( nCached >= MAX_CACHED_THREADS ){ break; } /* Wait until we get re-used (by TcreateThread). No need to update the * blockState, since we aren't active anymore */ sem_wait( &cur->sem); /* * we have already been moved back to the activeThreads list by * Tcreate (saves us a lock) */ DBG( JTHREAD, TMSG_SHORT( "reused thread ", cur)) } tDispose( cur); return 0;}/* * Create a new native thread for a given Java Thread object. Note * that we are called from Thread.start(), i.e. we are already allowed to * call func() (which happens to be threads.c:firstStartThread) * * Our call graph looks like this: * * Thread.start * startThread * Kaffe_ThreadInterface.create (firstStartThread) == Tcreate * pthread_create(..tRun..) * creator-thread * -------------------------------------------------------------- * created-thread * tRun * firstStartThread * Thread.run * exitThread * Thread.finish * Kaffe_ThreadInterface.exit == Texit */jthread_tjthread_create ( unsigned char pri, void* func, int daemon, void* jlThread, size_t threadStackSize ){ jthread_t cur = jthread_current(); jthread_t nt; struct sched_param sp; int iLockRoot; /* if we are the first one, it's seriously broken */ assert( activeThreads != 0 ); /* * This is a safeguard to avoid creating too many new threads * because of a high Tcreate call frequency from a high priority * thread (which doesn't give exiters a chance to aquire the lock * to update the cache list). */ if ( cache == 0 ) { while ( pendingExits && (cache == 0) ) sched_yield(); } sp.sched_priority = priorities[pri]; if ( !daemon ) nonDaemons++; if ( cache ) { TLOCK( cur); /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tLock */ /* move thread from the cache to the active list */ nt = cache; cache = cache->next; nCached--; nt->next = activeThreads; activeThreads = nt; nt->data.jlThread = jlThread; nt->daemon = daemon; nt->func = func; nt->stackCur = 0; pthread_setschedparam( nt->tid, SCHEDULE_POLICY, &sp); DBG( JTHREAD, TMSG_SHORT( "create recycled ", nt)) /* resurrect it */ nt->active = 1; sem_post( &nt->sem); TUNLOCK( cur); /* ---------------------------------------------------- tLock */ } else { if ( nSysThreads++ > MAX_SYS_THREADS ){ // bail out, we exceeded our physical thread limit DBG( JTHREAD, dprintf( "too many threads (%d)\n", nSysThreads)) return (0); } nt = thread_malloc( sizeof(struct _jthread) ); pthread_attr_init( &nt->attr); pthread_attr_setschedparam( &nt->attr, &sp); pthread_attr_setschedpolicy( &nt->attr, SCHEDULE_POLICY); pthread_attr_setstacksize( &nt->attr, threadStackSize); nt->data.jlThread = jlThread; nt->func = func; nt->suspendState = 0; nt->stackMin = 0; nt->stackMax = 0; nt->stackCur = 0; nt->daemon = daemon; DBG( JTHREAD, TMSG_SHORT( "create new ", nt)) /* init our cv and mux fields for locking */ tInitLock( nt); /* Link the new one into the activeThreads list. We lock until * the newly created thread is set up correctly (i.e. is walkable) */ TLOCK( cur); /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tLock */ nt->active = 1; nt->next = activeThreads; activeThreads = nt; /* Note that we don't directly start 'func' because we (a) still need to * set the thread specifics, and (b) we need a looper for our thread * recycling. We create the new thread while still holding the lock, because * we otherwise might have a invalid tid in the activeList. The new thread * in turn doesn't need the lock until it exits */ pthread_create( &nt->tid, &nt->attr, tRun, nt); /* wait until the thread specific data has been set, and the new thread * is in a suspendable state */ sem_wait( &nt->sem); TUNLOCK( cur); /* ---------------------------------------------------- tLock */ } 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 ){ pthread_detach( nt->tid); sem_destroy( &nt->sem); thread_free( nt);}/* * Function to be called (by threads.c firstStartThread) when the thread leaves * the user thread function */voidjthread_exit ( void ){ jthread_t cur = jthread_current(); jthread_t t; int iLockRoot; /* * 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 */ if ( --nonDaemons == 0 ) { TLOCK( cur); /* ++++++++++++++++++++++++++++++++++++++++++++++++++++ tLock */ DBG( JTHREAD, dprintf("exit on last nonDaemon\n")) /* * 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 ){ pthread_cancel( t->tid); } for ( t=activeThreads; t != NULL; t = t->next ){ if ( t != cur ) { pthread_cancel( t->tid); } }#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 */ pthread_cancel( firstThread->tid); } pthread_exit( 0); /* pretty useless, but clean */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -