3

Consider the following program:

#define _POSIX_C_SOURCE 200809L
#include <time.h>
#include <pthread.h>
#include <signal.h>

void timerfunc(union sigval val) { }

int main()
{
        struct sigevent sev = { .sigev_notify = SIGEV_THREAD,
                .sigev_notify_function = timerfunc };
        timer_t t;
        timer_create(CLOCK_REALTIME, &sev, &t);
        timer_delete(t);
        pthread_exit(0);
}

Linked with glibc, it not only fails to terminate, but it is unkillable except by kill -9/SIGKILL. Is this behavior permitted by the standard? Are there good workarounds aside from either always explicitly exiting the process (as opposed to just exiting all threads)?

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711

2 Answers2

3

Well, POSIX specifically says it is

...impossible to determine the lifetime of the created thread...

which implies that any lifetime is allowed.

SIGEV_THREAD is simply bad mojo, and should be avoided.

caf
  • 233,326
  • 40
  • 323
  • 462
  • That's a shame, because it's otherwise an appealing interface that's usable from library code and does not affect the calling program's signal handlers or other global state, and the callback is not restricted to async-signal-safe functions. – R.. GitHub STOP HELPING ICE Apr 06 '11 at 14:45
  • 1
    I suppose one could get the thread-callback semantics (ability to call async-signal-unsafe) functions by using a `SIGEV_SIGNAL` timer that calls `sem_post` on every expiration, and a thread that loops on `sem_trywait` and then `sem_wait` to count the number of "overruns" and wait for the next timer expiration... – R.. GitHub STOP HELPING ICE Apr 06 '11 at 14:48
  • For what it's worth, I experimented and found the `sem_post` method works extremely well. – R.. GitHub STOP HELPING ICE Apr 06 '11 at 23:25
  • 1
    @R.: Some of the other problems with `SIGEV_THREAD` are that it's not specified what attributes (of those not covered by `pthread_attr_t`) the new thread inherits, and that there's no way to report the error if the new thread can't be created. You could just spawn the thread upfront and have it `nanosleep()`, instead of using timers at all. – caf Apr 07 '11 at 00:53
  • `nanosleep` wouldn't work so well, but I suppose `clock_nanosleep` (with `TIMER_ABSTIME`) would work very well for interval timing. – R.. GitHub STOP HELPING ICE Apr 07 '11 at 01:28
  • Anyway, accepted. Between the citation, "bad mojo", and your comments elaborating, it's pretty clear the answer is just "don't use `SIGEV_THREAD` for anything". – R.. GitHub STOP HELPING ICE Apr 07 '11 at 01:39
  • @R.: Ahh yes, `clock_nanosleep()` is indeed the function I was intending to refer to. – caf Apr 07 '11 at 02:40
0

Does pthread_exit work with the main process thread? I've always wondered if pthread functions work on threads not created by pthread_create.

johnnycrash
  • 5,184
  • 5
  • 34
  • 58
  • Yes, they generally work on the main thread. I believe it may be unspecified whether the main thread is joinable or detached, but otherwise everything works as expected - cancellation, signal delivery, thread exit, etc.. – R.. GitHub STOP HELPING ICE Apr 06 '11 at 23:20
  • Are you saying they are documented to work as if the main process thread was created with pthread_create, or that you have noticed empirically they seem to work fine? Also, did you try timer_create...timer_delete...pthread_exit in a thread that you actually created with pthread_create? – johnnycrash Apr 07 '11 at 01:18
  • Sorry to beat the dead horse. – johnnycrash Apr 07 '11 at 01:18
  • It wouldn't help. The reason for the behavior is clear - glibc creates a manager thread for timers that blocks all signals and sleeps forever. – R.. GitHub STOP HELPING ICE Apr 07 '11 at 01:26
  • What if you canceled that thread? If you created a test timer on a short interval...like 1 ns, you could then figure out what thread glibc was using. You could change its signal mask for whatever thats worth. What happens if you call pthread_exit in the timer function? – johnnycrash Apr 07 '11 at 02:01
  • You can't perform any operations on a library-internal thread since there's no way to get its thread id. – R.. GitHub STOP HELPING ICE Apr 07 '11 at 14:23
  • If code were executing inside your timer callback func, what would the thread id be? Wouldn't it be the internal glibc thread? If it were, then a "fake" callback just to get the id could work. – johnnycrash Apr 07 '11 at 16:32
  • Nope. A much smarter implementation is to create one permanent thread per timer (there is no possibility of failure at event-delivery time this way, and no thread left over after timers are deleted), but glibc's approach is to create one manager thread that starts a new thread every time the timer expires. The handler runs in this new thread. Thus there is no way to access the manager thread. – R.. GitHub STOP HELPING ICE Apr 07 '11 at 16:49
  • Omg it actually calls create_thread each time the timer expires? Well for most applications that don't care about performance...probably ok, but certainly that wouldn't scale to lots of timers on short intervals. – johnnycrash Apr 07 '11 at 19:20
  • @johnnycrash: "wouldn't scale to lots of timers on short intervals" - on the other hand it prevents one timer from hanging up delivery of other timer expirations. – Michael Burr Apr 07 '11 at 20:01
  • True. But why not create a single thread for each timer like R.. says? I guess the reason would be potentially you could have 4000 timers and run out of threads. As usual, no single method works for everything. – johnnycrash Apr 07 '11 at 20:29
  • I believe the reason for not doing the latter is that the standard is unclear what effect calling `pthread_exit` or `pthread_cancel` from/on the timer-handler thread should be. However, an implementation could special-case this, catching an attempt to terminate the event handler and returning into the main loop, making the issue irrelevant. – R.. GitHub STOP HELPING ICE Apr 07 '11 at 20:56