1 /* 2 * emscripten fiber coroutine initialization code 3 * based on coroutine-ucontext.c 4 * 5 * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws> 6 * Copyright (C) 2011 Kevin Wolf <kwolf@redhat.com> 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.0 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 20 */ 21 22 #include "qemu/osdep.h" 23 #include "qemu/coroutine_int.h" 24 #include "qemu/coroutine-tls.h" 25 26 #include <emscripten/fiber.h> 27 28 typedef struct { 29 Coroutine base; 30 void *stack; 31 size_t stack_size; 32 33 void *asyncify_stack; 34 size_t asyncify_stack_size; 35 36 CoroutineAction action; 37 38 emscripten_fiber_t fiber; 39 } CoroutineEmscripten; 40 41 /** 42 * Per-thread coroutine bookkeeping 43 */ 44 QEMU_DEFINE_STATIC_CO_TLS(Coroutine *, current); 45 QEMU_DEFINE_STATIC_CO_TLS(CoroutineEmscripten *, leader); 46 size_t leader_asyncify_stack_size = COROUTINE_STACK_SIZE; 47 48 static void coroutine_trampoline(void *co_) 49 { 50 Coroutine *co = co_; 51 52 while (true) { 53 co->entry(co->entry_arg); 54 qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); 55 } 56 } 57 58 Coroutine *qemu_coroutine_new(void) 59 { 60 CoroutineEmscripten *co; 61 62 co = g_malloc0(sizeof(*co)); 63 64 co->stack_size = COROUTINE_STACK_SIZE; 65 co->stack = qemu_alloc_stack(&co->stack_size); 66 67 co->asyncify_stack_size = COROUTINE_STACK_SIZE; 68 co->asyncify_stack = g_malloc0(co->asyncify_stack_size); 69 emscripten_fiber_init(&co->fiber, coroutine_trampoline, &co->base, 70 co->stack, co->stack_size, co->asyncify_stack, 71 co->asyncify_stack_size); 72 73 return &co->base; 74 } 75 76 void qemu_coroutine_delete(Coroutine *co_) 77 { 78 CoroutineEmscripten *co = DO_UPCAST(CoroutineEmscripten, base, co_); 79 80 qemu_free_stack(co->stack, co->stack_size); 81 g_free(co->asyncify_stack); 82 g_free(co); 83 } 84 85 CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, 86 CoroutineAction action) 87 { 88 CoroutineEmscripten *from = DO_UPCAST(CoroutineEmscripten, base, from_); 89 CoroutineEmscripten *to = DO_UPCAST(CoroutineEmscripten, base, to_); 90 91 set_current(to_); 92 to->action = action; 93 emscripten_fiber_swap(&from->fiber, &to->fiber); 94 return from->action; 95 } 96 97 Coroutine *qemu_coroutine_self(void) 98 { 99 Coroutine *self = get_current(); 100 101 if (!self) { 102 CoroutineEmscripten *leaderp = get_leader(); 103 if (!leaderp) { 104 leaderp = g_malloc0(sizeof(*leaderp)); 105 leaderp->asyncify_stack = g_malloc0(leader_asyncify_stack_size); 106 leaderp->asyncify_stack_size = leader_asyncify_stack_size; 107 emscripten_fiber_init_from_current_context( 108 &leaderp->fiber, 109 leaderp->asyncify_stack, 110 leaderp->asyncify_stack_size); 111 leaderp->stack = leaderp->fiber.stack_limit; 112 leaderp->stack_size = 113 leaderp->fiber.stack_base - leaderp->fiber.stack_limit; 114 set_leader(leaderp); 115 } 116 self = &leaderp->base; 117 set_current(self); 118 } 119 return self; 120 } 121 122 bool qemu_in_coroutine(void) 123 { 124 Coroutine *self = get_current(); 125 126 return self && self->caller; 127 } 128