21

Can I spawn a thread with pthread_create and use std::mutex inside of it safely?

I would think that if std::mutex is implemented as a pthread_mutex_t then it would be fine but I don't see this documented anywhere

For example:

#include <pthread.h>
#include <mutex>

namespace {
std::mutex global_lock;
}

void* thread_func(void* vp) {
    // std::mutex used in thread spawned with pthread_create
    std::lock_guard<std::mutex> guard(global_lock);
    // critical section
    return nullptr;
}

int main() {
    pthread_t tid;
    pthread_create(&tid, nullptr, thread_func, nullptr);
    pthread_join(tid, NULL);
}

BTW I'm running Debian Wheezy.

Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
  • Spawning a thread and locking a mutex are two separate concepts. Is your question actually about mixing concurrency control from STL and PThread or just this one instance in particular? – user7116 Mar 12 '13 at 17:29
  • @sixlettervariables mixing control in general. – Ryan Haining Mar 12 '13 at 17:30
  • Can you elaborate on the requirements/use case for this? Do you need to run the code from within C APIs? For this case this might be OK (since a C API running with pthread will need a pthread based C++ implementation). – πάντα ῥεῖ Mar 12 '13 at 17:46
  • @g-makulik I'm not sure I understand what you're asking. My particular use case isn't as much what I'm concerned with, as whether these can be mixed in general. I don't intend to use any C API. let's assume C++11 all the way through. – Ryan Haining Mar 12 '13 at 17:49

4 Answers4

7

You could on my machine (Debian too). But I'm not sure if I would call this safe.

If you look at the relevant file, /usr/include/c++/4.7/i486-linux-gnu/bits/gthr-default.h in my case, you will see that there will be a 1:1 mapping to the pthreads api. <mutex> uses __gthread_mutex_lock for locking which is defined exactly there to pthread_mutex_lock. Or you will see that std::thread declares typedef __gthread_t native_handle_type;

I don't know if there is a documented way to check if pthreads are used. But gthr-default.h defines _GLIBCXX_GCC_GTHR_POSIX_H as include guard and I think as long as this macro is defined, you can assume that you can mix them both.

Edit: Given the hint from @Wakely, I would write:

template <typename T>
using strip = typename std::remove_pointer<typename std::decay<T>::type>::type;

static_assert(std::is_same<strip<std::thread::native_handle_type>, pthread_t>::value,
              "libstdc++ doesn't use pthread_t");
ipc
  • 8,045
  • 29
  • 33
  • 2
    Don't rely on implementation details like that macro, it could change next release (something this broke Boost.Thread recently.) `std::is_same` is a pretty good indicator Pthreads is used, although `native_handle_type` could be `pthread_t*` or another related type and the test would fail – Jonathan Wakely Mar 12 '13 at 23:34
  • Looks like `native_handle_type` isn't a pointer, and it is `pthread_t` on gcc on my platform, so the `strip` part isn't needed (but still cool, I didn't know you could do that). – jtbr Feb 23 '16 at 01:07
7

There's no guarentee in any spec that it will work, but it's likely that any C++ implementation on an OS that uses pthreads as its only real threading library will use pthreads underneath C++ threads, so it will likely work.

You will likely run into problems if you later try to port the code to some other platform that uses something other than pthreads, even if that platform supports pthreads too (eg, windows).

The questions is, why bother and risk it? If you're using C++11 std::mutex, why not use std::thread as well?

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • 2
    I probably won't risk it, but I thought maybe it would be known to be safe. The reasons for wanting this are a but contrived. But let's just pretend that I'm using existing C++ code and updating it. Thanks for the answer. – Ryan Haining Mar 12 '13 at 20:52
  • 6
    Why bother? Because `pthread_create` supports customization via `pthread_attr_t` that `std::thread` doesn't – Jonathan Wakely Mar 12 '13 at 23:36
  • 2
    Even when the C++11 thread implementation isn't based on pthreads, it's very likely that any platform that has both APIs available implements them in terms of a common underlying platform API. Despite the lack of guarantee I think there is a reasonable expectation of practical portability. I think platforms lacking an implementation of one or the other will be *far* more common than platforms that implement the two APIs incompatibly. – Casey Aug 02 '13 at 15:06
  • `std::thread` does not provide any way to set or otherwise control the stack size. So if you want to do that, you need to write non-portable code and carefully consult your implementation's documentation about how to do that. – Chris Dodd Dec 11 '16 at 22:39
3

Both std::thread and std::mutex have a native_handle method which allows you to dig down to the platform implementation of the given object. This says to me that the standard threading library is designed to play nice with the platform implementation.

As an aside std::thread and std::mutex are different objects that do different things viz. manage threads and provide cross thread synchronization. In the end the kernel does the heavy lifting.

So if you are not worried about portability, I cannot see why this should be an issue.

As an aside, sometimes you may need the native platform implementation so as to provide you with the richer feature-set that the platform allows. For example BSD threading allows different types of threads and some threading libraries allow you to set the stack size of your new thread. The C++ threading APIs are a portable lowest common denominator.

doron
  • 27,972
  • 12
  • 65
  • 103
  • Maybe worth adding to this that threads, like processes, are implemented by the kernel on most/all operating systems that use pthreads (and probably most operating systems generally), meaning **there is really only one kind of thread** at the system level regardless of which userspace lib you use. Not so sure about mutexes, but if I had to guess I'd say the same is true there. – CodeClown42 Jan 11 '19 at 17:11
0

If your question is: can I use freely switch between one type of mutex and another at random? Then the answer is no (unless all the bottom layers use the same implementation, such as pthread_mutex.)

However, if you have different groups of resources that you want to protect, any one group of resources can be protected with any one implementation. Actually, it can at times be better to use a semaphore (i.e. semaphore are useful to define a one lock for write, many locks for reads).

So, if you have 4 groups of resources you are managing, you may use:

  • std::mutex,
  • pthread_mutex,
  • boost::mutex,
  • semaphores.

What you cannot do is use the boost::mutex to access data protected by the semaphores and vice versa, or std::mutex to use things protected by pthread_mutex.

As a simple example, in terms of code, this means a getter and a setter would be done this way:

void set(int new_value)
{
    guard lock(my_mutex);
    m_value = new_value;
}

int get() const
{
    guard lock(my_mutex);
    return m_value;
}

The two functions use the same mutex (here my_mutex) and obvious the mutex has one type.

Opposed to that, you could not do that:

void set(int new_value)
{
    guard lock(this_mutex_here);
    m_value = new_value;
}

int get() const
{
    SafeGuard lock(that_mutex_there);
    return m_value;
}

In this second example, you use two different mutexes and that won't work as expected because the lock in set() won't block the lock in get() and vice versa. So there is nothing safe about it (even if one of the guards is called SafeGuard.)

So the rule is, if you protect m_value with a mutex named my_mutex, any time you access m_value you must lock the my_mytex mutex. Which implementation you are using does not matter, as long as you are consistent in this way.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156