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-common.h" 26 #include "qemu/coroutine.h" 27 #include "qemu/coroutine_int.h" 28 #include "qemu/queue.h" 29 #include "trace.h" 30 31 void qemu_co_queue_init(CoQueue *queue) 32 { 33 QTAILQ_INIT(&queue->entries); 34 } 35 36 void coroutine_fn qemu_co_queue_wait(CoQueue *queue) 37 { 38 Coroutine *self = qemu_coroutine_self(); 39 QTAILQ_INSERT_TAIL(&queue->entries, self, co_queue_next); 40 qemu_coroutine_yield(); 41 assert(qemu_in_coroutine()); 42 } 43 44 /** 45 * qemu_co_queue_run_restart: 46 * 47 * Enter each coroutine that was previously marked for restart by 48 * qemu_co_queue_next() or qemu_co_queue_restart_all(). This function is 49 * invoked by the core coroutine code when the current coroutine yields or 50 * terminates. 51 */ 52 void qemu_co_queue_run_restart(Coroutine *co) 53 { 54 Coroutine *next; 55 56 trace_qemu_co_queue_run_restart(co); 57 while ((next = QTAILQ_FIRST(&co->co_queue_wakeup))) { 58 QTAILQ_REMOVE(&co->co_queue_wakeup, next, co_queue_next); 59 qemu_coroutine_enter(next, NULL); 60 } 61 } 62 63 static bool qemu_co_queue_do_restart(CoQueue *queue, bool single) 64 { 65 Coroutine *self = qemu_coroutine_self(); 66 Coroutine *next; 67 68 if (QTAILQ_EMPTY(&queue->entries)) { 69 return false; 70 } 71 72 while ((next = QTAILQ_FIRST(&queue->entries)) != NULL) { 73 QTAILQ_REMOVE(&queue->entries, next, co_queue_next); 74 QTAILQ_INSERT_TAIL(&self->co_queue_wakeup, next, co_queue_next); 75 trace_qemu_co_queue_next(next); 76 if (single) { 77 break; 78 } 79 } 80 return true; 81 } 82 83 bool coroutine_fn qemu_co_queue_next(CoQueue *queue) 84 { 85 assert(qemu_in_coroutine()); 86 return qemu_co_queue_do_restart(queue, true); 87 } 88 89 void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue) 90 { 91 assert(qemu_in_coroutine()); 92 qemu_co_queue_do_restart(queue, false); 93 } 94 95 bool qemu_co_enter_next(CoQueue *queue) 96 { 97 Coroutine *next; 98 99 next = QTAILQ_FIRST(&queue->entries); 100 if (!next) { 101 return false; 102 } 103 104 QTAILQ_REMOVE(&queue->entries, next, co_queue_next); 105 qemu_coroutine_enter(next, NULL); 106 return true; 107 } 108 109 bool qemu_co_queue_empty(CoQueue *queue) 110 { 111 return QTAILQ_FIRST(&queue->entries) == NULL; 112 } 113 114 void qemu_co_mutex_init(CoMutex *mutex) 115 { 116 memset(mutex, 0, sizeof(*mutex)); 117 qemu_co_queue_init(&mutex->queue); 118 } 119 120 void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex) 121 { 122 Coroutine *self = qemu_coroutine_self(); 123 124 trace_qemu_co_mutex_lock_entry(mutex, self); 125 126 while (mutex->locked) { 127 qemu_co_queue_wait(&mutex->queue); 128 } 129 130 mutex->locked = true; 131 132 trace_qemu_co_mutex_lock_return(mutex, self); 133 } 134 135 void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) 136 { 137 Coroutine *self = qemu_coroutine_self(); 138 139 trace_qemu_co_mutex_unlock_entry(mutex, self); 140 141 assert(mutex->locked == true); 142 assert(qemu_in_coroutine()); 143 144 mutex->locked = false; 145 qemu_co_queue_next(&mutex->queue); 146 147 trace_qemu_co_mutex_unlock_return(mutex, self); 148 } 149 150 void qemu_co_rwlock_init(CoRwlock *lock) 151 { 152 memset(lock, 0, sizeof(*lock)); 153 qemu_co_queue_init(&lock->queue); 154 } 155 156 void qemu_co_rwlock_rdlock(CoRwlock *lock) 157 { 158 while (lock->writer) { 159 qemu_co_queue_wait(&lock->queue); 160 } 161 lock->reader++; 162 } 163 164 void qemu_co_rwlock_unlock(CoRwlock *lock) 165 { 166 assert(qemu_in_coroutine()); 167 if (lock->writer) { 168 lock->writer = false; 169 qemu_co_queue_restart_all(&lock->queue); 170 } else { 171 lock->reader--; 172 assert(lock->reader >= 0); 173 /* Wakeup only one waiting writer */ 174 if (!lock->reader) { 175 qemu_co_queue_next(&lock->queue); 176 } 177 } 178 } 179 180 void qemu_co_rwlock_wrlock(CoRwlock *lock) 181 { 182 while (lock->writer || lock->reader) { 183 qemu_co_queue_wait(&lock->queue); 184 } 185 lock->writer = true; 186 } 187