7

I am trying to figure out how to get rid of a reliance on the pthread_timedjoin_np because I am trying to build some code on OSX.

Right now I have a Queue of threads that I am popping from, doing that pthread_timedjoin_np and if they dont return, they get pushed back on the queue.

The end of the thread_function that is called for each thread does a pthread_exit(0); so that the recieving thread can check for a return value of zero.

I thought i might try to use pthread_cond_timedwait() to achieve a similar effect, however I think i am missing a step.

I thought I would be able to make worker Thread A signal a condition AND pthread_exit() within a mutex, , and worker Thread B could wake up on the signal, and then pthread_join(). The problem is, Thread B doesn't know which thread threw the conditional signal. Do I need to explicitly pass that as part of the conditonal signal or what?

Thanks

Derek

Derek
  • 11,715
  • 32
  • 127
  • 228
  • Not a duplicate, but some of the answers may be helpful: http://stackoverflow.com/questions/73468/non-blocking-pthread-join – Corbin Jul 18 '12 at 22:20

3 Answers3

9

Here is a portable implementation of pthread_timedjoin_np. It's a bit costly, but it's a full drop-in replacement:

struct args {
    int joined;
    pthread_t td;
    pthread_mutex_t mtx;
    pthread_cond_t cond;
    void **res;
};

static void *waiter(void *ap)
{
    struct args *args = ap;
    pthread_join(args->td, args->res);
    pthread_mutex_lock(&args->mtx);
    args->joined = 1;
    pthread_mutex_unlock(&args->mtx);
    pthread_cond_signal(&args->cond);
    return 0;
}

int pthread_timedjoin_np(pthread_t td, void **res, struct timespec *ts)
{
    pthread_t tmp;
    int ret;
    struct args args = { .td = td, .res = res };

    pthread_mutex_init(&args.mtx, 0);
    pthread_cond_init(&args.cond, 0);
    pthread_mutex_lock(&args.mtx);

    ret = pthread_create(&tmp, 0, waiter, &args);
    if (!ret)
            do ret = pthread_cond_timedwait(&args.cond, &args.mtx, ts);
        while (!args.joined && ret != ETIMEDOUT);

    pthread_mutex_unlock(&args.mtx);

    pthread_cancel(tmp);
    pthread_join(tmp, 0);

    pthread_cond_destroy(&args.cond);
    pthread_mutex_destroy(&args.mtx);

    return args.joined ? 0 : ret;
}

There may be small errors since I wrote this on the spot and did not test it, but the concept is sound.

Mark Smith
  • 880
  • 1
  • 8
  • 25
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • Before you posted I had actually come up with a plan similar to Martins, if that doesnt seem to be working well I will give this a try. – Derek Jul 19 '12 at 13:36
  • Martin's solution is cleaner from a standpoint of not having wasteful overhead and being conceptually better. Mine is just a drop-in replacement for the non-portable function that allows you to fix non-portable code without redesigning anything. – R.. GitHub STOP HELPING ICE Jul 19 '12 at 18:22
  • There's something wrong with this code: there's a `goto done` without a `done` label – lornova Jul 21 '21 at 15:28
  • My understanding is that this code is correct because once `pthread_join()` has returned, even if the waiter thread is being concurrently cancelled, it will still execute until a cancellation point, so `args->joined = 1` will always be called before it is read in the main thread. So either `pthread_join()` is cancelled (so the thread remains joinable) and `0` is returned, or `pthread_join()` returned and `1` is returned by `pthread_timedjoin_np()`. Is that correct? – Giovanni Mascellani Nov 25 '22 at 13:45
  • 1
    @GiovanniMascellani: Your analysis looks correct to me. – R.. GitHub STOP HELPING ICE Nov 26 '22 at 15:16
2

Producer-consumer queue. Have the threads queue *themselves, and so their results,(if any), to the queue before they exit. Wait on the queue.

No polling, no latency.

With your current design, you would have to join() the returned threads get the valueptr and to ensure that they are destroyed.

Maybe you could sometime move to a real threadpool, where task items are queued to threads that never terminate, (so eliminating thread create/terminate/destroy overhead)?

Martin James
  • 24,453
  • 3
  • 36
  • 60
  • re: real threadpool..i had this idea before. I am working in the Qt framework and I have used their pool/future model before, but I am not sure if I am going to be able to use that in the name of portability of this code – Derek Jul 19 '12 at 13:34
  • I ended up trying to implement something like this - originally the main thread was just throwing all the workers into a queue, but I re-worked it a bit to make it so that the woekers add themselves to a queue when they are done, and then thrown a condition signal to let another thread know to pull something the thread off that queue and join it. – Derek Jul 19 '12 at 13:35
0

solution with alarm.

pthread should enable cancel, so it can stop by external.(even with pthread_timedjoin_np).

pthread_timedjoin_np return with ETIMEOUT after waited time.

  1. set alarm, use alarm also can give "TIMEOUT" signal.
  2. In handler, just pthread_cancel it. (only timeout run this).
  3. pthread_join it in main thread.
  4. reset alarm

I write test code in here:github

liuyang1
  • 1,575
  • 1
  • 15
  • 23
  • github link says 404 – Sam Ginrich Apr 25 '22 at 18:41
  • `pthread_cancel` is not async-signal-safe, and therefore calling it from a signal handler for `alarm` is undefined under most conditions. Moreover I'm not sure how this is supposed to some the problem, since it results in some thread getting cancelled (and eventually terminating) which was not part of the goal. – R.. GitHub STOP HELPING ICE Oct 24 '22 at 12:41