@@ -173,6 +173,30 @@ var LibraryPThread = {
173173 if ( ENVIRONMENT_IS_PTHREAD && _pthread_self ( ) ) ___pthread_tsd_run_dtors ( ) ;
174174 } ,
175175
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+
176200 // Called when we are performing a pthread_exit(), either explicitly called
177201 // by programmer, or implicitly when leaving the thread main function.
178202 threadExit : function ( exitCode ) {
@@ -181,24 +205,8 @@ var LibraryPThread = {
181205#if ASSERTIONS
182206 err ( 'Pthread 0x' + tb . toString ( 16 ) + ' exited.' ) ;
183207#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+
202210 if ( ENVIRONMENT_IS_PTHREAD ) {
203211 // Note: in theory we would like to return any offscreen canvases back to the main thread,
204212 // 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 = {
208216 } ,
209217
210218 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*/ ) ;
218220 postMessage ( { 'cmd' : 'cancelDone' } ) ;
219221 } ,
220222
@@ -494,9 +496,23 @@ var LibraryPThread = {
494496 $cleanupThread : function ( pthread_ptr ) {
495497 if ( ENVIRONMENT_IS_PTHREAD ) throw 'Internal Error! cleanupThread() can only ever be called from main application thread!' ;
496498 if ( ! pthread_ptr ) throw 'Internal Error! Null pthread_ptr in cleanupThread!' ;
497- { { { makeSetValue ( 'pthread_ptr ', C_STRUCTS . pthread . self , 0 , 'i32 ') } } } ;
498499 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.
499514 if ( pthread ) {
515+ { { { makeSetValue ( 'pthread_ptr' , C_STRUCTS . pthread . self , 0 , 'i32' ) } } } ;
500516 var worker = pthread . worker ;
501517 PThread . returnWorkerToPool ( worker ) ;
502518 }
0 commit comments