(file) Return to timer.c CVS log (file) (dir) Up to [OMI] / omi / base

  1 krisbash 1.1 #include <pal/palcommon.h>
  2              #include <pal/atomic.h>
  3              #include <pal/lock.h>
  4              #include <pal/sleep.h>
  5              #include <base/Strand.h>
  6              #include <base/log.h>
  7              /*
  8               *==============================================================================
  9               *
 10               *
 11               * Windows Implementation
 12               *
 13               *
 14               *==============================================================================
 15               */
 16              #if defined(CONFIG_OS_WINDOWS)
 17              
 18              void Timer_SetSelector(
 19                  _In_ Selector* selector )
 20              {
 21                  /* Selector is not needed for Windows, so this is a NO-OP */
 22 krisbash 1.1     PAL_UNUSED(selector);
 23              }
 24              
 25              VOID CALLBACK _WinTimerCallback(
 26                _Inout_      PTP_CALLBACK_INSTANCE Instance,
 27                _Inout_opt_  PVOID Context,
 28                _Inout_      PTP_TIMER pTimer )
 29              {
 30                  HMODULE newHModule;
 31                  PAL_UNUSED(pTimer);
 32              
 33                  /* CallbackMayRunLong provides a hint to thread pool that this handler
 34                   * may take a long time to execute.  It allows the thread pool to optimize
 35                   * as needed. */
 36                  if (CallbackMayRunLong( Instance ))
 37                  {
 38                      trace_Timer_CallbackMayRunLong_True();
 39                  }
 40                  else
 41                  {
 42                      trace_Timer_CallbackMayRunLong_False();
 43 krisbash 1.1     }
 44              
 45                  GetModuleHandleExW(
 46                      GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
 47                      (LPCWSTR) _WinTimerCallback,
 48                      &newHModule);
 49              
 50                  FreeLibraryWhenCallbackReturns(Instance, newHModule);
 51              
 52                  _Strand_ScheduleTimer( (Strand*)Context );
 53              }
 54              
 55              /* SHIM to fault inject CreateThreadpoolTimer */
 56              PTP_TIMER WINAPI CreateThreadpoolTimer_Injected(_In_ PTP_TIMER_CALLBACK pfnti,  
 57                                                     _Inout_opt_  PVOID pv,
 58                                                     _In_opt_     PTP_CALLBACK_ENVIRON pcbe,
 59                                                     NitsCallSite cs)
 60              {
 61                  return CreateThreadpoolTimer(pfnti, pv, pcbe);
 62              }
 63              
 64 krisbash 1.1 /* Starts a Timer using the specified values. */
 65              _Use_decl_annotations_
 66              TimerResult Timer_Start( 
 67                  Timer* timer, 
 68                  Strand* strand )
 69              {
 70                  DEBUG_ASSERT( timer );
 71                  DEBUG_ASSERT( strand );
 72                  
 73                  if (NULL != timer->pTimer)
 74                  {
 75                      trace_Timer_CannotStartTimer_AlreadyRunning( timer );
 76                      return TimerResult_InvalidArgument;
 77                  }
 78              
 79                  timer->reason = TimerReason_Expired;
 80                  
 81                  timer->pTimer = CreateThreadpoolTimer_Injected( _WinTimerCallback, strand, NULL, NitsHere() );
 82              
 83                  if (NULL == timer->pTimer)
 84                  {
 85 krisbash 1.1         trace_Timer_Initialization_Failed(GetLastError());
 86                      return TimerResult_Failed;
 87                  }
 88                  /* Convert specified interval and start the timer */
 89                  {
 90                      FILETIME ft;
 91                      ULARGE_INTEGER tmp;
 92              
 93                      tmp.QuadPart = 0;
 94                      tmp.QuadPart -= timer->timeoutInUsec * 10; /* convert to 100 nanosecond units and set time */
 95                      ft.dwLowDateTime = tmp.u.LowPart;
 96                      ft.dwHighDateTime = tmp.u.HighPart;
 97              
 98                      SetThreadpoolTimer( timer->pTimer, &ft, 0, 0);
 99                  }
100              
101                  trace_Timer_Started_MSCVER(timer->timeoutInUsec * 10);
102                  return TimerResult_Success;
103              }
104              
105              _Use_decl_annotations_
106 krisbash 1.1 void Timer_Fire(
107                  Timer* timer,
108                  Strand* strand, 
109                  TimerReason reason )
110              {
111                  DEBUG_ASSERT( timer );
112                  DEBUG_ASSERT( strand );
113              
114                  /* Timer_Close NULLs pTimer, so a NULL value means that the Timer has been
115                   * closed, but not restarted. */
116                  if (NULL != timer->pTimer)
117                  {
118                      SetThreadpoolTimer( timer->pTimer, NULL, 0, 0 );   /* Triggers immediate cancel and clears pending callbacks */
119                      WaitForThreadpoolTimerCallbacks( timer->pTimer, MI_TRUE ); /* Blocks until all pending callbacks have completed */
120              
121                      timer->reason = reason;
122              
123                      trace_Timer_ManualTrigger( timer, strand );
124                      _Strand_ScheduleTimer( strand );
125                  }
126              }
127 krisbash 1.1 
128              _Use_decl_annotations_
129              void Timer_Close(
130                  Timer* timer )
131              {
132                  DEBUG_ASSERT( timer );
133                  
134                  /* 
135                   * Note: It is not necessary to call SetThreadpoolTimer( , NULL, , ) and
136                   * WaitForThreadpoolTimerCallbacks( , MI_TRUE ) here because the timer will
137                   * have already been cancelled or triggered in order for execution to reach
138                   * this point.
139                   * 
140                   * Per MSDN: pTimer is freed immediately if ther are no callbacks pending.
141                   *           Otherwise it is freed after the last callback completes.
142                   */
143                  if (NULL == timer ||
144                      NULL == timer->pTimer)
145                  {
146                      trace_Timer_Double_Close( timer );
147                  }
148 krisbash 1.1     else
149                  {
150                      CloseThreadpoolTimer( timer->pTimer );
151                      timer->pTimer = NULL;
152                      timer->reason = TimerReason_Expired;
153                      trace_Timer_Close( timer );
154                  }
155              }
156              
157              #endif /* defined(CONFIG_OS_WINDOWS) */
158              
159              /*
160               *==============================================================================
161               *
162               *
163               * POSIX Implementation
164               *
165               *
166               *==============================================================================
167               */
168              
169 krisbash 1.1 #if defined(CONFIG_POSIX)
170              
171              static Selector* timerSelector = NULL;
172              
173              void Timer_SetSelector(
174                  _In_ Selector* selector )
175              {
176                  timerSelector = selector;
177              }
178              
179              MI_Boolean _HandlerTimerCallback(
180                  Selector* sel,
181                  Handler* handler,
182                  MI_Uint32 mask, 
183                  MI_Uint64 currentTimeUsec)
184              {
185                  if (mask & SELECTOR_TIMEOUT)
186                  {
187                      /* Returns MI_FALSE to signal it is OK to remove the Handler.
188                       * actual handling will occur once the Selector calls this func
189                       * again with SELECTOR_REMOVE.  This is expected to happen 
190 krisbash 1.1          * immediately. */
191                      return MI_FALSE;
192                  }
193                  else if (mask & SELECTOR_REMOVE || mask & SELECTOR_DESTROY)
194                  {
195                      _Strand_ScheduleTimer( (Strand*)handler->data );
196                  }
197                  else if (mask & SELECTOR_ADD)
198                  {
199                      trace_Timer_Selector_Added();
200                  }
201                  else
202                  {
203                      /* No other mask values should be passed.  If so, this will catch them. */
204                      trace_Timer_Unexpected_Selector_Mask( mask );
205                      DEBUG_ASSERT(MI_FALSE);
206                  }
207              
208                  return MI_TRUE;
209              }
210              
211 krisbash 1.1 /* State checks have already been performed.  This function just needs to
212               * perform the requested action according to its OS type. */
213              _Use_decl_annotations_
214              TimerResult Timer_Start( 
215                  Timer* timer, 
216                  Strand* strand )
217              {
218                  PAL_Uint64 currentTimeUsec = 0;
219              
220                  DEBUG_ASSERT( timer );
221                  DEBUG_ASSERT( strand );
222              
223                  timer->selector = timerSelector; /* Keeping it as a member allows it to be checked once */
224              
225                  if (NULL == timer->selector ||
226                      NULL == timer->selector->rep)
227                  {
228                      trace_Timer_Selector_Missing( timer->selector );
229                      return TimerResult_InvalidArgument;
230                  }
231              
232 krisbash 1.1     if (MI_RESULT_OK == Selector_ContainsHandler(timer->selector, &timer->handler))
233                  {
234                      trace_Timer_CannotStartTimer_AlreadyRunning( timer );
235                      return TimerResult_InvalidArgument;
236                  }
237                  
238                  if (PAL_TRUE != PAL_Time(&currentTimeUsec))
239                  {
240                      trace_Timer_Cannot_AccessCurrentTime();
241                      return TimerResult_Failed;
242                  }
243              
244                  timer->reason = TimerReason_Expired;
245              
246                  timer->handler.fireTimeoutAt = currentTimeUsec + timer->timeoutInUsec;
247                  timer->handler.sock = INVALID_SOCK;
248                  timer->handler.data = strand;
249                  timer->handler.callback = _HandlerTimerCallback;
250              
251                  if (MI_RESULT_OK != Selector_AddHandler( timer->selector, &timer->handler ))
252                  {
253 krisbash 1.1         trace_Timer_Cannot_AddHandlerToSelector( timer->selector );
254                      return TimerResult_Failed;
255                  }
256              
257                  trace_Timer_Started_POSIX( timer->timeoutInUsec );
258                  return TimerResult_Success;
259              }
260              
261              _Use_decl_annotations_
262              void Timer_Fire(
263                  Timer* timer,
264                  Strand* strand,
265                  TimerReason reason )
266              {
267                  DEBUG_ASSERT( timer );
268                  DEBUG_ASSERT( strand );
269              
270                  /* Handler is zero'd during Timer_Start and Timer_Close.  A NULL callback
271                   * means that this timer is not active. */
272                  if (NULL != timer->handler.callback) // TODO: Selector_ContainsHandler is a more thorough check, but less efficient
273                  {
274 krisbash 1.1         PAL_Uint64 currentTimeUsec = 0;
275              
276                      if (PAL_TRUE != PAL_Time(&currentTimeUsec))
277                      {
278                          /* Guarantee it will be a time in the past if current time is not accessible. */
279                          currentTimeUsec = TIME_NEVER + 1;
280                          trace_Timer_Cannot_AccessCurrentTime();
281                      }
282              
283                      if ( TimerReason_Canceled == reason || timer->handler.fireTimeoutAt > currentTimeUsec )
284                      {
285                          /* Due to how Selector works, Selector will not check for timeouts 
286                           * during long running operations.  In those instances, the time
287                           * of fireTimeoutAt will have already occurred, but the Selector
288                           * will have not gotten a chance to detect it yet. That is treated
289                           * as the default scenario.  This signals that the timeout is treated
290                           * as a manuallly triggered timeout. */
291                          timer->reason = reason;
292                      }
293                      timer->handler.fireTimeoutAt = currentTimeUsec;
294              
295 krisbash 1.1         trace_Timer_ManualTrigger( timer, strand );
296                      
297                      Selector_Wakeup( timer->selector, MI_TRUE );
298                  }
299               }
300              
301              _Use_decl_annotations_
302              void Timer_Close(
303                  Timer* timer )
304              {
305                  DEBUG_ASSERT( timer );
306              
307                  if (NULL == timer->handler.callback)
308                  {
309                      trace_Timer_Double_Close( timer );
310                  }
311                  else
312                  {
313                      /* Handler is not present in the Selector's list since this will only 
314                       * trigger, if SELECTOR_REMOVE has gone through _HandlerTimerCallback,
315                       * so it is OK to zero out.
316 krisbash 1.1          */
317                      memset( &timer->handler, 0, sizeof(Handler) );
318                      timer->reason = TimerReason_Expired;
319              
320                      trace_Timer_Close( timer );
321                  }
322              }
323              
324              #endif /* defined(CONFIG_POSIX) */
325              

ViewCVS 0.9.2