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, lParam1000, 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, lParam1000, 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: