xref: /openbmc/qemu/util/qemu-coroutine-lock.c (revision 91bfcdb0)
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