Есть рабочая библиотека, которую необходимо использовать в многопоточном приложении. Все ошибки из вызовов данной библиотеки возвращаются через глобальную переменную, описанную в этой же библиотеке. Проанализировав код функций, становится понятно, что некоторую часть из них можно было бы беспрепятственно использовать в многопоточном приложении, если бы не эта глобальная переменная с кодом ошибки. Пока что мне приходилось в подобных случаях переписывать функции библиотеки, передавая в них указатель на переменную, куда сохранять код ошибки, это решение мне не нравится, так как ведет к полному перелопачиванию библиотеки.
Насколько мне известно, когда-то переменная errno тоже была глобальной для всей программы, но спустя некоторое время она была переопределена глобальной в пределах потока, т.е. для каждого потока выполнения она своя. Может ли кто-то подсказать, как проделать то же самое, или что-то близкое по смыслу с переменной кода ошибки в библиотеке, чтобы она была для каждого потока - своя, и чтобы не приходилось переписывать всю библиотеку?
Ответ
@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 "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
",
(long)gettid(), ptr, ptr ? *((int *)ptr) : 0);
free(ptr);
}
static void
make_key ()
{
printf("make_key: %ld
", (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
", (long)gettid(), ptr);
pthread_setspecific(key, ptr);
}
return ptr;
}
::::::::::::::
c.c
::::::::::::::
#include
#include "ptlib.h"
void *
tfunc (void *a)
{
usleep(rand() % 100000);
printf("thread-run: tid=%ld a=%d lib_errno=%d
",
(long)gettid(), *((int *)a), lib_errno);
lib_errno = *((int *)a) + 100;
usleep(rand() % 100000);
printf("thread-fin: tid=%ld a=%d lib_errno=%d
",
(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
", (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
", (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$
На первый взгляд, вроде работает...
Комментариев нет:
Отправить комментарий