// Copyright 2019 The Marl Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Minimal assembly implementations of fiber context switching for Unix-based // platforms. // // Note: Unlike makecontext, swapcontext or the Windows fiber APIs, these // assembly implementations *do not* save or restore signal masks, // floating-point control or status registers, FS and GS segment registers, // thread-local storage state nor any SIMD registers. This should not be a // problem as the marl scheduler requires fibers to be executed on the same // thread throughout their lifetime. #if defined(__x86_64__) #include "osfiber_asm_x64.h" #elif defined(__i386__) #include "osfiber_asm_x86.h" #elif defined(__aarch64__) #include "osfiber_asm_aarch64.h" #elif defined(__arm__) #include "osfiber_asm_arm.h" #elif defined(__powerpc64__) #include "osfiber_asm_ppc64.h" #elif defined(__mips__) && _MIPS_SIM == _ABI64 #include "osfiber_asm_mips64.h" #elif defined(__riscv) && __riscv_xlen == 64 #include "osfiber_asm_rv64.h" #elif defined(__loongarch__) && _LOONGARCH_SIM == _ABILP64 #include "osfiber_asm_loongarch64.h" #elif defined(__EMSCRIPTEN__) #include "osfiber_emscripten.h" #else #error "Unsupported target" #endif #include "marl/export.h" #include "marl/memory.h" #include #include extern "C" { #if defined(__EMSCRIPTEN__) MARL_EXPORT void marl_main_fiber_init(marl_fiber_context* ctx); #else MARL_EXPORT inline void marl_main_fiber_init(marl_fiber_context*) {} #endif MARL_EXPORT extern void marl_fiber_set_target(marl_fiber_context*, void* stack, uint32_t stack_size, void (*target)(void*), void* arg); MARL_EXPORT extern void marl_fiber_swap(marl_fiber_context* from, const marl_fiber_context* to); } // extern "C" namespace marl { class OSFiber { public: inline OSFiber(Allocator*); inline ~OSFiber(); // createFiberFromCurrentThread() returns a fiber created from the current // thread. MARL_NO_EXPORT static inline Allocator::unique_ptr createFiberFromCurrentThread(Allocator* allocator); // createFiber() returns a new fiber with the given stack size that will // call func when switched to. func() must end by switching back to another // fiber, and must not return. MARL_NO_EXPORT static inline Allocator::unique_ptr createFiber( Allocator* allocator, size_t stackSize, const std::function& func); // switchTo() immediately switches execution to the given fiber. // switchTo() must be called on the currently executing fiber. MARL_NO_EXPORT inline void switchTo(OSFiber*); private: MARL_NO_EXPORT static inline void run(OSFiber* self); Allocator* allocator; marl_fiber_context context; std::function target; Allocation stack; }; OSFiber::OSFiber(Allocator* allocator) : allocator(allocator) {} OSFiber::~OSFiber() { if (stack.ptr != nullptr) { allocator->free(stack); } } Allocator::unique_ptr OSFiber::createFiberFromCurrentThread( Allocator* allocator) { auto out = allocator->make_unique(allocator); out->context = {}; marl_main_fiber_init(&out->context); return out; } Allocator::unique_ptr OSFiber::createFiber( Allocator* allocator, size_t stackSize, const std::function& func) { Allocation::Request request; request.size = stackSize; request.alignment = 16; request.usage = Allocation::Usage::Stack; #if MARL_USE_FIBER_STACK_GUARDS request.useGuards = true; #endif auto out = allocator->make_unique(allocator); out->context = {}; out->target = func; out->stack = allocator->allocate(request); marl_fiber_set_target( &out->context, out->stack.ptr, static_cast(stackSize), reinterpret_cast(&OSFiber::run), out.get()); return out; } void OSFiber::run(OSFiber* self) { self->target(); } void OSFiber::switchTo(OSFiber* fiber) { marl_fiber_swap(&context, &fiber->context); } } // namespace marl