#c #unix #многопоточность
Есть рабочая библиотека, которую необходимо использовать в многопоточном приложении. Все ошибки из вызовов данной библиотеки возвращаются через глобальную переменную, описанную в этой же библиотеке. Проанализировав код функций, становится понятно, что некоторую часть из них можно было бы беспрепятственно использовать в многопоточном приложении, если бы не эта глобальная переменная с кодом ошибки. Пока что мне приходилось в подобных случаях переписывать функции библиотеки, передавая в них указатель на переменную, куда сохранять код ошибки, это решение мне не нравится, так как ведет к полному перелопачиванию библиотеки. Насколько мне известно, когда-то переменная errno тоже была глобальной для всей программы, но спустя некоторое время она была переопределена глобальной в пределах потока, т.е. для каждого потока выполнения она своя. Может ли кто-то подсказать, как проделать то же самое, или что-то близкое по смыслу с переменной кода ошибки в библиотеке, чтобы она была для каждого потока - своя, и чтобы не приходилось переписывать всю библиотеку?
Ответы
Ответ 1
@margosh, немного почитал маны и вот что получилось (интересующую Вас переменную обозвал lib_errno, при создании (первое обращение к ней) в каждом потоке она обнуляется): avp@avp-ubu1:hashcode$ more ptlib.h ptlib.c c.c | cat :::::::::::::: ptlib.h :::::::::::::: #ifndef _PTLIB_H #define _PTLIB_H #ifdef __cplusplus extern "C" { #endif pid_t gettid(void); int *lib_errno_func(void); #ifdef __cplusplus } #endif #define lib_errno (*lib_errno_func()) #endif :::::::::::::: ptlib.c :::::::::::::: #include#include #include #include #include #include #include "ptlib.h" pid_t gettid() { return syscall(SYS_gettid); } static pthread_once_t key_is_initialized = PTHREAD_ONCE_INIT; static pthread_key_t key; static void destruct_thread_value (void *ptr) { printf("destruct: %ld %p value %d\n", (long)gettid(), ptr, ptr ? *((int *)ptr) : 0); free(ptr); } static void make_key () { printf("make_key: %ld\n", (long)gettid()); pthread_key_create(&key, destruct_thread_value); } int * lib_errno_func () { int *ptr; pthread_once(&key_is_initialized, make_key); if (!(ptr = (typeof(ptr))pthread_getspecific(key))) { ptr = (typeof(ptr))calloc(sizeof(int), 1); printf("construct: %ld %p\n", (long)gettid(), ptr); pthread_setspecific(key, ptr); } return ptr; } :::::::::::::: c.c :::::::::::::: #include #include #include #include #include "ptlib.h" void * tfunc (void *a) { usleep(rand() % 100000); printf("thread-run: tid=%ld a=%d lib_errno=%d\n", (long)gettid(), *((int *)a), lib_errno); lib_errno = *((int *)a) + 100; usleep(rand() % 100000); printf("thread-fin: tid=%ld a=%d lib_errno=%d\n", (long)gettid(), *((int *)a), lib_errno); return 0; } int main (int ac, char *av[]) { int n = atoi(av[1] ? av[1] : "3"); if (n < 1) n = 3; pthread_t tid[n]; printf("main-run: tid=%ld lib_errno=%d\n", (long)gettid(), lib_errno); int i; for (i = 0; i < n; i++) { int *pv = (int *)malloc(sizeof(int)); *pv = i; pthread_create(tid + i, 0, tfunc, pv); } for (i = 0; i < n; i++) pthread_join(tid[i], 0); printf("main-fin: tid=%ld lib_errno=%d\n", (long)gettid(), lib_errno); return puts("End") == EOF; } avp@avp-ubu1:hashcode$ gcc -pthread -O2 -c ptlib.c -Wall avp@avp-ubu1:hashcode$ g++ -pthread c.c ptlib.o avp@avp-ubu1:hashcode$ ./a.out make_key: 5556 construct: 5556 0x8ca010 main-run: tid=5556 lib_errno=0 construct: 5558 0x7f22c40008c0 thread-run: tid=5558 a=1 lib_errno=0 thread-fin: tid=5558 a=1 lib_errno=101 destruct: 5558 0x7f22c40008c0 value 101 construct: 5557 0x7f22c40008c0 thread-run: tid=5557 a=0 lib_errno=0 construct: 5559 0x7f22bc0008c0 thread-run: tid=5559 a=2 lib_errno=0 thread-fin: tid=5559 a=2 lib_errno=102 destruct: 5559 0x7f22bc0008c0 value 102 thread-fin: tid=5557 a=0 lib_errno=100 destruct: 5557 0x7f22c40008c0 value 100 main-fin: tid=5556 lib_errno=0 End avp@avp-ubu1:hashcode$ avp@avp-ubu1:hashcode$ На первый взгляд, вроде работает... Ответ 2
Во-первых, уже давно практически все компиляторы предоставляют собственные средства для создания thread local переменных. В MSVC это declspec(thread), в GCC - __thread. Во-вторых, начиная с С11 в языке появился стандартный спецификатор _Thread_local, объявляющий thread local переменные. В любом случае, можно просто подсмотреть, что сделано на вашей платформе с errno и повторить то же самое.
Комментариев нет:
Отправить комментарий