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(&lt,
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, &lt->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(&lt->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(&lt->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