// 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. #if !defined(_XOPEN_SOURCE) // This must come before other #includes, otherwise we'll end up with ucontext_t // definition mismatches, leading to memory corruption hilarity. #define _XOPEN_SOURCE #endif // !defined(_XOPEN_SOURCE) #include "marl/debug.h" #include "marl/memory.h" #include #include #include #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif // defined(__clang__) namespace marl { class OSFiber { public: inline OSFiber(Allocator*); inline ~OSFiber(); // createFiberFromCurrentThread() returns a fiber created from the current // thread. 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. 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. inline void switchTo(OSFiber*); private: Allocator* allocator; ucontext_t 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 = {}; getcontext(&out->context); return out; } Allocator::unique_ptr OSFiber::createFiber( Allocator* allocator, size_t stackSize, const std::function& func) { union Args { OSFiber* self; struct { int a; int b; }; }; struct Target { static void Main(int a, int b) { Args u; u.a = a; u.b = b; u.self->target(); } }; 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->stack = allocator->allocate(request); out->target = func; auto res = getcontext(&out->context); (void)res; MARL_ASSERT(res == 0, "getcontext() returned %d", int(res)); out->context.uc_stack.ss_sp = out->stack.ptr; out->context.uc_stack.ss_size = stackSize; out->context.uc_link = nullptr; Args args{}; args.self = out.get(); makecontext(&out->context, reinterpret_cast(&Target::Main), 2, args.a, args.b); return out; } void OSFiber::switchTo(OSFiber* fiber) { auto res = swapcontext(&context, &fiber->context); (void)res; MARL_ASSERT(res == 0, "swapcontext() returned %d", int(res)); } } // namespace marl #if defined(__clang__) #pragma clang diagnostic pop #endif // defined(__clang__)