Lely core libraries  2.3.4
timer.c
Go to the documentation of this file.
1 
24 #include "io.h"
25 
26 #if !LELY_NO_STDIO && _WIN32
27 
28 #include "../timer.h"
29 #include <lely/io2/ctx.h>
30 #include <lely/io2/sys/clock.h>
31 #include <lely/io2/sys/timer.h>
32 #include <lely/io2/win32/poll.h>
33 #include <lely/util/errnum.h>
34 #include <lely/util/time.h>
35 #include <lely/util/util.h>
36 
37 #include <assert.h>
38 #include <stdlib.h>
39 
40 static io_ctx_t *io_timer_impl_dev_get_ctx(const io_dev_t *dev);
41 static ev_exec_t *io_timer_impl_dev_get_exec(const io_dev_t *dev);
42 static size_t io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task);
43 static size_t io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task);
44 
45 // clang-format off
46 static const struct io_dev_vtbl io_timer_impl_dev_vtbl = {
47  &io_timer_impl_dev_get_ctx,
48  &io_timer_impl_dev_get_exec,
49  &io_timer_impl_dev_cancel,
50  &io_timer_impl_dev_abort
51 };
52 // clang-format on
53 
54 static io_dev_t *io_timer_impl_get_dev(const io_timer_t *timer);
55 static io_clock_t *io_timer_impl_get_clock(const io_timer_t *timer);
56 static int io_timer_impl_getoverrun(const io_timer_t *timer);
57 static int io_timer_impl_gettime(
58  const io_timer_t *timer, struct itimerspec *value);
59 static int io_timer_impl_settime(io_timer_t *timer, int flags,
60  const struct itimerspec *value, struct itimerspec *ovalue);
61 static void io_timer_impl_submit_wait(
62  io_timer_t *timer, struct io_timer_wait *wait);
63 
64 // clang-format off
65 static const struct io_timer_vtbl io_timer_impl_vtbl = {
66  &io_timer_impl_get_dev,
67  &io_timer_impl_get_clock,
68  &io_timer_impl_getoverrun,
69  &io_timer_impl_gettime,
70  &io_timer_impl_settime,
71  &io_timer_impl_submit_wait
72 };
73 // clang-format on
74 
75 static void io_timer_impl_svc_shutdown(struct io_svc *svc);
76 
77 // clang-format off
78 static const struct io_svc_vtbl io_timer_impl_svc_vtbl = {
79  NULL,
80  &io_timer_impl_svc_shutdown
81 };
82 // clang-format on
83 
84 struct io_timer_impl {
85  const struct io_dev_vtbl *dev_vptr;
86  const struct io_timer_vtbl *timer_vptr;
87  struct io_svc svc;
88  io_ctx_t *ctx;
89  ev_exec_t *exec;
90  clockid_t clockid;
91 #if !LELY_NO_THREADS
92  CRITICAL_SECTION CriticalSection1;
93 #endif
94  int shutdown;
95  struct sllist queue;
96 #if !LELY_NO_THREADS
97  CRITICAL_SECTION CriticalSection2;
98 #endif
99  struct itimerspec value;
100  int overrun;
101 #if !LELY_NO_THREADS
102  CRITICAL_SECTION CriticalSection3;
103 #endif
104  HANDLE Timer;
105 };
106 
107 static void CALLBACK io_timer_impl_func(
108  PVOID lpParameter, BOOLEAN TimerOrWaitFired);
109 
110 static inline struct io_timer_impl *io_timer_impl_from_dev(const io_dev_t *dev);
111 static inline struct io_timer_impl *io_timer_impl_from_timer(
112  const io_timer_t *timer);
113 static inline struct io_timer_impl *io_timer_impl_from_svc(
114  const struct io_svc *svc);
115 
116 static void io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
117  struct ev_task *task);
118 
119 void *
120 io_timer_alloc(void)
121 {
122  struct io_timer_impl *impl = malloc(sizeof(*impl));
123  if (!impl) {
124  set_errc(errno2c(errno));
125  return NULL;
126  }
127  return &impl->timer_vptr;
128 }
129 
130 void
131 io_timer_free(void *ptr)
132 {
133  if (ptr)
134  free(io_timer_impl_from_timer(ptr));
135 }
136 
137 io_timer_t *
138 io_timer_init(io_timer_t *timer, io_poll_t *poll, ev_exec_t *exec,
139  clockid_t clockid)
140 {
141  struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
142  assert(poll);
143  assert(exec);
144  io_ctx_t *ctx = io_poll_get_ctx(poll);
145  assert(ctx);
146 
147  impl->dev_vptr = &io_timer_impl_dev_vtbl;
148  impl->timer_vptr = &io_timer_impl_vtbl;
149 
150  impl->svc = (struct io_svc)IO_SVC_INIT(&io_timer_impl_svc_vtbl);
151  impl->ctx = ctx;
152 
153  impl->exec = exec;
154 
155  impl->clockid = clockid;
156 
157 #if !LELY_NO_THREADS
158  InitializeCriticalSection(&impl->CriticalSection1);
159 #endif
160  impl->shutdown = 0;
161  sllist_init(&impl->queue);
162 
163 #if !LELY_NO_THREADS
164  InitializeCriticalSection(&impl->CriticalSection2);
165 #endif
166  impl->value = (struct itimerspec){ { 0, 0 }, { 0, 0 } };
167  impl->overrun = 0;
168 
169 #if !LELY_NO_THREADS
170  InitializeCriticalSection(&impl->CriticalSection3);
171 #endif
172  impl->Timer = NULL;
173 
174  io_ctx_insert(impl->ctx, &impl->svc);
175 
176  return timer;
177 }
178 
179 void
180 io_timer_fini(io_timer_t *timer)
181 {
182  struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
183 
184  io_ctx_remove(impl->ctx, &impl->svc);
185  // Cancel all pending tasks.
186  io_timer_impl_svc_shutdown(&impl->svc);
187 
188  if (impl->Timer)
189  DeleteTimerQueueTimer(NULL, impl->Timer, INVALID_HANDLE_VALUE);
190 
191 #if !LELY_NO_THREADS
192  DeleteCriticalSection(&impl->CriticalSection3);
193  DeleteCriticalSection(&impl->CriticalSection2);
194  DeleteCriticalSection(&impl->CriticalSection1);
195 #endif
196 }
197 
198 io_timer_t *
199 io_timer_create(io_poll_t *poll, ev_exec_t *exec, clockid_t clockid)
200 {
201  DWORD dwErrCode = 0;
202 
203  io_timer_t *timer = io_timer_alloc();
204  if (!timer) {
205  dwErrCode = GetLastError();
206  goto error_alloc;
207  }
208 
209  io_timer_t *tmp = io_timer_init(timer, poll, exec, clockid);
210  if (!tmp) {
211  dwErrCode = GetLastError();
212  goto error_init;
213  }
214  timer = tmp;
215 
216  return timer;
217 
218 error_init:
219  io_timer_free((void *)timer);
220 error_alloc:
221  SetLastError(dwErrCode);
222  return NULL;
223 }
224 
225 void
227 {
228  if (timer) {
229  io_timer_fini(timer);
230  io_timer_free((void *)timer);
231  }
232 }
233 
234 static io_ctx_t *
235 io_timer_impl_dev_get_ctx(const io_dev_t *dev)
236 {
237  const struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
238 
239  return impl->ctx;
240 }
241 
242 static ev_exec_t *
243 io_timer_impl_dev_get_exec(const io_dev_t *dev)
244 {
245  const struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
246 
247  return impl->exec;
248 }
249 
250 static size_t
251 io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task)
252 {
253  struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
254 
255  struct sllist queue;
256  sllist_init(&queue);
257 
258  io_timer_impl_pop(impl, &queue, task);
259 
260  return io_timer_wait_queue_post(&queue, -1, ERROR_OPERATION_ABORTED);
261 }
262 
263 static size_t
264 io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task)
265 {
266  struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
267 
268  struct sllist queue;
269  sllist_init(&queue);
270 
271  io_timer_impl_pop(impl, &queue, task);
272 
273  return ev_task_queue_abort(&queue);
274 }
275 
276 static io_dev_t *
277 io_timer_impl_get_dev(const io_timer_t *timer)
278 {
279  const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
280 
281  return &impl->dev_vptr;
282 }
283 
284 static io_clock_t *
285 io_timer_impl_get_clock(const io_timer_t *timer)
286 {
287  const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
288  assert(impl->clockid == CLOCK_REALTIME
289  || impl->clockid == CLOCK_MONOTONIC);
290 
291  switch (impl->clockid) {
292  case CLOCK_REALTIME: return IO_CLOCK_REALTIME;
293  case CLOCK_MONOTONIC: return IO_CLOCK_MONOTONIC;
294  default: return NULL;
295  }
296 }
297 
298 static int
299 io_timer_impl_getoverrun(const io_timer_t *timer)
300 {
301  const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
302 
303 #if !LELY_NO_THREADS
304  EnterCriticalSection((LPCRITICAL_SECTION)&impl->CriticalSection2);
305 #endif
306  int overrun = impl->overrun;
307 #if !LELY_NO_THREADS
308  LeaveCriticalSection((LPCRITICAL_SECTION)&impl->CriticalSection2);
309 #endif
310 
311  return overrun;
312 }
313 
314 static int
315 io_timer_impl_gettime(const io_timer_t *timer, struct itimerspec *value)
316 {
317  const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
318  assert(value);
319 
320 #if !LELY_NO_THREADS
321  EnterCriticalSection((LPCRITICAL_SECTION)&impl->CriticalSection2);
322 #endif
323  struct itimerspec value_ = impl->value;
324 #if !LELY_NO_THREADS
325  LeaveCriticalSection((LPCRITICAL_SECTION)&impl->CriticalSection2);
326 #endif
327 
328  if (value_.it_value.tv_sec || value_.it_value.tv_nsec) {
329  struct timespec now = { 0, 0 };
330  if (clock_gettime(impl->clockid, &now) == -1) {
331  set_errc(errno2c(errno));
332  return -1;
333  }
334  timespec_sub(&value_.it_value, &now);
335  }
336 
337  if (value)
338  *value = value_;
339 
340  return 0;
341 }
342 
343 static int
344 io_timer_impl_settime(io_timer_t *timer, int flags,
345  const struct itimerspec *value, struct itimerspec *ovalue)
346 {
347  struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
348  assert(value);
349 
350  struct timespec now = { 0, 0 };
351  if (clock_gettime(impl->clockid, &now) == -1) {
352  set_errc(errno2c(errno));
353  return -1;
354  }
355 
356  ULONGLONG DueTime = 0;
357  ULONGLONG Period = 0;
358 
359  struct timespec period = value->it_interval;
360  struct timespec expiry = value->it_value;
361  int arm = expiry.tv_sec || expiry.tv_nsec;
362  if (arm) {
363  if (period.tv_nsec < 0 || period.tv_nsec >= 1000000000l) {
364  SetLastError(ERROR_INVALID_PARAMETER);
365  return -1;
366  }
367  if (period.tv_sec < 0)
368  period = (struct timespec){ 0, 0 };
369  Period = (ULONGLONG)period.tv_sec * 1000
370  + (period.tv_nsec + 999999l) / 1000000l;
371  period.tv_nsec = (Period % 1000) * 1000000l;
372  if (Period > ULONG_MAX) {
373  SetLastError(ERROR_INVALID_PARAMETER);
374  return -1;
375  }
376 
377  if (expiry.tv_nsec < 0 || expiry.tv_nsec >= 1000000000l) {
378  SetLastError(ERROR_INVALID_PARAMETER);
379  return -1;
380  }
381  if (flags & TIMER_ABSTIME)
382  timespec_sub(&expiry, &now);
383  if (expiry.tv_sec < 0)
384  expiry = (struct timespec){ 0, 0 };
385  DueTime = (ULONGLONG)expiry.tv_sec * 1000
386  + (expiry.tv_nsec + 999999l) / 1000000l;
387  if (DueTime > ULONG_MAX) {
388  SetLastError(ERROR_INVALID_PARAMETER);
389  return -1;
390  }
391 
392  timespec_add(&expiry, &now);
393  } else {
394  period = (struct timespec){ 0, 0 };
395  }
396 
397  int result = 0;
398  DWORD dwErrCode = GetLastError();
399 
400 #if !LELY_NO_THREADS
401  EnterCriticalSection(&impl->CriticalSection3);
402 #endif
403 
404  if (impl->Timer) {
405  DeleteTimerQueueTimer(NULL, impl->Timer, INVALID_HANDLE_VALUE);
406  impl->Timer = NULL;
407  }
408 
409 #if !LELY_NO_THREADS
410  EnterCriticalSection(&impl->CriticalSection2);
411 #endif
412 
413  struct itimerspec ovalue_ = impl->value;
414  if (ovalue_.it_value.tv_sec || ovalue_.it_value.tv_nsec)
415  timespec_sub(&ovalue_.it_value, &now);
416  impl->value = (struct itimerspec){ period, expiry };
417  impl->overrun = 0;
418 
419  // TODO: Check if WT_EXECUTEDEFAULT is the best option.
420  ULONG Flags = WT_EXECUTEDEFAULT;
421  if (!Period)
422  Flags |= WT_EXECUTEONLYONCE;
423 
424  // clang-format off
425  if (arm && !CreateTimerQueueTimer(&impl->Timer, NULL,
426  &io_timer_impl_func, impl, DueTime, Period, Flags)) {
427  // clang-format on
428  result = -1;
429  dwErrCode = GetLastError();
430 
431  impl->value = (struct itimerspec){ { 0, 0 }, { 0, 0 } };
432  }
433 
434 #if !LELY_NO_THREADS
435  LeaveCriticalSection(&impl->CriticalSection2);
436  LeaveCriticalSection(&impl->CriticalSection3);
437 #endif
438 
439  if (ovalue)
440  *ovalue = ovalue_;
441 
442  SetLastError(dwErrCode);
443  return result;
444 }
445 
446 static void
447 io_timer_impl_submit_wait(io_timer_t *timer, struct io_timer_wait *wait)
448 {
449  struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
450  assert(wait);
451  struct ev_task *task = &wait->task;
452 
453  if (!task->exec)
454  task->exec = impl->exec;
455  ev_exec_on_task_init(task->exec);
456 
457 #if !LELY_NO_THREADS
458  EnterCriticalSection(&impl->CriticalSection1);
459 #endif
460  if (impl->shutdown) {
461 #if !LELY_NO_THREADS
462  LeaveCriticalSection(&impl->CriticalSection1);
463 #endif
464  io_timer_wait_post(wait, -1, ERROR_OPERATION_ABORTED);
465  } else {
466  sllist_push_back(&impl->queue, &task->_node);
467 #if !LELY_NO_THREADS
468  LeaveCriticalSection(&impl->CriticalSection1);
469 #endif
470  }
471 }
472 
473 static void
474 io_timer_impl_svc_shutdown(struct io_svc *svc)
475 {
476  struct io_timer_impl *impl = io_timer_impl_from_svc(svc);
477  io_dev_t *dev = &impl->dev_vptr;
478 
479 #if !LELY_NO_THREADS
480  EnterCriticalSection(&impl->CriticalSection1);
481 #endif
482  int shutdown = !impl->shutdown;
483  impl->shutdown = 1;
484 #if !LELY_NO_THREADS
485  LeaveCriticalSection(&impl->CriticalSection1);
486 #endif
487 
488  if (shutdown)
489  io_timer_impl_dev_cancel(dev, NULL);
490 }
491 
492 static void CALLBACK
493 io_timer_impl_func(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
494 {
495  struct io_timer_impl *impl = lpParameter;
496  assert(impl);
497  (void)TimerOrWaitFired;
498 
499  int errsv = errno;
500  int overrun = 0;
501  int errc = 0;
502 
503  struct timespec now = { 0, 0 };
504  if (clock_gettime(impl->clockid, &now) == -1) {
505  errc = errno2c(errno);
506  overrun = -1;
507  }
508 
509  struct sllist queue;
510  sllist_init(&queue);
511 
512 #if !LELY_NO_THREADS
513  EnterCriticalSection(&impl->CriticalSection2);
514 #endif
515 
516  if (overrun >= 0) {
517  struct timespec *period = &impl->value.it_interval;
518  struct timespec *expiry = &impl->value.it_value;
519  if (period->tv_sec || period->tv_nsec) {
520  assert(!(period->tv_nsec % 1000000l));
521  ULONG Period = period->tv_sec * 1000
522  + period->tv_nsec / 1000000l;
523  LONGLONG overrun = timespec_diff_msec(&now, expiry)
524  / Period;
525  timespec_add_msec(expiry, (overrun + 1) * Period);
526  impl->overrun = MIN(MAX(overrun, INT_MIN), INT_MAX);
527  } else {
528  *expiry = (struct timespec){ 0, 0 };
529  impl->overrun = 0;
530  }
531  overrun = impl->overrun;
532  }
533 
534  if (overrun >= 0 || errc)
535  sllist_append(&queue, &impl->queue);
536 
537 #if !LELY_NO_THREADS
538  LeaveCriticalSection(&impl->CriticalSection2);
539 #endif
540 
541  io_timer_wait_queue_post(&queue, overrun, errc);
542  errno = errsv;
543 }
544 
545 static inline struct io_timer_impl *
546 io_timer_impl_from_dev(const io_dev_t *dev)
547 {
548  assert(dev);
549 
550  return structof(dev, struct io_timer_impl, dev_vptr);
551 }
552 
553 static inline struct io_timer_impl *
554 io_timer_impl_from_timer(const io_timer_t *timer)
555 {
556  assert(timer);
557 
558  return structof(timer, struct io_timer_impl, timer_vptr);
559 }
560 
561 static inline struct io_timer_impl *
562 io_timer_impl_from_svc(const struct io_svc *svc)
563 {
564  assert(svc);
565 
566  return structof(svc, struct io_timer_impl, svc);
567 }
568 
569 static void
570 io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
571  struct ev_task *task)
572 {
573  assert(impl);
574  assert(queue);
575 
576 #if !LELY_NO_THREADS
577  EnterCriticalSection(&impl->CriticalSection1);
578 #endif
579  if (!task)
580  sllist_append(queue, &impl->queue);
581  else if (sllist_remove(&impl->queue, &task->_node))
582  sllist_push_back(queue, &task->_node);
583 #if !LELY_NO_THREADS
584  LeaveCriticalSection(&impl->CriticalSection1);
585 #endif
586 }
587 
588 #endif // !LELY_NO_STDIO && _WIN32
const struct io_clock_vtbl *const io_clock_t
An abstract clock.
Definition: clock.h:36
This header file is part of the I/O library; it contains the I/O context and service declarations.
void io_ctx_insert(io_ctx_t *ctx, struct io_svc *svc)
Registers an I/O service with an I/O context.
Definition: ctx.c:126
#define IO_SVC_INIT(vptr)
The static initializer for io_svc.
Definition: ctx.h:57
void io_ctx_remove(io_ctx_t *ctx, struct io_svc *svc)
Unregisters an I/O service with an I/O context.
Definition: ctx.c:141
This header file is part of the utilities library; it contains the native and platform-independent er...
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:944
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:46
void ev_exec_on_task_init(ev_exec_t *exec)
Indicates to the specified executor that a task will be submitted for execution in the future.
Definition: exec.h:106
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
Definition: ev.h:29
This header file is part of the I/O library; it contains the I/O system timer declarations.
const struct io_timer_vtbl *const io_timer_t
An abstract timer.
Definition: timer.h:38
This is the public header file of the utilities library.
#define structof(ptr, type, member)
Obtains the address of a structure from the address of one of its members.
Definition: util.h:93
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
#define MAX(a, b)
Returns the maximum of a and b.
Definition: util.h:65
const struct io_dev_vtbl *const io_dev_t
An abstract I/O device.
Definition: dev.h:35
io_ctx_t * io_poll_get_ctx(const io_poll_t *poll)
Returns a pointer to the I/O context with which the I/O polling instance is registered.
Definition: poll.c:275
This header file is part of the I/O library; it contains the I/O polling declarations for Windows.
io_timer_t * io_timer_create(io_poll_t *poll, ev_exec_t *exec, clockid_t clockid)
Creates a new I/O system timer.
Definition: timer.c:243
void io_timer_destroy(io_timer_t *timer)
Destroys an I/O system timer.
Definition: timer.c:270
void sllist_init(struct sllist *list)
Initializes a singly-linked list.
Definition: sllist.h:194
struct sllist * sllist_append(struct sllist *dst, struct sllist *src)
Appends the singly-linked list at src to the one at dst.
Definition: sllist.h:257
struct slnode * sllist_remove(struct sllist *list, struct slnode *node)
Removes a node from a singly-linked list.
Definition: sllist.c:46
void sllist_push_back(struct sllist *list, struct slnode *node)
Pushes a node to the back of a singly-linked list.
Definition: sllist.h:232
This is the internal header file of the Windows-specific I/O declarations.
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
An I/O polling interface.
Definition: poll.c:51
An executable task.
Definition: task.h:41
ev_exec_t * exec
A pointer to the executor to which the task is (to be) submitted.
Definition: task.h:43
Definition: ctx.c:38
The virtual table of an I/O service.
Definition: ctx.h:67
An I/O service.
Definition: ctx.h:49
A wait operation suitable for use with an I/O timer.
Definition: timer.h:54
struct ev_task task
The task (to be) submitted upon completion (or cancellation) of the wait operation.
Definition: timer.h:59
A singly-linked list.
Definition: sllist.h:52
This header file is part of the I/O library; it contains the standard system clock definitions.
#define IO_CLOCK_REALTIME
The io_clock_t pointer representing the POSIX realtime clock.
Definition: clock.h:36
#define IO_CLOCK_MONOTONIC
The io_clock_t pointer representing the POSIX monotonic clock.
Definition: clock.h:43
size_t ev_task_queue_abort(struct sllist *queue)
Aborts the tasks in queue by invoking ev_exec_on_task_fini() for each of them.
Definition: task.c:55
This header file is part of the utilities library; it contains the time function declarations.
void timespec_add(struct timespec *tp, const struct timespec *inc)
Adds the time interval *inc to the time at tp.
Definition: time.h:118
void timespec_sub(struct timespec *tp, const struct timespec *dec)
Subtracts the time interval *dec from the time at tp.
Definition: time.h:165
void timespec_add_msec(struct timespec *tp, uint_least64_t msec)
Adds msec milliseconds to the time at tp.
Definition: time.h:141
int_least64_t timespec_diff_msec(const struct timespec *t1, const struct timespec *t2)
Returns the time difference (in milliseconds) between *t1 and *t2.
Definition: time.h:221