1 /* 2 * Win32 coroutine initialization code 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/coroutine_int.h" 27 #include "qemu/coroutine-tls.h" 28 29 typedef struct 30 { 31 Coroutine base; 32 33 LPVOID fiber; 34 CoroutineAction action; 35 } CoroutineWin32; 36 37 QEMU_DEFINE_STATIC_CO_TLS(CoroutineWin32, leader); 38 QEMU_DEFINE_STATIC_CO_TLS(Coroutine *, current); 39 40 /* This function is marked noinline to prevent GCC from inlining it 41 * into coroutine_trampoline(). If we allow it to do that then it 42 * hoists the code to get the address of the TLS variable "current" 43 * out of the while() loop. This is an invalid transformation because 44 * the SwitchToFiber() call may be called when running thread A but 45 * return in thread B, and so we might be in a different thread 46 * context each time round the loop. 47 */ 48 CoroutineAction __attribute__((noinline)) 49 qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, 50 CoroutineAction action) 51 { 52 CoroutineWin32 *from = DO_UPCAST(CoroutineWin32, base, from_); 53 CoroutineWin32 *to = DO_UPCAST(CoroutineWin32, base, to_); 54 55 set_current(to_); 56 57 to->action = action; 58 SwitchToFiber(to->fiber); 59 return from->action; 60 } 61 62 static void CALLBACK coroutine_trampoline(void *co_) 63 { 64 Coroutine *co = co_; 65 66 while (true) { 67 co->entry(co->entry_arg); 68 qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); 69 } 70 } 71 72 Coroutine *qemu_coroutine_new(void) 73 { 74 const size_t stack_size = COROUTINE_STACK_SIZE; 75 CoroutineWin32 *co; 76 77 co = g_malloc0(sizeof(*co)); 78 co->fiber = CreateFiber(stack_size, coroutine_trampoline, &co->base); 79 return &co->base; 80 } 81 82 void qemu_coroutine_delete(Coroutine *co_) 83 { 84 CoroutineWin32 *co = DO_UPCAST(CoroutineWin32, base, co_); 85 86 DeleteFiber(co->fiber); 87 g_free(co); 88 } 89 90 Coroutine *qemu_coroutine_self(void) 91 { 92 Coroutine *current = get_current(); 93 94 if (!current) { 95 CoroutineWin32 *leader = get_ptr_leader(); 96 97 current = &leader->base; 98 set_current(current); 99 leader->fiber = ConvertThreadToFiber(NULL); 100 } 101 return current; 102 } 103 104 bool qemu_in_coroutine(void) 105 { 106 Coroutine *current = get_current(); 107 108 return current && current->caller; 109 } 110