@@ -189,9 +189,15 @@ var LibraryPThread = {
189189 Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . threadExitCode } } } ) >> 2 , exitCode ) ;
190190 // When we publish this, the main thread is free to deallocate the thread object and we are done.
191191 // Therefore set _pthread_self = 0; above to 'release' the object in this worker thread.
192- Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . threadStatus } } } ) >> 2 , 1 ) ; // Mark the thread as no longer running.
193-
194- _emscripten_futex_wake ( tb + { { { C_STRUCTS . pthread . threadStatus } } } , { { { cDefine ( 'INT_MAX' ) } } } ) ; // wake all threads
192+ // Mark the thread as no longer running.
193+ var detach_state = Atomics . load ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . detach_state } } } ) >> 2 ) ;
194+ if ( detach_state == { { { cDefine ( 'DT_DETACHED' ) } } } ) {
195+ PThread . setDetachState ( tb , { { { cDefine ( 'DT_EXITED' ) } } } ) ;
196+ } else {
197+ PThread . setDetachState ( tb , { { { cDefine ( 'DT_EXITING' ) } } } ) ;
198+ // wake any threads that might be waiting for us to exit
199+ _emscripten_futex_wake ( tb + { { { C_STRUCTS . pthread . detach_state } } } , { { { cDefine ( 'INT_MAX' ) } } } ) ;
200+ }
195201
196202 // Not hosting a pthread anymore in this worker, reset the info structures to null.
197203 __emscripten_thread_init ( 0 , 0 , 0 ) ; // Unregister the thread block inside the wasm module.
@@ -219,6 +225,36 @@ var LibraryPThread = {
219225 }
220226 } ,
221227
228+ #if ASSERTIONS
229+ detachStateToString : function ( state ) {
230+ if ( state === { { { cDefine ( 'DT_EXITED' ) } } } ) return 'DT_EXITED ';
231+ if ( state === { { { cDefine ( 'DT_EXITING' ) } } } ) return 'DT_EXITING ';
232+ if ( state === { { { cDefine ( 'DT_JOINABLE' ) } } } ) return 'DT_JOINABLE ';
233+ if ( state === { { { cDefine ( 'DT_DETACHED' ) } } } ) return 'DT_DETACHED ';
234+ assert ( false ) ;
235+ } ,
236+ #endif
237+
238+ setDetachState : function ( thread , newstate ) {
239+ #if PTHREADS_DEBUG
240+ var oldstate = Atomics . load ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . detach_state } } } ) >> 2 ) ;
241+ var oldname = PThread . detachStateToString ( oldstate ) ;
242+ var newname = PThread . detachStateToString ( newstate ) ;
243+ console . log ( 'thread 0x' + thread . toString ( 16 ) + ' state change: ' + oldname + ' -> ' + newname ) ;
244+ #endif
245+ Atomics . store ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . detach_state } } } ) >> 2 , newstate ) ;
246+ } ,
247+
248+ swapDetachState : function ( thread , oldstate , newstate ) {
249+ #if PTHREADS_DEBUG
250+ var oldstate = Atomics . load ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . detach_state } } } ) >> 2 ) ;
251+ var oldname = PThread . detachStateToString ( oldstate ) ;
252+ var newname = PThread . detachStateToString ( newstate ) ;
253+ console . log ( 'thread 0x' + thread . toString ( 16 ) + ' state change (swap): ' + oldname + ' -> ' + newname ) ;
254+ #endif
255+ return Atomics . compareExchange ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . detach_state } } } ) >> 2 , oldstate , newstate ) ;
256+ } ,
257+
222258 threadCancel : function ( ) {
223259 PThread . runExitHandlersAndDeinitThread ( _pthread_self ( ) , - 1 /*PTHREAD_CANCELED*/ ) ;
224260 postMessage ( { 'cmd' : 'cancelDone' } ) ;
@@ -383,9 +419,12 @@ var LibraryPThread = {
383419 } else if ( cmd === 'alert' ) {
384420 alert ( 'Thread ' + d [ 'threadId' ] + ': ' + d [ 'text' ] ) ;
385421 } else if ( cmd === 'exit' ) {
386- var detached = worker . pthread && Atomics . load ( HEAPU32 , ( worker . pthread . threadInfoStruct + { { { C_STRUCTS . pthread . detached } } } ) >> 2 ) ;
387- if ( detached ) {
388- PThread . returnWorkerToPool ( worker ) ;
422+ if ( worker . pthread ) {
423+ var detach_state = Atomics . load ( HEAPU32 , ( worker . pthread . threadInfoStruct + { { { C_STRUCTS . pthread . detach_state } } } ) >> 2 ) ;
424+ // Only if it is fully exited can we return a worker the pool
425+ if ( detach_state == { { { cDefine ( 'DT_EXITED' ) } } } ) {
426+ PThread . returnWorkerToPool ( worker ) ;
427+ }
389428 }
390429 } else if ( cmd === 'exitProcess' ) {
391430 // A pthread has requested to exit the whole application process (runtime).
@@ -583,21 +622,21 @@ var LibraryPThread = {
583622 worker : worker ,
584623 stackBase : threadParams . stackBase ,
585624 stackSize : threadParams . stackSize ,
625+ initialState : { { { cDefine ( 'DT_JOINABLE' ) } } } ,
586626 allocatedOwnStack : threadParams . allocatedOwnStack ,
587627 // Info area for this thread in Emscripten HEAP (shared)
588628 threadInfoStruct : threadParams . pthread_ptr
589629 } ;
590630 var tis = pthread . threadInfoStruct >> 2 ;
591631 // spawnThread is always called with a zero-initialized thread struct so
592632 // no need to set any valudes to zero here.
593- Atomics . store ( HEAPU32 , tis + ( { { { C_STRUCTS . pthread . detached } } } >> 2 ) , threadParams . detached ) ;
594- Atomics . store ( HEAPU32 , tis + ( { { { C_STRUCTS . pthread . tsd } } } >> 2 ) , tlsMemory ) ; // Init thread-local-storage memory array.
595- Atomics. store ( HEAPU32 , tis + ( { { { C_STRUCTS . pthread . tid } } } >> 2 ) , pthread . threadInfoStruct ) ; // Main thread ID.
633+ PThread . setDetachState ( pthread . threadInfoStruct , threadParams . initialState ) ;
634+ // Init thread-local-storage memory array.
635+ Atomics . store ( HEAPU32 , tis + ( { { { C_STRUCTS . pthread . tsd } } } >> 2 ) , tlsMemory ) ;
636+ // Main thread ID.
637+ Atomics. store ( HEAPU32 , tis + ( { { { C_STRUCTS . pthread . tid } } } >> 2 ) , pthread . threadInfoStruct ) ;
596638 Atomics . store ( HEAPU32 , tis + ( { { { C_STRUCTS . pthread . stack_size } } } >> 2 ) , threadParams . stackSize ) ;
597639 Atomics . store ( HEAPU32 , tis + ( { { { C_STRUCTS . pthread . stack } } } >> 2 ) , stackHigh ) ;
598- Atomics . store ( HEAPU32 , tis + ( { { { C_STRUCTS . pthread . attr } } } >> 2 ) , threadParams . stackSize ) ;
599- Atomics . store ( HEAPU32 , tis + ( { { { C_STRUCTS . pthread . attr } } } + 8 >> 2 ) , stackHigh ) ;
600- Atomics . store ( HEAPU32 , tis + ( { { { C_STRUCTS . pthread . attr } } } + 12 >> 2 ) , threadParams . detached ) ;
601640
602641 var global_libc = _emscripten_get_global_libc ( ) ;
603642 var global_locale = global_libc + { { { C_STRUCTS . libc . global_locale } } } ;
@@ -770,23 +809,16 @@ var LibraryPThread = {
770809
771810 var stackSize = 0 ;
772811 var stackBase = 0 ;
773- // Default thread attr is PTHREAD_CREATE_JOINABLE , i.e. start as not detached.
774- var detached = 0 ;
812+ // Default thread state is DT_JOINABLE , i.e. start as not detached.
813+ var initialState = { { { cDefine ( 'DT_JOINABLE' ) } } } ;
775814 // When musl creates C11 threads it passes __ATTRP_C11_THREAD (-1) which
776815 // treat as if it was NULL.
777816 if ( attr && attr != { { { cDefine ( '__ATTRP_C11_THREAD' ) } } } ) {
778- stackSize = { { { makeGetValue ( 'attr' , 0 , 'i32' ) } } } ;
779- // Musl has a convention that the stack size that is stored to the pthread
780- // attribute structure is always musl's #define DEFAULT_STACK_SIZE
781- // smaller than the actual created stack size. That is, stored stack size
782- // of 0 would mean a stack of DEFAULT_STACK_SIZE in size. All musl
783- // functions hide this impl detail, and offset the size transparently, so
784- // pthread_*() API user does not see this offset when operating with
785- // the pthread API. When reading the structure directly on JS side
786- // however, we need to offset the size manually here.
787- stackSize += 81920 /*DEFAULT_STACK_SIZE*/ ;
788- stackBase = { { { makeGetValue ( 'attr' , 8 , 'i32' ) } } } ;
789- detached = { { { makeGetValue ( 'attr' , 12 /*_a_detach*/ , 'i32' ) } } } !== 0 /*PTHREAD_CREATE_JOINABLE*/ ;
817+ stackSize = { { { makeGetValue ( 'attr' , 0 /*_a_stacksize*/ , 'i32' ) } } } ;
818+ stackBase = { { { makeGetValue ( 'attr' , 8 /*_a_stackaddr*/ , 'i32' ) } } } ;
819+ if ( { { { makeGetValue ( 'attr ', 12 / * _a_detach * / , 'i32 ') } } } ) {
820+ initialState = { { { cDefine ( 'DT_DETACHED' ) } } } ;
821+ }
790822 } else {
791823 // According to
792824 // http://man7.org/linux/man-pages/man3/pthread_create.3.html, default
@@ -834,7 +866,7 @@ var LibraryPThread = {
834866 stackBase : stackBase ,
835867 stackSize : stackSize ,
836868 allocatedOwnStack : allocatedOwnStack ,
837- detached : detached ,
869+ initialState : initialState ,
838870 startRoutine : start_routine ,
839871 pthread_ptr : threadInfoStruct ,
840872 arg : arg ,
@@ -870,8 +902,8 @@ var LibraryPThread = {
870902 if ( ! tb ) return ;
871903 var cancelDisabled = Atomics . load ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . canceldisable } } } ) >> 2 ) ;
872904 if ( cancelDisabled ) return ;
873- var canceled = Atomics . load ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . threadStatus } } } ) >> 2 ) ;
874- if ( canceled == 2 ) throw 'Canceled!' ;
905+ var canceled = Atomics . load ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . cancel } } } ) >> 2 ) ;
906+ if ( canceled ) throw 'Canceled!' ;
875907 } ,
876908
877909#if MINIMAL_RUNTIME
@@ -913,28 +945,34 @@ var LibraryPThread = {
913945 }
914946 var self = { { { makeGetValue ( 'thread' , C_STRUCTS . pthread . self , 'i32' ) } } } ;
915947 if ( self !== thread ) {
916- err ( 'pthread_join attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!' ) ;
948+ err ( 'pthread_join attempted on thread 0x ' + thread . toString ( 16 ) + ', which does not point to a valid thread, or does not exist anymore!' ) ;
917949 return ERRNO_CODES . ESRCH ;
918950 }
919951
920- var detached = Atomics . load ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . detached } } } ) >> 2 ) ;
921- if ( detached ) {
922- err ( 'Attempted to join thread ' + thread + ', which was already detached!' ) ;
952+ var detach_state = Atomics . load ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . detach_state } } } ) >> 2 ) ;
953+ if ( detach_state == { { { cDefine ( 'DT_DETACHED' ) } } } ) {
954+ err ( 'Attempted to join thread 0x ' + thread . toString ( 16 ) + ', which was already detached!' ) ;
923955 return ERRNO_CODES . EINVAL ; // The thread is already detached, can no longer join it!
924956 }
925957
958+ if ( detach_state == { { { cDefine ( 'DT_EXITED' ) } } } ) {
959+ err ( 'Attempted to join thread 0x' + thread . toString ( 16 ) + ', which was already joined!' ) ;
960+ return ERRNO_CODES . EINVAL ;
961+ }
962+
926963#if ASSERTIONS || IN_TEST_HARNESS || ! MINIMAL_RUNTIME || ! ALLOW_BLOCKING_ON_MAIN_THREAD
927964 if ( block ) {
928965 _emscripten_check_blocking_allowed ( ) ;
929966 }
930967#endif
931968
932969 for ( ; ; ) {
933- var threadStatus = Atomics . load ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . threadStatus } } } ) >> 2 ) ;
934- if ( threadStatus == 1 ) { // Exited ?
970+ var detach_state = Atomics . load ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . detach_state } } } ) >> 2 ) ;
971+ if ( detach_state == { { { cDefine ( 'DT_EXITING' ) } } } ) { // Exiting ?
935972 var threadExitCode = Atomics . load ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . threadExitCode } } } ) >> 2 ) ;
936973 if ( status ) { { { makeSetValue ( 'status' , 0 , 'threadExitCode' , 'i32' ) } } } ;
937- Atomics . store ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . detached } } } ) >> 2 , 1 ) ; // Mark the thread as detached.
974+ // Mark the thread as exited.
975+ PThread . setDetachState ( thread, { { { cDefine ( 'DT_EXITED' ) } } } ) ;
938976
939977 if ( ! ENVIRONMENT_IS_PTHREAD ) cleanupThread ( thread ) ;
940978 else postMessage ( { 'cmd' : 'cleanupThread' , 'thread' : thread } ) ;
@@ -950,7 +988,7 @@ var LibraryPThread = {
950988 // runtime and launched main()), assist pthreads in performing operations
951989 // that they need to access the Emscripten main runtime for.
952990 if ( ! ENVIRONMENT_IS_PTHREAD ) _emscripten_main_thread_process_queued_calls ( ) ;
953- _emscripten_futex_wait ( thread + { { { C_STRUCTS . pthread . threadStatus } } } , threadStatus , ENVIRONMENT_IS_PTHREAD ? 100 : 1 ) ;
991+ _emscripten_futex_wait ( thread + { { { C_STRUCTS . pthread . detach_state } } } , detach_state , ENVIRONMENT_IS_PTHREAD ? 100 : 1 ) ;
954992 }
955993 } ,
956994
@@ -1003,7 +1041,8 @@ var LibraryPThread = {
10031041 err ( 'pthread_cancel attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!' ) ;
10041042 return ERRNO_CODES . ESRCH ;
10051043 }
1006- Atomics . compareExchange ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . threadStatus } } } ) >> 2 , 0 , 2 ) ; // Signal the thread that it needs to cancel itself.
1044+ // Signal the thread that it needs to cancel itself.
1045+ Atomics . store ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . cancel } } } ) >> 2 , 1 ) ;
10071046 if ( ! ENVIRONMENT_IS_PTHREAD ) cancelThread ( thread ) ;
10081047 else postMessage ( { 'cmd ': 'cancelThread ', 'thread ': thread } ) ;
10091048 return 0 ;
@@ -1020,12 +1059,8 @@ var LibraryPThread = {
10201059 err ( 'pthread_detach attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!' ) ;
10211060 return ERRNO_CODES . ESRCH ;
10221061 }
1023- // Follow musl convention: detached:0 means not detached, 1 means the thread
1024- // was created as detached, and 2 means that the thread was detached via
1025- // pthread_detach.
1026- var wasDetached = Atomics . compareExchange ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . detached } } } ) >> 2 , 0 , 2 ) ;
1027-
1028- return wasDetached ? ERRNO_CODES . EINVAL : 0 ;
1062+ var oldState = PThread . swapDetachState ( thread , { { { cDefine ( 'DT_JOINABLE' ) } } } , { { { cDefine ( 'DT_DETACHED' ) } } } ) ;
1063+ return oldState != { { { cDefine ( 'DT_JOINABLE' ) } } } ? ERRNO_CODES . EINVAL : 0 ;
10291064 } ,
10301065
10311066 // C11 threads function.
0 commit comments