Bug 36676 - time() bug in httpd/os/win32/util_win32.c:wait_for_many_objects()
Summary: time() bug in httpd/os/win32/util_win32.c:wait_for_many_objects()
Status: NEW
Alias: None
Product: Apache httpd-2
Classification: Unclassified
Component: Core (show other bugs)
Version: 2.5-HEAD
Hardware: PC Windows Server 2003
: P2 normal (vote)
Target Milestone: ---
Assignee: Apache HTTPD Bugs Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-09-15 21:07 UTC by slakr
Modified: 2011-11-16 22:35 UTC (History)
0 users



Attachments

Note You need to log in before you can comment on or make changes to this bug.
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. 

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;
}
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.