| /* |
| * emscripten fiber coroutine initialization code |
| * based on coroutine-ucontext.c |
| * |
| * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws> |
| * Copyright (C) 2011 Kevin Wolf <kwolf@redhat.com> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.0 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qemu/coroutine_int.h" |
| #include "qemu/coroutine-tls.h" |
| |
| #include <emscripten/fiber.h> |
| |
| typedef struct { |
| Coroutine base; |
| void *stack; |
| size_t stack_size; |
| |
| void *asyncify_stack; |
| size_t asyncify_stack_size; |
| |
| CoroutineAction action; |
| |
| emscripten_fiber_t fiber; |
| } CoroutineEmscripten; |
| |
| /** |
| * Per-thread coroutine bookkeeping |
| */ |
| QEMU_DEFINE_STATIC_CO_TLS(Coroutine *, current); |
| QEMU_DEFINE_STATIC_CO_TLS(CoroutineEmscripten *, leader); |
| size_t leader_asyncify_stack_size = COROUTINE_STACK_SIZE; |
| |
| static void coroutine_trampoline(void *co_) |
| { |
| Coroutine *co = co_; |
| |
| while (true) { |
| co->entry(co->entry_arg); |
| qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); |
| } |
| } |
| |
| Coroutine *qemu_coroutine_new(void) |
| { |
| CoroutineEmscripten *co; |
| |
| co = g_malloc0(sizeof(*co)); |
| |
| co->stack_size = COROUTINE_STACK_SIZE; |
| co->stack = qemu_alloc_stack(&co->stack_size); |
| |
| co->asyncify_stack_size = COROUTINE_STACK_SIZE; |
| co->asyncify_stack = g_malloc0(co->asyncify_stack_size); |
| emscripten_fiber_init(&co->fiber, coroutine_trampoline, &co->base, |
| co->stack, co->stack_size, co->asyncify_stack, |
| co->asyncify_stack_size); |
| |
| return &co->base; |
| } |
| |
| void qemu_coroutine_delete(Coroutine *co_) |
| { |
| CoroutineEmscripten *co = DO_UPCAST(CoroutineEmscripten, base, co_); |
| |
| qemu_free_stack(co->stack, co->stack_size); |
| g_free(co->asyncify_stack); |
| g_free(co); |
| } |
| |
| CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, |
| CoroutineAction action) |
| { |
| CoroutineEmscripten *from = DO_UPCAST(CoroutineEmscripten, base, from_); |
| CoroutineEmscripten *to = DO_UPCAST(CoroutineEmscripten, base, to_); |
| |
| set_current(to_); |
| to->action = action; |
| emscripten_fiber_swap(&from->fiber, &to->fiber); |
| return from->action; |
| } |
| |
| Coroutine *qemu_coroutine_self(void) |
| { |
| Coroutine *self = get_current(); |
| |
| if (!self) { |
| CoroutineEmscripten *leaderp = get_leader(); |
| if (!leaderp) { |
| leaderp = g_malloc0(sizeof(*leaderp)); |
| leaderp->asyncify_stack = g_malloc0(leader_asyncify_stack_size); |
| leaderp->asyncify_stack_size = leader_asyncify_stack_size; |
| emscripten_fiber_init_from_current_context( |
| &leaderp->fiber, |
| leaderp->asyncify_stack, |
| leaderp->asyncify_stack_size); |
| leaderp->stack = leaderp->fiber.stack_limit; |
| leaderp->stack_size = |
| leaderp->fiber.stack_base - leaderp->fiber.stack_limit; |
| set_leader(leaderp); |
| } |
| self = &leaderp->base; |
| set_current(self); |
| } |
| return self; |
| } |
| |
| bool qemu_in_coroutine(void) |
| { |
| Coroutine *self = get_current(); |
| |
| return self && self->caller; |
| } |