// 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. staticconstint TIMER_SIGNAL = (__SIGRTMIN + 0);
// The fields below are only needed for a SIGEV_THREAD timer.
Lock startup_handshake_lock;
pthread_t callback_thread; void (*callback)(sigval_t);
sigval_t callback_argument;
atomic_bool deleted; // Set when the timer is deleted, to prevent further calling of callback.
};
// Check that our parent managed to create the kernel timer and bail if not...
timer->startup_handshake_lock.lock(); if (timer->kernel_timer_id == -1) {
free(timer); return nullptr;
}
// Give ourselves a specific meaningful name now we have a kernel timer. char name[16]; // 16 is the kernel-imposed limit.
snprintf(name, sizeof(name), "POSIX timer %d", to_kernel_timer_id(timer));
pthread_setname_np(timer->callback_thread, name);
while (true) { // Wait for a signal...
siginfo_t si = {}; if (__rt_sigtimedwait(&sigset, &si, nullptr, sizeof(sigset)) == -1) continue;
if (si.si_code == SI_TIMER) { // This signal was sent because a timer fired, so call the callback.
// All events to the callback will be ignored when the timer is deleted. if (atomic_load(&timer->deleted) == true) { continue;
}
timer->callback(timer->callback_argument);
} elseif (si.si_code == SI_TKILL) { // This signal was sent because someone wants us to exit.
free(timer); return nullptr;
}
}
}
// 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; return0;
}
// Otherwise, this must be SIGEV_THREAD timer...
timer->callback = evp->sigev_notify_function;
timer->callback_argument = evp->sigev_value;
atomic_store_explicit(&timer->deleted, false, memory_order_relaxed);
// Check arguments that the kernel doesn't care about but we do. if (timer->callback == nullptr) {
free(timer);
errno = EINVAL; return -1;
}
// 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.
sigset64_t sigset = {};
sigaddset64(&sigset, TIMER_SIGNAL);
sigset64_t old_sigset;
// Prevent the child thread from running until the timer has been created.
timer->startup_handshake_lock.init(false);
timer->startup_handshake_lock.lock();
// Use __rt_sigprocmask instead of sigprocmask64 to avoid filtering out TIMER_SIGNAL.
__rt_sigprocmask(SIG_BLOCK, &sigset, &old_sigset, sizeof(sigset));
int rc = pthread_create(&timer->callback_thread, &thread_attributes, __timer_thread_start, timer);
// Try to create the kernel timer.
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);
rc = __timer_create(clock_id, &se, &timer->kernel_timer_id);
// Let the child run (whether we created the kernel timer or not).
timer->startup_handshake_lock.unlock(); // If __timer_create(2) failed, the child will kill itself and free the // timer struct, so we just need to exit. 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);
}
// https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/timer_settime.html // When using timer_settime to disarm a repeatable SIGEV_THREAD timer with a very small // period (like below 1ms), the kernel may continue to send events to the callback thread // for a few extra times. This behavior is fine because in POSIX standard: The effect of // disarming or resetting a timer with pending expiration notifications is unspecified. int timer_settime(timer_t id, int flags, const itimerspec* ts, itimerspec* ots) {
PosixTimer* timer= reinterpret_cast<PosixTimer*>(id); return __timer_settime(timer->kernel_timer_id, flags, ts, ots);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.