windows/TimeSupport.cpp
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* */
3 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
4 /* Copyright (c) 2005-2009 Rexx Language Association. All rights reserved. */
5 /* */
6 /* This program and the accompanying materials are made available under */
7 /* the terms of the Common Public License v1.0 which accompanies this */
8 /* distribution. A copy is also available at the following address: */
9 /* http://www.oorexx.org/license.html */
10 /* */
11 /* Redistribution and use in source and binary forms, with or */
12 /* without modification, are permitted provided that the following */
13 /* conditions are met: */
14 /* */
15 /* Redistributions of source code must retain the above copyright */
16 /* notice, this list of conditions and the following disclaimer. */
17 /* Redistributions in binary form must reproduce the above copyright */
18 /* notice, this list of conditions and the following disclaimer in */
19 /* the documentation and/or other materials provided with the distribution. */
20 /* */
21 /* Neither the name of Rexx Language Association nor the names */
22 /* of its contributors may be used to endorse or promote products */
23 /* derived from this software without specific prior written permission. */
24 /* */
25 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
26 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
27 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
28 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
29 /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
30 /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
31 /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
32 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
33 /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
34 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
35 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36 /* */
37 /*----------------------------------------------------------------------------*/
38 
39 #include "RexxCore.h"
40 #include "IntegerClass.hpp"
41 #include "RexxDateTime.hpp"
42 #include "Interpreter.hpp"
43 #include "SystemInterpreter.hpp"
44 #include <time.h>
45 
47 
48 #define TIMESLICE_STACKSIZE 2048
49 #define TIMESLICEMS 10
50 
51 
52 #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
53 
54 struct timezone
55 {
56  int tz_minuteswest; // minutes West of Greenwich
57  int tz_dsttime; // the daylight savings time correction
58 };
59 
60 
61 /*********************************************************************/
62 /* */
63 /* Subroutine Name: SysGetCurrentTime */
64 /* */
65 /* Function: gets the time and date from the system clock */
66 /*********************************************************************/
68 /*********************************************************************/
69 /* Function: Return a time stamp to the kernel date/time functions. */
70 /*********************************************************************/
71 {
72  FILETIME systemFileTime;
73  FILETIME localFileTime;
74  uint64_t sysTimeStamp = 0;
75  uint64_t localTimeStamp = 0;
76  // this retrieves the time as UTC, in a form we can do arithmetic with
77  GetSystemTimeAsFileTime(&systemFileTime);
78  // this converts the time to the local time zone
79  FileTimeToLocalFileTime(&systemFileTime, &localFileTime);
80 
81  // now get these as long values so we can do math with them
82  sysTimeStamp |= systemFileTime.dwHighDateTime;
83  sysTimeStamp <<= 32;
84  sysTimeStamp |= systemFileTime.dwLowDateTime;
85  // the resolution of this is in tenths of micro seconds. Convert to microseconds
86  // which is a more realistic value
87  sysTimeStamp = sysTimeStamp / 10UL;
88 
89  localTimeStamp |= localFileTime.dwHighDateTime;
90  localTimeStamp <<= 32;
91  localTimeStamp |= localFileTime.dwLowDateTime;
92  localTimeStamp = localTimeStamp / 10UL;
93 
94  // ok, we can use this to calculate the timestamp directly
95  Date->timeZoneOffset = localTimeStamp - sysTimeStamp;
96  // The Citrix environment gives a strange result here that is off by
97  // a few micro seconds. This gives the DateTime class fits, so do an
98  // extra rounding operation here to the nearest second. If the offset is
99  // negative, then do the rounding using the absolute value
100  if ( Date->timeZoneOffset < 0)
101  {
102  Date->timeZoneOffset = -Date->timeZoneOffset;
103  Date->timeZoneOffset = (Date->timeZoneOffset + 500000UL) / 1000000UL;
104  Date->timeZoneOffset *= 1000000UL;
105  Date->timeZoneOffset = -Date->timeZoneOffset;
106  }
107  else
108  {
109  Date->timeZoneOffset = (Date->timeZoneOffset + 500000UL) / 1000000UL;
110  Date->timeZoneOffset *= 1000000UL;
111  }
112 
113  SYSTEMTIME localTime;
114 
115  // get a localized version of the time
116  FileTimeToSystemTime(&localFileTime, &localTime);
117 
118  Date->hours = localTime.wHour;
119  Date->minutes = localTime.wMinute;
120  Date->seconds = localTime.wSecond;
121  Date->microseconds = localTime.wMilliseconds * 1000;
122  Date->day = localTime.wDay;
123  Date->month = localTime.wMonth;
124  Date->year = localTime.wYear;
125 }
126 
127 /*********************************************************************/
128 /* */
129 /* Subroutine Name: TimeSliceControl */
130 /* */
131 /* The thread function for the time slice timer */
132 /* */
133 /*********************************************************************/
134 
135 DWORD WINAPI TimeSliceControl(void * args)
136 {
137 #ifdef TIMESLICE
138  do
139  {
140  Sleep(TIMESLICEMS);
142  } while (!Interpreter::isTerminated());
144 #endif
145  return 0;
146 }
147 
148 
150 /******************************************************************************/
151 /* Function: Make sure we have a Timer running and reset TimeSlice Sem */
152 /******************************************************************************/
153 {
154 #ifdef TIMESLICE
155  ULONG thread;
156  if (timeSliceTimerThread == 0)
157  { /* Is there a timer? */
158 
159  /* create a time slice timer thread */
160 
161  timeSliceTimerThread = CreateThread(NULL, TIMESLICE_STACKSIZE, TimeSliceControl, NULL, 0, &thread);
162  SetThreadPriority(timeSliceTimerThread, THREAD_PRIORITY_NORMAL+1); /* set a higher priority */
163  }
165 #endif
166 }
167 
168 
170 /******************************************************************************/
171 /* Function: Stop the time slice timer thread */
172 /******************************************************************************/
173 {
174 #ifdef TIMESLICE
175  TerminateThread(timeSliceTimerThread, 0);
177 #endif
178 }
179 
180 
181 /**
182  * Wait for a window timer message or for an event to be signaled.
183  *
184  * @param hev Handle to an event object. If this event is signaled, the
185  * function returns.
186  */
187 static void waitTimerOrEvent(HANDLE hev)
188 {
189  MSG msg = {0};
190 
191  /** Wait for a window timer message or for the event to be signaled. Note
192  * that MsgWaitForMultipleObjects() will return if a *new* message is
193  * placed in the message queue. Once PeekMesage() is called, all messages
194  * in the queue need to be processed before MsgWaitForMultipleObjects() is
195  * called.
196  */
197  do
198  {
199  while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
200  {
201  // Check for the timer message.
202  if ( msg.message == WM_TIMER )
203  {
204  return;
205  }
206  TranslateMessage(&msg);
207  DispatchMessage(&msg);
208 
209  // Check if signaled.
210  if ( WaitForSingleObject(hev, 0) == WAIT_OBJECT_0 )
211  {
212  return;
213  }
214  }
215  } while ( MsgWaitForMultipleObjects(1, &hev, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1 );
216 }
217 
218 
219 /*********************************************************************/
220 /* */
221 /* Subroutine Name: alarm_starTimer */
222 /* */
223 /* Function: starts a timer and waits for it to expire. */
224 /* An event semaphore is created that can be */
225 /* used to cancel the timer. */
226 /* */
227 /* Arguments: numdays - number of whole days until timer */
228 /* should expire. */
229 /* alarmtime - fractional portion (less than a */
230 /* day) until timer should expire, expressed in */
231 /* milliseconds. */
232 /*********************************************************************/
233 RexxMethod2(int, alarm_startTimer,
234  wholenumber_t, numdays,
235  wholenumber_t, alarmtime)
236 {
237  bool fState = false; /* Initial state of semaphore */
238  unsigned int msecInADay = 86400000; /* number of milliseconds in a day */
239  HANDLE SemHandle = 0; /* Event-semaphore handle */
240  UINT_PTR TimerHandle = 0; /* Timer handle */
241 
242  /* Create an event semaphore that can be used to cancel the alarm. */
243  SemHandle = CreateEvent(NULL, TRUE, fState, NULL);
244  if ( !SemHandle )
245  {
246  context->RaiseException0(Rexx_Error_System_service);
247  return 0;
248  }
249 
250  /* set the state variables */
251  context->SetObjectVariable("EVENTSEMHANDLE", context->NewPointer(SemHandle));
252  context->SetObjectVariable("TIMERSTARTED", context->True());
253 
254  if ( numdays > 0 )
255  {
256  /** Alarm is for some day in the future, start a timer that wakes up
257  * once a day.
258  */
259  TimerHandle = SetTimer(NULL, 0, msecInADay, NULL);
260  if ( TimerHandle == 0 )
261  {
262  /* Couldn't create a timer, raise an exception. */
263  CloseHandle(SemHandle);
264  context->RaiseException0(Rexx_Error_System_service);
265  return 0;
266  }
267 
268  while ( numdays > 0 )
269  {
270  /* Wait for the WM_TIMER message or for the alarm to be canceled. */
271  waitTimerOrEvent(SemHandle);
272 
273  /* Check if the alarm is canceled. */
274  RexxObjectPtr cancelObj = context->GetObjectVariable("CANCELED");
275 
276  if (cancelObj == context->True())
277  {
278  /* Alarm is canceled, delete timer, close semaphore, return. */
279  KillTimer(NULL, TimerHandle);
280  CloseHandle(SemHandle);
281  return 0;
282  }
283  numdays--;
284  }
285  /* Done with the daily timer, delete it. */
286  KillTimer(NULL, TimerHandle);
287  }
288 
289  if ( alarmtime > 0 )
290  {
291  /* Start a timer for the fractional portion of the alarm. */
292  TimerHandle = SetTimer(NULL, 0, (UINT)alarmtime, NULL);
293  if ( !TimerHandle )
294  {
295  /* Couldn't create a timer, raise an exception. */
296  CloseHandle(SemHandle);
297  context->RaiseException0(Rexx_Error_System_service);
298  return 0;
299  }
300 
301  /* Wait for the WM_TIMER message or for the alarm to be canceled. */
302  waitTimerOrEvent(SemHandle);
303 
304  /** Total alarm time has expired, or the alarm was canceled. We don't
305  * care which, just clean up and return.
306  */
307  KillTimer(NULL, TimerHandle);
308  }
309 
310  CloseHandle(SemHandle);
311  return 0;
312 }
313 
314 
315 /*********************************************************************/
316 /* */
317 /* Subroutine Name: alarm_stopTimer */
318 /* */
319 /* Function: stops an asynchronous timer. */
320 /* */
321 /* Arguments: eventSemHandle - handle to event semaphore */
322 /* used to signal the timer should be canceled. */
323 /*********************************************************************/
324 RexxMethod1(int, alarm_stopTimer, POINTER, eventSemHandle)
325 {
326  /* Post the event semaphore to signal the alarm should be canceled. */
327  if ( ! SetEvent((HANDLE)eventSemHandle) )
328  {
329  /* Raise an error if the semaphore could not be posted. */
330  context->RaiseException0(Rexx_Error_System_service);
331  }
332  return 0;
333 }
334 
static void clearTimeSliceElapsed()
static void setTimeSliceElapsed()
static bool isTerminated()
Definition: Interpreter.hpp:91
int64_t timeZoneOffset
static void getCurrentTime(RexxDateTime *Date)
static void startTimeSlice()
static void setTimeSliceTimerThread(HANDLE h)
#define Rexx_Error_System_service
Definition: oorexxerrors.h:451
struct _RexxObjectPtr * RexxObjectPtr
Definition: rexx.h:127
ssize_t wholenumber_t
Definition: rexx.h:230
void * POINTER
Definition: rexx.h:79
RexxMethod1(int, alarm_stopTimer, POINTER, eventSemHandle)
static void waitTimerOrEvent(HANDLE hev)
#define TIMESLICE_STACKSIZE
RexxMethod2(int, alarm_startTimer, wholenumber_t, numdays, wholenumber_t, alarmtime)
#define TIMESLICEMS
DWORD WINAPI TimeSliceControl(void *args)
unsigned __int64 uint64_t