Bug 36676

Summary: time() bug in httpd/os/win32/util_win32.c:wait_for_many_objects()
Product: Apache httpd-2 Reporter: slakr <achernow>
Component: CoreAssignee: Apache HTTPD Bugs Mailing List <bugs>
Status: NEW ---    
Severity: normal    
Priority: P2    
Version: 2.5-HEAD   
Target Milestone: ---   
Hardware: PC   
OS: Windows Server 2003   

Description slakr 2005-09-15 21:07:49 UTC
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. 

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)
  return li.QuadPart / freq_per_msec;

/* in case machine doesn't support performace counter, not based on OSVer. */
static LONGLONG time_clock(void)
  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)
  /* winblows states some . */
  li.QuadPart = 0;
  if(QueryPerformanceFrequency(&li) && li.QuadPart)
    freq_per_msec = li.QuadPart / 1000;
    wait_clock = s_PerformanceClock;
    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 dwIndex=0;
  BOOL bFirst = TRUE;
  //tStopTime = time(NULL) + dwSeconds;
  tStopTime = wait_clock() + dwMilliseconds;
  do {
    if (!bFirst)
      Sleep(1000 /*100*/); // Why 1 second?
      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) {                                          
    // 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;
Comment 1 slakr 2005-09-15 21:17:51 UTC
Sorry, I have a typo in util_win32_init(). s_PerformanceClock should be freq_clock.