Back to index

lightning-sunbird  0.9+nobinonly
win32_threads.c
Go to the documentation of this file.
00001 #ifdef WIN32_THREADS
00002 
00003 #include "gc_priv.h"
00004 
00005 #define STRICT
00006 #include <windows.h>
00007 
00008 #define MAX_THREADS 64
00009 
00010 struct thread_entry {
00011   LONG in_use;
00012   DWORD id;
00013   HANDLE handle;
00014   void *stack;              /* The cold end of the stack.   */
00015                      /* 0 ==> entry not valid.   */
00016                      /* !in_use ==> stack == 0   */
00017   CONTEXT context;
00018   GC_bool suspended;
00019 };
00020 
00021 volatile GC_bool GC_please_stop = FALSE;
00022 
00023 volatile struct thread_entry thread_table[MAX_THREADS];
00024 
00025 void GC_stop_world()
00026 {
00027   DWORD thread_id = GetCurrentThreadId();
00028   int i;
00029 
00030   GC_please_stop = TRUE;
00031   for (i = 0; i < MAX_THREADS; i++)
00032     if (thread_table[i].stack != 0
00033        && thread_table[i].id != thread_id) {
00034       if (SuspendThread(thread_table[i].handle) == (DWORD)-1)
00035        ABORT("SuspendThread failed");
00036       thread_table[i].suspended = TRUE;
00037     }
00038 }
00039 
00040 void GC_start_world()
00041 {
00042   DWORD thread_id = GetCurrentThreadId();
00043   int i;
00044   for (i = 0; i < MAX_THREADS; i++)
00045     if (thread_table[i].stack != 0 && thread_table[i].suspended
00046        && thread_table[i].id != thread_id) {
00047       if (ResumeThread(thread_table[i].handle) == (DWORD)-1)
00048        ABORT("ResumeThread failed");
00049       thread_table[i].suspended = FALSE;
00050     }
00051   GC_please_stop = FALSE;
00052 }
00053 
00054 ptr_t GC_current_stackbottom()
00055 {
00056   DWORD thread_id = GetCurrentThreadId();
00057   int i;
00058   for (i = 0; i < MAX_THREADS; i++)
00059     if (thread_table[i].stack && thread_table[i].id == thread_id)
00060       return thread_table[i].stack;
00061   ABORT("no thread table entry for current thread");
00062 }
00063 
00064 ptr_t GC_get_lo_stack_addr(ptr_t s)
00065 {
00066     ptr_t bottom;
00067     MEMORY_BASIC_INFORMATION info;
00068     VirtualQuery(s, &info, sizeof(info));
00069     do {
00070        bottom = info.BaseAddress;
00071        VirtualQuery(bottom - 1, &info, sizeof(info));
00072     } while ((info.Protect & PAGE_READWRITE) && !(info.Protect & PAGE_GUARD));
00073     return(bottom);
00074 }
00075 
00076 void GC_push_all_stacks()
00077 {
00078   DWORD thread_id = GetCurrentThreadId();
00079   int i;
00080   for (i = 0; i < MAX_THREADS; i++)
00081     if (thread_table[i].stack) {
00082       ptr_t bottom = GC_get_lo_stack_addr(thread_table[i].stack);
00083       if (thread_table[i].id == thread_id)
00084        GC_push_all(&i, thread_table[i].stack);
00085       else {
00086        thread_table[i].context.ContextFlags
00087                      = (CONTEXT_INTEGER|CONTEXT_CONTROL);
00088        if (!GetThreadContext(thread_table[i].handle,
00089                            &thread_table[i].context))
00090          ABORT("GetThreadContext failed");
00091        if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack
00092            || thread_table[i].context.Esp < (DWORD)bottom)
00093            ABORT("Thread stack pointer out of range");
00094        GC_push_one ((word) thread_table[i].context.Edi);
00095        GC_push_one ((word) thread_table[i].context.Esi);
00096        GC_push_one ((word) thread_table[i].context.Ebx);
00097        GC_push_one ((word) thread_table[i].context.Edx);
00098        GC_push_one ((word) thread_table[i].context.Ecx);
00099        GC_push_one ((word) thread_table[i].context.Eax);
00100        GC_push_all_stack(thread_table[i].context.Esp, thread_table[i].stack);
00101       }
00102     }
00103 }
00104 
00105 void GC_get_next_stack(char *start, char **lo, char **hi)
00106 {
00107     int i;
00108 #   define ADDR_LIMIT (char *)(-1L)
00109     char * current_min = ADDR_LIMIT;
00110 
00111     for (i = 0; i < MAX_THREADS; i++) {
00112        char * s = (char *)thread_table[i].stack;
00113 
00114        if (0 != s && s > start && s < current_min) {
00115            current_min = s;
00116        }
00117     }
00118     *hi = current_min;
00119     if (current_min == ADDR_LIMIT) {
00120        *lo = ADDR_LIMIT;
00121        return;
00122     }
00123     *lo = GC_get_lo_stack_addr(current_min);
00124     if (*lo < start) *lo = start;
00125 }
00126 
00127 LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
00128 
00129 /*
00130  * This isn't generally safe, since DllMain is not premptible.
00131  * If another thread holds the lock while this runs we're in trouble.
00132  * Pontus Rydin suggests wrapping the thread start routine instead.
00133  */
00134 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
00135 {
00136   switch (reason) {
00137   case DLL_PROCESS_ATTACH:
00138     InitializeCriticalSection(&GC_allocate_ml);
00139     GC_init();       /* Force initialization before thread attach.    */
00140     /* fall through */
00141   case DLL_THREAD_ATTACH:
00142     {
00143       int i;
00144       /* It appears to be unsafe to acquire a lock here, since this   */
00145       /* code is apparently not preeemptible on some systems.         */
00146       /* (This is based on complaints, not on Microsoft's official    */
00147       /* documentation, which says this should perform "only simple   */
00148       /* inititalization tasks".)                              */
00149       /* Hence we make do with nonblocking synchronization.           */
00150 
00151       /* The following should be a noop according to the win32 */
00152       /* documentation.  There is empirical evidence that it   */
00153       /* isn't.             - HB                               */
00154 #     ifndef SMALL_CONFIG
00155        if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
00156 #     endif
00157 
00158       for (i = 0; InterlockedExchange(&thread_table[i].in_use,1) != 0; i++) {
00159        /* Compare-and-swap would make this cleaner, but that's not    */
00160        /* supported before Windows 98 and NT 4.0.  In Windows 2000,   */
00161        /* InterlockedExchange is supposed to be replaced by           */
00162        /* InterlockedExchangePointer, but that's not really what I    */
00163        /* want here.                                           */
00164        if (i == MAX_THREADS - 1)
00165          ABORT("too many threads");
00166       }
00167       thread_table[i].id = GetCurrentThreadId();
00168       if (!DuplicateHandle(GetCurrentProcess(),
00169                           GetCurrentThread(),
00170                         GetCurrentProcess(),
00171                         &thread_table[i].handle,
00172                         0,
00173                         0,
00174                         DUPLICATE_SAME_ACCESS)) {
00175            DWORD last_error = GetLastError();
00176            GC_printf1("Last error code: %lx\n", last_error);
00177            ABORT("DuplicateHandle failed");
00178       }
00179       thread_table[i].stack = GC_get_stack_base();
00180       /* If this thread is being created while we are trying to stop  */
00181       /* the world, wait here.  Hopefully this can't happen on any    */
00182       /* systems that don't allow us to block here.                   */
00183       while (GC_please_stop) Sleep(20);
00184     }
00185     break;
00186   case DLL_PROCESS_DETACH:
00187   case DLL_THREAD_DETACH:
00188     {
00189       int i;
00190       DWORD thread_id = GetCurrentThreadId();
00191       LOCK();
00192       for (i = 0;
00193            thread_table[i].stack == 0 || thread_table[i].id != thread_id;
00194           i++) {
00195        if (i == MAX_THREADS - 1)
00196          ABORT("thread not found on detach");
00197       }
00198       thread_table[i].stack = 0;
00199       thread_table[i].in_use = FALSE;
00200       CloseHandle(thread_table[i].handle);
00201       BZERO(&thread_table[i].context, sizeof(CONTEXT));
00202       UNLOCK();
00203     }
00204     break;
00205   }
00206   return TRUE;
00207 }
00208 
00209 #endif /* WIN32_THREADS */