diff -urp src.old/winsup/cygwin/fork.cc src/winsup/cygwin/fork.cc --- src.old/winsup/cygwin/fork.cc Wed Aug 14 14:20:24 2002 +++ src/winsup/cygwin/fork.cc Wed Aug 7 17:14:54 2002 @@ -311,7 +311,7 @@ fork_child (HANDLE& hParent, dll *&first if ((*t)->clear_on_fork ()) (*t)->set (); - user_data->threadinterface->fixup_after_fork (); + MT_INTERFACE->fixup_after_fork (); /* Initialize signal/process handling */ sigproc_init (); @@ -356,6 +356,8 @@ fork_parent (HANDLE& hParent, dll *&firs /* call the pthread_atfork prepare functions */ __pthread_atforkprepare (); + MT_INTERFACE->fixup_before_fork (); + subproc_init (); int c_flags = GetPriorityClass (hMainProc) /*| @@ -652,11 +654,15 @@ fork () child_info_fork ch; int res = setjmp (ch.jmp); - if (res) res = fork_child (grouped.hParent, grouped.first_dll, grouped.load_dlls); else - res = fork_parent (grouped.hParent, grouped.first_dll, grouped.load_dlls, esp, ch); + { + /* Protect pthread_keys local buf from being overwritten by simultanous forks */ + EnterCriticalSection (&MT_INTERFACE->fork_lock); + res = fork_parent (grouped.hParent, grouped.first_dll, grouped.load_dlls, esp, ch); + LeaveCriticalSection (&MT_INTERFACE->fork_lock); + } MALLOC_CHECK; syscall_printf ("%d = fork()", res); diff -urp src.old/winsup/cygwin/init.cc src/winsup/cygwin/init.cc --- src.old/winsup/cygwin/init.cc Wed Aug 14 14:20:24 2002 +++ src/winsup/cygwin/init.cc Wed Aug 14 14:23:30 2002 @@ -18,6 +18,9 @@ int NO_COPY dynamically_loaded; extern "C" int WINAPI dll_entry (HANDLE h, DWORD reason, void *static_load) { + if (reason == DLL_THREAD_DETACH || reason == DLL_PROCESS_DETACH) + MT_INTERFACE->run_key_dtors (); + switch (reason) { case DLL_PROCESS_ATTACH: @@ -25,12 +28,8 @@ WINAPI dll_entry (HANDLE h, DWORD reason dynamically_loaded = (static_load == NULL); break; case DLL_THREAD_ATTACH: - if (user_data->threadinterface) - { - if (!TlsSetValue(user_data->threadinterface->reent_index, - &user_data->threadinterface->reents)) - api_fatal("Sig proc MT init failed\n"); - } + if (MT_INTERFACE->reent_key.set (&MT_INTERFACE->reents)) + api_fatal("Sig proc MT init failed\n"); break; case DLL_PROCESS_DETACH: break; diff -urp src.old/winsup/cygwin/thread.cc src/winsup/cygwin/thread.cc --- src.old/winsup/cygwin/thread.cc Wed Aug 14 14:20:24 2002 +++ src/winsup/cygwin/thread.cc Wed Aug 14 14:20:39 2002 @@ -46,121 +46,28 @@ details. */ extern int threadsafe; -/*pthread_key_destructor_list class: to-be threadsafe single linked list - *FIXME: Put me in a dedicated file, or a least a tools area ! - */ - -pthread_key_destructor * -pthread_key_destructor::InsertAfter (pthread_key_destructor *node) -{ - pthread_key_destructor *temp = next; - next = node; - return temp; -} - -pthread_key_destructor * -pthread_key_destructor::UnlinkNext () -{ - pthread_key_destructor *temp = next; - if (next) - next = next->Next (); - return temp; -} - -pthread_key_destructor * -pthread_key_destructor::Next () -{ - return next; -} - -void -pthread_key_destructor_list::Insert (pthread_key_destructor *node) -{ - if (!node) - return; - head = node->InsertAfter (head); - if (!head) - head = node; /*first node special case */ -} - - /*remove a given dataitem, wherever in the list it is */ -pthread_key_destructor * -pthread_key_destructor_list::Remove (pthread_key *key) -{ - if (!key) - return NULL; - if (!head) - return NULL; - if (key == head->key) - return Pop (); - pthread_key_destructor *temp = head; - while (temp && temp->Next () && !(key == temp->Next ()->key)) - { - temp = temp->Next (); - } - if (temp) - return temp->UnlinkNext (); - return NULL; -} - - /*get the first item and remove at the same time */ -pthread_key_destructor * -pthread_key_destructor_list::Pop () -{ - pthread_key_destructor *temp = head; - head = head->Next (); - return temp; -} - -pthread_key_destructor:: -pthread_key_destructor (void (*thedestructor) (void *), pthread_key *key) -{ - destructor = thedestructor; - next = NULL; - this->key = key; -} - -void -pthread_key_destructor_list::IterateNull () -{ - pthread_key_destructor *temp = head; - while (temp) - { - temp->destructor ((temp->key)->get ()); - temp = temp->Next (); - } -} - - -#define MT_INTERFACE user_data->threadinterface struct _reent * _reent_clib () { - int tmp = GetLastError (); struct __reent_t *_r = - (struct __reent_t *) TlsGetValue (MT_INTERFACE->reent_index); - + (struct __reent_t *) MT_INTERFACE->reent_key.get (); #ifdef _CYG_THREAD_FAILSAFE if (_r == 0) system_printf ("local thread storage not inited"); #endif - - SetLastError (tmp); return _r->_clib; } struct _winsup_t * _reent_winsup () { - int tmp = GetLastError (); - struct __reent_t *_r; - _r = (struct __reent_t *) TlsGetValue (MT_INTERFACE->reent_index); + struct __reent_t *_r = + (struct __reent_t *) MT_INTERFACE->reent_key.get (); #ifdef _CYG_THREAD_FAILSAFE if (_r == 0) system_printf ("local thread storage not inited"); #endif - SetLastError (tmp); return _r->_winsup; } @@ -265,7 +172,6 @@ MTinterface::Init (int forked) semalist.index = 0; #endif - reent_index = TlsAlloc (); reents._clib = _impure_ptr; reents._winsup = &winsup_reent; @@ -277,36 +183,18 @@ MTinterface::Init (int forked) winsup_reent._process_facility = 0; #endif - TlsSetValue (reent_index, &reents); - // the static reent_data will be used in the main thread - - - if (!indexallocated) + if (!forked) { - indexallocated = (-1); - thread_self_dwTlsIndex = TlsAlloc (); - if (thread_self_dwTlsIndex == TLS_OUT_OF_INDEXES) - system_printf - ("local storage for thread couldn't be set\nThis means that we are not thread safe!"); - } - - concurrency = 0; - threadcount = 1; /*1 current thread when Init occurs.*/ - - mainthread.win32_obj_id = myself->hProcess; - mainthread.setThreadIdtoCurrent (); - /*store the main thread's self pointer */ - TlsSetValue (thread_self_dwTlsIndex, &mainthread); + init_crit_section (&fork_lock); - if (forked) - return; - - mutexs = NULL; - conds = NULL; - semaphores = NULL; + init_pthread (&mainthread); + /*store the main thread's reents and self pointer */ + reent_key.set (&reents); + thread_self_key.set (&mainthread); + } + /* else: pthread initialization is done in fixup_after_fork */ /*possible the atfork lists should be inited here as well */ - #if 0 item->function = NULL; @@ -316,10 +204,52 @@ MTinterface::Init (int forked) #endif } +void +MTinterface::init_pthread (pthread *thread) +{ + if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), + GetCurrentProcess (), &thread->win32_obj_id, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + + { + system_printf ("Failed to duplicate handle for mainthread"); + thread->win32_obj_id = NULL; + } + thread->setThreadIdtoCurrent (); + thread->cancel_event = ::CreateEvent (NULL,TRUE,FALSE,NULL); + if (!thread->cancel_event) + { + system_printf ("couldn't create cancel event for mainthread, LastError %E"); + } + thread->attr.joinable = PTHREAD_CREATE_JOINABLE; + thread->joiner = NULL; +} + +void +MTinterface::fixup_before_fork (void) +{ + pthread_key *key = keys; + debug_printf ("keys is %x",keys); + while (key) + { + key->fixup_before_fork (); + key = key->next; + } +} + /* This function is called from a single threaded process */ void MTinterface::fixup_after_fork (void) { + threadcount = 1; + + pthread_key *key = keys; + debug_printf ("keys is %x",keys); + while (key) + { + key->fixup_after_fork (); + key = key->next; + } pthread_mutex *mutex = mutexs; debug_printf ("mutexs is %x",mutexs); while (mutex) @@ -341,6 +271,41 @@ MTinterface::fixup_after_fork (void) sem->fixup_after_fork (); sem = sem->next; } + + init_crit_section (&fork_lock); + + pthread *thread = pthread::self(false); + if (!thread) + { + init_pthread (&mainthread); + /*store the main thread's reents and self pointer */ + reent_key.set (&reents); + thread_self_key.set (&mainthread); + } + else + init_pthread (thread); +} + +/* It is assumed that a key will not be deleted while it's destructors + are run, otherwise a lock must be added to avoid race condition */ +void +MTinterface::run_key_dtors (void) +{ + pthread_key *key = keys; + debug_printf ("keys is %x",keys); + while (key) + { + if (key->destructor) + { + void *value = key->get (); + if (value) + { + key->destructor (value); + key->set (NULL); + } + } + key = key->next; + } } /* pthread calls */ @@ -348,9 +313,40 @@ MTinterface::fixup_after_fork (void) /* static methods */ pthread * -pthread::self () +pthread::self (const bool auto_init) +{ + pthread *thread = (pthread *) MT_INTERFACE->thread_self_key.get (); + if (!thread && auto_init) + { + /* thread was not created by pthread_create nor it is the mainthread + Try to allocate a structure for it. + This thread will be marked detached */ + thread = new pthread (); + if (thread) + { + if (verifyable_object_isvalid (&thread, PTHREAD_MAGIC) != VALID_OBJECT) + { + delete thread; + return NULL; + } + + MT_INTERFACE->init_pthread (thread); + thread->attr.joinable = PTHREAD_CREATE_DETACHED; + thread->joiner = thread; + MT_INTERFACE->thread_self_key.set (thread); + } + } + + return thread; +} + +void +pthread::destructor (void *value) { - return (pthread *) TlsGetValue (MT_INTERFACE->thread_self_dwTlsIndex); + pthread *thread = (pthread *) value; + /* cleanup thread if thread is detached and not joined */ + if (thread && __pthread_equal(&thread->joiner, &thread)) + delete thread; } /* member methods */ @@ -400,7 +396,7 @@ pthread::create (void *(*func) (void *), cancel_event = ::CreateEvent (NULL,TRUE,FALSE,NULL); if (!cancel_event) { - system_printf ("couldn't create cancel event, this %p LastError %d", this, GetLastError () ); + system_printf ("couldn't create cancel event, this %p LastError %E", this); /*we need the event for correct behaviour */ magic = 0; return; @@ -436,17 +432,13 @@ pthread::exit (void *value_ptr) // run cleanup handlers pop_all_cleanup_handlers (); - MT_INTERFACE->destructors.IterateNull (); - mutex.Lock (); - // cleanup if thread is in detached state and not joined - if( __pthread_equal(&joiner, &thread ) ) - delete this; - else + if (!__pthread_equal(&joiner, &thread)) { return_ptr = value_ptr; mutex.UnLock (); } + // else: cleanup is now done in key destructor function if (InterlockedDecrement (&MT_INTERFACE->threadcount) == 0) ::exit (0); @@ -964,38 +956,70 @@ pthread_cond::fixup_after_fork () } -pthread_key::pthread_key (void (*destructor) (void *)):verifyable_object (PTHREAD_KEY_MAGIC) +pthread_key::pthread_key (void (*thedestructor) (void *)) : + verifyable_object (PTHREAD_KEY_MAGIC), + destructor(thedestructor) { dwTlsIndex = TlsAlloc (); if (dwTlsIndex == TLS_OUT_OF_INDEXES) magic = 0; - else if (destructor) - { - MT_INTERFACE->destructors. - Insert (new pthread_key_destructor (destructor, this)); - } + /* threadsafe addition is easy */ + next = (pthread_key *) InterlockedExchangePointer (&MT_INTERFACE->keys, this); } pthread_key::~pthread_key () { - if (pthread_key_destructor *dest = MT_INTERFACE->destructors.Remove (this)) - delete dest; TlsFree (dwTlsIndex); + /* I'm not 100% sure the next bit is threadsafe. I think it is... */ + if (MT_INTERFACE->keys == this) + InterlockedExchangePointer (&MT_INTERFACE->keys, this->next); + else + { + pthread_key *tempkey = MT_INTERFACE->keys; + while (tempkey->next && tempkey->next != this) + tempkey = tempkey->next; + /* but there may be a race between the loop above and this statement */ + InterlockedExchangePointer (&tempkey->next, this->next); + } } int pthread_key::set (const void *value) { - /*the OS function doesn't perform error checking */ - TlsSetValue (dwTlsIndex, (void *) value); + if (!TlsSetValue (dwTlsIndex, (void *) value)) + return EINVAL; return 0; } void * pthread_key::get () { - set_errno (0); - return TlsGetValue (dwTlsIndex); + void *result; + int last_error = GetLastError (); + + if (dwTlsIndex == TLS_OUT_OF_INDEXES) + result = NULL; + else + result = TlsGetValue (dwTlsIndex); + + SetLastError (last_error); + + return result; +} + +void +pthread_key::fixup_before_fork () +{ + fork_buf = get (); +} + +void +pthread_key::fixup_after_fork () +{ + dwTlsIndex = TlsAlloc (); + if (dwTlsIndex == TLS_OUT_OF_INDEXES) + api_fatal ("pthread_key::fixup_after_fork () failed to reallocate Tls storage"); + set (fork_buf); } /*pshared mutexs: @@ -1274,17 +1298,15 @@ pthread::thread_init_wrapper (void *_arg local_winsup._process_logmask = LOG_UPTO (LOG_DEBUG); - /*This is not checked by the OS !! */ - if (!TlsSetValue (MT_INTERFACE->reent_index, &local_reent)) + if (MT_INTERFACE->reent_key.set (&local_reent)) + system_printf ("local storage for thread couldn't be set"); + if (MT_INTERFACE->thread_self_key.set (thread)) system_printf ("local storage for thread couldn't be set"); - - /*the OS doesn't check this for <= 64 Tls entries (pre win2k) */ - TlsSetValue (MT_INTERFACE->thread_self_dwTlsIndex, thread); thread->mutex.Lock (); // if thread is detached force cleanup on exit if (thread->attr.joinable == PTHREAD_CREATE_DETACHED && thread->joiner == NULL) - thread->joiner = pthread::self (); + thread->joiner = thread; thread->mutex.UnLock (); #ifdef _CYG_THREAD_FAILSAFE @@ -1831,8 +1853,7 @@ __pthread_setspecific (pthread_key_t key { if (verifyable_object_isvalid (&key, PTHREAD_KEY_MAGIC) != VALID_OBJECT) return EINVAL; - (key)->set (value); - return 0; + return (key)->set (value); } void * diff -urp src.old/winsup/cygwin/thread.h src/winsup/cygwin/thread.h --- src.old/winsup/cygwin/thread.h Wed Aug 14 14:20:24 2002 +++ src/winsup/cygwin/thread.h Wed Aug 14 14:20:39 2002 @@ -178,43 +178,20 @@ class pthread_key:public verifyable_obje public: DWORD dwTlsIndex; + void *fork_buf; + void (*destructor) (void *); + class pthread_key *next; + int set (const void *); void *get (); - pthread_key (void (*)(void *)); - ~pthread_key (); -}; - -/* FIXME: test using multiple inheritance and merging key_destructor into pthread_key - * for efficiency */ -class pthread_key_destructor -{ -public: - void (*destructor) (void *); - pthread_key_destructor *InsertAfter (pthread_key_destructor * node); - pthread_key_destructor *UnlinkNext (); - pthread_key_destructor *Next (); - - pthread_key_destructor (void (*thedestructor) (void *), pthread_key * key); - pthread_key_destructor *next; - pthread_key *key; -}; + void fixup_before_fork (); + void fixup_after_fork (); -class pthread_key_destructor_list -{ -public: - void Insert (pthread_key_destructor * node); -/* remove a given dataitem, wherever in the list it is */ - pthread_key_destructor *Remove (pthread_key_destructor * item); -/* get the first item and remove at the same time */ - pthread_key_destructor *Pop (); - pthread_key_destructor *Remove (pthread_key * key); - void IterateNull (); -private: - pthread_key_destructor * head; + pthread_key (void (*)(void *)); + ~pthread_key (); }; - class pthread_attr:public verifyable_object { public: @@ -304,7 +281,10 @@ public: void push_cleanup_handler (__pthread_cleanup_handler *handler); void pop_cleanup_handler (int const execute); - static pthread* self (); + static pthread* self (const bool auto_init = true); + + static void destructor (void *value); + static void *thread_init_wrapper (void *); private: @@ -382,37 +362,49 @@ class MTinterface { public: // General - DWORD reent_index; - DWORD thread_self_dwTlsIndex; - /* we may get 0 for the Tls index.. grrr */ - int indexallocated; int concurrency; long int threadcount; // Used for main thread data, and sigproc thread struct __reent_t reents; struct _winsup_t winsup_reent; - pthread mainthread; - pthread_key_destructor_list destructors; callback *pthread_prepare; callback *pthread_child; callback *pthread_parent; - // list of mutex's. USE THREADSAFE INSERTS AND DELETES. + class pthread_key * keys; class pthread_mutex * mutexs; class pthread_cond * conds; class semaphore * semaphores; + pthread mainthread; + + pthread_key reent_key; + pthread_key thread_self_key; + + CRITICAL_SECTION fork_lock; + void Init (int); + static void init_pthread (pthread *thread); + void fixup_before_fork (void); void fixup_after_fork (void); - MTinterface ():reent_index (0), indexallocated (0), threadcount (1) - { - pthread_prepare = NULL; - pthread_child = NULL; - pthread_parent = NULL; - } + void run_key_dtors (void); + + static void init_crit_section (LPCRITICAL_SECTION crit_section) + { + /* This is required for a clean reinitialization after fork */ + memset (crit_section, 0, sizeof(*crit_section)); + InitializeCriticalSection (crit_section); + } + + MTinterface (): concurrency (0), threadcount (1), + pthread_prepare (NULL), pthread_child (NULL), pthread_parent (NULL), + keys (NULL), mutexs (NULL), conds (NULL), semaphores (NULL), + mainthread (), reent_key (NULL), thread_self_key (pthread::destructor) + { + } }; void __pthread_atforkprepare(void); @@ -527,6 +519,8 @@ int __sem_wait (sem_t * sem); int __sem_trywait (sem_t * sem); int __sem_post (sem_t * sem); }; + +#define MT_INTERFACE user_data->threadinterface #endif // MT_SAFE