2 * SPDX-License-Identifier: BSD-3-Clause
3 * Copyright 2015 Intel Corporation.
4 * Copyright 2012 Hasan Alayli <halayli@gmail.com>
26 #include "lthread_api.h"
28 #include "lthread_timer.h"
29 #include "lthread_tls.h"
30 #include "lthread_objcache.h"
31 #include "lthread_diag.h"
35 * This function gets called after an lthread function has returned.
37 void _lthread_exit_handler(struct lthread *lt)
40 lt->state |= BIT(ST_LT_EXITED);
42 if (!(lt->state & BIT(ST_LT_DETACH))) {
43 /* thread is this not explicitly detached
44 * it must be joinable, so we call lthread_exit().
49 /* if we get here the thread is detached so we can reschedule it,
50 * allowing the scheduler to free it
57 * Free resources allocated to an lthread
59 void _lthread_free(struct lthread *lt)
62 DIAG_EVENT(lt, LT_DIAG_LTHREAD_FREE, lt, 0);
64 /* invoke any user TLS destructor functions */
65 _lthread_tls_destroy(lt);
67 /* free memory allocated for TLS defined using RTE_PER_LTHREAD macros */
68 if (sizeof(void *) < (uint64_t)RTE_PER_LTHREAD_SECTION_SIZE)
69 _lthread_objcache_free(lt->tls->root_sched->per_lthread_cache,
70 lt->per_lthread_data);
72 /* free pthread style TLS memory */
73 _lthread_objcache_free(lt->tls->root_sched->tls_cache, lt->tls);
76 _lthread_objcache_free(lt->stack_container->root_sched->stack_cache,
79 /* now free the thread */
80 _lthread_objcache_free(lt->root_sched->lthread_cache, lt);
85 * Allocate a stack and maintain a cache of stacks
87 struct lthread_stack *_stack_alloc(void)
89 struct lthread_stack *s;
91 s = _lthread_objcache_alloc((THIS_SCHED)->stack_cache);
92 RTE_ASSERT(s != NULL);
94 s->root_sched = THIS_SCHED;
95 s->stack_size = LTHREAD_MAX_STACK_SIZE;
100 * Execute a ctx by invoking the start function
101 * On return call an exit handler if the user has provided one
103 static void _lthread_exec(void *arg)
105 struct lthread *lt = (struct lthread *)arg;
107 /* invoke the contexts function */
109 /* do exit handling */
110 if (lt->exit_handler != NULL)
111 lt->exit_handler(lt);
115 * Initialize an lthread
116 * Set its function, args, and exit handler
119 _lthread_init(struct lthread *lt,
120 lthread_func_t fun, void *arg, lthread_exit_func exit_handler)
123 /* set ctx func and args */
126 lt->exit_handler = exit_handler;
128 /* set initial state */
129 lt->birth = _sched_now();
130 lt->state = BIT(ST_LT_INIT);
131 lt->join = LT_JOIN_INITIAL;
135 * set the lthread stack
137 void _lthread_set_stack(struct lthread *lt, void *stack, size_t stack_size)
141 lt->stack_size = stack_size;
143 arch_set_stack(lt, _lthread_exec);
147 * Create an lthread on the current scheduler
148 * If there is no current scheduler on this pthread then first create one
151 lthread_create(struct lthread **new_lt, int lcore_id,
152 lthread_func_t fun, void *arg)
154 if ((new_lt == NULL) || (fun == NULL))
155 return POSIX_ERRNO(EINVAL);
158 lcore_id = rte_lcore_id();
159 else if (lcore_id > LTHREAD_MAX_LCORES)
160 return POSIX_ERRNO(EINVAL);
162 struct lthread *lt = NULL;
164 if (THIS_SCHED == NULL) {
165 THIS_SCHED = _lthread_sched_create(0);
166 if (THIS_SCHED == NULL) {
167 perror("Failed to create scheduler");
168 return POSIX_ERRNO(EAGAIN);
172 /* allocate a thread structure */
173 lt = _lthread_objcache_alloc((THIS_SCHED)->lthread_cache);
175 return POSIX_ERRNO(EAGAIN);
177 bzero(lt, sizeof(struct lthread));
178 lt->root_sched = THIS_SCHED;
180 /* set the function args and exit handlder */
181 _lthread_init(lt, fun, arg, _lthread_exit_handler);
183 /* put it in the ready queue */
187 lcore_id = rte_lcore_id();
189 DIAG_CREATE_EVENT(lt, LT_DIAG_LTHREAD_CREATE);
192 _ready_queue_insert(_lthread_sched_get(lcore_id), lt);
197 * Schedules lthread to sleep for `nsecs`
198 * setting the lthread state to LT_ST_SLEEPING.
199 * lthread state is cleared upon resumption or expiry.
201 static inline void _lthread_sched_sleep(struct lthread *lt, uint64_t nsecs)
203 uint64_t state = lt->state;
204 uint64_t clks = _ns_to_clks(nsecs);
207 _timer_start(lt, clks);
208 lt->state = state | BIT(ST_LT_SLEEPING);
210 DIAG_EVENT(lt, LT_DIAG_LTHREAD_SLEEP, clks, 0);
217 * Cancels any running timer.
218 * This can be called multiple times on the same lthread regardless if it was
221 int _lthread_desched_sleep(struct lthread *lt)
223 uint64_t state = lt->state;
225 if (state & BIT(ST_LT_SLEEPING)) {
227 state &= (CLEARBIT(ST_LT_SLEEPING) & CLEARBIT(ST_LT_EXPIRED));
228 lt->state = state | BIT(ST_LT_READY);
235 * set user data pointer in an lthread
237 void lthread_set_data(void *data)
239 if (sizeof(void *) == RTE_PER_LTHREAD_SECTION_SIZE)
240 THIS_LTHREAD->per_lthread_data = data;
244 * Retrieve user data pointer from an lthread
246 void *lthread_get_data(void)
248 return THIS_LTHREAD->per_lthread_data;
252 * Return the current lthread handle
254 struct lthread *lthread_current(void)
256 struct lthread_sched *sched = THIS_SCHED;
259 return sched->current_lthread;
266 * Tasklet to cancel a thread
271 struct lthread *lt = (struct lthread *) arg;
273 lt->state |= BIT(ST_LT_CANCELLED);
280 * Mark the specified as canceled
282 int lthread_cancel(struct lthread *cancel_lt)
286 if ((cancel_lt == NULL) || (cancel_lt == THIS_LTHREAD))
287 return POSIX_ERRNO(EINVAL);
289 DIAG_EVENT(cancel_lt, LT_DIAG_LTHREAD_CANCEL, cancel_lt, 0);
291 if (cancel_lt->sched != THIS_SCHED) {
293 /* spawn task-let to cancel the thread */
295 cancel_lt->sched->lcore_id,
300 cancel_lt->state |= BIT(ST_LT_CANCELLED);
305 * Suspend the current lthread for specified time
307 void lthread_sleep(uint64_t nsecs)
309 struct lthread *lt = THIS_LTHREAD;
311 _lthread_sched_sleep(lt, nsecs);
316 * Suspend the current lthread for specified time
318 void lthread_sleep_clks(uint64_t clks)
320 struct lthread *lt = THIS_LTHREAD;
321 uint64_t state = lt->state;
324 _timer_start(lt, clks);
325 lt->state = state | BIT(ST_LT_SLEEPING);
327 DIAG_EVENT(lt, LT_DIAG_LTHREAD_SLEEP, clks, 0);
332 * Requeue the current thread to the back of the ready queue
334 void lthread_yield(void)
336 struct lthread *lt = THIS_LTHREAD;
338 DIAG_EVENT(lt, LT_DIAG_LTHREAD_YIELD, 0, 0);
340 _ready_queue_insert(THIS_SCHED, lt);
341 ctx_switch(&(THIS_SCHED)->ctx, <->ctx);
345 * Exit the current lthread
346 * If a thread is joining pass the user pointer to it
348 void lthread_exit(void *ptr)
350 struct lthread *lt = THIS_LTHREAD;
352 /* if thread is detached (this is not valid) just exit */
353 if (lt->state & BIT(ST_LT_DETACH))
356 /* There is a race between lthread_join() and lthread_exit()
357 * - if exit before join then we suspend and resume on join
358 * - if join before exit then we resume the joining thread
360 if ((lt->join == LT_JOIN_INITIAL)
361 && rte_atomic64_cmpset(<->join, LT_JOIN_INITIAL,
364 DIAG_EVENT(lt, LT_DIAG_LTHREAD_EXIT, 1, 0);
366 /* set the exit value */
367 if ((ptr != NULL) && (lt->lt_join->lt_exit_ptr != NULL))
368 *(lt->lt_join->lt_exit_ptr) = ptr;
370 /* let the joining thread know we have set the exit value */
371 lt->join = LT_JOIN_EXIT_VAL_SET;
374 DIAG_EVENT(lt, LT_DIAG_LTHREAD_EXIT, 0, 0);
375 /* set the exit value */
376 if ((ptr != NULL) && (lt->lt_join->lt_exit_ptr != NULL))
377 *(lt->lt_join->lt_exit_ptr) = ptr;
378 /* let the joining thread know we have set the exit value */
379 lt->join = LT_JOIN_EXIT_VAL_SET;
380 _ready_queue_insert(lt->lt_join->sched,
381 (struct lthread *)lt->lt_join);
385 /* wait until the joinging thread has collected the exit value */
386 while (lt->join != LT_JOIN_EXIT_VAL_READ)
389 /* reset join state */
390 lt->join = LT_JOIN_INITIAL;
392 /* detach it so its resources can be released */
393 lt->state |= (BIT(ST_LT_DETACH) | BIT(ST_LT_EXITED));
398 * Suspend until the joined thread returns
400 int lthread_join(struct lthread *lt, void **ptr)
403 return POSIX_ERRNO(EINVAL);
405 struct lthread *current = THIS_LTHREAD;
406 uint64_t lt_state = lt->state;
408 /* invalid to join a detached thread, or a thread that is joined */
409 if ((lt_state & BIT(ST_LT_DETACH)) || (lt->join == LT_JOIN_THREAD_SET))
410 return POSIX_ERRNO(EINVAL);
411 /* pointer to the joining thread and a poingter to return a value */
412 lt->lt_join = current;
413 current->lt_exit_ptr = ptr;
414 /* There is a race between lthread_join() and lthread_exit()
415 * - if join before exit we suspend and will resume when exit is called
416 * - if exit before join we resume the exiting thread
418 if ((lt->join == LT_JOIN_INITIAL)
419 && rte_atomic64_cmpset(<->join, LT_JOIN_INITIAL,
420 LT_JOIN_THREAD_SET)) {
422 DIAG_EVENT(current, LT_DIAG_LTHREAD_JOIN, lt, 1);
425 DIAG_EVENT(current, LT_DIAG_LTHREAD_JOIN, lt, 0);
426 _ready_queue_insert(lt->sched, lt);
429 /* wait for exiting thread to set return value */
430 while (lt->join != LT_JOIN_EXIT_VAL_SET)
433 /* collect the return value */
435 *ptr = *current->lt_exit_ptr;
437 /* let the exiting thread proceed to exit */
438 lt->join = LT_JOIN_EXIT_VAL_READ;
444 * Detach current lthread
445 * A detached thread cannot be joined
447 void lthread_detach(void)
449 struct lthread *lt = THIS_LTHREAD;
451 DIAG_EVENT(lt, LT_DIAG_LTHREAD_DETACH, 0, 0);
453 uint64_t state = lt->state;
455 lt->state = state | BIT(ST_LT_DETACH);
459 * Set function name of an lthread
460 * this is a debug aid
462 void lthread_set_funcname(const char *f)
464 struct lthread *lt = THIS_LTHREAD;
466 strncpy(lt->funcname, f, sizeof(lt->funcname));
467 lt->funcname[sizeof(lt->funcname)-1] = 0;