# ts_free_thread() frees only temporary in multiple parallel threads

- Thread: https://externals.io/message/50916
- Original archive: https://news-web.php.net/php.internals/50916
- Started by Gary Zipfel on 2010-12-08T20:50:13+00:00
- Messages: 1
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #50916 — Gary Zipfel — 2010-12-08T20:50:13+00:00

URL: https://externals.io/message/50916#50916

I found a solution that solves my problem for almost 99%.
Everything gets freed forever now, but there are still some byte per
thread left that only get freed at tsrm_shutdown().

I tracked the problem down to the max_execution -timer in
zend_execute_API.c where ts_resource_ex() gets called and allocates the
resources for my threads that have been already destroyed.
So I added a check around it if the target thread is still running so
that no more resources will be allocated for the already destroyed ones.

But I would be really thankful if someone could tell if this is really
the solution or if I'm still missing a detail. To me it looks like a bug
in the Zend engine.


Heres the patch for PHP 5.2.9: (currently Windows only)

*** C:\php529\Zend\zend_execute_API.c.ORIG	2009-01-15 14:23:42.000000000
+0100
--- C:\php529\Zend\zend_execute_API.c	2010-12-08 17:38:26.334868100 +0100
***************
*** 1340,1352 ****
  #ifdef ZEND_WIN32
  static LRESULT CALLBACK zend_timeout_WndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
  {
  	switch (message) {
  		case WM_DESTROY:
  			PostQuitMessage(0);
  			break;
  		case WM_REGISTER_ZEND_TIMEOUT:
  			/* wParam is the thread id pointer, lParam is the timeout amount in
seconds */
! 			if (lParam==0) {
  				KillTimer(timeout_window, wParam);
  			} else {
  #ifdef ZTS
--- 1340,1354 ----
  #ifdef ZEND_WIN32
  static LRESULT CALLBACK zend_timeout_WndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
  {
+ 	HANDLE target_thread_handle;
+
  	switch (message) {
  		case WM_DESTROY:
  			PostQuitMessage(0);
  			break;
  		case WM_REGISTER_ZEND_TIMEOUT:
  			/* wParam is the thread id pointer, lParam is the timeout amount in
seconds */
! 			if (lParam==0) {
  				KillTimer(timeout_window, wParam);
  			} else {
  #ifdef ZTS
***************
*** 1354,1366 ****
  #endif
  				SetTimer(timeout_window, wParam, lParam*1000, NULL);
  #ifdef ZTS
! 				tsrm_ls = ts_resource_ex(0, &wParam);
! 				if (!tsrm_ls) {
! 					/* shouldn't normally happen */
! 					break;
  				}
! #endif
  				EG(timed_out) = 0;
  			}
  			break;
  		case WM_UNREGISTER_ZEND_TIMEOUT:
--- 1356,1375 ----
  #endif
  				SetTimer(timeout_window, wParam, lParam*1000, NULL);
  #ifdef ZTS
! 				target_thread_handle = OpenThread(THREAD_QUERY_INFORMATION, FALSE,
wParam);
! 				if(target_thread_handle != NULL)
! 				{
! 					tsrm_ls = ts_resource_ex(0, &wParam);
! 					if (!tsrm_ls) {
! 						/* shouldn't normally happen */
! 						break;
! 					}
! 					CloseHandle(target_thread_handle);
! 					EG(timed_out) = 0;
  				}
! #else
  				EG(timed_out) = 0;
+ #endif
  			}
  			break;
  		case WM_UNREGISTER_ZEND_TIMEOUT:
***************
*** 1370,1384 ****
  		case WM_TIMER: {
  #ifdef ZTS
  				void ***tsrm_ls;
!
! 				tsrm_ls = ts_resource_ex(0, &wParam);
! 				if (!tsrm_ls) {
! 					/* Thread died before receiving its timeout? */
! 					break;
  				}
! #endif
  				KillTimer(timeout_window, wParam);
  				EG(timed_out) = 1;
  			}
  			break;
  		default:
--- 1379,1401 ----
  		case WM_TIMER: {
  #ifdef ZTS
  				void ***tsrm_ls;
! 				
! 				target_thread_handle = OpenThread(THREAD_QUERY_INFORMATION, FALSE,
wParam);
! 				if(target_thread_handle != NULL)
! 				{
! 					tsrm_ls = ts_resource_ex(0, &wParam);
! 					if (!tsrm_ls) {
! 						/* Thread died before receiving its timeout? */
! 						break;
! 					}
!                   CloseHandle(target_thread_handle);
! 					EG(timed_out) = 1;
! 					KillTimer(timeout_window, wParam);
  				}
! #else
  				KillTimer(timeout_window, wParam);
  				EG(timed_out) = 1;
+ #endif
  			}
  			break;
  		default:
