Skip to content

Commit 452205f

Browse files
committed
posix: pthread: implement pthread spinlocks
pthread_spinlock_t support is part of the base threading requirement. Signed-off-by: Christopher Friedt <[email protected]>
1 parent 28a8b15 commit 452205f

File tree

5 files changed

+213
-0
lines changed

5 files changed

+213
-0
lines changed

include/zephyr/posix/posix_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ typedef struct pthread_attr pthread_attr_t;
5555
BUILD_ASSERT(sizeof(pthread_attr_t) >= sizeof(struct pthread_attr));
5656

5757
typedef uint32_t pthread_t;
58+
typedef uint32_t pthread_spinlock_t;
5859

5960
/* Semaphore */
6061
typedef struct k_sem sem_t;

include/zephyr/posix/pthread.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ extern "C" {
2525
#define PTHREAD_CREATE_DETACHED 0
2626
#define PTHREAD_CREATE_JOINABLE 1
2727

28+
/* Pthread resource visibility */
29+
#define PTHREAD_PROCESS_PRIVATE 0
30+
#define PTHREAD_PROCESS_SHARED 1
31+
2832
/* Pthread cancellation */
2933
#define _PTHREAD_CANCEL_POS 0
3034
#define PTHREAD_CANCEL_ENABLE (0U << _PTHREAD_CANCEL_POS)
@@ -495,6 +499,45 @@ int pthread_setname_np(pthread_t thread, const char *name);
495499
*/
496500
int pthread_getname_np(pthread_t thread, char *name, size_t len);
497501

502+
#ifdef CONFIG_PTHREAD_IPC
503+
504+
/**
505+
* @brief Destroy a pthread_spinlock_t.
506+
*
507+
* See IEEE 1003.1
508+
*/
509+
int pthread_spin_destroy(pthread_spinlock_t *lock);
510+
511+
/**
512+
* @brief Initialize a thread_spinlock_t.
513+
*
514+
* See IEEE 1003.1
515+
*/
516+
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
517+
518+
/**
519+
* @brief Lock a previously initialized thread_spinlock_t.
520+
*
521+
* See IEEE 1003.1
522+
*/
523+
int pthread_spin_lock(pthread_spinlock_t *lock);
524+
525+
/**
526+
* @brief Attempt to lock a previously initialized thread_spinlock_t.
527+
*
528+
* See IEEE 1003.1
529+
*/
530+
int pthread_spin_trylock(pthread_spinlock_t *lock);
531+
532+
/**
533+
* @brief Unlock a previously locked thread_spinlock_t.
534+
*
535+
* See IEEE 1003.1
536+
*/
537+
int pthread_spin_unlock(pthread_spinlock_t *lock);
538+
539+
#endif
540+
498541
#ifdef __cplusplus
499542
}
500543
#endif

lib/posix/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC pthread_mutex.c)
2626
zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC pthread_barrier.c)
2727
zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC pthread.c)
2828
zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC pthread_sched.c)
29+
zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC pthread_spinlock.c)
2930
zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK clock.c)
3031
zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK nanosleep.c)
3132
zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK sleep.c)

lib/posix/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ config MAX_PTHREAD_BARRIER_COUNT
7474
help
7575
Maximum number of simultaneously active keys in a POSIX application.
7676

77+
config MAX_PTHREAD_SPINLOCK_COUNT
78+
int "Maximum simultaneously active spinlocks in a POSIX application"
79+
default 5
80+
range 0 255
81+
help
82+
Maximum number of simultaneously active spinlocks in a POSIX application.
83+
7784
config SEM_VALUE_MAX
7885
int "Maximum semaphore limit"
7986
default 32767

lib/posix/pthread_spinlock.c

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright (c) 2023, Meta
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "posix_internal.h"
8+
9+
#include <zephyr/init.h>
10+
#include <zephyr/kernel.h>
11+
#include <zephyr/posix/pthread.h>
12+
#include <zephyr/sys/bitarray.h>
13+
14+
union _spinlock_storage {
15+
struct k_spinlock lock;
16+
uint8_t byte;
17+
};
18+
#if !defined(CONFIG_SMP) && !defined(CONFIG_SPIN_VALIDATE)
19+
BUILD_ASSERT(sizeof(struct k_spinlock) == 0,
20+
"please remove the _spinlock_storage workaround if, at some point, k_spinlock is no "
21+
"longer zero bytes when CONFIG_SMP=n && CONFIG_SPIN_VALIDATE=n");
22+
#endif
23+
24+
static union _spinlock_storage posix_spinlock_pool[CONFIG_MAX_PTHREAD_SPINLOCK_COUNT];
25+
static k_spinlock_key_t posix_spinlock_key[CONFIG_MAX_PTHREAD_SPINLOCK_COUNT];
26+
SYS_BITARRAY_DEFINE_STATIC(posix_spinlock_bitarray, CONFIG_MAX_PTHREAD_SPINLOCK_COUNT);
27+
28+
/*
29+
* We reserve the MSB to mark a pthread_spinlock_t as initialized (from the
30+
* perspective of the application). With a linear space, this means that
31+
* the theoretical pthread_spinlock_t range is [0,2147483647].
32+
*/
33+
BUILD_ASSERT(CONFIG_MAX_PTHREAD_SPINLOCK_COUNT < PTHREAD_OBJ_MASK_INIT,
34+
"CONFIG_MAX_PTHREAD_SPINLOCK_COUNT is too high");
35+
36+
static inline size_t posix_spinlock_to_offset(struct k_spinlock *l)
37+
{
38+
return (union _spinlock_storage *)l - posix_spinlock_pool;
39+
}
40+
41+
static inline size_t to_posix_spinlock_idx(pthread_spinlock_t lock)
42+
{
43+
return mark_pthread_obj_uninitialized(lock);
44+
}
45+
46+
static struct k_spinlock *get_posix_spinlock(pthread_spinlock_t *lock)
47+
{
48+
size_t bit;
49+
int actually_initialized;
50+
51+
if (lock == NULL) {
52+
return NULL;
53+
}
54+
55+
/* if the provided spinlock does not claim to be initialized, its invalid */
56+
bit = to_posix_spinlock_idx(*lock);
57+
if (!is_pthread_obj_initialized(*lock)) {
58+
return NULL;
59+
}
60+
61+
/* Mask off the MSB to get the actual bit index */
62+
if (sys_bitarray_test_bit(&posix_spinlock_bitarray, bit, &actually_initialized) < 0) {
63+
return NULL;
64+
}
65+
66+
if (actually_initialized == 0) {
67+
/* The spinlock claims to be initialized but is actually not */
68+
return NULL;
69+
}
70+
71+
return (struct k_spinlock *)&posix_spinlock_pool[bit];
72+
}
73+
74+
int pthread_spin_init(pthread_spinlock_t *lock, int pshared)
75+
{
76+
int ret;
77+
size_t bit;
78+
79+
if (lock == NULL ||
80+
!(pshared == PTHREAD_PROCESS_PRIVATE || pshared == PTHREAD_PROCESS_SHARED)) {
81+
/* not specified as part of POSIX but this is the Linux behavior */
82+
return EINVAL;
83+
}
84+
85+
ret = sys_bitarray_alloc(&posix_spinlock_bitarray, 1, &bit);
86+
if (ret < 0) {
87+
return ENOMEM;
88+
}
89+
90+
*lock = mark_pthread_obj_initialized(bit);
91+
92+
return 0;
93+
}
94+
95+
int pthread_spin_destroy(pthread_spinlock_t *lock)
96+
{
97+
int err;
98+
size_t bit;
99+
struct k_spinlock *l;
100+
101+
l = get_posix_spinlock(lock);
102+
if (l == NULL) {
103+
/* not specified as part of POSIX but this is the Linux behavior */
104+
return EINVAL;
105+
}
106+
107+
bit = posix_spinlock_to_offset(l);
108+
err = sys_bitarray_free(&posix_spinlock_bitarray, 1, bit);
109+
__ASSERT_NO_MSG(err == 0);
110+
111+
return 0;
112+
}
113+
114+
int pthread_spin_lock(pthread_spinlock_t *lock)
115+
{
116+
size_t bit;
117+
struct k_spinlock *l;
118+
119+
l = get_posix_spinlock(lock);
120+
if (l == NULL) {
121+
/* not specified as part of POSIX but this is the Linux behavior */
122+
return EINVAL;
123+
}
124+
125+
bit = posix_spinlock_to_offset(l);
126+
posix_spinlock_key[bit] = k_spin_lock(l);
127+
128+
return 0;
129+
}
130+
131+
int pthread_spin_trylock(pthread_spinlock_t *lock)
132+
{
133+
size_t bit;
134+
struct k_spinlock *l;
135+
136+
l = get_posix_spinlock(lock);
137+
if (l == NULL) {
138+
/* not specified as part of POSIX but this is the Linux behavior */
139+
return EINVAL;
140+
}
141+
142+
bit = posix_spinlock_to_offset(l);
143+
return k_spin_trylock(l, &posix_spinlock_key[bit]);
144+
}
145+
146+
int pthread_spin_unlock(pthread_spinlock_t *lock)
147+
{
148+
size_t bit;
149+
struct k_spinlock *l;
150+
151+
l = get_posix_spinlock(lock);
152+
if (l == NULL) {
153+
/* not specified as part of POSIX but this is the Linux behavior */
154+
return EINVAL;
155+
}
156+
157+
bit = posix_spinlock_to_offset(l);
158+
k_spin_unlock(l, posix_spinlock_key[bit]);
159+
160+
return 0;
161+
}

0 commit comments

Comments
 (0)