Skip to content

Commit 2d43d40

Browse files
committed
Support process shared pthread_rwlock
Cosmo now has a non-nsync implementation of POSIX read-write locks. It's possible to call pthread_rwlockattr_setpshared in PTHREAD_PROCESS_SHARED mode. Furthermore, if cosmo is built with PTHREAD_USE_NSYNC set to zero, then Cosmo shouldn't use nsync at all. That's helpful if you want to not link any Apache 2.0 licensed code.
1 parent c22b413 commit 2d43d40

11 files changed

+179
-35
lines changed

libc/thread/pthread_rwlock_destroy.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,30 @@
1616
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
19+
#include "libc/errno.h"
20+
#include "libc/intrin/atomic.h"
1921
#include "libc/str/str.h"
2022
#include "libc/thread/thread.h"
23+
#include "third_party/nsync/mu.h"
2124

2225
/**
2326
* Destroys read-write lock.
2427
*
2528
* @return 0 on success, or error number on failure
26-
* @raise EINVAL if any threads still hold the lock
29+
* @raise EBUSY if any threads still hold the lock
2730
*/
2831
errno_t pthread_rwlock_destroy(pthread_rwlock_t *rwlock) {
32+
33+
// check if lock is held
34+
if (!rwlock->_pshared) {
35+
nsync_mu *mu = (nsync_mu *)rwlock->_nsync;
36+
if (atomic_load_explicit(&mu->word, memory_order_relaxed))
37+
return EBUSY;
38+
} else {
39+
if (atomic_load_explicit(&rwlock->_word, memory_order_relaxed))
40+
return EBUSY;
41+
}
42+
2943
memset(rwlock, -1, sizeof(*rwlock));
3044
return 0;
3145
}

libc/thread/pthread_rwlock_init.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
*/
2727
errno_t pthread_rwlock_init(pthread_rwlock_t *rwlock,
2828
const pthread_rwlockattr_t *attr) {
29-
*rwlock = (pthread_rwlock_t){0};
29+
*rwlock = (pthread_rwlock_t){
30+
._pshared = attr ? *attr : PTHREAD_PROCESS_PRIVATE,
31+
};
3032
return 0;
3133
}

libc/thread/pthread_rwlock_rdlock.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
19+
#include "libc/intrin/atomic.h"
1920
#include "libc/thread/thread.h"
2021
#include "third_party/nsync/mu.h"
2122

@@ -24,7 +25,25 @@
2425
*
2526
* @return 0 on success, or errno on error
2627
*/
27-
errno_t pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) {
28-
nsync_mu_rlock((nsync_mu *)rwlock);
29-
return 0;
28+
errno_t pthread_rwlock_rdlock(pthread_rwlock_t *lk) {
29+
30+
#if PTHREAD_USE_NSYNC
31+
// use nsync if possible
32+
if (!lk->_pshared) {
33+
nsync_mu_rlock((nsync_mu *)lk->_nsync);
34+
return 0;
35+
}
36+
#endif
37+
38+
// naive implementation
39+
uint32_t w = 0;
40+
for (;;) {
41+
if (w & 1)
42+
for (;;)
43+
if (~(w = atomic_load_explicit(&lk->_word, memory_order_relaxed)) & 1)
44+
break;
45+
if (atomic_compare_exchange_weak_explicit(
46+
&lk->_word, &w, w + 2, memory_order_acquire, memory_order_relaxed))
47+
return 0;
48+
}
3049
}

libc/thread/pthread_rwlock_tryrdlock.c

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/errno.h"
20+
#include "libc/intrin/atomic.h"
2021
#include "libc/thread/thread.h"
2122
#include "third_party/nsync/mu.h"
2223

@@ -29,9 +30,26 @@
2930
* @raise EINVAL if `rwlock` doesn't refer to an initialized r/w lock
3031
*/
3132
errno_t pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) {
32-
if (nsync_mu_rtrylock((nsync_mu *)rwlock)) {
33-
return 0;
34-
} else {
35-
return EBUSY;
33+
34+
#if PTHREAD_USE_NSYNC
35+
// use nsync if possible
36+
if (!rwlock->_pshared) {
37+
if (nsync_mu_rtrylock((nsync_mu *)rwlock->_nsync)) {
38+
return 0;
39+
} else {
40+
return EBUSY;
41+
}
42+
}
43+
#endif
44+
45+
// naive implementation
46+
uint32_t word = 0;
47+
for (;;) {
48+
if (word & 1)
49+
return EBUSY;
50+
if (atomic_compare_exchange_weak_explicit(&rwlock->_word, &word, word + 2,
51+
memory_order_acquire,
52+
memory_order_relaxed))
53+
return 0;
3654
}
3755
}

libc/thread/pthread_rwlock_trywrlock.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/errno.h"
20+
#include "libc/intrin/atomic.h"
2021
#include "libc/thread/thread.h"
2122
#include "third_party/nsync/mu.h"
2223

@@ -28,10 +29,23 @@
2829
* @raise EINVAL if `rwlock` doesn't refer to an initialized r/w lock
2930
*/
3031
errno_t pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) {
31-
if (nsync_mu_trylock((nsync_mu *)rwlock)) {
32-
rwlock->_iswrite = 1;
33-
return 0;
34-
} else {
35-
return EBUSY;
32+
33+
#if PTHREAD_USE_NSYNC
34+
// use nsync if possible
35+
if (!rwlock->_pshared) {
36+
if (nsync_mu_trylock((nsync_mu *)rwlock->_nsync)) {
37+
rwlock->_iswrite = 1;
38+
return 0;
39+
} else {
40+
return EBUSY;
41+
}
3642
}
43+
#endif
44+
45+
// naive implementation
46+
uint32_t word = 0;
47+
if (atomic_compare_exchange_strong_explicit(
48+
&rwlock->_word, &word, 1, memory_order_acquire, memory_order_relaxed))
49+
return 0;
50+
return EBUSY;
3751
}

libc/thread/pthread_rwlock_unlock.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
19+
#include "libc/errno.h"
20+
#include "libc/intrin/atomic.h"
1921
#include "libc/thread/thread.h"
2022
#include "third_party/nsync/mu.h"
2123

@@ -26,11 +28,33 @@
2628
* @raise EINVAL if lock is in a bad state
2729
*/
2830
errno_t pthread_rwlock_unlock(pthread_rwlock_t *rwlock) {
29-
if (rwlock->_iswrite) {
30-
rwlock->_iswrite = 0;
31-
nsync_mu_unlock((nsync_mu *)rwlock);
32-
} else {
33-
nsync_mu_runlock((nsync_mu *)rwlock);
31+
32+
#if PTHREAD_USE_NSYNC
33+
// use nsync if possible
34+
if (!rwlock->_pshared) {
35+
if (rwlock->_iswrite) {
36+
rwlock->_iswrite = 0;
37+
nsync_mu_unlock((nsync_mu *)rwlock->_nsync);
38+
} else {
39+
nsync_mu_runlock((nsync_mu *)rwlock->_nsync);
40+
}
41+
return 0;
42+
}
43+
#endif
44+
45+
// naive implementation
46+
uint32_t word = atomic_load_explicit(&rwlock->_word, memory_order_relaxed);
47+
for (;;) {
48+
if (word & 1) {
49+
atomic_store_explicit(&rwlock->_word, 0, memory_order_release);
50+
return 0;
51+
} else if (word) {
52+
if (atomic_compare_exchange_weak_explicit(&rwlock->_word, &word, word - 2,
53+
memory_order_release,
54+
memory_order_relaxed))
55+
return 0;
56+
} else {
57+
return EPERM;
58+
}
3459
}
35-
return 0;
3660
}

libc/thread/pthread_rwlock_wrlock.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
19+
#include "libc/intrin/atomic.h"
1920
#include "libc/thread/thread.h"
2021
#include "third_party/nsync/mu.h"
2122

@@ -25,7 +26,24 @@
2526
* @return 0 on success, or errno on error
2627
*/
2728
errno_t pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) {
28-
nsync_mu_lock((nsync_mu *)rwlock);
29-
rwlock->_iswrite = 1;
30-
return 0;
29+
30+
#if PTHREAD_USE_NSYNC
31+
// use nsync if possible
32+
if (!rwlock->_pshared) {
33+
nsync_mu_lock((nsync_mu *)rwlock->_nsync);
34+
rwlock->_iswrite = 1;
35+
return 0;
36+
}
37+
#endif
38+
39+
// naive implementation
40+
uint32_t w = 0;
41+
for (;;) {
42+
if (atomic_compare_exchange_weak_explicit(
43+
&rwlock->_word, &w, 1, memory_order_acquire, memory_order_relaxed))
44+
return 0;
45+
for (;;)
46+
if (!(w = atomic_load_explicit(&rwlock->_word, memory_order_relaxed)))
47+
break;
48+
}
3149
}

libc/thread/pthread_rwlockattr_getpshared.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*
2424
* @param pshared is set to one of the following
2525
* - `PTHREAD_PROCESS_PRIVATE` (default)
26-
* - `PTHREAD_PROCESS_SHARED` (unsupported)
26+
* - `PTHREAD_PROCESS_SHARED`
2727
* @return 0 on success, or error on failure
2828
*/
2929
errno_t pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,

libc/thread/pthread_rwlockattr_setpshared.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@
2424
*
2525
* @param pshared can be one of
2626
* - `PTHREAD_PROCESS_PRIVATE` (default)
27-
* - `PTHREAD_PROCESS_SHARED` (unsupported)
27+
* - `PTHREAD_PROCESS_SHARED`
2828
* @return 0 on success, or error on failure
2929
* @raises EINVAL if `pshared` is invalid
3030
*/
3131
errno_t pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) {
3232
switch (pshared) {
3333
case PTHREAD_PROCESS_PRIVATE:
34+
case PTHREAD_PROCESS_SHARED:
3435
*attr = pshared;
3536
return 0;
3637
default:

libc/thread/thread.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,15 @@ typedef struct pthread_cond_s {
107107
} pthread_cond_t;
108108

109109
typedef struct pthread_rwlock_s {
110-
void *_nsync[2];
111-
char _iswrite;
110+
union {
111+
void *_nsync[2];
112+
struct {
113+
uint32_t _nsync_word;
114+
char _pshared;
115+
char _iswrite;
116+
_PTHREAD_ATOMIC(uint32_t) _word;
117+
};
118+
};
112119
} pthread_rwlock_t;
113120

114121
typedef struct pthread_barrier_s {

test/libc/thread/pthread_rwlock_rdlock_test.c

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,48 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/atomic.h"
20+
#include "libc/calls/calls.h"
2021
#include "libc/mem/gc.h"
2122
#include "libc/mem/mem.h"
2223
#include "libc/testlib/testlib.h"
2324
#include "libc/thread/thread.h"
2425

25-
#define ITERATIONS 50000
26-
#define READERS 8
27-
#define WRITERS 2
26+
#define READERS 8
27+
#define WRITERS 2
28+
#define READER_ITERATIONS 10000
29+
#define WRITER_ITERATIONS 1000
2830

31+
int writes;
2932
atomic_int reads;
30-
atomic_int writes;
3133
pthread_rwlock_t lock;
34+
pthread_rwlockattr_t attr;
3235
pthread_barrier_t barrier;
3336

37+
FIXTURE(pthread_rwlock, private) {
38+
reads = 0;
39+
writes = 0;
40+
ASSERT_EQ(0, pthread_rwlockattr_init(&attr));
41+
ASSERT_EQ(0, pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE));
42+
ASSERT_EQ(0, pthread_rwlock_init(&lock, &attr));
43+
ASSERT_EQ(0, pthread_rwlockattr_destroy(&attr));
44+
}
45+
46+
FIXTURE(pthread_rwlock, pshared) {
47+
reads = 0;
48+
writes = 0;
49+
ASSERT_EQ(0, pthread_rwlockattr_init(&attr));
50+
ASSERT_EQ(0, pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED));
51+
ASSERT_EQ(0, pthread_rwlock_init(&lock, &attr));
52+
ASSERT_EQ(0, pthread_rwlockattr_destroy(&attr));
53+
}
54+
55+
void TearDown(void) {
56+
ASSERT_EQ(0, pthread_rwlock_destroy(&lock));
57+
}
58+
3459
void *Reader(void *arg) {
3560
pthread_barrier_wait(&barrier);
36-
for (int i = 0; i < ITERATIONS; ++i) {
61+
for (int i = 0; i < READER_ITERATIONS; ++i) {
3762
ASSERT_EQ(0, pthread_rwlock_rdlock(&lock));
3863
++reads;
3964
ASSERT_EQ(0, pthread_rwlock_unlock(&lock));
@@ -43,10 +68,12 @@ void *Reader(void *arg) {
4368

4469
void *Writer(void *arg) {
4570
pthread_barrier_wait(&barrier);
46-
for (int i = 0; i < ITERATIONS; ++i) {
71+
for (int i = 0; i < WRITER_ITERATIONS; ++i) {
4772
ASSERT_EQ(0, pthread_rwlock_wrlock(&lock));
4873
++writes;
4974
ASSERT_EQ(0, pthread_rwlock_unlock(&lock));
75+
for (volatile int i = 0; i < 100; ++i)
76+
pthread_pause_np();
5077
}
5178
return 0;
5279
}
@@ -62,7 +89,7 @@ TEST(pthread_rwlock_rdlock, test) {
6289
for (i = 0; i < READERS + WRITERS; ++i) {
6390
EXPECT_SYS(0, 0, pthread_join(t[i], 0));
6491
}
65-
EXPECT_EQ(READERS * ITERATIONS, reads);
66-
EXPECT_EQ(WRITERS * ITERATIONS, writes);
92+
EXPECT_EQ(READERS * READER_ITERATIONS, reads);
93+
EXPECT_EQ(WRITERS * WRITER_ITERATIONS, writes);
6794
ASSERT_EQ(0, pthread_barrier_destroy(&barrier));
6895
}

0 commit comments

Comments
 (0)