@@ -161,6 +161,23 @@ TSRM_API bool tsrm_startup(int expected_threads, int expected_resources, int deb
161161 return 1 ;
162162}/*}}}*/
163163
164+ static void ts_free_resources (tsrm_tls_entry * thread_resources )
165+ {
166+ /* Need to destroy in reverse order to respect dependencies. */
167+ for (int i = thread_resources -> count - 1 ; i >= 0 ; i -- ) {
168+ if (!resource_types_table [i ].done ) {
169+ if (resource_types_table [i ].dtor ) {
170+ resource_types_table [i ].dtor (thread_resources -> storage [i ]);
171+ }
172+
173+ if (!resource_types_table [i ].fast_offset ) {
174+ free (thread_resources -> storage [i ]);
175+ }
176+ }
177+ }
178+
179+ free (thread_resources -> storage );
180+ }
164181
165182/* Shutdown TSRM (call once for the entire process) */
166183TSRM_API void tsrm_shutdown (void )
@@ -182,22 +199,12 @@ TSRM_API void tsrm_shutdown(void)
182199
183200 while (p ) {
184201 next_p = p -> next ;
185- for (int j = 0 ; j < p -> count ; j ++ ) {
186- if (p -> storage [j ]) {
187- if (resource_types_table ) {
188- if (!resource_types_table [j ].done ) {
189- if (resource_types_table [j ].dtor ) {
190- resource_types_table [j ].dtor (p -> storage [j ]);
191- }
192-
193- if (!resource_types_table [j ].fast_offset ) {
194- free (p -> storage [j ]);
195- }
196- }
197- }
198- }
202+ if (resource_types_table ) {
203+ /* This call will already free p->storage for us */
204+ ts_free_resources (p );
205+ } else {
206+ free (p -> storage );
199207 }
200- free (p -> storage );
201208 free (p );
202209 p = next_p ;
203210 }
@@ -361,7 +368,13 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz
361368 return * rsrc_id ;
362369}/*}}}*/
363370
371+ static void set_thread_local_storage_resource_to (tsrm_tls_entry * thread_resource )
372+ {
373+ tsrm_tls_set (thread_resource );
374+ TSRMLS_CACHE = thread_resource ;
375+ }
364376
377+ /* Must be called with tsmm_mutex held */
365378static void allocate_new_resource (tsrm_tls_entry * * thread_resources_ptr , THREAD_T thread_id )
366379{/*{{{*/
367380 TSRM_ERROR ((TSRM_ERROR_LEVEL_CORE , "Creating data structures for thread %x" , thread_id ));
@@ -375,8 +388,7 @@ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_
375388 (* thread_resources_ptr )-> next = NULL ;
376389
377390 /* Set thread local storage to this new thread resources structure */
378- tsrm_tls_set (* thread_resources_ptr );
379- TSRMLS_CACHE = * thread_resources_ptr ;
391+ set_thread_local_storage_resource_to (* thread_resources_ptr );
380392
381393 if (tsrm_new_thread_begin_handler ) {
382394 tsrm_new_thread_begin_handler (thread_id );
@@ -399,17 +411,14 @@ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_
399411 if (tsrm_new_thread_end_handler ) {
400412 tsrm_new_thread_end_handler (thread_id );
401413 }
402-
403- tsrm_mutex_unlock (tsmm_mutex );
404414}/*}}}*/
405415
406-
407416/* fetches the requested resource for the current thread */
408417TSRM_API void * ts_resource_ex (ts_rsrc_id id , THREAD_T * th_id )
409418{/*{{{*/
410419 THREAD_T thread_id ;
411420 int hash_value ;
412- tsrm_tls_entry * thread_resources ;
421+ tsrm_tls_entry * thread_resources , * * last_thread_resources ;
413422
414423 if (!th_id ) {
415424 /* Fast path for looking up the resources for the current
@@ -440,25 +449,55 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
440449
441450 if (!thread_resources ) {
442451 allocate_new_resource (& tsrm_tls_table [hash_value ], thread_id );
452+ tsrm_mutex_unlock (tsmm_mutex );
443453 return ts_resource_ex (id , & thread_id );
444454 } else {
445- do {
446- if (thread_resources -> thread_id == thread_id ) {
447- break ;
448- }
455+ last_thread_resources = & tsrm_tls_table [hash_value ];
456+ while (thread_resources -> thread_id != thread_id ) {
457+ last_thread_resources = & thread_resources -> next ;
449458 if (thread_resources -> next ) {
450459 thread_resources = thread_resources -> next ;
451460 } else {
452461 allocate_new_resource (& thread_resources -> next , thread_id );
462+ tsrm_mutex_unlock (tsmm_mutex );
453463 return ts_resource_ex (id , & thread_id );
454- /*
455- * thread_resources = thread_resources->next;
456- * break;
457- */
458464 }
459- } while (thread_resources );
465+ }
466+ }
467+
468+ /* It's possible that the current thread resources are requested, and that we get here.
469+ * This means that the TSRM key pointer and cached pointer are NULL, but there is still
470+ * a thread resource associated with this ID in the hashtable. This can occur if a thread
471+ * goes away, but its resources are never cleaned up, and then that thread ID is reused.
472+ * Since we don't always have a way to know when a thread goes away, we can't clean up
473+ * the thread's resources before the new thread spawns.
474+ * To solve this issue, we'll free up the old thread resources gracefully (gracefully
475+ * because there might still be resources open like database connection which need to
476+ * be shut down cleanly). After freeing up, we'll create the new resources for this thread
477+ * as if the stale resources never existed in the first place. From that point forward,
478+ * it is as if that situation never occurred.
479+ * The fact that this situation happens isn't that bad because a child process containing
480+ * threads will eventually be respawned anyway by the SAPI, so the stale threads won't last
481+ * forever. */
482+ TSRM_ASSERT (thread_resources -> thread_id == thread_id );
483+ if (thread_id == tsrm_thread_id () && !tsrm_tls_get ()) {
484+ tsrm_tls_entry * next = thread_resources -> next ;
485+ /* In case that extensions don't use the pointer passed from the dtor, but incorrectly
486+ * use the global pointer, we need to setup the global pointer temporarily here. */
487+ set_thread_local_storage_resource_to (thread_resources );
488+ /* Free up the old resource from the old thread instance */
489+ ts_free_resources (thread_resources );
490+ free (thread_resources );
491+ /* Allocate a new resource at the same point in the linked list, and relink the next pointer */
492+ allocate_new_resource (last_thread_resources , thread_id );
493+ thread_resources = * last_thread_resources ;
494+ thread_resources -> next = next ;
495+ /* We don't have to tail-call ts_resource_ex, we can take the fast path to the return
496+ * because we already have the correct pointer. */
460497 }
498+
461499 tsrm_mutex_unlock (tsmm_mutex );
500+
462501 /* Read a specific resource from the thread's resources.
463502 * This is called outside of a mutex, so have to be aware about external
464503 * changes to the structure as we read it.
@@ -483,17 +522,7 @@ void ts_free_thread(void)
483522
484523 while (thread_resources ) {
485524 if (thread_resources -> thread_id == thread_id ) {
486- for (int i = 0 ; i < thread_resources -> count ; i ++ ) {
487- if (resource_types_table [i ].dtor ) {
488- resource_types_table [i ].dtor (thread_resources -> storage [i ]);
489- }
490- }
491- for (int i = 0 ; i < thread_resources -> count ; i ++ ) {
492- if (!resource_types_table [i ].fast_offset ) {
493- free (thread_resources -> storage [i ]);
494- }
495- }
496- free (thread_resources -> storage );
525+ ts_free_resources (thread_resources );
497526 if (last ) {
498527 last -> next = thread_resources -> next ;
499528 } else {
0 commit comments