Here's a not-quite minimal MCVE (Minimal, Complete, Verifiable Example) program closely based on what is shown in the question:
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static int join = 1;
static void *th2(void *args)
{
printf("%s: %d (%p)\n", __func__, *(int *)args, args);
sleep(1);
printf("0x%X\n", *(int *)args);
fflush(stdout);
pthread_exit(NULL);
}
static void *th1(void *args)
{
assert(args == NULL);
pthread_t tid;
int var = 10;
printf("%s: %d (%p)\n", __func__, var, (void *)&var);
pthread_create(&tid, NULL, th2, &var);
if (join)
pthread_join(tid, NULL);
pthread_exit(NULL);
}
/*---*/
static void *th4(void *args)
{
printf("%s: %d (%p)\n", __func__, *(int *)args, args);
sleep(1);
printf("0x%X\n", *(int *)args);
fflush(stdout);
pthread_exit(NULL);
}
static void *th3(void *args)
{
assert(args == NULL);
pthread_t tid;
int *var = malloc(sizeof *var);
*var = 10;
printf("%s: %d (%p)\n", __func__, *var, (void *)var);
pthread_create(&tid, NULL, th4, var);
if (join)
{
pthread_join(tid, NULL);
free(var);
}
/* else leak memory for var */
pthread_exit(NULL);
}
int main(int argc, char **argv)
{
pthread_t t1;
pthread_t t3;
if (argc > 1 && argv[argc] == NULL)
join = 0;
printf("%s pthread_join() on sub-threads\n", join ? "Using" : "Not using");
printf("launch 1\n");
pthread_create(&t1, NULL, th1, NULL);
pthread_join(t1, NULL);
printf("launch 3\n");
pthread_create(&t3, NULL, th3, NULL);
pthread_join(t3, NULL);
printf("finished\n");
return 0;
}
It is set up so that if a command line argument is passed, the sub-threads th1()
and th3()
do not do pthread_join()
before exiting; if no argument is passed, they do wait.
When compiled as pth19
and run (on a Mac running macOS 10.14.2 Mojave, using GCC 8.2.0), I get:
$ pth19
Using pthread_join() on sub-threads
launch 1
th1: 10 (0x70000bda2f04)
th2: 10 (0x70000bda2f04)
0xA
launch 3
th3: 10 (0x7fa0a9500000)
th4: 10 (0x7fa0a9500000)
0xA
finished
$ pth19 1
Not using pthread_join() on sub-threads
launch 1
th1: 10 (0x70000690ff04)
Segmentation fault: 11
$
When used with the pthread_join()
calls, it works correctly and as expected.
When the joins are omitted, the code crashes — which is one way 'undefined behaviour' manifests itself. When you don't join the th2
and th4
threads, the th1
and th3
threads can leave the others accessing data that's no longer valid. (Granted, the allocated memory wasn't freed in the original, but the crash was happening before the memory allocation.)
Be careful to ensure that the threads only access valid data.
Do not try sharing data between threads like this; you're taking a job that's already hard (thread programming correctly is hard) and making it even harder.