@@ -189,9 +189,15 @@ var LibraryPThread = {
189
189
Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . threadExitCode } } } ) >> 2 , exitCode ) ;
190
190
// When we publish this, the main thread is free to deallocate the thread object and we are done.
191
191
// 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
+ }
195
201
196
202
// Not hosting a pthread anymore in this worker, reset the info structures to null.
197
203
__emscripten_thread_init ( 0 , 0 , 0 ) ; // Unregister the thread block inside the wasm module.
@@ -219,6 +225,36 @@ var LibraryPThread = {
219
225
}
220
226
} ,
221
227
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 ASSERTIONS
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 ASSERTIONS
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
+
222
258
threadCancel : function ( ) {
223
259
PThread . runExitHandlersAndDeinitThread ( _pthread_self ( ) , - 1 /*PTHREAD_CANCELED*/ ) ;
224
260
postMessage ( { 'cmd' : 'cancelDone' } ) ;
@@ -383,9 +419,12 @@ var LibraryPThread = {
383
419
} else if ( cmd === 'alert' ) {
384
420
alert ( 'Thread ' + d [ 'threadId' ] + ': ' + d [ 'text' ] ) ;
385
421
} 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
+ }
389
428
}
390
429
} else if ( cmd === 'exitProcess' ) {
391
430
// A pthread has requested to exit the whole application process (runtime).
@@ -583,21 +622,21 @@ var LibraryPThread = {
583
622
worker : worker ,
584
623
stackBase : threadParams . stackBase ,
585
624
stackSize : threadParams . stackSize ,
625
+ initialState : { { { cDefine ( 'DT_JOINABLE' ) } } } ,
586
626
allocatedOwnStack : threadParams . allocatedOwnStack ,
587
627
// Info area for this thread in Emscripten HEAP (shared)
588
628
threadInfoStruct : threadParams . pthread_ptr
589
629
} ;
590
630
var tis = pthread . threadInfoStruct >> 2 ;
591
631
// spawnThread is always called with a zero-initialized thread struct so
592
632
// 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 ) ;
596
638
Atomics . store ( HEAPU32 , tis + ( { { { C_STRUCTS . pthread . stack_size } } } >> 2 ) , threadParams . stackSize ) ;
597
639
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 ) ;
601
640
602
641
var global_libc = _emscripten_get_global_libc ( ) ;
603
642
var global_locale = global_libc + { { { C_STRUCTS . libc . global_locale } } } ;
@@ -770,23 +809,16 @@ var LibraryPThread = {
770
809
771
810
var stackSize = 0 ;
772
811
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' ) } } } ;
775
814
// When musl creates C11 threads it passes __ATTRP_C11_THREAD (-1) which
776
815
// treat as if it was NULL.
777
816
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
+ }
790
822
} else {
791
823
// According to
792
824
// http://man7.org/linux/man-pages/man3/pthread_create.3.html, default
@@ -834,7 +866,7 @@ var LibraryPThread = {
834
866
stackBase : stackBase ,
835
867
stackSize : stackSize ,
836
868
allocatedOwnStack : allocatedOwnStack ,
837
- detached : detached ,
869
+ initialState : initialState ,
838
870
startRoutine : start_routine ,
839
871
pthread_ptr : threadInfoStruct ,
840
872
arg : arg ,
@@ -870,8 +902,8 @@ var LibraryPThread = {
870
902
if ( ! tb ) return ;
871
903
var cancelDisabled = Atomics . load ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . canceldisable } } } ) >> 2 ) ;
872
904
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!' ;
875
907
} ,
876
908
877
909
#if MINIMAL_RUNTIME
@@ -913,28 +945,34 @@ var LibraryPThread = {
913
945
}
914
946
var self = { { { makeGetValue ( 'thread' , C_STRUCTS . pthread . self , 'i32' ) } } } ;
915
947
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!' ) ;
917
949
return ERRNO_CODES . ESRCH ;
918
950
}
919
951
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!' ) ;
923
955
return ERRNO_CODES . EINVAL ; // The thread is already detached, can no longer join it!
924
956
}
925
957
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
+
926
963
#if ASSERTIONS || IN_TEST_HARNESS || ! MINIMAL_RUNTIME || ! ALLOW_BLOCKING_ON_MAIN_THREAD
927
964
if ( block ) {
928
965
_emscripten_check_blocking_allowed ( ) ;
929
966
}
930
967
#endif
931
968
932
969
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 ?
935
972
var threadExitCode = Atomics . load ( HEAPU32 , ( thread + { { { C_STRUCTS . pthread . threadExitCode } } } ) >> 2 ) ;
936
973
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' ) } } } ) ;
938
976
939
977
if ( ! ENVIRONMENT_IS_PTHREAD ) cleanupThread ( thread ) ;
940
978
else postMessage ( { 'cmd' : 'cleanupThread' , 'thread' : thread } ) ;
@@ -950,7 +988,7 @@ var LibraryPThread = {
950
988
// runtime and launched main()), assist pthreads in performing operations
951
989
// that they need to access the Emscripten main runtime for.
952
990
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 ) ;
954
992
}
955
993
} ,
956
994
@@ -1003,7 +1041,8 @@ var LibraryPThread = {
1003
1041
err ( 'pthread_cancel attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!' ) ;
1004
1042
return ERRNO_CODES . ESRCH ;
1005
1043
}
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 ) ;
1007
1046
if ( ! ENVIRONMENT_IS_PTHREAD ) cancelThread ( thread ) ;
1008
1047
else postMessage ( { 'cmd ': 'cancelThread ', 'thread ': thread } ) ;
1009
1048
return 0 ;
@@ -1020,12 +1059,8 @@ var LibraryPThread = {
1020
1059
err ( 'pthread_detach attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!' ) ;
1021
1060
return ERRNO_CODES . ESRCH ;
1022
1061
}
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 ;
1029
1064
} ,
1030
1065
1031
1066
// C11 threads function.
0 commit comments