1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  * Copyright 2015 Intel Corporation.
4  * Copyright 2012 Hasan Alayli <[email protected]>
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdint.h>
11 #include <stddef.h>
12 #include <limits.h>
13 #include <inttypes.h>
14 #include <unistd.h>
15 #include <pthread.h>
16 #include <fcntl.h>
17 #include <sys/time.h>
18 #include <sys/mman.h>
19 #include <errno.h>
20 
21 #include <rte_log.h>
22 #include <rte_common.h>
23 
24 #include "lthread_api.h"
25 #include "lthread_diag_api.h"
26 #include "lthread_diag.h"
27 #include "lthread_int.h"
28 #include "lthread_sched.h"
29 #include "lthread_queue.h"
30 #include "lthread_objcache.h"
31 #include "lthread_timer.h"
32 #include "lthread_mutex.h"
33 #include "lthread_cond.h"
34 
35 /*
36  * Create a condition variable
37  */
38 int
39 lthread_cond_init(char *name, struct lthread_cond **cond,
40 		  __rte_unused const struct lthread_condattr *attr)
41 {
42 	struct lthread_cond *c;
43 
44 	if (cond == NULL)
45 		return POSIX_ERRNO(EINVAL);
46 
47 	/* allocate a condition variable from cache */
48 	c = _lthread_objcache_alloc((THIS_SCHED)->cond_cache);
49 
50 	if (c == NULL)
51 		return POSIX_ERRNO(EAGAIN);
52 
53 	c->blocked = _lthread_queue_create("blocked");
54 	if (c->blocked == NULL) {
55 		_lthread_objcache_free((THIS_SCHED)->cond_cache, (void *)c);
56 		return POSIX_ERRNO(EAGAIN);
57 	}
58 
59 	if (name == NULL)
60 		strncpy(c->name, "no name", sizeof(c->name));
61 	else
62 		strncpy(c->name, name, sizeof(c->name));
63 	c->name[sizeof(c->name)-1] = 0;
64 
65 	c->root_sched = THIS_SCHED;
66 
67 	(*cond) = c;
68 	DIAG_CREATE_EVENT((*cond), LT_DIAG_COND_CREATE);
69 	return 0;
70 }
71 
72 /*
73  * Destroy a condition variable
74  */
75 int lthread_cond_destroy(struct lthread_cond *c)
76 {
77 	if (c == NULL) {
78 		DIAG_EVENT(c, LT_DIAG_COND_DESTROY, c, POSIX_ERRNO(EINVAL));
79 		return POSIX_ERRNO(EINVAL);
80 	}
81 
82 	/* try to free it */
83 	if (_lthread_queue_destroy(c->blocked) < 0) {
84 		/* queue in use */
85 		DIAG_EVENT(c, LT_DIAG_COND_DESTROY, c, POSIX_ERRNO(EBUSY));
86 		return POSIX_ERRNO(EBUSY);
87 	}
88 
89 	/* okay free it */
90 	_lthread_objcache_free(c->root_sched->cond_cache, c);
91 	DIAG_EVENT(c, LT_DIAG_COND_DESTROY, c, 0);
92 	return 0;
93 }
94 
95 /*
96  * Wait on a condition variable
97  */
98 int lthread_cond_wait(struct lthread_cond *c, __rte_unused uint64_t reserved)
99 {
100 	struct lthread *lt = THIS_LTHREAD;
101 
102 	if (c == NULL) {
103 		DIAG_EVENT(c, LT_DIAG_COND_WAIT, c, POSIX_ERRNO(EINVAL));
104 		return POSIX_ERRNO(EINVAL);
105 	}
106 
107 
108 	DIAG_EVENT(c, LT_DIAG_COND_WAIT, c, 0);
109 
110 	/* queue the current thread in the blocked queue
111 	 * this will be written when we return to the scheduler
112 	 * to ensure that the current thread context is saved
113 	 * before any signal could result in it being dequeued and
114 	 * resumed
115 	 */
116 	lt->pending_wr_queue = c->blocked;
117 	_suspend();
118 
119 	/* the condition happened */
120 	return 0;
121 }
122 
123 /*
124  * Signal a condition variable
125  * attempt to resume any blocked thread
126  */
127 int lthread_cond_signal(struct lthread_cond *c)
128 {
129 	struct lthread *lt;
130 
131 	if (c == NULL) {
132 		DIAG_EVENT(c, LT_DIAG_COND_SIGNAL, c, POSIX_ERRNO(EINVAL));
133 		return POSIX_ERRNO(EINVAL);
134 	}
135 
136 	lt = _lthread_queue_remove(c->blocked);
137 
138 	if (lt != NULL) {
139 		/* okay wake up this thread */
140 		DIAG_EVENT(c, LT_DIAG_COND_SIGNAL, c, lt);
141 		_ready_queue_insert((struct lthread_sched *)lt->sched, lt);
142 	}
143 	return 0;
144 }
145 
146 /*
147  * Broadcast a condition variable
148  */
149 int lthread_cond_broadcast(struct lthread_cond *c)
150 {
151 	struct lthread *lt;
152 
153 	if (c == NULL) {
154 		DIAG_EVENT(c, LT_DIAG_COND_BROADCAST, c, POSIX_ERRNO(EINVAL));
155 		return POSIX_ERRNO(EINVAL);
156 	}
157 
158 	DIAG_EVENT(c, LT_DIAG_COND_BROADCAST, c, 0);
159 	do {
160 		/* drain the queue waking everybody */
161 		lt = _lthread_queue_remove(c->blocked);
162 
163 		if (lt != NULL) {
164 			DIAG_EVENT(c, LT_DIAG_COND_BROADCAST, c, lt);
165 			/* wake up */
166 			_ready_queue_insert((struct lthread_sched *)lt->sched,
167 					    lt);
168 		}
169 	} while (!_lthread_queue_empty(c->blocked));
170 	_reschedule();
171 	DIAG_EVENT(c, LT_DIAG_COND_BROADCAST, c, 0);
172 	return 0;
173 }
174 
175 /*
176  * return the diagnostic ref val stored in a condition var
177  */
178 uint64_t
179 lthread_cond_diag_ref(struct lthread_cond *c)
180 {
181 	if (c == NULL)
182 		return 0;
183 	return c->diag_ref;
184 }
185