@@ -161,6 +161,23 @@ TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debu
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 )
@@ -183,25 +200,13 @@ TSRM_API void tsrm_shutdown(void)
183200 tsrm_tls_entry * p = tsrm_tls_table [i ], * next_p ;
184201
185202 while (p ) {
186- int j ;
187-
188203 next_p = p -> next ;
189- for (j = 0 ; j < p -> count ; j ++ ) {
190- if (p -> storage [j ]) {
191- if (resource_types_table ) {
192- if (!resource_types_table [j ].done ) {
193- if (resource_types_table [j ].dtor ) {
194- resource_types_table [j ].dtor (p -> storage [j ]);
195- }
196-
197- if (!resource_types_table [j ].fast_offset ) {
198- free (p -> storage [j ]);
199- }
200- }
201- }
202- }
204+ if (resource_types_table ) {
205+ /* This call will already free p->storage for us */
206+ ts_free_resources (p );
207+ } else {
208+ free (p -> storage );
203209 }
204- free (p -> storage );
205210 free (p );
206211 p = next_p ;
207212 }
@@ -367,7 +372,13 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz
367372 return * rsrc_id ;
368373}/*}}}*/
369374
375+ static void set_thread_local_storage_resource_to (tsrm_tls_entry * thread_resource )
376+ {
377+ tsrm_tls_set (thread_resource );
378+ TSRMLS_CACHE = thread_resource ;
379+ }
370380
381+ /* Must be called with tsmm_mutex held */
371382static void allocate_new_resource (tsrm_tls_entry * * thread_resources_ptr , THREAD_T thread_id )
372383{/*{{{*/
373384 int i ;
@@ -383,8 +394,7 @@ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_
383394 (* thread_resources_ptr )-> next = NULL ;
384395
385396 /* Set thread local storage to this new thread resources structure */
386- tsrm_tls_set (* thread_resources_ptr );
387- TSRMLS_CACHE = * thread_resources_ptr ;
397+ set_thread_local_storage_resource_to (* thread_resources_ptr );
388398
389399 if (tsrm_new_thread_begin_handler ) {
390400 tsrm_new_thread_begin_handler (thread_id );
@@ -407,17 +417,14 @@ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_
407417 if (tsrm_new_thread_end_handler ) {
408418 tsrm_new_thread_end_handler (thread_id );
409419 }
410-
411- tsrm_mutex_unlock (tsmm_mutex );
412420}/*}}}*/
413421
414-
415422/* fetches the requested resource for the current thread */
416423TSRM_API void * ts_resource_ex (ts_rsrc_id id , THREAD_T * th_id )
417424{/*{{{*/
418425 THREAD_T thread_id ;
419426 int hash_value ;
420- tsrm_tls_entry * thread_resources ;
427+ tsrm_tls_entry * thread_resources , * * last_thread_resources ;
421428
422429 if (!th_id ) {
423430 /* Fast path for looking up the resources for the current
@@ -448,25 +455,55 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
448455
449456 if (!thread_resources ) {
450457 allocate_new_resource (& tsrm_tls_table [hash_value ], thread_id );
458+ tsrm_mutex_unlock (tsmm_mutex );
451459 return ts_resource_ex (id , & thread_id );
452460 } else {
453- do {
454- if (thread_resources -> thread_id == thread_id ) {
455- break ;
456- }
461+ last_thread_resources = & tsrm_tls_table [hash_value ];
462+ while (thread_resources -> thread_id != thread_id ) {
463+ last_thread_resources = & thread_resources -> next ;
457464 if (thread_resources -> next ) {
458465 thread_resources = thread_resources -> next ;
459466 } else {
460467 allocate_new_resource (& thread_resources -> next , thread_id );
468+ tsrm_mutex_unlock (tsmm_mutex );
461469 return ts_resource_ex (id , & thread_id );
462- /*
463- * thread_resources = thread_resources->next;
464- * break;
465- */
466470 }
467- } while (thread_resources );
471+ }
472+ }
473+
474+ /* It's possible that the current thread resources are requested, and that we get here.
475+ * This means that the TSRM key pointer and cached pointer are NULL, but there is still
476+ * a thread resource associated with this ID in the hashtable. This can occur if a thread
477+ * goes away, but its resources are never cleaned up, and then that thread ID is reused.
478+ * Since we don't always have a way to know when a thread goes away, we can't clean up
479+ * the thread's resources before the new thread spawns.
480+ * To solve this issue, we'll free up the old thread resources gracefully (gracefully
481+ * because there might still be resources open like database connection which need to
482+ * be shut down cleanly). After freeing up, we'll create the new resources for this thread
483+ * as if the stale resources never existed in the first place. From that point forward,
484+ * it is as if that situation never occurred.
485+ * The fact that this situation happens isn't that bad because a child process containing
486+ * threads will eventually be respawned anyway by the SAPI, so the stale threads won't last
487+ * forever. */
488+ TSRM_ASSERT (thread_resources -> thread_id == thread_id );
489+ if (thread_id == tsrm_thread_id () && !tsrm_tls_get ()) {
490+ tsrm_tls_entry * next = thread_resources -> next ;
491+ /* In case that extensions don't use the pointer passed from the dtor, but incorrectly
492+ * use the global pointer, we need to setup the global pointer temporarily here. */
493+ set_thread_local_storage_resource_to (thread_resources );
494+ /* Free up the old resource from the old thread instance */
495+ ts_free_resources (thread_resources );
496+ free (thread_resources );
497+ /* Allocate a new resource at the same point in the linked list, and relink the next pointer */
498+ allocate_new_resource (last_thread_resources , thread_id );
499+ thread_resources = * last_thread_resources ;
500+ thread_resources -> next = next ;
501+ /* We don't have to tail-call ts_resource_ex, we can take the fast path to the return
502+ * because we already have the correct pointer. */
468503 }
504+
469505 tsrm_mutex_unlock (tsmm_mutex );
506+
470507 /* Read a specific resource from the thread's resources.
471508 * This is called outside of a mutex, so have to be aware about external
472509 * changes to the structure as we read it.
@@ -479,7 +516,6 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
479516void ts_free_thread (void )
480517{/*{{{*/
481518 tsrm_tls_entry * thread_resources ;
482- int i ;
483519 THREAD_T thread_id = tsrm_thread_id ();
484520 int hash_value ;
485521 tsrm_tls_entry * last = NULL ;
@@ -492,17 +528,7 @@ void ts_free_thread(void)
492528
493529 while (thread_resources ) {
494530 if (thread_resources -> thread_id == thread_id ) {
495- for (i = 0 ; i < thread_resources -> count ; i ++ ) {
496- if (resource_types_table [i ].dtor ) {
497- resource_types_table [i ].dtor (thread_resources -> storage [i ]);
498- }
499- }
500- for (i = 0 ; i < thread_resources -> count ; i ++ ) {
501- if (!resource_types_table [i ].fast_offset ) {
502- free (thread_resources -> storage [i ]);
503- }
504- }
505- free (thread_resources -> storage );
531+ ts_free_resources (thread_resources );
506532 if (last ) {
507533 last -> next = thread_resources -> next ;
508534 } else {
0 commit comments