[BACK]Return to win32_threads.c CVS log [TXT][DIR] Up to [local] / OpenXM_contrib2 / asir2000 / gc

Diff for /OpenXM_contrib2/asir2000/gc/win32_threads.c between version 1.3 and 1.5

version 1.3, 2001/04/20 07:39:20 version 1.5, 2002/07/24 08:00:12
Line 1 
Line 1 
 #if defined(GC_WIN32_THREADS) || defined(WIN32_THREADS)  #if defined(GC_WIN32_THREADS)
   
 #include "private/gc_priv.h"  #include "private/gc_priv.h"
   
   #ifdef CYGWIN32
   # include <errno.h>
   
    /* Cygwin-specific forward decls */
   # undef pthread_create
   # undef pthread_sigmask
   # undef pthread_join
   # undef dlopen
   
   # define DEBUG_CYGWIN_THREADS 0
   
     GC_bool GC_thr_initialized = FALSE;
     void * GC_start_routine(void * arg);
     void GC_thread_exit_proc(void *arg);
   
   #endif
   
   
 #if 0  #if 0
 #define STRICT  #define STRICT
 #include <windows.h>  #include <windows.h>
Line 18  struct thread_entry {
Line 36  struct thread_entry {
                         /* !in_use ==> stack == 0       */                          /* !in_use ==> stack == 0       */
   CONTEXT context;    CONTEXT context;
   GC_bool suspended;    GC_bool suspended;
   
   # ifdef CYGWIN32
       void *status; /* hold exit value until join in case it's a pointer */
       pthread_t pthread_id;
   # endif
   
 };  };
   
 volatile GC_bool GC_please_stop = FALSE;  volatile GC_bool GC_please_stop = FALSE;
Line 29  void GC_push_thread_structures GC_PROTO((void))
Line 53  void GC_push_thread_structures GC_PROTO((void))
     /* Unlike the other threads implementations, the thread table here  */      /* Unlike the other threads implementations, the thread table here  */
     /* contains no pointers to the collectable heap.  Thus we have      */      /* contains no pointers to the collectable heap.  Thus we have      */
     /* no private structures we need to preserve.                       */      /* no private structures we need to preserve.                       */
   # ifdef CYGWIN32
     { int i; /* pthreads may keep a pointer in the thread exit value */
       for (i = 0; i < MAX_THREADS; i++)
         if (thread_table[i].in_use) GC_push_all((ptr_t)&(thread_table[i].status),(ptr_t)(&(thread_table[i].status)+1));
     }
   #endif
 }  }
   
 void GC_stop_world()  void GC_stop_world()
Line 36  void GC_stop_world()
Line 66  void GC_stop_world()
   DWORD thread_id = GetCurrentThreadId();    DWORD thread_id = GetCurrentThreadId();
   int i;    int i;
   
   #ifdef CYGWIN32
     if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()");
   #endif
   
   GC_please_stop = TRUE;    GC_please_stop = TRUE;
   for (i = 0; i < MAX_THREADS; i++)    for (i = 0; i < MAX_THREADS; i++)
     if (thread_table[i].stack != 0      if (thread_table[i].stack != 0
Line 53  void GC_stop_world()
Line 87  void GC_stop_world()
         DWORD exitCode;          DWORD exitCode;
         if (GetExitCodeThread(thread_table[i].handle,&exitCode) &&          if (GetExitCodeThread(thread_table[i].handle,&exitCode) &&
             exitCode != STILL_ACTIVE) {              exitCode != STILL_ACTIVE) {
             thread_table[i].stack = 0;              thread_table[i].stack = 0; /* prevent stack from being pushed */
   #       ifndef CYGWIN32
             /* this breaks pthread_join on Cygwin, which is guaranteed to only see user pthreads */
             thread_table[i].in_use = FALSE;              thread_table[i].in_use = FALSE;
             CloseHandle(thread_table[i].handle);              CloseHandle(thread_table[i].handle);
             BZERO((void *)(&thread_table[i].context), sizeof(CONTEXT));              BZERO((void *)(&thread_table[i].context), sizeof(CONTEXT));
   #endif
             continue;              continue;
         }          }
         if (SuspendThread(thread_table[i].handle) == (DWORD)-1)          if (SuspendThread(thread_table[i].handle) == (DWORD)-1)
Line 134  void GC_push_all_stacks()
Line 171  void GC_push_all_stacks()
                                 (LPCONTEXT)&thread_table[i].context))                                  (LPCONTEXT)&thread_table[i].context))
           ABORT("GetThreadContext failed");            ABORT("GetThreadContext failed");
 #       ifdef I386  #       ifdef I386
           if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack  
               || thread_table[i].context.Esp < (DWORD)bottom)  
               ABORT("Thread stack pointer out of range");  
           GC_push_one ((word) thread_table[i].context.Edi);            GC_push_one ((word) thread_table[i].context.Edi);
           GC_push_one ((word) thread_table[i].context.Esi);            GC_push_one ((word) thread_table[i].context.Esi);
           GC_push_one ((word) thread_table[i].context.Ebp);            GC_push_one ((word) thread_table[i].context.Ebp);
Line 144  void GC_push_all_stacks()
Line 178  void GC_push_all_stacks()
           GC_push_one ((word) thread_table[i].context.Edx);            GC_push_one ((word) thread_table[i].context.Edx);
           GC_push_one ((word) thread_table[i].context.Ecx);            GC_push_one ((word) thread_table[i].context.Ecx);
           GC_push_one ((word) thread_table[i].context.Eax);            GC_push_one ((word) thread_table[i].context.Eax);
           GC_push_all_stack((char *) thread_table[i].context.Esp,            if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack
                             thread_table[i].stack);                || thread_table[i].context.Esp < (DWORD)bottom) {
                 WARN("Thread stack pointer 0x%lx out of range, pushing everything",
                      thread_table[i].context.Esp);
                 GC_push_all_stack((char *) bottom, thread_table[i].stack);
             } else {
                 GC_push_all_stack((char *) thread_table[i].context.Esp,
                                   thread_table[i].stack);
             }
 #       else  #       else
 #       ifdef ARM32  #       ifdef ARM32
           if (thread_table[i].context.Sp >= (DWORD)thread_table[i].stack            if (thread_table[i].context.Sp >= (DWORD)thread_table[i].stack
Line 331  void GC_get_next_stack(char *start, char **lo, char **
Line 372  void GC_get_next_stack(char *start, char **lo, char **
     if (*lo < start) *lo = start;      if (*lo < start) *lo = start;
 }  }
   
   #if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))
   
 # ifdef MSWINCE  HANDLE WINAPI GC_CreateThread(
       LPSECURITY_ATTRIBUTES lpThreadAttributes,
       DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
       LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
   {
       return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,
                           lpParameter, dwCreationFlags, lpThreadId);
   }
   
   #else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))  */
   
 typedef struct {  typedef struct {
     HANDLE child_ready_h, parent_ready_h;      HANDLE child_ready_h, parent_ready_h;
     volatile struct thread_entry * entry;      volatile struct thread_entry * entry;
Line 437  static DWORD WINAPI thread_start(LPVOID arg)
Line 488  static DWORD WINAPI thread_start(LPVOID arg)
     /* Clear the thread entry even if we exit with an exception.        */      /* Clear the thread entry even if we exit with an exception.        */
     /* This is probably pointless, since an uncaught exception is       */      /* This is probably pointless, since an uncaught exception is       */
     /* supposed to result in the process being killed.                  */      /* supposed to result in the process being killed.                  */
   #ifndef __GNUC__
     __try {      __try {
   #endif /* __GNUC__ */
         ret = args.start (args.param);          ret = args.start (args.param);
   #ifndef __GNUC__
     } __finally {      } __finally {
   #endif /* __GNUC__ */
         LOCK();          LOCK();
         args.entry->stack = 0;          args.entry->stack = 0;
         args.entry->in_use = FALSE;          args.entry->in_use = FALSE;
               /* cast away volatile qualifier */                /* cast away volatile qualifier */
         BZERO((void *) &args.entry->context, sizeof(CONTEXT));          BZERO((void *) &args.entry->context, sizeof(CONTEXT));
         UNLOCK();          UNLOCK();
   #ifndef __GNUC__
     }      }
   #endif /* __GNUC__ */
   
     return ret;      return ret;
 }  }
   #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))  */
   
   #ifdef MSWINCE
   
 typedef struct {  typedef struct {
     HINSTANCE hInstance;      HINSTANCE hInstance;
     HINSTANCE hPrevInstance;      HINSTANCE hPrevInstance;
Line 504  DWORD WINAPI main_thread_start(LPVOID arg)
Line 564  DWORD WINAPI main_thread_start(LPVOID arg)
   
 LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);  LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
   
   /* threadAttach/threadDetach routines used by both CYGWIN and DLL implementation,
      since both recieve explicit notification on thread creation/destruction
    */
   void threadAttach() {
     int i;
     /* It appears to be unsafe to acquire a lock here, since this */
     /* code is apparently not preeemptible on some systems.       */
     /* (This is based on complaints, not on Microsoft's official  */
     /* documentation, which says this should perform "only simple */
     /* inititalization tasks".)                                   */
     /* Hence we make do with nonblocking synchronization.         */
   
     /* The following should be a noop according to the win32      */
     /* documentation.  There is empirical evidence that it        */
     /* isn't.             - HB                                    */
   # if defined(MPROTECT_VDB)
      if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
   # endif
                   /* cast away volatile qualifier */
     for (i = 0; InterlockedExchange((LONG*)&thread_table[i].in_use,1) != 0; i++) {
       /* Compare-and-swap would make this cleaner, but that's not         */
       /* supported before Windows 98 and NT 4.0.  In Windows 2000,        */
       /* InterlockedExchange is supposed to be replaced by                */
       /* InterlockedExchangePointer, but that's not really what I */
       /* want here.                                                       */
       if (i == MAX_THREADS - 1)
         ABORT("too many threads");
     }
     thread_table[i].id = GetCurrentThreadId();
   # ifdef CYGWIN32
       thread_table[i].pthread_id = pthread_self();
   # endif
     if (!DuplicateHandle(GetCurrentProcess(),
                          GetCurrentThread(),
                          GetCurrentProcess(),
                          (HANDLE*)&thread_table[i].handle,
                          0,
                          0,
                          DUPLICATE_SAME_ACCESS)) {
           DWORD last_error = GetLastError();
           GC_printf1("Last error code: %lx\n", last_error);
           ABORT("DuplicateHandle failed");
     }
     thread_table[i].stack = GC_get_stack_base();
     if (thread_table[i].stack == NULL)
       ABORT("Failed to find stack base in threadAttach");
     /* If this thread is being created while we are trying to stop        */
     /* the world, wait here.  Hopefully this can't happen on any  */
     /* systems that don't allow us to block here.                 */
     while (GC_please_stop) Sleep(20);
   }
   
   void threadDetach(DWORD thread_id) {
     int i;
   
     LOCK();
     for (i = 0;
          i < MAX_THREADS &&
          !thread_table[i].in_use || thread_table[i].id != thread_id;
          i++) {}
     if (i >= MAX_THREADS ) {
       WARN("thread %ld not found on detach", (GC_word)thread_id);
     }
     else {
       thread_table[i].stack = 0;
       thread_table[i].in_use = FALSE;
       CloseHandle(thread_table[i].handle);
         /* cast away volatile qualifier */
       BZERO((void *)&thread_table[i].context, sizeof(CONTEXT));
     }
     UNLOCK();
   }
   
   #ifdef CYGWIN32
   
   /* Called by GC_init() - we hold the allocation lock.   */
   void GC_thr_init() {
       if (GC_thr_initialized) return;
       GC_thr_initialized = TRUE;
   
   #if 0
       /* this might already be handled in GC_init... */
       InitializeCriticalSection(&GC_allocate_ml);
   #endif
   
       /* Add the initial thread, so we can stop it.       */
       threadAttach();
   }
   
   struct start_info {
       void *(*start_routine)(void *);
       void *arg;
   };
   
   int GC_pthread_join(pthread_t pthread_id, void **retval) {
       int result;
       int i;
   
   #   if DEBUG_CYGWIN_THREADS
         GC_printf3("thread 0x%x(0x%x) is joining thread 0x%x.\n",(int)pthread_self(),
                    GetCurrentThreadId(), (int)pthread_id);
   #   endif
   
       /* Can't do any table lookups here, because thread being joined
          might not have registered itself yet */
   
       result = pthread_join(pthread_id, retval);
   
       LOCK();
       for (i = 0; !thread_table[i].in_use || thread_table[i].pthread_id != pthread_id;
            i++) {
         if (i == MAX_THREADS - 1) {
           GC_printf1("Failed to find thread 0x%x in pthread_join()\n", pthread_id);
           ABORT("thread not found on detach");
         }
       }
       UNLOCK();
       threadDetach(thread_table[i].id);
   
   #   if DEBUG_CYGWIN_THREADS
         GC_printf3("thread 0x%x(0x%x) completed join with thread 0x%x.\n",
                    (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id);
   #   endif
   
       return result;
   }
   
   /* Cygwin-pthreads calls CreateThread internally, but it's not
    * easily interceptible by us..
    *   so intercept pthread_create instead
    */
   int
   GC_pthread_create(pthread_t *new_thread,
                     const pthread_attr_t *attr,
                     void *(*start_routine)(void *), void *arg) {
       int result;
       struct start_info * si;
   
       if (!GC_is_initialized) GC_init();
                   /* make sure GC is initialized (i.e. main thread is attached) */
   
       /* This is otherwise saved only in an area mmapped by the thread */
       /* library, which isn't visible to the collector.            */
       si = GC_malloc_uncollectable(sizeof(struct start_info));
       if (0 == si) return(EAGAIN);
   
       si -> start_routine = start_routine;
       si -> arg = arg;
   
   #   if DEBUG_CYGWIN_THREADS
         GC_printf2("About to create a thread from 0x%x(0x%x)\n",(int)pthread_self(),
                                                                 GetCurrentThreadId);
   #   endif
       result = pthread_create(new_thread, attr, GC_start_routine, si);
   
       if (result) { /* failure */
           GC_free(si);
       }
   
       return(result);
   }
   
   void * GC_start_routine(void * arg)
   {
       struct start_info * si = arg;
       void * result;
       void *(*start)(void *);
       void *start_arg;
       pthread_t pthread_id;
       int i;
   
   #   if DEBUG_CYGWIN_THREADS
         GC_printf2("thread 0x%x(0x%x) starting...\n",(int)pthread_self(),
                                                      GetCurrentThreadId());
   #   endif
   
       /* If a GC occurs before the thread is registered, that GC will     */
       /* ignore this thread.  That's fine, since it will block trying to  */
       /* acquire the allocation lock, and won't yet hold interesting      */
       /* pointers.                                                        */
       LOCK();
       /* We register the thread here instead of in the parent, so that    */
       /* we don't need to hold the allocation lock during pthread_create. */
       threadAttach();
       UNLOCK();
   
       start = si -> start_routine;
       start_arg = si -> arg;
       pthread_id = pthread_self();
   
       GC_free(si); /* was allocated uncollectable */
   
       pthread_cleanup_push(GC_thread_exit_proc, pthread_id);
       result = (*start)(start_arg);
       pthread_cleanup_pop(0);
   
   #   if DEBUG_CYGWIN_THREADS
         GC_printf2("thread 0x%x(0x%x) returned from start routine.\n",
                    (int)pthread_self(),GetCurrentThreadId());
   #   endif
   
       LOCK();
       for (i = 0; thread_table[i].pthread_id != pthread_id; i++) {
         if (i == MAX_THREADS - 1)
           ABORT("thread not found on exit");
       }
       thread_table[i].status = result;
       UNLOCK();
   
       return(result);
   }
   
   void GC_thread_exit_proc(void *arg)
   {
       pthread_t pthread_id = (pthread_t)arg;
       int i;
   
   #   if DEBUG_CYGWIN_THREADS
         GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n",(int)pthread_self(),GetCurrentThreadId());
   #   endif
   
       LOCK();
       for (i = 0; thread_table[i].pthread_id != pthread_id; i++) {
         if (i == MAX_THREADS - 1)
           ABORT("thread not found on exit");
       }
       UNLOCK();
   
   #if 0
       /* TODO: we need a way to get the exit value after a pthread_exit so we can stash it safely away */
       thread_table[i].status = ???
   #endif
   }
   
   /* nothing required here... */
   int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) {
     return pthread_sigmask(how, set, oset);
   }
   int GC_pthread_detach(pthread_t thread) {
     return pthread_detach(thread);
   }
   #else
   
 /*  /*
  * This isn't generally safe, since DllMain is not premptible.   * This isn't generally safe, since DllMain is not premptible.
  * If another thread holds the lock while this runs we're in trouble.   * If another thread holds the lock while this runs we're in trouble.
Line 517  BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVO
Line 820  BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVO
     GC_init();  /* Force initialization before thread attach.   */      GC_init();  /* Force initialization before thread attach.   */
     /* fall through */      /* fall through */
   case DLL_THREAD_ATTACH:    case DLL_THREAD_ATTACH:
     {      threadAttach();
       int i;  
       /* It appears to be unsafe to acquire a lock here, since this     */  
       /* code is apparently not preeemptible on some systems.           */  
       /* (This is based on complaints, not on Microsoft's official      */  
       /* documentation, which says this should perform "only simple     */  
       /* inititalization tasks".)                                       */  
       /* Hence we make do with nonblocking synchronization.             */  
   
       /* The following should be a noop according to the win32  */  
       /* documentation.  There is empirical evidence that it    */  
       /* isn't.         - HB                                    */  
 #     ifdef MPROTECT_VDB  
        if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);  
 #     endif  
   
       for (i = 0;  
                                /* cast away volatile qualifier */  
            InterlockedExchange((LPLONG) &thread_table[i].in_use, 1) != 0;  
            i++) {  
         /* Compare-and-swap would make this cleaner, but that's not     */  
         /* supported before Windows 98 and NT 4.0.  In Windows 2000,    */  
         /* InterlockedExchange is supposed to be replaced by            */  
         /* InterlockedExchangePointer, but that's not really what I     */  
         /* want here.                                                   */  
         if (i == MAX_THREADS - 1)  
           ABORT("too many threads");  
       }  
       thread_table[i].id = GetCurrentThreadId();  
       if (!DuplicateHandle(GetCurrentProcess(),  
                            GetCurrentThread(),  
                            GetCurrentProcess(),  
                            /* cast away volatile qualifier */  
                            (HANDLE *) &thread_table[i].handle,  
                            0,  
                            0,  
                            DUPLICATE_SAME_ACCESS)) {  
         DWORD last_error = GetLastError();  
         GC_printf1("Last error code: %lx\n", last_error);  
         ABORT("DuplicateHandle failed");  
       }  
       thread_table[i].stack = GC_get_stack_base();  
       /* If this thread is being created while we are trying to stop    */  
       /* the world, wait here.  Hopefully this can't happen on any      */  
       /* systems that don't allow us to block here.                     */  
       while (GC_please_stop) Sleep(20);  
     }  
     break;      break;
   
   case DLL_THREAD_DETACH:    case DLL_THREAD_DETACH:
     {      threadDetach(GetCurrentThreadId());
       int i;  
       DWORD thread_id = GetCurrentThreadId();  
       LOCK();  
       for (i = 0;  
            i < MAX_THREADS &&  
            (thread_table[i].stack == 0 || thread_table[i].id != thread_id);  
            i++) {}  
       if (i >= MAX_THREADS) {  
           WARN("thread %ld not found on detach", (GC_word)thread_id);  
       } else {  
           thread_table[i].stack = 0;  
           thread_table[i].in_use = FALSE;  
           CloseHandle(thread_table[i].handle);  
             /* cast away volatile qualifier */  
           BZERO((void *) &thread_table[i].context, sizeof(CONTEXT));  
       }  
       UNLOCK();  
     }  
     break;      break;
   
   case DLL_PROCESS_DETACH:    case DLL_PROCESS_DETACH:
     {      {
       int i;        int i;
Line 611  BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVO
Line 852  BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVO
   }    }
   return TRUE;    return TRUE;
 }  }
   #endif /* CYGWIN32 */
   
 # endif /* !MSWINCE */  # endif /* !MSWINCE */
   
 #endif /* WIN32_THREADS */  #endif /* GC_WIN32_THREADS */

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.5

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>