How to create a library which uses mutexes only if

2020-03-06 03:13发布

I'm creating a C library on Linux which has several functions, which together operate upon some global data. In order for these functions to be thread safe, they must employ mutexes at the appropriate points in the code.

In Linux, in order to use pthreads in an application, one needs to link in the appropriate library, -lpthread. In the case of my library once compiled, I'd like to make it work both if the user of it decided to use pthreads in their application, as well as if they don't.

In the case where a developer does not use threads in their application, they will not link against pthreads. Therefore I'd like my compiled library to not require it, and furthermore, employing mutexes in a single threaded application uses needless overhead (not to mention is silly).

Is there some kind of way to write code (with GCC extensions if necessary) that a certain block of code will only run if certain symbols were linked in? I'm aware I can use dlopen() and friends, but that in itself would require some of what I'm trying to avoid. I imagine what I'm looking for must exist, as several standard functions are in the same boat, and would require mutexes to be thread safe (and they are), but work even when not linked with pthreads.

On this point, I notice that FreeBSD's popen() function on line 66 & 67 employs a non portable check - isthreaded, to determine if threads are used or not, and whether to use mutexes. I doubt anything like that is standardized in any way. But more to the point such code can't compile and link if the symbols aren't recognized, which in Linux, the mutex symbols won't even be present if pthread is not linked.

To summarize: On Linux, how does one create a library, which knows when threads are also used, and if so, employs mutexes where appropriate, and does not require linking against pthreads, unless the application developer specifically wants to use threading somewhere?

2条回答
够拽才男人
2楼-- · 2020-03-06 04:04

After some testing, it seems that Linux already does what I want automatically! You only need to link against pthreads if you use threading, not if you just want pthread mutex support.

In this test case:

#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;
}

When compiling this without pthreads linked, I see "Mutex locked!" twice. Which indicates that pthread_mutex_lock() is essentially a non-op. But with pthreads linked, running this application will stall after the first time "Mutex locked!" is printed.

Therefore, I can use mutexes in my library where appropriate, and don't need to require pthreads to use, and no (signifigant?) overhead where it isn't needed.

查看更多
爷的心禁止访问
3楼-- · 2020-03-06 04:09

The usual solutions are:

  1. Use a #define switch to control at build time whether to call the pthreads functions or not, and have your build process create two versions of your library: one pthread-aware and one not, with different names. Rely on the user of your library to link against the correct one.

  2. Don't call the pthreads functions directly, but instead call user-provided lock and unlock callbacks (and thread-local-storage too, if you need that). The library user is responsible for allocating and calling the appropriate locking mechanisms, which also allows them to use a non-pthreads threading library.

  3. Do nothing at all, and merely document that user code should ensure that your library functions aren't entered at the same time from multiple threads.

glibc does something different again - it uses tricks with lazy binding symbols to call the pthreads functions only if they are linked into the binary. This isn't portable though, because it relies on specific details of the glibc implementation of pthreads. See the definition of __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
查看更多
登录 后发表回答