Lely core libraries  2.2.5
timer.c
Go to the documentation of this file.
1 
24 #include "io.h"
25 
26 #ifdef __linux__
27 
28 #include "../timer.h"
29 #include <lely/io2/ctx.h>
30 #include <lely/io2/posix/poll.h>
31 #include <lely/io2/sys/clock.h>
32 #include <lely/io2/sys/timer.h>
33 #include <lely/util/util.h>
34 
35 #include <assert.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <stdlib.h>
39 
40 #if !LELY_NO_THREADS
41 #include <pthread.h>
42 #include <sched.h>
43 #endif
44 #include <unistd.h>
45 
46 #include <sys/timerfd.h>
47 
48 static io_ctx_t *io_timer_impl_dev_get_ctx(const io_dev_t *dev);
49 static ev_exec_t *io_timer_impl_dev_get_exec(const io_dev_t *dev);
50 static size_t io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task);
51 static size_t io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task);
52 
53 // clang-format off
54 static const struct io_dev_vtbl io_timer_impl_dev_vtbl = {
55  &io_timer_impl_dev_get_ctx,
56  &io_timer_impl_dev_get_exec,
57  &io_timer_impl_dev_cancel,
58  &io_timer_impl_dev_abort
59 };
60 // clang-format on
61 
62 static io_dev_t *io_timer_impl_get_dev(const io_timer_t *timer);
63 static io_clock_t *io_timer_impl_get_clock(const io_timer_t *timer);
64 static int io_timer_impl_getoverrun(const io_timer_t *timer);
65 static int io_timer_impl_gettime(
66  const io_timer_t *timer, struct itimerspec *value);
67 static int io_timer_impl_settime(io_timer_t *timer, int flags,
68  const struct itimerspec *value, struct itimerspec *ovalue);
69 static void io_timer_impl_submit_wait(
70  io_timer_t *timer, struct io_timer_wait *wait);
71 
72 // clang-format off
73 static const struct io_timer_vtbl io_timer_impl_vtbl = {
74  &io_timer_impl_get_dev,
75  &io_timer_impl_get_clock,
76  &io_timer_impl_getoverrun,
77  &io_timer_impl_gettime,
78  &io_timer_impl_settime,
79  &io_timer_impl_submit_wait
80 };
81 // clang-format on
82 
83 static int io_timer_impl_svc_notify_fork(
84  struct io_svc *svc, enum io_fork_event e);
85 static void io_timer_impl_svc_shutdown(struct io_svc *svc);
86 
87 // clang-format off
88 static const struct io_svc_vtbl io_timer_impl_svc_vtbl = {
89  &io_timer_impl_svc_notify_fork,
90  &io_timer_impl_svc_shutdown
91 };
92 // clang-format on
93 
94 struct io_timer_impl {
95  const struct io_dev_vtbl *dev_vptr;
96  const struct io_timer_vtbl *timer_vptr;
97  io_poll_t *poll;
98  struct io_svc svc;
99  io_ctx_t *ctx;
100  ev_exec_t *exec;
101  clockid_t clockid;
102  struct io_poll_watch watch;
103  int tfd;
104  struct ev_task wait_task;
105 #if !LELY_NO_THREADS
106  pthread_mutex_t mtx;
107 #endif
108  unsigned shutdown : 1;
109  unsigned wait_posted : 1;
110  struct sllist wait_queue;
111  int overrun;
112 };
113 
114 static void io_timer_impl_watch_func(struct io_poll_watch *watch, int events);
115 static void io_timer_impl_wait_task_func(struct ev_task *task);
116 
117 static inline struct io_timer_impl *io_timer_impl_from_dev(const io_dev_t *dev);
118 static inline struct io_timer_impl *io_timer_impl_from_timer(
119  const io_timer_t *timer);
120 static inline struct io_timer_impl *io_timer_impl_from_svc(
121  const struct io_svc *svc);
122 
123 static void io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
124  struct ev_task *task);
125 
126 static int io_timer_impl_open(struct io_timer_impl *impl);
127 static int io_timer_impl_close(struct io_timer_impl *impl);
128 
129 void *
130 io_timer_alloc(void)
131 {
132  struct io_timer_impl *impl = malloc(sizeof(*impl));
133  // cppcheck-suppress memleak symbolName=impl
134  return impl ? &impl->timer_vptr : NULL;
135 }
136 
137 void
138 io_timer_free(void *ptr)
139 {
140  if (ptr)
141  free(io_timer_impl_from_timer(ptr));
142 }
143 
144 io_timer_t *
145 io_timer_init(io_timer_t *timer, io_poll_t *poll, ev_exec_t *exec,
146  clockid_t clockid)
147 {
148  struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
149  assert(poll);
150  assert(exec);
151  io_ctx_t *ctx = io_poll_get_ctx(poll);
152  assert(ctx);
153 
154  int errsv = 0;
155 
156  impl->dev_vptr = &io_timer_impl_dev_vtbl;
157  impl->timer_vptr = &io_timer_impl_vtbl;
158 
159  impl->poll = poll;
160 
161  impl->svc = (struct io_svc)IO_SVC_INIT(&io_timer_impl_svc_vtbl);
162  impl->ctx = ctx;
163 
164  impl->exec = exec;
165 
166  impl->clockid = clockid;
167 
168  impl->watch = (struct io_poll_watch)IO_POLL_WATCH_INIT(
169  &io_timer_impl_watch_func);
170  impl->tfd = -1;
171 
172  impl->wait_task = (struct ev_task)EV_TASK_INIT(
173  impl->exec, &io_timer_impl_wait_task_func);
174 
175 #if !LELY_NO_THREADS
176  if ((errsv = pthread_mutex_init(&impl->mtx, NULL)))
177  goto error_init_mtx;
178 #endif
179 
180  impl->shutdown = 0;
181  impl->wait_posted = 0;
182 
183  sllist_init(&impl->wait_queue);
184 
185  impl->overrun = 0;
186 
187  if (io_timer_impl_open(impl) == -1) {
188  errsv = errno;
189  goto error_open;
190  }
191 
192  io_ctx_insert(impl->ctx, &impl->svc);
193 
194  return timer;
195 
196  // io_timer_impl_close(impl);
197 error_open:
198 #if !LELY_NO_THREADS
199  pthread_mutex_destroy(&impl->mtx);
200 error_init_mtx:
201 #endif
202  errno = errsv;
203  return NULL;
204 }
205 
206 void
207 io_timer_fini(io_timer_t *timer)
208 {
209  struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
210 
211  io_ctx_remove(impl->ctx, &impl->svc);
212  // Cancel all pending tasks.
213  io_timer_impl_svc_shutdown(&impl->svc);
214 
215 #if !LELY_NO_THREADS
216  pthread_mutex_lock(&impl->mtx);
217  // If necessary, busy-wait until io_timer_impl_wait_task_func()
218  // completes.
219  while (impl->wait_posted) {
220  // Try to abort io_timer_impl_wait_task_func().
221  if (ev_exec_abort(impl->wait_task.exec, &impl->wait_task))
222  break;
223  pthread_mutex_unlock(&impl->mtx);
224  sched_yield();
225  pthread_mutex_lock(&impl->mtx);
226  }
227  pthread_mutex_unlock(&impl->mtx);
228 #endif
229 
230  // Disarm the timer.
231  io_timer_impl_close(impl);
232 
233 #if !LELY_NO_THREADS
234  pthread_mutex_destroy(&impl->mtx);
235 #endif
236 }
237 
238 io_timer_t *
239 io_timer_create(io_poll_t *poll, ev_exec_t *exec, clockid_t clockid)
240 {
241  int errsv = 0;
242 
243  io_timer_t *timer = io_timer_alloc();
244  if (!timer) {
245  errsv = errno;
246  goto error_alloc;
247  }
248 
249  io_timer_t *tmp = io_timer_init(timer, poll, exec, clockid);
250  if (!tmp) {
251  errsv = errno;
252  goto error_init;
253  }
254  timer = tmp;
255 
256  return timer;
257 
258 error_init:
259  io_timer_free((void *)timer);
260 error_alloc:
261  errno = errsv;
262  return NULL;
263 }
264 
265 void
267 {
268  if (timer) {
269  io_timer_fini(timer);
270  io_timer_free((void *)timer);
271  }
272 }
273 
274 static io_ctx_t *
275 io_timer_impl_dev_get_ctx(const io_dev_t *dev)
276 {
277  const struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
278 
279  return impl->ctx;
280 }
281 
282 static ev_exec_t *
283 io_timer_impl_dev_get_exec(const io_dev_t *dev)
284 {
285  const struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
286 
287  return impl->exec;
288 }
289 
290 static size_t
291 io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task)
292 {
293  struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
294 
295  struct sllist queue;
296  sllist_init(&queue);
297 
298  io_timer_impl_pop(impl, &queue, task);
299 
300  return io_timer_wait_queue_post(&queue, -1, ECANCELED);
301 }
302 
303 static size_t
304 io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task)
305 {
306  struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
307 
308  struct sllist queue;
309  sllist_init(&queue);
310 
311  io_timer_impl_pop(impl, &queue, task);
312 
313  return ev_task_queue_abort(&queue);
314 }
315 
316 static io_dev_t *
317 io_timer_impl_get_dev(const io_timer_t *timer)
318 {
319  const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
320 
321  return &impl->dev_vptr;
322 }
323 
324 static io_clock_t *
325 io_timer_impl_get_clock(const io_timer_t *timer)
326 {
327  const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
328  assert(impl->clockid == CLOCK_REALTIME
329  || impl->clockid == CLOCK_MONOTONIC);
330 
331  switch (impl->clockid) {
332  case CLOCK_REALTIME: return IO_CLOCK_REALTIME;
333  case CLOCK_MONOTONIC: return IO_CLOCK_MONOTONIC;
334  default: return NULL;
335  }
336 }
337 
338 static int
339 io_timer_impl_getoverrun(const io_timer_t *timer)
340 {
341  const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
342 
343 #if !LELY_NO_THREADS
344  pthread_mutex_lock((pthread_mutex_t *)&impl->mtx);
345 #endif
346  int overrun = impl->overrun;
347 #if !LELY_NO_THREADS
348  pthread_mutex_unlock((pthread_mutex_t *)&impl->mtx);
349 #endif
350 
351  return overrun;
352 }
353 
354 static int
355 io_timer_impl_gettime(const io_timer_t *timer, struct itimerspec *value)
356 {
357  const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
358 
359  return timerfd_gettime(impl->tfd, value);
360 }
361 
362 static int
363 io_timer_impl_settime(io_timer_t *timer, int flags,
364  const struct itimerspec *value, struct itimerspec *ovalue)
365 {
366  struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
367 
368  int flags_ = 0;
369  if (flags & TIMER_ABSTIME)
370  flags_ |= TFD_TIMER_ABSTIME;
371 
372  return timerfd_settime(impl->tfd, flags_, value, ovalue);
373 }
374 
375 static void
376 io_timer_impl_submit_wait(io_timer_t *timer, struct io_timer_wait *wait)
377 {
378  struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
379  assert(wait);
380  struct ev_task *task = &wait->task;
381 
382  if (!task->exec)
383  task->exec = impl->exec;
384  ev_exec_on_task_init(task->exec);
385 
386 #if !LELY_NO_THREADS
387  pthread_mutex_lock(&impl->mtx);
388 #endif
389  if (impl->shutdown) {
390 #if !LELY_NO_THREADS
391  pthread_mutex_unlock(&impl->mtx);
392 #endif
393  io_timer_wait_post(wait, -1, ECANCELED);
394  } else {
395  sllist_push_back(&impl->wait_queue, &task->_node);
396 #if !LELY_NO_THREADS
397  pthread_mutex_unlock(&impl->mtx);
398 #endif
399  }
400 }
401 
402 static int
403 io_timer_impl_svc_notify_fork(struct io_svc *svc, enum io_fork_event e)
404 {
405  struct io_timer_impl *impl = io_timer_impl_from_svc(svc);
406 
407  if (e != IO_FORK_CHILD || impl->shutdown)
408  return 0;
409 
410  int result = 0;
411  int errsv = errno;
412 
413  struct itimerspec value = { { 0, 0 }, { 0, 0 } };
414  if (timerfd_gettime(impl->tfd, &value) == -1 && !result) {
415  errsv = errno;
416  result = -1;
417  }
418 
419  if (io_timer_impl_close(impl) == -1 && !result) {
420  errsv = errno;
421  result = -1;
422  }
423 
424  if (io_timer_impl_open(impl) == -1 && !result) {
425  errsv = errno;
426  result = -1;
427  }
428 
429  if (timerfd_settime(impl->tfd, 0, &value, NULL) == -1 && !result) {
430  errsv = errno;
431  result = -1;
432  }
433 
434  errno = errsv;
435  return result;
436 }
437 
438 static void
439 io_timer_impl_svc_shutdown(struct io_svc *svc)
440 {
441  struct io_timer_impl *impl = io_timer_impl_from_svc(svc);
442  io_dev_t *dev = &impl->dev_vptr;
443 
444 #if !LELY_NO_THREADS
445  pthread_mutex_lock(&impl->mtx);
446 #endif
447  int shutdown = !impl->shutdown;
448  impl->shutdown = 1;
449  if (shutdown) {
450  // Stop monitoring the timer file descriptor.
451  io_poll_watch(impl->poll, impl->tfd, 0, &impl->watch);
452  // Try to abort io_timer_impl_wait_task_func().
453  // clang-format off
454  if (impl->wait_posted && ev_exec_abort(impl->wait_task.exec,
455  &impl->wait_task))
456  // clang-format on
457  impl->wait_posted = 0;
458  }
459 #if !LELY_NO_THREADS
460  pthread_mutex_unlock(&impl->mtx);
461 #endif
462 
463  if (shutdown)
464  // Cancel all pending operations.
465  io_timer_impl_dev_cancel(dev, NULL);
466 }
467 
468 static void
469 io_timer_impl_watch_func(struct io_poll_watch *watch, int events)
470 {
471  assert(watch);
472  struct io_timer_impl *impl =
473  structof(watch, struct io_timer_impl, watch);
474  (void)events;
475 
476 #if !LELY_NO_THREADS
477  pthread_mutex_lock(&impl->mtx);
478 #endif
479  int post_wait = !impl->wait_posted && !impl->shutdown;
480  if (post_wait)
481  impl->wait_posted = 1;
482 #if !LELY_NO_THREADS
483  pthread_mutex_unlock(&impl->mtx);
484 #endif
485  if (post_wait)
486  ev_exec_post(impl->wait_task.exec, &impl->wait_task);
487 }
488 
489 static void
490 io_timer_impl_wait_task_func(struct ev_task *task)
491 {
492  assert(task);
493  struct io_timer_impl *impl =
494  structof(task, struct io_timer_impl, wait_task);
495 
496  int errsv = errno;
497  int overrun = -1;
498  int events = 0;
499 
500  ssize_t result;
501  for (;;) {
502  uintmax_t value = 0;
503  do {
504  errno = 0;
505  result = read(impl->tfd, &value, sizeof(value));
506  } while (result == -1 && errno == EINTR);
507  if (result != sizeof(value))
508  break;
509 
510  if (value > (uintmax_t)INT_MAX + 1)
511  value = (uintmax_t)INT_MAX + 1;
512  if (overrun > (int)(INT_MAX - value))
513  overrun = INT_MAX;
514  else
515  overrun += value;
516  }
517  if (result == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
518  errno = 0;
519  events |= IO_EVENT_IN;
520  }
521 
522  struct sllist queue;
523  sllist_init(&queue);
524 
525 #if !LELY_NO_THREADS
526  pthread_mutex_lock(&impl->mtx);
527 #endif
528  if (overrun >= 0)
529  impl->overrun = overrun;
530  if (overrun >= 0 || errno)
531  sllist_append(&queue, &impl->wait_queue);
532 
533  if (events && !impl->shutdown)
534  io_poll_watch(impl->poll, impl->tfd, events, &impl->watch);
535  impl->wait_posted = 0;
536 #if !LELY_NO_THREADS
537  pthread_mutex_unlock(&impl->mtx);
538 #endif
539 
540  io_timer_wait_queue_post(&queue, overrun, errno);
541  errno = errsv;
542 }
543 
544 static inline struct io_timer_impl *
545 io_timer_impl_from_dev(const io_dev_t *dev)
546 {
547  assert(dev);
548 
549  return structof(dev, struct io_timer_impl, dev_vptr);
550 }
551 
552 static inline struct io_timer_impl *
553 io_timer_impl_from_timer(const io_timer_t *timer)
554 {
555  assert(timer);
556 
557  return structof(timer, struct io_timer_impl, timer_vptr);
558 }
559 
560 static inline struct io_timer_impl *
561 io_timer_impl_from_svc(const struct io_svc *svc)
562 {
563  assert(svc);
564 
565  return structof(svc, struct io_timer_impl, svc);
566 }
567 
568 static void
569 io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
570  struct ev_task *task)
571 {
572  assert(impl);
573  assert(queue);
574 
575 #if !LELY_NO_THREADS
576  pthread_mutex_lock(&impl->mtx);
577 #endif
578  if (!task)
579  sllist_append(queue, &impl->wait_queue);
580  else if (sllist_remove(&impl->wait_queue, &task->_node))
581  sllist_push_back(queue, &task->_node);
582 #if !LELY_NO_THREADS
583  pthread_mutex_unlock(&impl->mtx);
584 #endif
585 }
586 
587 static int
588 io_timer_impl_open(struct io_timer_impl *impl)
589 {
590  assert(impl);
591 
592  int errsv = 0;
593 
594  if (io_timer_impl_close(impl) == -1) {
595  errsv = errno;
596  goto error_close;
597  }
598 
599  impl->tfd = timerfd_create(impl->clockid, TFD_NONBLOCK | TFD_CLOEXEC);
600  if (impl->tfd == -1) {
601  errsv = errno;
602  goto error_timerfd_create;
603  }
604 
605  if (io_poll_watch(impl->poll, impl->tfd, IO_EVENT_IN, &impl->watch)
606  == -1) {
607  errsv = errno;
608  goto error_poll_watch;
609  }
610 
611  return 0;
612 
613 error_poll_watch:
614  close(impl->tfd);
615  impl->tfd = -1;
616 error_timerfd_create:
617 error_close:
618  errno = errsv;
619  return -1;
620 }
621 
622 static int
623 io_timer_impl_close(struct io_timer_impl *impl)
624 {
625  assert(impl);
626 
627  int tfd = impl->tfd;
628  if (tfd == -1)
629  return 0;
630  impl->tfd = -1;
631 
632  int result = 0;
633  int errsv = errno;
634 
635  if (!impl->shutdown
636  && io_poll_watch(impl->poll, tfd, 0, &impl->watch) == -1
637  && !result) {
638  errsv = errno;
639  result = -1;
640  }
641 
642  if (close(tfd) == -1 && !result) {
643  errsv = errno;
644  result = -1;
645  }
646 
647  errno = errsv;
648  return result;
649 }
650 
651 #endif // __linux__
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:121
io_fork_event
The type of event generated by an I/O context before and after a process fork.
Definition: ctx.h:37
@ IO_FORK_CHILD
The event generated after the fork in the child process.
Definition: ctx.h:43
#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:136
@ IO_EVENT_IN
Data (other than priority data) MAY be read without blocking.
Definition: event.h:35
size_t ev_exec_abort(ev_exec_t *exec, struct ev_task *task)
Aborts the specified task submitted to *exec, if it has not yet begun executing, or all pending tasks...
Definition: exec.h:138
void ev_exec_post(ev_exec_t *exec, struct ev_task *task)
Submits *task to *exec for execution.
Definition: exec.h:126
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:108
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
const struct io_dev_vtbl *const io_dev_t
An abstract I/O device.
Definition: dev.h:35
This header file is part of the I/O library; it contains the I/O polling declarations for POSIX platf...
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
#define IO_POLL_WATCH_INIT(func)
The static initializer for io_poll_watch.
Definition: poll.h:65
int io_poll_watch(io_poll_t *poll, io_handle_t handle, struct io_event *event, int keep)
Registers an I/O device with an I/O polling interface and instructs it to watch for certain events.
Definition: poll.c:249
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:239
void io_timer_destroy(io_timer_t *timer)
Destroys an I/O system timer.
Definition: timer.c:266
void sllist_init(struct sllist *list)
Initializes a singly-linked list.
Definition: sllist.h:184
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:233
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:213
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:48
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:35
An object representing a file descriptor being monitored for I/O events.
Definition: poll.h:56
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:51
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
#define EV_TASK_INIT(exec, func)
The static initializer for ev_task.
Definition: task.h:53
This header file is part of the C11 and POSIX compatibility library; it includes <unistd....