Как создать библиотеку, которая использует мьютексы, только если pthread связан?

Я создаю библиотеку C в Linux, которая имеет несколько функций, которые совместно работают с некоторыми глобальными данными. Чтобы эти функции были поточно-ориентированными, они должны использовать мьютексы в соответствующих точках кода.

В Linux, чтобы использовать pthreads в приложении, необходимо создать ссылку в соответствующей библиотеке -lpthread. В случае, когда моя библиотека была скомпилирована, я бы хотел, чтобы она работала как в том случае, если пользователь решил использовать pthreads в своем приложении, так и в случае, если они этого не делают.

В случае, если разработчик не использует потоки в своем приложении, они не будут ссылаться на pthreads. Поэтому я бы хотел, чтобы моя скомпилированная библиотека не требовала этого, и, кроме того, использование мьютексов в однопоточном приложении требует ненужных накладных расходов (не говоря уже о том, что это глупо).

Есть ли какой-то способ написания кода (с расширениями GCC, если это необходимо), который будет запускаться определенным блоком кода, только если определенные символы были связаны? Я знаю, что могу использовать dlopen() и друзей, но это само по себе потребует того, чего я пытаюсь избежать. Я предполагаю, что то, что я ищу, должно существовать, так как несколько стандартных функций находятся в одной лодке и требуют, чтобы мьютексы были поточно-безопасными (и они есть), но работали, даже если они не связаны с pthreads.

В этом месте я заметил, что функция popen () в FreeBSD в строках 66 и 67 использует непереносимый поток проверки, чтобы определить, используются потоки или нет, и использовать ли мьютексы. Я сомневаюсь, что что-либо подобное стандартизировано в любом случае. Но, что более важно, такой код не может компилироваться и связываться, если символы не распознаются, что в Linux, символы мьютекса даже не будут присутствовать, если pthread не связан.

Подводя итог: Как в Linux можно создать библиотеку, которая знает, когда также используются потоки, и, если это так, использует мьютексы, где это уместно, и не требует связывания с pthreads, если разработчик приложения специально не хочет где-то использовать потоки?

2 ответа

Решение

После некоторого тестирования кажется, что Linux уже делает то, что я хочу автоматически! Вам нужно связываться с pthreads только в том случае, если вы используете многопоточность, а не в том случае, если вам нужна только поддержка мьютекса pthread.

В этом тестовом случае:

#include <stdio.h>
#include <errno.h>
#include <pthread.h>

int main()
{
  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  if (!(errno = pthread_mutex_lock(&mutex))) { puts("Mutex locked!"); }
  else { perror("Could not lock mutex"); }

  if (!(errno = pthread_mutex_lock(&mutex))) { puts("Mutex locked!"); }
  else { perror("Could not lock mutex"); }

  return 0;
}

При компиляции без привязки к pthreads я вижу "Mutex заблокирован!" дважды. Что указывает на то, что pthread_mutex_lock() по сути не работает. Но с pthreads, связанными, запуск этого приложения остановится после того, как "Mutex заблокирован!" печатается.

Поэтому я могу использовать взаимные исключения в моей библиотеке, где это уместно, и мне не нужно требовать использования pthreads, и нет (значительных?) Накладных расходов там, где это не нужно.

Обычные решения:

  1. Использовать #define переключитесь на контроль во время сборки, вызывать ли функции pthreads или нет, и ваш процесс сборки создаст две версии вашей библиотеки: одну с поддержкой pthread и одну с разными именами. Положитесь на пользователя вашей библиотеки, чтобы связать ее с нужной.

  2. Не вызывайте функции pthreads напрямую, а вместо этого вызывайте предоставленный пользователем lock а также unlock обратные вызовы (и thread-local-storage тоже, если вам это нужно). Пользователь библиотеки отвечает за выделение и вызов соответствующих механизмов блокировки, что также позволяет им использовать потоковую библиотеку без pthreads.

  3. Ничего не делайте, и просто документально подтверждайте, что код пользователя должен гарантировать, что функции вашей библиотеки не будут вводиться одновременно из нескольких потоков.

glibc снова делает что-то другое - он использует трюки с ленивыми связывающими символами для вызова функций pthreads, только если они связаны в двоичный файл. Это не переносимо, потому что оно опирается на конкретные детали реализации pthreads в glibc. Смотрите определение__libc_maybe_call():

#ifdef __PIC__
# define __libc_maybe_call(FUNC, ARGS, ELSE) \
  (__extension__ ({ __typeof (FUNC) *_fn = (FUNC); \
                    _fn != NULL ? (*_fn) ARGS : ELSE; }))
#else
# define __libc_maybe_call(FUNC, ARGS, ELSE) \
  (FUNC != NULL ? FUNC ARGS : ELSE)
#endif
Другие вопросы по тегам