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 QTAILQ_INIT(&queue->entries); 35 } 36 37 void coroutine_fn qemu_co_queue_wait(CoQueue *queue) 38 { 39 Coroutine *self = qemu_coroutine_self(); 40 QTAILQ_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 = QTAILQ_FIRST(&co->co_queue_wakeup))) { 59 QTAILQ_REMOVE(&co->co_queue_wakeup, next, co_queue_next); 60 qemu_coroutine_enter(next, NULL); 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 (QTAILQ_EMPTY(&queue->entries)) { 70 return false; 71 } 72 73 while ((next = QTAILQ_FIRST(&queue->entries)) != NULL) { 74 QTAILQ_REMOVE(&queue->entries, next, co_queue_next); 75 QTAILQ_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 = QTAILQ_FIRST(&queue->entries); 101 if (!next) { 102 return false; 103 } 104 105 QTAILQ_REMOVE(&queue->entries, next, co_queue_next); 106 qemu_coroutine_enter(next, NULL); 107 return true; 108 } 109 110 bool qemu_co_queue_empty(CoQueue *queue) 111 { 112 return QTAILQ_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 133 trace_qemu_co_mutex_lock_return(mutex, self); 134 } 135 136 void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) 137 { 138 Coroutine *self = qemu_coroutine_self(); 139 140 trace_qemu_co_mutex_unlock_entry(mutex, self); 141 142 assert(mutex->locked == true); 143 assert(qemu_in_coroutine()); 144 145 mutex->locked = false; 146 qemu_co_queue_next(&mutex->queue); 147 148 trace_qemu_co_mutex_unlock_return(mutex, self); 149 } 150 151 void qemu_co_rwlock_init(CoRwlock *lock) 152 { 153 memset(lock, 0, sizeof(*lock)); 154 qemu_co_queue_init(&lock->queue); 155 } 156 157 void qemu_co_rwlock_rdlock(CoRwlock *lock) 158 { 159 while (lock->writer) { 160 qemu_co_queue_wait(&lock->queue); 161 } 162 lock->reader++; 163 } 164 165 void qemu_co_rwlock_unlock(CoRwlock *lock) 166 { 167 assert(qemu_in_coroutine()); 168 if (lock->writer) { 169 lock->writer = false; 170 qemu_co_queue_restart_all(&lock->queue); 171 } else { 172 lock->reader--; 173 assert(lock->reader >= 0); 174 /* Wakeup only one waiting writer */ 175 if (!lock->reader) { 176 qemu_co_queue_next(&lock->queue); 177 } 178 } 179 } 180 181 void qemu_co_rwlock_wrlock(CoRwlock *lock) 182 { 183 while (lock->writer || lock->reader) { 184 qemu_co_queue_wait(&lock->queue); 185 } 186 lock->writer = true; 187 } 188