Shared-memory pthread condition variable not working

I’m trying to implement a simple cross-process notify/observe system, using a pthread mutex and condition variable in shared (mapped) memory. It seems to be working fine (on macOS 11.6) if one process calls pthread_cond_wait and then another calls pthread_cond_broadcast — the waiting process indeed wakes up. However, if two processes try to observe at the same time, the second one's call to pthread_cond_wait fails with EINVAL. I’m wondering if I’m just doing something wrong in my setup, or if this sort of usage isn’t supported.

Basically I create and mmap a file, initialize a pthread mutex and condition in the mapped memory using the setpshared attributes, then lock the mutex and notify or wait on the condition. Actual source code here:

I’m aware that there are a few dozen 🙄 Apple IPC APIs that are probably preferred over these POSIX ones. I’ve used some in the past. I’m doing it this way because:

  • (a) this is in a cross-platform project and it would be nice to share code between Unix platforms, at least Darwin and Linux;
  • (b) the thing I’m notifying/observing about is a database file, so tying the notifications to a side file next to the database provides ideal scoping;
  • (c) it’s similar in principle to the usage of shared memory for locking in SQLite and LMDB. (The difference is, I’m doing notification not locking.)

Any advice?

—Jens

I ran into this problem recently on macOS 14.5. I came across this Stack Overflow post that describes a similar issue. One of the commenters there had a work-around where you can cast the pthread_cond_t to a type that exposes its internal implementation and manually set the busy field to NULL before calling pthread_cond_wait(). I was able to confirm that this works by testing the following code snippet.

typedef struct {
    long sig;
    uint32_t lock;
    uint32_t unused;
    char *busy;
} ndrx_osx_pthread_cond_t;

ndrx_osx_pthread_cond_t *cond_ptr = (ndrx_osx_pthread_cond_t *)&cond;
cond_ptr->busy = NULL;
pthread_cond_wait(&cond, &mutex.mtx);

According to the Stack Overflow post, another process will set busy to its mutex address, but because shmat() does not guarantee that addresses will be equal across processes, the values fail the equality check done in pthread_cond_wait(), causing it to return EINVAL.

This seems like a hacky work-around, but it worked for me. Hope this helps.

Shared-memory pthread condition variable not working
 
 
Q