I am currently developing a socket server for winblows and wanted to emulate SIGCHLDs in the parent process ... unix fork() feel. To do this, I have to keep an array of win32 process HANDLE objects returned by CreateProcess() that I pass to WSAWaitForMultipleEvents(), along with some WSAEVENTs. The problem is that once again winblows has an annoying limit: MAXIMUM_WAIT_OBJECTS. I search the interent for ideas and came across apaches wait_for_many_objects function in the http-2.0 project. It's in os/win32/util_win32.c. I found bug in there and thought I would submit the solution I used in my code. BUG: The function uses time() to mark the wait_stop_time. It also uses time() to verify when to expire the wait attempt. The problem here is if someone plays with the windows clock (a person, ntp, etc). If you set the clock back a few hours, the wait period goes way beyond what it is supposed to. The solution is to use the win32 QueryPerformanceCounter() function (shown below). Here are the modifications I made. static LONGLONG freq_per_msec=0; static void (*wait_clock)(void); static LONGLONG freq_clock(void) { LARGE_INTEGER li; QueryPerformanceCounter(&li); return li.QuadPart / freq_per_msec; } /* in case machine doesn't support performace counter, not based on OSVer. */ static LONGLONG time_clock(void) { FILETIME ft; LARGE_INTEGER li; GetSystemTimeAsFileTime(&ft); li.LowPart = ft.dwLowDateTime; li.HighPart = ft.dwHighDateTime; return li.QuadPart / 10000; } /* * cache the frequency, in ms, and the correct wait func. winblows states some * hardware may not support the performace counter. In those cases, I used * GetSystemTimeAsFileTime() which suffers from the same issues as calling time (), * although it a faster call. */ void util_win32_init(void) { LARGE_INTEGER li; /* winblows states some . */ li.QuadPart = 0; if(QueryPerformanceFrequency(&li) && li.QuadPart) { freq_per_msec = li.QuadPart / 1000; wait_clock = s_PerformanceClock; } else { wait_clock = time_clock; } } /* * The Win32 call WaitForMultipleObjects will only allow you to wait for * a maximum of MAXIMUM_WAIT_OBJECTS (current 64). Since the threading * model in the multithreaded version of apache wants to use this call, * we are restricted to a maximum of 64 threads. This is a simplistic * routine that will increase this size. */ DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles, DWORD /*dwSeconds*/dwMilliseconds) { //time_t tStopTime; LONGLONG tStopTime; DWORD dwRet = WAIT_TIMEOUT; DWORD dwIndex=0; BOOL bFirst = TRUE; //tStopTime = time(NULL) + dwSeconds; tStopTime = wait_clock() + dwMilliseconds; do { if (!bFirst) Sleep(1000 /*100*/); // Why 1 second? else bFirst = FALSE; for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS < nCount; dwIndex++) { dwRet = WaitForMultipleObjects( min(MAXIMUM_WAIT_OBJECTS, nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)), lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS), 0, 0); if (dwRet != WAIT_TIMEOUT) { break; } } // personally, I would check dwRet before making the func call } while((dwRet == WAIT_TIMEOUT) && (wait_clock() < tStopTime)); //while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT)); return dwRet; }
Sorry, I have a typo in util_win32_init(). s_PerformanceClock should be freq_clock.