summaryrefslogtreecommitdiffstats
path: root/libc/bionic/posix_timers.cpp
blob: 3c664d9c857c3148d7aaf59ccd0fda0b24e5c52b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/*
 * Copyright (C) 2008 The Android Open Source Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "pthread_internal.h"
#include "private/bionic_futex.h"
#include "private/kernel_sigset_t.h"

#include <errno.h>
#include <stdio.h>
#include <string.h>

// System calls.
extern "C" int __rt_sigtimedwait(const sigset_t*, siginfo_t*, const struct timespec*, size_t);
extern "C" int __timer_create(clockid_t, sigevent*, __kernel_timer_t*);
extern "C" int __timer_delete(__kernel_timer_t);
extern "C" int __timer_getoverrun(__kernel_timer_t);
extern "C" int __timer_gettime(__kernel_timer_t, itimerspec*);
extern "C" int __timer_settime(__kernel_timer_t, int, const itimerspec*, itimerspec*);

// Most POSIX timers are handled directly by the kernel. We translate SIGEV_THREAD timers
// into SIGEV_THREAD_ID timers so the kernel handles all the time-related stuff and we just
// need to worry about running user code on a thread.

// We can't use SIGALRM because too many other C library functions throw that around, and since
// they don't send to a specific thread, all threads are eligible to handle the signal and we can
// end up with one of our POSIX timer threads handling it (meaning that the intended recipient
// doesn't). glibc uses SIGRTMIN for its POSIX timer implementation, so in the absence of any
// reason to use anything else, we use that too.
static const int TIMER_SIGNAL = (__SIGRTMIN + 0);

struct PosixTimer {
  __kernel_timer_t kernel_timer_id;

  int sigev_notify;

  // These fields are only needed for a SIGEV_THREAD timer.
  pthread_t callback_thread;
  void (*callback)(sigval_t);
  sigval_t callback_argument;
  volatile bool armed;
};

static __kernel_timer_t to_kernel_timer_id(timer_t timer) {
  return reinterpret_cast<PosixTimer*>(timer)->kernel_timer_id;
}

static void* __timer_thread_start(void* arg) {
  PosixTimer* timer = reinterpret_cast<PosixTimer*>(arg);

  kernel_sigset_t sigset;
  sigaddset(sigset.get(), TIMER_SIGNAL);

  while (true) {
    // Wait for a signal...
    siginfo_t si;
    memset(&si, 0, sizeof(si));
    int rc = __rt_sigtimedwait(sigset.get(), &si, NULL, sizeof(sigset));
    if (rc == -1) {
      continue;
    }

    if (si.si_code == SI_TIMER && timer->armed) {
      // This signal was sent because a timer fired, so call the callback.
      timer->callback(timer->callback_argument);
    } else if (si.si_code == SI_TKILL) {
      // This signal was sent because someone wants us to exit.
      free(timer);
      return NULL;
    }
  }
}

static void __timer_thread_stop(PosixTimer* timer) {
  // Immediately mark the timer as disarmed so even if some events
  // continue to happen, the callback won't be called.
  timer->armed = false;
  pthread_kill(timer->callback_thread, TIMER_SIGNAL);
}

// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_create.html
int timer_create(clockid_t clock_id, sigevent* evp, timer_t* timer_id) {
  PosixTimer* timer = reinterpret_cast<PosixTimer*>(malloc(sizeof(PosixTimer)));
  if (timer == NULL) {
    return -1;
  }

  timer->sigev_notify = (evp == NULL) ? SIGEV_SIGNAL : evp->sigev_notify;

  // If not a SIGEV_THREAD timer, the kernel can handle it without our help.
  if (timer->sigev_notify != SIGEV_THREAD) {
    if (__timer_create(clock_id, evp, &timer->kernel_timer_id) == -1) {
      free(timer);
      return -1;
    }

    *timer_id = timer;
    return 0;
  }

  // Otherwise, this must be SIGEV_THREAD timer...
  timer->callback = evp->sigev_notify_function;
  timer->callback_argument = evp->sigev_value;
  timer->armed = false;

  // Check arguments that the kernel doesn't care about but we do.
  if (timer->callback == NULL) {
    free(timer);
    errno = EINVAL;
    return -1;
  }

  // Create this timer's thread.
  pthread_attr_t thread_attributes;
  if (evp->sigev_notify_attributes == NULL) {
    pthread_attr_init(&thread_attributes);
  } else {
    thread_attributes = *reinterpret_cast<pthread_attr_t*>(evp->sigev_notify_attributes);
  }
  pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);

  // We start the thread with TIMER_SIGNAL blocked by blocking the signal here and letting it
  // inherit. If it tried to block the signal itself, there would be a race.
  kernel_sigset_t sigset;
  sigaddset(sigset.get(), TIMER_SIGNAL);
  kernel_sigset_t old_sigset;
  pthread_sigmask(SIG_BLOCK, sigset.get(), old_sigset.get());

  int rc = pthread_create(&timer->callback_thread, &thread_attributes, __timer_thread_start, timer);

  pthread_sigmask(SIG_SETMASK, old_sigset.get(), NULL);

  if (rc != 0) {
    free(timer);
    errno = rc;
    return -1;
  }

  sigevent se = *evp;
  se.sigev_signo = TIMER_SIGNAL;
  se.sigev_notify = SIGEV_THREAD_ID;
  se.sigev_notify_thread_id = pthread_gettid_np(timer->callback_thread);
  if (__timer_create(clock_id, &se, &timer->kernel_timer_id) == -1) {
    __timer_thread_stop(timer);
    return -1;
  }

  // Give the thread a meaningful name.
  // It can't do this itself because the kernel timer isn't created until after it's running.
  char name[32];
  snprintf(name, sizeof(name), "POSIX interval timer %d", to_kernel_timer_id(timer));
  pthread_setname_np(timer->callback_thread, name);

  *timer_id = timer;
  return 0;
}

// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_delete.html
int timer_delete(timer_t id) {
  int rc = __timer_delete(to_kernel_timer_id(id));
  if (rc == -1) {
    return -1;
  }

  PosixTimer* timer = reinterpret_cast<PosixTimer*>(id);
  if (timer->sigev_notify == SIGEV_THREAD) {
    // Stopping the timer's thread frees the timer data when it's safe.
    __timer_thread_stop(timer);
  } else {
    // For timers without threads, we can just free right away.
    free(timer);
  }

  return 0;
}

// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
int timer_gettime(timer_t id, itimerspec* ts) {
  return __timer_gettime(to_kernel_timer_id(id), ts);
}

// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
int timer_settime(timer_t id, int flags, const itimerspec* ts, itimerspec* ots) {
  PosixTimer* timer= reinterpret_cast<PosixTimer*>(id);
  int rc = __timer_settime(timer->kernel_timer_id, flags, ts, ots);
  if (rc == 0) {
    // Mark the timer as either being armed or disarmed. This avoids the
    // callback being called after the disarm for SIGEV_THREAD timers only.
    if (ts->it_value.tv_sec != 0 || ts->it_value.tv_nsec != 0) {
      timer->armed = true;
    } else {
      timer->armed = false;
    }
  }
  return rc;
}

// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
int timer_getoverrun(timer_t id) {
  return __timer_getoverrun(to_kernel_timer_id(id));
}