@@ -173,6 +173,30 @@ var LibraryPThread = {
173
173
if ( ENVIRONMENT_IS_PTHREAD && _pthread_self ( ) ) ___pthread_tsd_run_dtors ( ) ;
174
174
} ,
175
175
176
+ runExitHandlersAndDeinitThread : function ( tb , exitCode ) {
177
+ #if PTHREADS_PROFILING
178
+ var profilerBlock = Atomics . load ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . profilerBlock } } } ) >> 2 ) ;
179
+ Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . profilerBlock } } } ) >> 2 , 0 ) ;
180
+ _free ( profilerBlock ) ;
181
+ #endif
182
+
183
+ // Disable all cancellation so that executing the cleanup handlers won't trigger another JS
184
+ // canceled exception to be thrown.
185
+ Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . canceldisable } } } ) >> 2 , 1 /*PTHREAD_CANCEL_DISABLE*/ ) ;
186
+ Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . cancelasync } } } ) >> 2 , 0 /*PTHREAD_CANCEL_DEFERRED*/ ) ;
187
+ PThread . runExitHandlers ( ) ;
188
+
189
+ Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . threadExitCode } } } ) >> 2 , exitCode ) ;
190
+ // When we publish this, the main thread is free to deallocate the thread object and we are done.
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
195
+
196
+ // Not hosting a pthread anymore in this worker, reset the info structures to null.
197
+ __emscripten_thread_init( 0 , 0 , 0 ) ; // Unregister the thread block also inside the asm.js scope.
198
+ } ,
199
+
176
200
// Called when we are performing a pthread_exit(), either explicitly called
177
201
// by programmer, or implicitly when leaving the thread main function.
178
202
threadExit : function ( exitCode ) {
@@ -181,24 +205,8 @@ var LibraryPThread = {
181
205
#if ASSERTIONS
182
206
err ( 'Pthread 0x' + tb . toString ( 16 ) + ' exited.' ) ;
183
207
#endif
184
- #if PTHREADS_PROFILING
185
- var profilerBlock = Atomics . load ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . profilerBlock } } } ) >> 2 ) ;
186
- Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . profilerBlock } } } ) >> 2 , 0 ) ;
187
- _free ( profilerBlock ) ;
188
- #endif
189
- Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . threadExitCode } } } ) >> 2 , exitCode ) ;
190
- // When we publish this, the main thread is free to deallocate the thread object and we are done.
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 ) ;
193
-
194
- // Disable all cancellation so that executing the cleanup handlers won't trigger another JS
195
- // canceled exception to be thrown.
196
- Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . canceldisable } } } ) >> 2 , 1 /*PTHREAD_CANCEL_DISABLE*/ ) ;
197
- Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . cancelasync } } } ) >> 2 , 0 /*PTHREAD_CANCEL_DEFERRED*/ ) ;
198
- PThread . runExitHandlers ( ) ;
199
-
200
- _emscripten_futex_wake ( tb + { { { C_STRUCTS . pthread . threadStatus } } } , { { { cDefine ( 'INT_MAX' ) } } } ) ;
201
- __emscripten_thread_init ( 0 , 0 , 0 ) ; // Unregister the thread block also inside the asm.js scope.
208
+ PThread . runExitHandlersAndDeinitThread ( tb , exitCode ) ;
209
+
202
210
if ( ENVIRONMENT_IS_PTHREAD ) {
203
211
// Note: in theory we would like to return any offscreen canvases back to the main thread,
204
212
// but if we ever fetched a rendering context for them that would not be valid, so we don't try.
@@ -208,13 +216,7 @@ var LibraryPThread = {
208
216
} ,
209
217
210
218
threadCancel : function ( ) {
211
- PThread . runExitHandlers ( ) ;
212
- var tb = _pthread_self ( ) ;
213
- Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . threadExitCode } } } ) >> 2 , - 1 /*PTHREAD_CANCELED*/ ) ;
214
- Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . threadStatus } } } ) >> 2 , 1 ) ; // Mark the thread as no longer running.
215
- _emscripten_futex_wake ( tb + { { { C_STRUCTS . pthread . threadStatus } } } , { { { cDefine ( 'INT_MAX' ) } } } ) ; // wake all threads
216
- // Not hosting a pthread anymore in this worker, reset the info structures to null.
217
- __emscripten_thread_init( 0 , 0 , 0 ) ; // Unregister the thread block also inside the asm.js scope.
219
+ PThread . runExitHandlersAndDeinitThread ( _pthread_self ( ) , - 1 /*PTHREAD_CANCELED*/ ) ;
218
220
postMessage ( { 'cmd' : 'cancelDone' } ) ;
219
221
} ,
220
222
@@ -494,9 +496,23 @@ var LibraryPThread = {
494
496
$cleanupThread : function ( pthread_ptr ) {
495
497
if ( ENVIRONMENT_IS_PTHREAD ) throw 'Internal Error! cleanupThread() can only ever be called from main application thread!' ;
496
498
if ( ! pthread_ptr ) throw 'Internal Error! Null pthread_ptr in cleanupThread!' ;
497
- { { { makeSetValue ( 'pthread_ptr ', C_STRUCTS . pthread . self , 0 , 'i32 ') } } } ;
498
499
var pthread = PThread . pthreads [ pthread_ptr ] ;
500
+ // If pthread has been removed from this map this also means that pthread_ptr points
501
+ // to already freed data. Such situation may occur in following circumstances:
502
+ // 1. Joining cancelled thread - in such situation it may happen that pthread data will
503
+ // already be removed by handling 'cancelDone' message.
504
+ // 2. Joining thread from non-main browser thread (this also includes thread running main()
505
+ // when compiled with `PROXY_TO_PTHREAD`) - in such situation it may happen that following
506
+ // code flow occur (MB - Main Browser Thread, S1, S2 - Worker Threads):
507
+ // S2: thread ends, 'exit' message is sent to MB
508
+ // S1: calls pthread_join(S2), this causes:
509
+ // a. S2 is marked as detached,
510
+ // b. 'cleanupThread' message is sent to MB.
511
+ // MB: handles 'exit' message, as thread is detached, so returnWorkerToPool()
512
+ // is called and all thread related structs are freed/released.
513
+ // MB: handles 'cleanupThread' message which calls this function.
499
514
if ( pthread ) {
515
+ { { { makeSetValue ( 'pthread_ptr' , C_STRUCTS . pthread . self , 0 , 'i32' ) } } } ;
500
516
var worker = pthread . worker ;
501
517
PThread . returnWorkerToPool ( worker ) ;
502
518
}
0 commit comments