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