0

When we detach a thread, it is not associated with calling thread anymore then why did the join call succeed with return value 0??

// C program to show thread functions

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

void* func(void* arg)
{
    int x = pthread_detach(pthread_self());
    printf("x = %d\n", x);
    printf("Error description is : %s\n",strerror(x));
    printf("Inside the thread\n");
    pthread_exit(NULL);
}

void fun()
{
    pthread_t ptid;
    pthread_create(&ptid, NULL, &func, NULL);
    int x = pthread_join(ptid, NULL);
    printf("Error description is : %s\n",strerror(x));
    pthread_exit(NULL);
}

int main()
{
    fun();
    return 0;
}

Output -

x = 0
Error description is : Success
Inside the thread
Error description is : Success

as you can see from above code, pthread_join returned success on an already detached thread.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • This does not answer the question, but do note that no two threads are *ever* associated with each other in the sense that your "associated with calling thread" seems imply. That is, there is no special relationship between a given thread and the one that started it. Any thread can join any joinable thread. – John Bollinger Feb 15 '23 at 15:17
  • This also does not answer the question, but note that function names are *automatically* converted to pointers. It is neither necessary nor idiomatic to write `&func`. Simply writing `func` without any argument list is usual. – John Bollinger Feb 15 '23 at 15:23
  • 1
    It is _very_ unusual for a thread to call detach on itself -- usually the creating thread either cares or doesn't care to wait (join) the new thread it just created, and if doesn't, it should detach the new thread. That would avoid the race here. – Employed Russian Feb 19 '23 at 16:06

1 Answers1

2

I can reproduce the behavior you describe with your program, using glibc 2.28 and gcc 8.5.0.

This variation produces behavior more in line with what I think you expected:

// C program to show thread functions

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <semaphore.h>

sem_t sem;

void *func(void *arg) {
    int x = pthread_detach(pthread_self());

    sem_post(&sem);

    printf("x = %d\n", x);
    printf("Error description in second thread is : %s\n",strerror(x));
    printf("Inside the thread\n");
    pthread_exit(NULL);
}

void fun() {
    pthread_t ptid;
    pthread_create(&ptid, NULL, &func, NULL);
    sem_wait(&sem);
    int x = pthread_join(ptid, NULL);
    printf("Error description in initial thread is: %s\n",strerror(x));
    pthread_exit(NULL);
}

int main() {
    sem_init(&sem, 0, 0);
    fun();
    return 0;
}

I modified the output a bit, but the main difference is that in this version, the initial thread waits to attempt to join the second one until the second signals via a semaphore that it has detached itself. This is the output I get:

x = 0
Error description in second thread is : Success
Inside the thread
Error description in initial thread is: Invalid argument

In this program, then, the pthread_join() call returns EINVAL, consistent with the target thread not being joinable.

This seems to reflect a race condition. Apparently, in the implementation I am testing and probably in yours as well, if pthread_join() proceeds far enough before the target thread detaches itself, then it waits for the target thread to terminate, even though eventually that thread is detached.

Note, however, that it is erroneous in the first place to attempt to both join and detach the same thread. POSIX makes only recommendations for the behavior in such case, while leaving it formally undefined (as far as POSIX is concerned). Glibc's pthread_join will detect the case where the target thread is already detached and fail with EINVAL, but the documentation doesn't say anything about what happens if a thread is detached while another one is trying to join it. Erroneous behavior does not necessarily produce predictable results.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157