Skip to content

Commit a490081

Browse files
committed
posix: timer: use async pthread cancellation
Previously, Zephyr's POSIX API did not differentiate between deferred and asynchronous pthread cancellation. In fact all pthread cancellation was asynchronous. According to the spec, all pthreads should be created with deferred cancellation by default. Note: PTHREAD_CANCEL_ASYNCHRONOUS means cancel asynchronously with respect to cancellation points (but synchronously with respect to the thread that callse pthread_cancel(), which is perhaps unintuitive). The POSIX timer relied on this non-standard convention. Oddly, this change prevents what would have otherwise been a regression that would have been caused by fixing pthread behaviour (in a separate commit). We are effectively uncovering bugs which were probably always present in the pthread.c and timer.c implementations going back quite a few years. Signed-off-by: Christopher Friedt <[email protected]>
1 parent d9d592d commit a490081

File tree

1 file changed

+101
-31
lines changed

1 file changed

+101
-31
lines changed

lib/posix/timer.c

Lines changed: 101 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
/*
22
* Copyright (c) 2018 Intel Corporation
3+
* Copyright (c) 2024, Meta
34
*
45
* SPDX-License-Identifier: Apache-2.0
56
*/
6-
#include <zephyr/kernel.h>
77
#include <errno.h>
8-
#include <string.h>
8+
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/logging/log.h>
911
#include <zephyr/posix/pthread.h>
10-
#include <zephyr/sys/printk.h>
1112
#include <zephyr/posix/signal.h>
1213
#include <zephyr/posix/time.h>
1314

1415
#define ACTIVE 1
1516
#define NOT_ACTIVE 0
1617

18+
LOG_MODULE_REGISTER(posix_timer);
19+
1720
static void zephyr_timer_wrapper(struct k_timer *ztimer);
1821

1922
struct timer_obj {
2023
struct k_timer ztimer;
21-
struct sigevent sev;
24+
struct sigevent evp;
2225
struct k_sem sem_cond;
2326
pthread_t thread;
2427
struct timespec interval; /* Reload value */
@@ -37,23 +40,53 @@ static void zephyr_timer_wrapper(struct k_timer *ztimer)
3740

3841
if (timer->reload == 0U) {
3942
timer->status = NOT_ACTIVE;
43+
LOG_DBG("timer %p not active", timer);
44+
return;
45+
}
46+
47+
if (timer->evp.sigev_notify == SIGEV_NONE) {
48+
LOG_DBG("SIGEV_NONE");
49+
return;
4050
}
4151

42-
(timer->sev.sigev_notify_function)(timer->sev.sigev_value);
52+
if (timer->evp.sigev_notify_function == NULL) {
53+
LOG_DBG("NULL sigev_notify_function");
54+
return;
55+
}
56+
57+
LOG_DBG("calling sigev_notify_function %p", timer->evp.sigev_notify_function);
58+
(timer->evp.sigev_notify_function)(timer->evp.sigev_value);
4359
}
4460

4561
static void *zephyr_thread_wrapper(void *arg)
4662
{
63+
int ret;
4764
struct timer_obj *timer = (struct timer_obj *)arg;
4865

66+
ret = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
67+
__ASSERT(ret == 0, "pthread_setcanceltype() failed: %d", ret);
68+
69+
if (timer->evp.sigev_notify_attributes == NULL) {
70+
ret = pthread_detach(pthread_self());
71+
__ASSERT(ret == 0, "pthread_detach() failed: %d", ret);
72+
}
73+
4974
while (1) {
5075
if (timer->reload == 0U) {
5176
timer->status = NOT_ACTIVE;
77+
LOG_DBG("timer %p not active", timer);
5278
}
5379

54-
k_sem_take(&timer->sem_cond, K_FOREVER);
80+
ret = k_sem_take(&timer->sem_cond, K_FOREVER);
81+
__ASSERT(ret == 0, "k_sem_take() failed: %d", ret);
82+
83+
if (timer->evp.sigev_notify_function == NULL) {
84+
LOG_DBG("NULL sigev_notify_function");
85+
continue;
86+
}
5587

56-
(timer->sev.sigev_notify_function)(timer->sev.sigev_value);
88+
LOG_DBG("calling sigev_notify_function %p", timer->evp.sigev_notify_function);
89+
(timer->evp.sigev_notify_function)(timer->evp.sigev_value);
5790
}
5891

5992
return NULL;
@@ -77,52 +110,89 @@ static void zephyr_timer_interrupt(struct k_timer *ztimer)
77110
*/
78111
int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid)
79112
{
113+
int ret = 0;
114+
int detachstate;
80115
struct timer_obj *timer;
81116
const k_timeout_t alloc_timeout = K_MSEC(CONFIG_TIMER_CREATE_WAIT);
82117

83-
if (evp == NULL || (evp->sigev_notify != SIGEV_NONE && evp->sigev_notify != SIGEV_SIGNAL &&
84-
evp->sigev_notify != SIGEV_THREAD)) {
118+
if (evp == NULL || timerid == NULL) {
85119
errno = EINVAL;
86120
return -1;
87121
}
88122

89-
if (k_mem_slab_alloc(&posix_timer_slab, (void **)&timer, alloc_timeout) == 0) {
90-
(void)memset(timer, 0, sizeof(struct timer_obj));
91-
} else {
123+
if (k_mem_slab_alloc(&posix_timer_slab, (void **)&timer, alloc_timeout) != 0) {
124+
LOG_DBG("k_mem_slab_alloc() failed: %d", ret);
92125
errno = ENOMEM;
93126
return -1;
94127
}
95128

96-
timer->sev.sigev_notify_function = evp->sigev_notify_function;
97-
timer->sev.sigev_value = evp->sigev_value;
98-
timer->interval.tv_sec = 0;
99-
timer->interval.tv_nsec = 0;
100-
timer->reload = 0U;
101-
timer->status = NOT_ACTIVE;
129+
*timer = (struct timer_obj){0};
130+
timer->evp = *evp;
131+
evp = &timer->evp;
102132

103-
if (evp->sigev_notify == SIGEV_NONE) {
133+
switch (evp->sigev_notify) {
134+
case SIGEV_NONE:
104135
k_timer_init(&timer->ztimer, NULL, NULL);
105-
} else if (evp->sigev_notify == SIGEV_THREAD) {
106-
int ret;
136+
break;
137+
case SIGEV_SIGNAL:
138+
k_timer_init(&timer->ztimer, zephyr_timer_wrapper, NULL);
139+
break;
140+
case SIGEV_THREAD:
141+
if (evp->sigev_notify_attributes != NULL) {
142+
ret = pthread_attr_getdetachstate(evp->sigev_notify_attributes,
143+
&detachstate);
144+
if (ret != 0) {
145+
LOG_DBG("pthread_attr_getdetachstate() failed: %d", ret);
146+
errno = ret;
147+
ret = -1;
148+
goto free_timer;
149+
}
150+
151+
if (detachstate != PTHREAD_CREATE_DETACHED) {
152+
ret = pthread_attr_setdetachstate(evp->sigev_notify_attributes,
153+
PTHREAD_CREATE_DETACHED);
154+
if (ret != 0) {
155+
LOG_DBG("pthread_attr_setdetachstate() failed: %d", ret);
156+
errno = ret;
157+
ret = -1;
158+
goto free_timer;
159+
}
160+
}
161+
}
162+
163+
ret = k_sem_init(&timer->sem_cond, 0, 1);
164+
if (ret != 0) {
165+
LOG_DBG("k_sem_init() failed: %d", ret);
166+
errno = -ret;
167+
ret = -1;
168+
goto free_timer;
169+
}
107170

108-
timer->sev.sigev_notify = SIGEV_THREAD;
109-
k_sem_init(&timer->sem_cond, 0, 1);
110171
ret = pthread_create(&timer->thread, evp->sigev_notify_attributes,
111172
zephyr_thread_wrapper, timer);
112173
if (ret != 0) {
113-
k_mem_slab_free(&posix_timer_slab, (void *) &timer);
114-
return ret;
174+
LOG_DBG("pthread_create() failed: %d", ret);
175+
errno = ret;
176+
ret = -1;
177+
goto free_timer;
115178
}
116179

117-
pthread_detach(timer->thread);
118180
k_timer_init(&timer->ztimer, zephyr_timer_interrupt, NULL);
119-
} else {
120-
k_timer_init(&timer->ztimer, zephyr_timer_wrapper, NULL);
181+
break;
182+
default:
183+
ret = -1;
184+
errno = EINVAL;
185+
goto free_timer;
121186
}
122187

123188
*timerid = (timer_t)timer;
189+
goto out;
124190

125-
return 0;
191+
free_timer:
192+
k_mem_slab_free(&posix_timer_slab, (void *)&timer);
193+
194+
out:
195+
return ret;
126196
}
127197

128198
/**
@@ -262,8 +332,8 @@ int timer_delete(timer_t timerid)
262332
k_timer_stop(&timer->ztimer);
263333
}
264334

265-
if (timer->sev.sigev_notify == SIGEV_THREAD) {
266-
pthread_cancel(timer->thread);
335+
if (timer->evp.sigev_notify == SIGEV_THREAD) {
336+
(void)pthread_cancel(timer->thread);
267337
}
268338

269339
k_mem_slab_free(&posix_timer_slab, (void *)timer);

0 commit comments

Comments
 (0)