📄 thread-impl.c
字号:
/* * thread-impl.c - pthread based ThreadInterface implementation * * Copyright (c) 1998 * Transvirtual Technologies, Inc. All rights reserved. * * Copyright (c) 2005, 2006 * Kaffe.org contributors. See ChangeLog for details. * All rights reserved. * * See the file "license.terms" for information on usage and redistribution * of this file. */#include "lerrno.h"#include <limits.h>#include "config.h"#include "config-std.h"#include "config-signal.h"#include "config-setjmp.h"#include "config-io.h"#ifdef HAVE_STRING_H#include <string.h>#endif#ifdef HAVE_SYS_TYPES_H#include <sys/types.h>#endif#ifdef HAVE_SYS_TIME_H#include <sys/time.h>#endif#ifdef HAVE_SYS_RESOURCE_H#include <sys/resource.h>#endif#include "locks.h"#include "thread-impl.h"#include "debug.h"#include "md.h"#include "gc.h"#include "thread.h"#ifdef KAFFE_BOEHM_GC#include <gc/gc.h>#endif/* define _GNU_SOURCE for pthread_yield on linux */#ifndef _GNU_SOURCE#define _GNU_SOURCE#endif#include <pthread.h>#if !defined(HAVE_PTHREAD_YIELD) && defined(HAVE_SCHED_YIELD)#if defined(HAVE_SCHED_H)#include <sched.h>#endif // SCHED_H#endif // SCHED_YIELD && !PTHREAD_YIELD#ifndef MAINSTACKSIZE#define MAINSTACKSIZE (1024*1024)#endif#if defined(KAFFE_VMDEBUG)static char stat_act[] = { ' ', 'a' };static char stat_susp[] = { ' ', 's', ' ', 'r', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };static char stat_block[] = { ' ', 'T', 'm', ' ', 'c', ' ', ' ', ' ', 't', ' ', ' ' };#define TMSG_SHORT(_msg,_nt) \ dprintf(_msg" %p [java:%p]\n", \ _nt, _nt->data.jlThread)#define TMSG_LONG(_msg,_nt) \ dprintf(_msg" %p [java:%p], stack [%p..%p..%p], state: %c%c%c\n", \ _nt, _nt->data.jlThread, _nt->stackMin, _nt->stackCur, _nt->stackMax, \ stat_act[_nt->active], stat_susp[_nt->suspendState], stat_block[_nt->blockState])#define CHECK_CURRENT_THREAD(_nt) \ if ( ((uintp) &_nt < (uintp) _nt->stackMin) || \ ((uintp) &_nt > (uintp) _nt->stackMax) ) { \ printf( "?? inconsistent current thread: %x [java: %x]\n", \ _nt, _nt->data.jlThread); \ tDump(); \ }#endif /* KAFFE_VMDEBUG *//*********************************************************************** * typedefs & defines *//* * This is the configurable section. Note that SCHED_FIFO is the only * schedule policy which conforms to the "old" Java thread model (with * stringent priorities), but it usually isn't available on desktop * OSes (or imposes certain restrictions, e.g. root privileges). */#if defined(HAVE_SCHED_OTHER_IN_SCHED) && !(defined(__FreeBSD_kernel__) && defined(__GLIBC__))#define SCHEDULE_POLICY SCHED_OTHER#else#undef SCHEDULE_POLICY#endif/* our upper limit for cached threads (0 = no caching at all) */#define MAX_CACHED_THREADS 0/* * Flag to say whether the thread subsystem has been initialized. */static char jthreadInitialized = false;/* * Signal to use to suspend all threads. */static int sigSuspend;/* * Signal to use to resume all threads. */static int sigResume;/* * Signal to use to dump thread internal state. */static int sigDump;/* * Signal to use to produce an interrupt. */static int sigInterrupt;/* * Signals used by the thread system to restard/cancel threads. */static int psigRestart;static int psigCancel;/** * This variable holds a pointer to the garbage collector which * will be used to allocate thread data. */static Collector *threadCollector;/*********************************************************************** * global data *//** We keep a list of all active threads, so that we can enumerate them */static jthread_t activeThreads;/** This mutex lock prevents somebody to modify or read the active thread * concurrently with some other threads. This prevents some bug that may appear * when a thread die, is created or is being walked. */static pthread_mutex_t activeThreadsLock = PTHREAD_MUTEX_INITIALIZER;/** This mutex lock protects calls into non-reentrant system services. */static pthread_mutex_t systemMutex = PTHREAD_MUTEX_INITIALIZER;/** We don't throw away threads when their user func terminates, but suspend * and cache them for later re-use */static jthread_t cache;/** The notorious first thread, which has to be handled differently because * it isn't created explicitly */static jthread_t firstThread;/** Number of active non-daemon threads (the last terminating nonDaemon * causes the process to shut down */static int nonDaemons;/** Number of system threads (either running (activeThreads) or * blocked (cache). We need this to implement our own barrier, since * many kernel thread systems don't behave graceful on exceeding their limit */static int nSysThreads;/** number of currently cached threads */static int nCached;/** map the Java priority levels to whatever the pthreads impl gives us */static int *priorities;/** thread-specific-data key to retrieve 'nativeData' */pthread_key_t ntKey;/** a hint to avoid unnecessary pthread_creates (with pending exits) */static volatile int pendingExits;/** level of critical sections (0 = none) */static int critSection;/** helper semaphore to signal completion of critical section enter/exit */static repsem_t critSem;/** Signal set which contains important signals for suspending threads. */static sigset_t suspendSet;/** This callback is to be called when a thread exits. */static void (*threadDestructor)(void *);/** This callback is called when all non-daemon threads exit. */static void (*runOnExit)(void);#ifdef KAFFE_VMDEBUG/** an optional deadlock watchdog thread (not in the activeThread list), * activated by KAFFE_VMDEBUG topic JTHREAD */static pthread_t deadlockWatchdog;/** * This is a debugging variable to analyze possible deadlock when dumping thread states. * It retains a pointer to the thread holding the thread list lock. */static jthread_t threadListOwner;#endif /* KAFFE_VMDEBUG */static void suspend_signal_handler ( int sig );static void resume_signal_handler ( int sig );static void tDispose ( jthread_t nt );static void *thread_malloc(size_t bytes){ return KGC_malloc(threadCollector, bytes, KGC_ALLOC_THREADCTX);}static inline voidprotectThreadList(jthread_t cur){ cur->blockState |= BS_THREAD; jmutex_lock(&activeThreadsLock);#ifdef KAFFE_VMDEBUG threadListOwner = cur;#endif}static inline voidunprotectThreadList(jthread_t cur){#ifdef KAFFE_VMDEBUG threadListOwner = NULL;#endif jmutex_unlock(&activeThreadsLock); cur->blockState &= ~BS_THREAD;}/*********************************************************************** * internal functions */#if defined(KAFFE_VMDEBUG)/* * dump a thread list, marking the supposed to be current thread */static voidtDumpList ( jthread_t cur, jthread_t list ){ int i; char a1, a2, a3; jthread_t t; for ( t=list, i=0; t; t=t->next, i++ ){ /* the supposed to be current thread? */ a1 = (t == cur) ? '*' : ' '; /* the current thread from a stack point-of view? */ a2 = (((uintp)&i > (uintp)t->stackMin) && ((uintp)&i < (uintp)t->stackMax)) ? 'S' : ' '; /* the first one? */ a3 = (t == firstThread) ? '1' : ' '; dprintf("%4d: %c%c%c %c%c%c %p [tid: %4ld, java: %p] " "stack: [%p..%p..%p]\n", i, a1, a2, a3, stat_act[t->active], stat_susp[t->suspendState], stat_block[t->blockState], t, (long)t->tid, t->data.jlThread, t->stackMin, t->stackCur, t->stackMax); }}#endif /* defined(KAFFE_VMDEBUG) *//* * dump the state of the threading system */static voidtDump (void){ DBG(JTHREAD, { jthread_t cur = jthread_current(); dprintf("\n======================== thread dump =========================\n"); dprintf("thread list lock owner: %p\n", threadListOwner); protectThreadList(cur); dprintf("state: nonDaemons: %d, critSection: %d\n", nonDaemons, critSection); dprintf("active threads:\n"); tDumpList( cur, activeThreads); dprintf("\ncached threads:\n"); tDumpList( cur, cache); unprotectThreadList(cur); dprintf("====================== end thread dump =======================\n"); });}/* * On demand debug signal to dump the current thread state(s) (requested * by a external "kill -s <sigDump> <proc-id>" */staticvoiddump_signal_handler (int sig UNUSED){ tDump();}#ifdef KAFFE_VMDEBUGstaticvoid* tWatchdogRun (void* p UNUSED){ jthread_t t; int life; while ( nonDaemons ) { life = 0; for ( t=activeThreads; t != NULL; t = t->next ){ /* * if we have a single thread that is not blocked at all, is in a * timeout wait, and is not suspended, we are still safe (even though * the timeout value might effectively be a deadlock) */ if ( (!t->blockState || (t->blockState == BS_SYSCALL) || (t->blockState == BS_CV_TO)) && !t->suspendState ){ life = 1; break; } } if ( !life ) { DBG( JTHREAD, dprintf("deadlock\n")); tDump(); KAFFEVM_ABORT(); } usleep( 5000); } return NULL;}staticvoid tStartDeadlockWatchdog (void){ pthread_attr_t attr;#if defined(SCHEDULE_POLICY) struct sched_param sp; sp.sched_priority = priorities[0]; /* looow */#endif pthread_attr_init( &attr);#if defined(SCHEDULE_POLICY) pthread_attr_setschedparam( &attr, &sp);#endif pthread_attr_setstacksize( &attr, 4096); pthread_create( &deadlockWatchdog, &attr, tWatchdogRun, NULL);}#endif /* KAFFE_VMDEBUG *//*********************************************************************** * thread system initialization *//** * Static initialisation of signal handlers. This function is called once. */static voidtInitSignalHandlers (void){ struct sigaction saSuspend, saResume, saInterrupt, saDump; unsigned int flags = 0;#if defined(SA_RESTART) flags |= SA_RESTART;#endif saSuspend.sa_flags = flags; saSuspend.sa_handler = suspend_signal_handler; sigemptyset( &saSuspend.sa_mask); sigaddset( &saSuspend.sa_mask, sigSuspend); sigaddset( &saSuspend.sa_mask, sigResume); if (psigRestart > 0) sigaddset( &saSuspend.sa_mask, psigRestart); if (psigCancel > 0) sigaddset( &saSuspend.sa_mask, psigCancel); sigaddset( &saSuspend.sa_mask, SIGSTOP); sigaddset( &saSuspend.sa_mask, SIGCONT); sigaddset( &saSuspend.sa_mask, SIGWINCH);#ifndef KAFFE_BOEHM_GC sigaction( sigSuspend, &saSuspend, NULL);#endif saResume.sa_flags = 0; /* Note that we do not want restart here. */ saResume.sa_handler = resume_signal_handler; saResume.sa_mask = saSuspend.sa_mask;#if !defined(KAFFE_BOEHM_GC) && !defined(KAFFE_BUGGY_NETBSD_SIGWAIT) sigaction( sigResume, &saResume, NULL);#endif saInterrupt.sa_flags = flags; saInterrupt.sa_handler = SIG_IGN; sigemptyset(&saInterrupt.sa_mask); sigaction( sigInterrupt, &saInterrupt, NULL); saDump.sa_flags = flags; saDump.sa_handler = dump_signal_handler; sigemptyset( &saDump.sa_mask); sigaction( sigDump, &saDump, NULL);}/** * Initialize signal numbers to use depending of realtime signal availabilities. * * ORIGINAL NOTE: * * Now it starts to get hackish - we have to pick some signals * for suspend/resume (enter/exitCritSect) which don't interfere * with pthread implementations. Note that we can't rely on when * a suspend signal is delivered, and it's therefore not safe * to mulitplex a sinle signal for both suspend & resume purposes */static voidtInitSignals(void){#if !defined(SIGRTMIN)#define SIGRTMIN -1#define SIGRTMAX -1#endif if (SIGRTMAX - SIGRTMIN < 7) {#if defined(OLD_LINUXTHREADS) && !defined(__CYGWIN__) sigSuspend = SIGURG; sigResume = SIGTSTP; sigDump = SIGXCPU;/* * Sneak these signal in from the thread library. */ psigRestart = SIGUSR1; psigCancel = SIGUSR2;#else // OLD_LINUXTHREADS sigSuspend = SIGUSR1; sigResume = SIGUSR2; sigDump = SIGURG;// PSIG_RESTART and PSIG_CANCEL are left undefined. psigRestart = -1; psigCancel = -1;#endif sigInterrupt = SIGCONT; } else { sigSuspend = SIGRTMIN+6; sigResume = SIGRTMIN+5; sigDump = SIGURG;/* * Sneak these signal in from the thread library. */ psigRestart = SIGRTMIN;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -