coroutine-win32.c (3120B)
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-common.h" 27#include "qemu/coroutine_int.h" 28 29typedef struct 30{ 31 Coroutine base; 32 33 LPVOID fiber; 34 CoroutineAction action; 35} CoroutineWin32; 36 37static __thread CoroutineWin32 leader; 38static __thread 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 */ 48CoroutineAction __attribute__((noinline)) 49qemu_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 current = to_; 56 57 to->action = action; 58 SwitchToFiber(to->fiber); 59 return from->action; 60} 61 62static 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 72Coroutine *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 82void 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 90Coroutine *qemu_coroutine_self(void) 91{ 92 if (!current) { 93 current = &leader.base; 94 leader.fiber = ConvertThreadToFiber(NULL); 95 } 96 return current; 97} 98 99bool qemu_in_coroutine(void) 100{ 101 return current && current->caller; 102}