Introduction
I have a program where child threads are created that I would like to profile with Valgrind memcheck. From the responses to a previous question I've asked, I will need to use joinable (rather than detached) threads in order to test and profile reliably with Valgrind memcheck.
Stack vs. Heap Allocation
My program is sufficiently large where I don't think I can create the thread and join it in the same scope. For this reason I allocate space for the pthread_t on the heap.
Attempt #1 - Joining Immediately
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <stdint.h>
void my_thread() {
printf("I'm in a child thread!\n");
pthread_exit(NULL);
}
pthread_t* const make_thread() {
pthread* const thread = malloc(sizeof(pthread_t));
pthread_create(thread, NULL, (void*) &my_thread, NULL));
return thread;
}
int main() {
printf("Hello, world!\n");
uint8_t i;
for(i = 0; i < 255; ++i) {
pthread_t* const thread_handle = make_thread();
pthread_join(*thread_handle, NULL);
free(thread_handle);
}
return 0;
}
This seems to make sense, but now I want to extend this example by not joining the thread immediately, and only joining on program exit (say, because these threads may become long-living). IOW the above example kind of defeats the purpose of multithreading.
I want to create threads and only really ever force a join on program exit.
Attempt #2 - Joining at the end
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <stdint.h>
#include <glib-2.0/glib.h>
#include <unistd.h>
void my_thread() {
sleep(3);
printf("I'm in a child thread!\n");
pthread_exit(NULL);
}
pthread_t* const make_thread() {
pthread* const thread = malloc(sizeof(pthread_t));
pthread_create(thread, NULL, (void*) &my_thread, NULL));
return thread;
}
int main() {
printf("Hello, world!\n");
GArray* const thread_handles = g_array_new(TRUE, TRUE, sizeof(pthread*));
// Important loop
uint8_t i;
for(i = 0; i < 255; ++i) {
pthread_t* const thread_handle = make_thread();
g_array_append_val(thread_handles, thread_handle);
}
for(i = 0; i < thread_handles->len; ++i) {
pthread_t* const thread_handle =
g_array_index(thread_handles, pthread*, i);
pthread_join(*thread_handle, NULL);
free(thread_handle);
}
g_array_free(thread_handles, TRUE);
return 0;
}
This is cool but what if "Important loop" is actually endless? How can I prevent thread_handles
from expanding until it takes up all available memory?
In the actual program (these are just minimal examples), the program receives network messages and then kicks off threads for some special types on network messages.