1 /* 2 * coroutine queues and locks 3 * 4 * Copyright (c) 2011 Kevin Wolf <kwolf@redhat.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include "qemu/osdep.h" 26 #include "qemu-common.h" 27 #include "qemu/coroutine.h" 28 #include "qemu/coroutine_int.h" 29 #include "qemu/queue.h" 30 #include "trace.h" 31 32 void qemu_co_queue_init(CoQueue *queue) 33 { 34 QSIMPLEQ_INIT(&queue->entries); 35 } 36 37 void coroutine_fn qemu_co_queue_wait(CoQueue *queue) 38 { 39 Coroutine *self = qemu_coroutine_self(); 40 QSIMPLEQ_INSERT_TAIL(&queue->entries, self, co_queue_next); 41 qemu_coroutine_yield(); 42 assert(qemu_in_coroutine()); 43 } 44 45 /** 46 * qemu_co_queue_run_restart: 47 * 48 * Enter each coroutine that was previously marked for restart by 49 * qemu_co_queue_next() or qemu_co_queue_restart_all(). This function is 50 * invoked by the core coroutine code when the current coroutine yields or 51 * terminates. 52 */ 53 void qemu_co_queue_run_restart(Coroutine *co) 54 { 55 Coroutine *next; 56 57 trace_qemu_co_queue_run_restart(co); 58 while ((next = QSIMPLEQ_FIRST(&co->co_queue_wakeup))) { 59 QSIMPLEQ_REMOVE_HEAD(&co->co_queue_wakeup, co_queue_next); 60 qemu_coroutine_enter(next); 61 } 62 } 63 64 static bool qemu_co_queue_do_restart(CoQueue *queue, bool single) 65 { 66 Coroutine *self = qemu_coroutine_self(); 67 Coroutine *next; 68 69 if (QSIMPLEQ_EMPTY(&queue->entries)) { 70 return false; 71 } 72 73 while ((next = QSIMPLEQ_FIRST(&queue->entries)) != NULL) { 74 QSIMPLEQ_REMOVE_HEAD(&queue->entries, co_queue_next); 75 QSIMPLEQ_INSERT_TAIL(&self->co_queue_wakeup, next, co_queue_next); 76 trace_qemu_co_queue_next(next); 77 if (single) { 78 break; 79 } 80 } 81 return true; 82 } 83 84 bool coroutine_fn qemu_co_queue_next(CoQueue *queue) 85 { 86 assert(qemu_in_coroutine()); 87 return qemu_co_queue_do_restart(queue, true); 88 } 89 90 void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue) 91 { 92 assert(qemu_in_coroutine()); 93 qemu_co_queue_do_restart(queue, false); 94 } 95 96 bool qemu_co_enter_next(CoQueue *queue) 97 { 98 Coroutine *next; 99 100 next = QSIMPLEQ_FIRST(&queue->entries); 101 if (!next) { 102 return false; 103 } 104 105 QSIMPLEQ_REMOVE_HEAD(&queue->entries, co_queue_next); 106 qemu_coroutine_enter(next); 107 return true; 108 } 109 110 bool qemu_co_queue_empty(CoQueue *queue) 111 { 112 return QSIMPLEQ_FIRST(&queue->entries) == NULL; 113 } 114 115 void qemu_co_mutex_init(CoMutex *mutex) 116 { 117 memset(mutex, 0, sizeof(*mutex)); 118 qemu_co_queue_init(&mutex->queue); 119 } 120 121 void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex) 122 { 123 Coroutine *self = qemu_coroutine_self(); 124 125 trace_qemu_co_mutex_lock_entry(mutex, self); 126 127 while (mutex->locked) { 128 qemu_co_queue_wait(&mutex->queue); 129 } 130 131 mutex->locked = true; 132 mutex->holder = self; 133 self->locks_held++; 134 135 trace_qemu_co_mutex_lock_return(mutex, self); 136 } 137 138 void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) 139 { 140 Coroutine *self = qemu_coroutine_self(); 141 142 trace_qemu_co_mutex_unlock_entry(mutex, self); 143 144 assert(mutex->locked == true); 145 assert(mutex->holder == self); 146 assert(qemu_in_coroutine()); 147 148 mutex->locked = false; 149 mutex->holder = NULL; 150 self->locks_held--; 151 qemu_co_queue_next(&mutex->queue); 152 153 trace_qemu_co_mutex_unlock_return(mutex, self); 154 } 155 156 void qemu_co_rwlock_init(CoRwlock *lock) 157 { 158 memset(lock, 0, sizeof(*lock)); 159 qemu_co_queue_init(&lock->queue); 160 } 161 162 void qemu_co_rwlock_rdlock(CoRwlock *lock) 163 { 164 Coroutine *self = qemu_coroutine_self(); 165 166 while (lock->writer) { 167 qemu_co_queue_wait(&lock->queue); 168 } 169 lock->reader++; 170 self->locks_held++; 171 } 172 173 void qemu_co_rwlock_unlock(CoRwlock *lock) 174 { 175 Coroutine *self = qemu_coroutine_self(); 176 177 assert(qemu_in_coroutine()); 178 if (lock->writer) { 179 lock->writer = false; 180 qemu_co_queue_restart_all(&lock->queue); 181 } else { 182 lock->reader--; 183 assert(lock->reader >= 0); 184 /* Wakeup only one waiting writer */ 185 if (!lock->reader) { 186 qemu_co_queue_next(&lock->queue); 187 } 188 } 189 self->locks_held--; 190 } 191 192 void qemu_co_rwlock_wrlock(CoRwlock *lock) 193 { 194 Coroutine *self = qemu_coroutine_self(); 195 196 while (lock->writer || lock->reader) { 197 qemu_co_queue_wait(&lock->queue); 198 } 199 lock->writer = true; 200 self->locks_held++; 201 } 202