2

I recently stumbled upon what I thought was quite a weird behavior of the pthreads library (or at least its implementation in Linux Mint 16, Ubuntu EGLIBC 2.17-93ubuntu4, NPTL 2.17, gcc Ubuntu/Linaro 4.8.1-10ubuntu9 ).

When compiling a pthreads program, I accidentally forgot to link it with the pthreads library (i.e., I forgot to add the -lpthread flag to the gcc command line). I did, however, compile the program with all warnings enabled (-Wall) and received absolutely no warnings or errors. When I ran the program, thought, it appeared as if the locks were simply not working. It took me a little while to figure out, but eventually I found out that despite all the calls to pthread_mutex_lock were returning 0 (success), the locks were not being set (i.e., for example, two sequential calls to pthread_mutex_lock on the same lock would not suspend execution).

Here's a proof of concept code that I used to test and reproduce the behavior:

int main( int argc, char **argv ) {

  pthread_mutex_t mutex;

  pthread_mutex_init( &mutex, NULL );
  pthread_mutex_lock( &mutex );
  pthread_mutex_lock( &mutex );
  pthread_mutex_unlock( &mutex );

  return 0;

}

If I compile it without the -lpthread flag, I get no errors or warnings and when I run it, it simple runs normally and finishes executing.

me@mybox /tmp $ gcc -Wall mutex.c -o mutex
me@mybox /tmp $ ./mutex
me@mybox /tmp $

If I compile it with the -lpthread flag, I also do not get any errors or warnings, but when I run it, it simply hangs (on the second call to pthread_mutex_lock) - which should be the expected behavior.

me@mybox /tmp $ gcc -Wall mutex.c -o mutex -lpthread
me@mybox /tmp $ ./mutex
^C
me@mybox /tmp $ 

Can anybody explain why, when the -lpthread flag is omitted (and thus the program is not linked to the pthreads library), the compiler shows no error or warnings, nor does the program during its execution, but pthread_mutex_lock simply returns 0 and does not hold the lock?

Thank you in advance for any clues you can provide.

arsk
  • 23
  • 4

1 Answers1

1

This is just a guess since I haven't looked at the code, but I believe the idea is to allow library code (either system level or third-party) to use pthread_mutex_lock in non-multi-threaded programs without incurring any overhead and without requiring libpthread to be linked. Since such programs do not have multiple threads, there is no need for any mutual exclusion, and the "dummy" implementation of mutexes does not cause any problems unless mutexes are being used in a less-conventional manner (e.g. using pthread_mutex_trylock to observe whether the mutex is already locked).

If my suspicions are correct, this is probably a "lesser of two evils". On the one hand, it's really wrong to have non-working versions of pthread_mutex_lock that silently fail when you forget to link libpthread. This could especially affect programs using process-shared mutexes which don't need to create threads in order to mmap and use as process-shared mutex another program created, where you would not observe other linking errors from missing pthread_create, etc. On the other hand, having a pthread_mutex_lock symbol available without libpthread discourages third-party library authors from making "thread support" an optional feature (to avoid having to link libpthread) and possibly requiring an "init threads" function which cannot actually be called safely in a multi-threaded program (this is a separate matter which we could discuss on a new question if you're interested). Such behavior by library authors used to be extremely common (and to some extent, still is) and makes multi-threaded programming much more painful than it should be due to having to fight with libraries that don't work "out of the box" in multi-threaded environments. So getting people to stop doing that has a lot of value.

Indeed, my suspicion is supported by the accepted answer to this question:

How to create a library which uses mutexes only if pthread is linked?

Further, the failure of the "dummy implementation" of mutexes is not entirely silent; an error is returned. You're just not checking for it.

Community
  • 1
  • 1
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • Thank you for your detailed answer R.., however, how exactly is the error returned? In the sample program I presented, indeed I did not care for the return of pthread_mutex_lock, but when I was investigating the problem I did check for the return and it was always 0 (success, according to the man page). Is there some other way of returning an error other than a non-zero return code in pthread_mutex_lock? – arsk Aug 25 '14 at 13:48
  • According to the answer I linked, the an error is returned, but I did not confirm this myself. Look at the sample program there. If you can't reproduce the results, perhaps post a comment there asking about it or a new question about the behavior..? – R.. GitHub STOP HELPING ICE Aug 25 '14 at 14:46
  • No, there's no error returned (not there, not here). The only difference is that (there) the sample program will print a message twice, instead of hanging, when two sequential calls to pthread_mutex_lock are performed. The return to pthread_mutex_lock is still 0 (success) though. Anyway, although I cannot sympathize with silent failures such as the one discussed in this question, I'm happy with the answer and will accept it now - thank you! – arsk Aug 27 '14 at 18:46
  • @arsk: My guess would be that glibc changed the behavior from returning an error to silent failure sometime between then and now, probably because library code trying to make use of this "dummy implementation" to avoid needing `-lpthread` was doing proper error-checking of the return value, and failing (or aborting the whole program...) when it got an error from `pthread_mutex_lock`. I'm with you in regards to considering this a really dubious hack, but I'm just trying to explain it. They should really just move the real mutex functions to `libc.so` and be done with it. – R.. GitHub STOP HELPING ICE Aug 27 '14 at 19:40