1a9643ea8Slogwang /*- 2a9643ea8Slogwang * BSD LICENSE 3a9643ea8Slogwang * 4a9643ea8Slogwang * Copyright(c) 2015 Intel Corporation. All rights reserved. 5a9643ea8Slogwang * All rights reserved. 6a9643ea8Slogwang * 7a9643ea8Slogwang * Redistribution and use in source and binary forms, with or without 8a9643ea8Slogwang * modification, are permitted provided that the following conditions 9a9643ea8Slogwang * are met: 10a9643ea8Slogwang * 11a9643ea8Slogwang * * Redistributions of source code must retain the above copyright 12a9643ea8Slogwang * notice, this list of conditions and the following disclaimer. 13a9643ea8Slogwang * * Redistributions in binary form must reproduce the above copyright 14a9643ea8Slogwang * notice, this list of conditions and the following disclaimer in 15a9643ea8Slogwang * the documentation and/or other materials provided with the 16a9643ea8Slogwang * distribution. 17a9643ea8Slogwang * * Neither the name of Intel Corporation nor the names of its 18a9643ea8Slogwang * contributors may be used to endorse or promote products derived 19a9643ea8Slogwang * from this software without specific prior written permission. 20a9643ea8Slogwang * 21a9643ea8Slogwang * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22a9643ea8Slogwang * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23a9643ea8Slogwang * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24a9643ea8Slogwang * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25a9643ea8Slogwang * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26a9643ea8Slogwang * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27a9643ea8Slogwang * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28a9643ea8Slogwang * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29a9643ea8Slogwang * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30a9643ea8Slogwang * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31a9643ea8Slogwang * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32a9643ea8Slogwang */ 33a9643ea8Slogwang 34a9643ea8Slogwang /* 35a9643ea8Slogwang * Some portions of this software is derived from the 36a9643ea8Slogwang * https://github.com/halayli/lthread which carrys the following license. 37a9643ea8Slogwang * 38a9643ea8Slogwang * Copyright (C) 2012, Hasan Alayli <[email protected]> 39a9643ea8Slogwang * 40a9643ea8Slogwang * Redistribution and use in source and binary forms, with or without 41a9643ea8Slogwang * modification, are permitted provided that the following conditions 42a9643ea8Slogwang * are met: 43a9643ea8Slogwang * 1. Redistributions of source code must retain the above copyright 44a9643ea8Slogwang * notice, this list of conditions and the following disclaimer. 45a9643ea8Slogwang * 2. Redistributions in binary form must reproduce the above copyright 46a9643ea8Slogwang * notice, this list of conditions and the following disclaimer in the 47a9643ea8Slogwang * documentation and/or other materials provided with the distribution. 48a9643ea8Slogwang * 49a9643ea8Slogwang * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 50a9643ea8Slogwang * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51a9643ea8Slogwang * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52a9643ea8Slogwang * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 53a9643ea8Slogwang * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54a9643ea8Slogwang * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55a9643ea8Slogwang * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56a9643ea8Slogwang * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57a9643ea8Slogwang * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58a9643ea8Slogwang * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59a9643ea8Slogwang * SUCH DAMAGE. 60a9643ea8Slogwang */ 61a9643ea8Slogwang 62a9643ea8Slogwang #define RTE_MEM 1 63a9643ea8Slogwang 64a9643ea8Slogwang #include <stdio.h> 65a9643ea8Slogwang #include <stdlib.h> 66a9643ea8Slogwang #include <string.h> 67a9643ea8Slogwang #include <stdint.h> 68a9643ea8Slogwang #include <stddef.h> 69a9643ea8Slogwang #include <limits.h> 70a9643ea8Slogwang #include <inttypes.h> 71a9643ea8Slogwang #include <unistd.h> 72a9643ea8Slogwang #include <pthread.h> 73a9643ea8Slogwang #include <fcntl.h> 74a9643ea8Slogwang #include <sys/time.h> 75a9643ea8Slogwang #include <sys/mman.h> 76a9643ea8Slogwang 77a9643ea8Slogwang #include <rte_log.h> 78a9643ea8Slogwang #include <ctx.h> 79*2bfe3f2eSlogwang #include <stack.h> 80a9643ea8Slogwang 81a9643ea8Slogwang #include "lthread_api.h" 82a9643ea8Slogwang #include "lthread.h" 83a9643ea8Slogwang #include "lthread_timer.h" 84a9643ea8Slogwang #include "lthread_tls.h" 85a9643ea8Slogwang #include "lthread_objcache.h" 86a9643ea8Slogwang #include "lthread_diag.h" 87a9643ea8Slogwang 88a9643ea8Slogwang 89a9643ea8Slogwang /* 90a9643ea8Slogwang * This function gets called after an lthread function has returned. 91a9643ea8Slogwang */ 92a9643ea8Slogwang void _lthread_exit_handler(struct lthread *lt) 93a9643ea8Slogwang { 94a9643ea8Slogwang 95a9643ea8Slogwang lt->state |= BIT(ST_LT_EXITED); 96a9643ea8Slogwang 97a9643ea8Slogwang if (!(lt->state & BIT(ST_LT_DETACH))) { 98a9643ea8Slogwang /* thread is this not explicitly detached 99a9643ea8Slogwang * it must be joinable, so we call lthread_exit(). 100a9643ea8Slogwang */ 101a9643ea8Slogwang lthread_exit(NULL); 102a9643ea8Slogwang } 103a9643ea8Slogwang 104a9643ea8Slogwang /* if we get here the thread is detached so we can reschedule it, 105a9643ea8Slogwang * allowing the scheduler to free it 106a9643ea8Slogwang */ 107a9643ea8Slogwang _reschedule(); 108a9643ea8Slogwang } 109a9643ea8Slogwang 110a9643ea8Slogwang 111a9643ea8Slogwang /* 112a9643ea8Slogwang * Free resources allocated to an lthread 113a9643ea8Slogwang */ 114a9643ea8Slogwang void _lthread_free(struct lthread *lt) 115a9643ea8Slogwang { 116a9643ea8Slogwang 117a9643ea8Slogwang DIAG_EVENT(lt, LT_DIAG_LTHREAD_FREE, lt, 0); 118a9643ea8Slogwang 119a9643ea8Slogwang /* invoke any user TLS destructor functions */ 120a9643ea8Slogwang _lthread_tls_destroy(lt); 121a9643ea8Slogwang 122a9643ea8Slogwang /* free memory allocated for TLS defined using RTE_PER_LTHREAD macros */ 123a9643ea8Slogwang if (sizeof(void *) < (uint64_t)RTE_PER_LTHREAD_SECTION_SIZE) 124a9643ea8Slogwang _lthread_objcache_free(lt->tls->root_sched->per_lthread_cache, 125a9643ea8Slogwang lt->per_lthread_data); 126a9643ea8Slogwang 127a9643ea8Slogwang /* free pthread style TLS memory */ 128a9643ea8Slogwang _lthread_objcache_free(lt->tls->root_sched->tls_cache, lt->tls); 129a9643ea8Slogwang 130a9643ea8Slogwang /* free the stack */ 131a9643ea8Slogwang _lthread_objcache_free(lt->stack_container->root_sched->stack_cache, 132a9643ea8Slogwang lt->stack_container); 133a9643ea8Slogwang 134a9643ea8Slogwang /* now free the thread */ 135a9643ea8Slogwang _lthread_objcache_free(lt->root_sched->lthread_cache, lt); 136a9643ea8Slogwang 137a9643ea8Slogwang } 138a9643ea8Slogwang 139a9643ea8Slogwang /* 140a9643ea8Slogwang * Allocate a stack and maintain a cache of stacks 141a9643ea8Slogwang */ 142a9643ea8Slogwang struct lthread_stack *_stack_alloc(void) 143a9643ea8Slogwang { 144a9643ea8Slogwang struct lthread_stack *s; 145a9643ea8Slogwang 146a9643ea8Slogwang s = _lthread_objcache_alloc((THIS_SCHED)->stack_cache); 147a9643ea8Slogwang RTE_ASSERT(s != NULL); 148a9643ea8Slogwang 149a9643ea8Slogwang s->root_sched = THIS_SCHED; 150a9643ea8Slogwang s->stack_size = LTHREAD_MAX_STACK_SIZE; 151a9643ea8Slogwang return s; 152a9643ea8Slogwang } 153a9643ea8Slogwang 154a9643ea8Slogwang /* 155a9643ea8Slogwang * Execute a ctx by invoking the start function 156a9643ea8Slogwang * On return call an exit handler if the user has provided one 157a9643ea8Slogwang */ 158a9643ea8Slogwang static void _lthread_exec(void *arg) 159a9643ea8Slogwang { 160a9643ea8Slogwang struct lthread *lt = (struct lthread *)arg; 161a9643ea8Slogwang 162a9643ea8Slogwang /* invoke the contexts function */ 163a9643ea8Slogwang lt->fun(lt->arg); 164a9643ea8Slogwang /* do exit handling */ 165a9643ea8Slogwang if (lt->exit_handler != NULL) 166a9643ea8Slogwang lt->exit_handler(lt); 167a9643ea8Slogwang } 168a9643ea8Slogwang 169a9643ea8Slogwang /* 170a9643ea8Slogwang * Initialize an lthread 171a9643ea8Slogwang * Set its function, args, and exit handler 172a9643ea8Slogwang */ 173a9643ea8Slogwang void 174a9643ea8Slogwang _lthread_init(struct lthread *lt, 175a9643ea8Slogwang lthread_func_t fun, void *arg, lthread_exit_func exit_handler) 176a9643ea8Slogwang { 177a9643ea8Slogwang 178a9643ea8Slogwang /* set ctx func and args */ 179a9643ea8Slogwang lt->fun = fun; 180a9643ea8Slogwang lt->arg = arg; 181a9643ea8Slogwang lt->exit_handler = exit_handler; 182a9643ea8Slogwang 183a9643ea8Slogwang /* set initial state */ 184a9643ea8Slogwang lt->birth = _sched_now(); 185a9643ea8Slogwang lt->state = BIT(ST_LT_INIT); 186a9643ea8Slogwang lt->join = LT_JOIN_INITIAL; 187a9643ea8Slogwang } 188a9643ea8Slogwang 189a9643ea8Slogwang /* 190a9643ea8Slogwang * set the lthread stack 191a9643ea8Slogwang */ 192a9643ea8Slogwang void _lthread_set_stack(struct lthread *lt, void *stack, size_t stack_size) 193a9643ea8Slogwang { 194a9643ea8Slogwang /* set stack */ 195a9643ea8Slogwang lt->stack = stack; 196a9643ea8Slogwang lt->stack_size = stack_size; 197a9643ea8Slogwang 198*2bfe3f2eSlogwang arch_set_stack(lt, _lthread_exec); 199a9643ea8Slogwang } 200a9643ea8Slogwang 201a9643ea8Slogwang /* 202a9643ea8Slogwang * Create an lthread on the current scheduler 203a9643ea8Slogwang * If there is no current scheduler on this pthread then first create one 204a9643ea8Slogwang */ 205a9643ea8Slogwang int 206a9643ea8Slogwang lthread_create(struct lthread **new_lt, int lcore_id, 207a9643ea8Slogwang lthread_func_t fun, void *arg) 208a9643ea8Slogwang { 209a9643ea8Slogwang if ((new_lt == NULL) || (fun == NULL)) 210a9643ea8Slogwang return POSIX_ERRNO(EINVAL); 211a9643ea8Slogwang 212a9643ea8Slogwang if (lcore_id < 0) 213a9643ea8Slogwang lcore_id = rte_lcore_id(); 214a9643ea8Slogwang else if (lcore_id > LTHREAD_MAX_LCORES) 215a9643ea8Slogwang return POSIX_ERRNO(EINVAL); 216a9643ea8Slogwang 217a9643ea8Slogwang struct lthread *lt = NULL; 218a9643ea8Slogwang 219a9643ea8Slogwang if (THIS_SCHED == NULL) { 220a9643ea8Slogwang THIS_SCHED = _lthread_sched_create(0); 221a9643ea8Slogwang if (THIS_SCHED == NULL) { 222a9643ea8Slogwang perror("Failed to create scheduler"); 223a9643ea8Slogwang return POSIX_ERRNO(EAGAIN); 224a9643ea8Slogwang } 225a9643ea8Slogwang } 226a9643ea8Slogwang 227a9643ea8Slogwang /* allocate a thread structure */ 228a9643ea8Slogwang lt = _lthread_objcache_alloc((THIS_SCHED)->lthread_cache); 229a9643ea8Slogwang if (lt == NULL) 230a9643ea8Slogwang return POSIX_ERRNO(EAGAIN); 231a9643ea8Slogwang 232a9643ea8Slogwang bzero(lt, sizeof(struct lthread)); 233a9643ea8Slogwang lt->root_sched = THIS_SCHED; 234a9643ea8Slogwang 235a9643ea8Slogwang /* set the function args and exit handlder */ 236a9643ea8Slogwang _lthread_init(lt, fun, arg, _lthread_exit_handler); 237a9643ea8Slogwang 238a9643ea8Slogwang /* put it in the ready queue */ 239a9643ea8Slogwang *new_lt = lt; 240a9643ea8Slogwang 241a9643ea8Slogwang if (lcore_id < 0) 242a9643ea8Slogwang lcore_id = rte_lcore_id(); 243a9643ea8Slogwang 244a9643ea8Slogwang DIAG_CREATE_EVENT(lt, LT_DIAG_LTHREAD_CREATE); 245a9643ea8Slogwang 246a9643ea8Slogwang rte_wmb(); 247a9643ea8Slogwang _ready_queue_insert(_lthread_sched_get(lcore_id), lt); 248a9643ea8Slogwang return 0; 249a9643ea8Slogwang } 250a9643ea8Slogwang 251a9643ea8Slogwang /* 252a9643ea8Slogwang * Schedules lthread to sleep for `nsecs` 253a9643ea8Slogwang * setting the lthread state to LT_ST_SLEEPING. 254a9643ea8Slogwang * lthread state is cleared upon resumption or expiry. 255a9643ea8Slogwang */ 256a9643ea8Slogwang static inline void _lthread_sched_sleep(struct lthread *lt, uint64_t nsecs) 257a9643ea8Slogwang { 258a9643ea8Slogwang uint64_t state = lt->state; 259a9643ea8Slogwang uint64_t clks = _ns_to_clks(nsecs); 260a9643ea8Slogwang 261a9643ea8Slogwang if (clks) { 262a9643ea8Slogwang _timer_start(lt, clks); 263a9643ea8Slogwang lt->state = state | BIT(ST_LT_SLEEPING); 264a9643ea8Slogwang } 265a9643ea8Slogwang DIAG_EVENT(lt, LT_DIAG_LTHREAD_SLEEP, clks, 0); 266a9643ea8Slogwang _suspend(); 267a9643ea8Slogwang } 268a9643ea8Slogwang 269a9643ea8Slogwang 270a9643ea8Slogwang 271a9643ea8Slogwang /* 272a9643ea8Slogwang * Cancels any running timer. 273a9643ea8Slogwang * This can be called multiple times on the same lthread regardless if it was 274a9643ea8Slogwang * sleeping or not. 275a9643ea8Slogwang */ 276a9643ea8Slogwang int _lthread_desched_sleep(struct lthread *lt) 277a9643ea8Slogwang { 278a9643ea8Slogwang uint64_t state = lt->state; 279a9643ea8Slogwang 280a9643ea8Slogwang if (state & BIT(ST_LT_SLEEPING)) { 281a9643ea8Slogwang _timer_stop(lt); 282a9643ea8Slogwang state &= (CLEARBIT(ST_LT_SLEEPING) & CLEARBIT(ST_LT_EXPIRED)); 283a9643ea8Slogwang lt->state = state | BIT(ST_LT_READY); 284a9643ea8Slogwang return 1; 285a9643ea8Slogwang } 286a9643ea8Slogwang return 0; 287a9643ea8Slogwang } 288a9643ea8Slogwang 289a9643ea8Slogwang /* 290a9643ea8Slogwang * set user data pointer in an lthread 291a9643ea8Slogwang */ 292a9643ea8Slogwang void lthread_set_data(void *data) 293a9643ea8Slogwang { 294a9643ea8Slogwang if (sizeof(void *) == RTE_PER_LTHREAD_SECTION_SIZE) 295a9643ea8Slogwang THIS_LTHREAD->per_lthread_data = data; 296a9643ea8Slogwang } 297a9643ea8Slogwang 298a9643ea8Slogwang /* 299a9643ea8Slogwang * Retrieve user data pointer from an lthread 300a9643ea8Slogwang */ 301a9643ea8Slogwang void *lthread_get_data(void) 302a9643ea8Slogwang { 303a9643ea8Slogwang return THIS_LTHREAD->per_lthread_data; 304a9643ea8Slogwang } 305a9643ea8Slogwang 306a9643ea8Slogwang /* 307a9643ea8Slogwang * Return the current lthread handle 308a9643ea8Slogwang */ 309a9643ea8Slogwang struct lthread *lthread_current(void) 310a9643ea8Slogwang { 311a9643ea8Slogwang struct lthread_sched *sched = THIS_SCHED; 312a9643ea8Slogwang 313a9643ea8Slogwang if (sched) 314a9643ea8Slogwang return sched->current_lthread; 315a9643ea8Slogwang return NULL; 316a9643ea8Slogwang } 317a9643ea8Slogwang 318a9643ea8Slogwang 319a9643ea8Slogwang 320a9643ea8Slogwang /* 321a9643ea8Slogwang * Tasklet to cancel a thread 322a9643ea8Slogwang */ 323a9643ea8Slogwang static void 324a9643ea8Slogwang _cancel(void *arg) 325a9643ea8Slogwang { 326a9643ea8Slogwang struct lthread *lt = (struct lthread *) arg; 327a9643ea8Slogwang 328a9643ea8Slogwang lt->state |= BIT(ST_LT_CANCELLED); 329a9643ea8Slogwang lthread_detach(); 330a9643ea8Slogwang } 331a9643ea8Slogwang 332a9643ea8Slogwang 333a9643ea8Slogwang /* 334a9643ea8Slogwang * Mark the specified as canceled 335a9643ea8Slogwang */ 336a9643ea8Slogwang int lthread_cancel(struct lthread *cancel_lt) 337a9643ea8Slogwang { 338a9643ea8Slogwang struct lthread *lt; 339a9643ea8Slogwang 340a9643ea8Slogwang if ((cancel_lt == NULL) || (cancel_lt == THIS_LTHREAD)) 341a9643ea8Slogwang return POSIX_ERRNO(EINVAL); 342a9643ea8Slogwang 343a9643ea8Slogwang DIAG_EVENT(cancel_lt, LT_DIAG_LTHREAD_CANCEL, cancel_lt, 0); 344a9643ea8Slogwang 345a9643ea8Slogwang if (cancel_lt->sched != THIS_SCHED) { 346a9643ea8Slogwang 347a9643ea8Slogwang /* spawn task-let to cancel the thread */ 348a9643ea8Slogwang lthread_create(<, 349a9643ea8Slogwang cancel_lt->sched->lcore_id, 350a9643ea8Slogwang _cancel, 351a9643ea8Slogwang cancel_lt); 352a9643ea8Slogwang return 0; 353a9643ea8Slogwang } 354a9643ea8Slogwang cancel_lt->state |= BIT(ST_LT_CANCELLED); 355a9643ea8Slogwang return 0; 356a9643ea8Slogwang } 357a9643ea8Slogwang 358a9643ea8Slogwang /* 359a9643ea8Slogwang * Suspend the current lthread for specified time 360a9643ea8Slogwang */ 361a9643ea8Slogwang void lthread_sleep(uint64_t nsecs) 362a9643ea8Slogwang { 363a9643ea8Slogwang struct lthread *lt = THIS_LTHREAD; 364a9643ea8Slogwang 365a9643ea8Slogwang _lthread_sched_sleep(lt, nsecs); 366a9643ea8Slogwang 367a9643ea8Slogwang } 368a9643ea8Slogwang 369a9643ea8Slogwang /* 370a9643ea8Slogwang * Suspend the current lthread for specified time 371a9643ea8Slogwang */ 372a9643ea8Slogwang void lthread_sleep_clks(uint64_t clks) 373a9643ea8Slogwang { 374a9643ea8Slogwang struct lthread *lt = THIS_LTHREAD; 375a9643ea8Slogwang uint64_t state = lt->state; 376a9643ea8Slogwang 377a9643ea8Slogwang if (clks) { 378a9643ea8Slogwang _timer_start(lt, clks); 379a9643ea8Slogwang lt->state = state | BIT(ST_LT_SLEEPING); 380a9643ea8Slogwang } 381a9643ea8Slogwang DIAG_EVENT(lt, LT_DIAG_LTHREAD_SLEEP, clks, 0); 382a9643ea8Slogwang _suspend(); 383a9643ea8Slogwang } 384a9643ea8Slogwang 385a9643ea8Slogwang /* 386a9643ea8Slogwang * Requeue the current thread to the back of the ready queue 387a9643ea8Slogwang */ 388a9643ea8Slogwang void lthread_yield(void) 389a9643ea8Slogwang { 390a9643ea8Slogwang struct lthread *lt = THIS_LTHREAD; 391a9643ea8Slogwang 392a9643ea8Slogwang DIAG_EVENT(lt, LT_DIAG_LTHREAD_YIELD, 0, 0); 393a9643ea8Slogwang 394a9643ea8Slogwang _ready_queue_insert(THIS_SCHED, lt); 395a9643ea8Slogwang ctx_switch(&(THIS_SCHED)->ctx, <->ctx); 396a9643ea8Slogwang } 397a9643ea8Slogwang 398a9643ea8Slogwang /* 399a9643ea8Slogwang * Exit the current lthread 400a9643ea8Slogwang * If a thread is joining pass the user pointer to it 401a9643ea8Slogwang */ 402a9643ea8Slogwang void lthread_exit(void *ptr) 403a9643ea8Slogwang { 404a9643ea8Slogwang struct lthread *lt = THIS_LTHREAD; 405a9643ea8Slogwang 406a9643ea8Slogwang /* if thread is detached (this is not valid) just exit */ 407a9643ea8Slogwang if (lt->state & BIT(ST_LT_DETACH)) 408a9643ea8Slogwang return; 409a9643ea8Slogwang 410a9643ea8Slogwang /* There is a race between lthread_join() and lthread_exit() 411a9643ea8Slogwang * - if exit before join then we suspend and resume on join 412a9643ea8Slogwang * - if join before exit then we resume the joining thread 413a9643ea8Slogwang */ 414a9643ea8Slogwang if ((lt->join == LT_JOIN_INITIAL) 415a9643ea8Slogwang && rte_atomic64_cmpset(<->join, LT_JOIN_INITIAL, 416a9643ea8Slogwang LT_JOIN_EXITING)) { 417a9643ea8Slogwang 418a9643ea8Slogwang DIAG_EVENT(lt, LT_DIAG_LTHREAD_EXIT, 1, 0); 419a9643ea8Slogwang _suspend(); 420a9643ea8Slogwang /* set the exit value */ 421a9643ea8Slogwang if ((ptr != NULL) && (lt->lt_join->lt_exit_ptr != NULL)) 422a9643ea8Slogwang *(lt->lt_join->lt_exit_ptr) = ptr; 423a9643ea8Slogwang 424a9643ea8Slogwang /* let the joining thread know we have set the exit value */ 425a9643ea8Slogwang lt->join = LT_JOIN_EXIT_VAL_SET; 426a9643ea8Slogwang } else { 427a9643ea8Slogwang 428a9643ea8Slogwang DIAG_EVENT(lt, LT_DIAG_LTHREAD_EXIT, 0, 0); 429a9643ea8Slogwang /* set the exit value */ 430a9643ea8Slogwang if ((ptr != NULL) && (lt->lt_join->lt_exit_ptr != NULL)) 431a9643ea8Slogwang *(lt->lt_join->lt_exit_ptr) = ptr; 432a9643ea8Slogwang /* let the joining thread know we have set the exit value */ 433a9643ea8Slogwang lt->join = LT_JOIN_EXIT_VAL_SET; 434a9643ea8Slogwang _ready_queue_insert(lt->lt_join->sched, 435a9643ea8Slogwang (struct lthread *)lt->lt_join); 436a9643ea8Slogwang } 437a9643ea8Slogwang 438a9643ea8Slogwang 439a9643ea8Slogwang /* wait until the joinging thread has collected the exit value */ 440a9643ea8Slogwang while (lt->join != LT_JOIN_EXIT_VAL_READ) 441a9643ea8Slogwang _reschedule(); 442a9643ea8Slogwang 443a9643ea8Slogwang /* reset join state */ 444a9643ea8Slogwang lt->join = LT_JOIN_INITIAL; 445a9643ea8Slogwang 446a9643ea8Slogwang /* detach it so its resources can be released */ 447a9643ea8Slogwang lt->state |= (BIT(ST_LT_DETACH) | BIT(ST_LT_EXITED)); 448a9643ea8Slogwang } 449a9643ea8Slogwang 450a9643ea8Slogwang /* 451a9643ea8Slogwang * Join an lthread 452a9643ea8Slogwang * Suspend until the joined thread returns 453a9643ea8Slogwang */ 454a9643ea8Slogwang int lthread_join(struct lthread *lt, void **ptr) 455a9643ea8Slogwang { 456a9643ea8Slogwang if (lt == NULL) 457a9643ea8Slogwang return POSIX_ERRNO(EINVAL); 458a9643ea8Slogwang 459a9643ea8Slogwang struct lthread *current = THIS_LTHREAD; 460a9643ea8Slogwang uint64_t lt_state = lt->state; 461a9643ea8Slogwang 462a9643ea8Slogwang /* invalid to join a detached thread, or a thread that is joined */ 463a9643ea8Slogwang if ((lt_state & BIT(ST_LT_DETACH)) || (lt->join == LT_JOIN_THREAD_SET)) 464a9643ea8Slogwang return POSIX_ERRNO(EINVAL); 465a9643ea8Slogwang /* pointer to the joining thread and a poingter to return a value */ 466a9643ea8Slogwang lt->lt_join = current; 467a9643ea8Slogwang current->lt_exit_ptr = ptr; 468a9643ea8Slogwang /* There is a race between lthread_join() and lthread_exit() 469a9643ea8Slogwang * - if join before exit we suspend and will resume when exit is called 470a9643ea8Slogwang * - if exit before join we resume the exiting thread 471a9643ea8Slogwang */ 472a9643ea8Slogwang if ((lt->join == LT_JOIN_INITIAL) 473a9643ea8Slogwang && rte_atomic64_cmpset(<->join, LT_JOIN_INITIAL, 474a9643ea8Slogwang LT_JOIN_THREAD_SET)) { 475a9643ea8Slogwang 476a9643ea8Slogwang DIAG_EVENT(current, LT_DIAG_LTHREAD_JOIN, lt, 1); 477a9643ea8Slogwang _suspend(); 478a9643ea8Slogwang } else { 479a9643ea8Slogwang DIAG_EVENT(current, LT_DIAG_LTHREAD_JOIN, lt, 0); 480a9643ea8Slogwang _ready_queue_insert(lt->sched, lt); 481a9643ea8Slogwang } 482a9643ea8Slogwang 483a9643ea8Slogwang /* wait for exiting thread to set return value */ 484a9643ea8Slogwang while (lt->join != LT_JOIN_EXIT_VAL_SET) 485a9643ea8Slogwang _reschedule(); 486a9643ea8Slogwang 487a9643ea8Slogwang /* collect the return value */ 488a9643ea8Slogwang if (ptr != NULL) 489a9643ea8Slogwang *ptr = *current->lt_exit_ptr; 490a9643ea8Slogwang 491a9643ea8Slogwang /* let the exiting thread proceed to exit */ 492a9643ea8Slogwang lt->join = LT_JOIN_EXIT_VAL_READ; 493a9643ea8Slogwang return 0; 494a9643ea8Slogwang } 495a9643ea8Slogwang 496a9643ea8Slogwang 497a9643ea8Slogwang /* 498a9643ea8Slogwang * Detach current lthread 499a9643ea8Slogwang * A detached thread cannot be joined 500a9643ea8Slogwang */ 501a9643ea8Slogwang void lthread_detach(void) 502a9643ea8Slogwang { 503a9643ea8Slogwang struct lthread *lt = THIS_LTHREAD; 504a9643ea8Slogwang 505a9643ea8Slogwang DIAG_EVENT(lt, LT_DIAG_LTHREAD_DETACH, 0, 0); 506a9643ea8Slogwang 507a9643ea8Slogwang uint64_t state = lt->state; 508a9643ea8Slogwang 509a9643ea8Slogwang lt->state = state | BIT(ST_LT_DETACH); 510a9643ea8Slogwang } 511a9643ea8Slogwang 512a9643ea8Slogwang /* 513a9643ea8Slogwang * Set function name of an lthread 514a9643ea8Slogwang * this is a debug aid 515a9643ea8Slogwang */ 516a9643ea8Slogwang void lthread_set_funcname(const char *f) 517a9643ea8Slogwang { 518a9643ea8Slogwang struct lthread *lt = THIS_LTHREAD; 519a9643ea8Slogwang 520a9643ea8Slogwang strncpy(lt->funcname, f, sizeof(lt->funcname)); 521a9643ea8Slogwang lt->funcname[sizeof(lt->funcname)-1] = 0; 522a9643ea8Slogwang } 523