Programs that use large amounts of thread-local storage can cause dav1d's thread creation to fail
The following program uses a large amount of thread-local storage and creates a thread with a custom stack size:
#include <pthread.h>
#include <stdio.h>
void *tile_task(void *const data) {
printf("Calling tile_task\n");
return NULL;
}
int main(int argc, char *argv[]) {
static volatile __thread char x[8340480];
(void)x;
pthread_attr_t thread_attr;
if (pthread_attr_init(&thread_attr)) {
printf("Failed in pthread_attr_init!\n");
return -1;
}
// Change this to something small (e.g., 1024 * 1024) to make it fail.
int res = pthread_attr_setstacksize(&thread_attr, 8192 * 1024);
if (res != 0) {
printf("pthread_attr_setstacksize returns %d.\n", res);
return -1;
}
pthread_t thread;
res = pthread_create(&thread, &thread_attr, tile_task, NULL);
if (res != 0) {
printf("pthread_create returns %d.\n", res);
return -1;
}
printf("Succeed!\n");
}
Compiling and running this (cc file.c -pthread && ./a.out
) works on my x86_64 Linux system with glibc. But if you change the value passed to pthread_attr_setstacksize()
to something smaller (e.g., 1024 * 1024
, which dav1d currently uses), then pthread_create()
will fail (with EINVAL
) (note that pthread_attr_setstacksize()
still succeeds).
This is an issue that was found in a real program that used some large thread-local storage, causing all of dav1d's pthread_create()
calls to fail.
@gramner found the old upstream glibc issue (https://sourceware.org/bugzilla/show_bug.cgi?id=11787). This is a glibc-specific issue where glibc subtracts the thread-local storage from the stack size, rather than adding the two together. The workaround people seem to use (including Rust) is to use glibc's private __pthread_get_minstack()
function to get the minimum stack size, and then add the desired stack size to that (e.g., __pthread_get_minstack(&thread_attr) + 1024 * 1024
). It's probably best to use dlsym
to get the function (which is also what Rust does).