101 lines
3.0 KiB
C++
101 lines
3.0 KiB
C++
// 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.
|
|
|
|
#include "marl/debug.h"
|
|
#include "marl/memory.h"
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
|
|
#define WIN32_LEAN_AND_MEAN 1
|
|
#include <Windows.h>
|
|
|
|
namespace marl {
|
|
|
|
class OSFiber {
|
|
public:
|
|
inline ~OSFiber();
|
|
|
|
// createFiberFromCurrentThread() returns a fiber created from the current
|
|
// thread.
|
|
static inline Allocator::unique_ptr<OSFiber> 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<OSFiber> createFiber(
|
|
Allocator* allocator,
|
|
size_t stackSize,
|
|
const std::function<void()>& func);
|
|
|
|
// switchTo() immediately switches execution to the given fiber.
|
|
// switchTo() must be called on the currently executing fiber.
|
|
inline void switchTo(OSFiber*);
|
|
|
|
private:
|
|
static inline void WINAPI run(void* self);
|
|
LPVOID fiber = nullptr;
|
|
bool isFiberFromThread = false;
|
|
std::function<void()> target;
|
|
};
|
|
|
|
OSFiber::~OSFiber() {
|
|
if (fiber != nullptr) {
|
|
if (isFiberFromThread) {
|
|
ConvertFiberToThread();
|
|
} else {
|
|
DeleteFiber(fiber);
|
|
}
|
|
}
|
|
}
|
|
|
|
Allocator::unique_ptr<OSFiber> OSFiber::createFiberFromCurrentThread(
|
|
Allocator* allocator) {
|
|
auto out = allocator->make_unique<OSFiber>();
|
|
out->fiber = ConvertThreadToFiberEx(nullptr, FIBER_FLAG_FLOAT_SWITCH);
|
|
out->isFiberFromThread = true;
|
|
MARL_ASSERT(out->fiber != nullptr,
|
|
"ConvertThreadToFiberEx() failed with error 0x%x",
|
|
int(GetLastError()));
|
|
return out;
|
|
}
|
|
|
|
Allocator::unique_ptr<OSFiber> OSFiber::createFiber(
|
|
Allocator* allocator,
|
|
size_t stackSize,
|
|
const std::function<void()>& func) {
|
|
auto out = allocator->make_unique<OSFiber>();
|
|
// stackSize is rounded up to the system's allocation granularity (typically
|
|
// 64 KB).
|
|
out->fiber = CreateFiberEx(stackSize - 1, stackSize, FIBER_FLAG_FLOAT_SWITCH,
|
|
&OSFiber::run, out.get());
|
|
out->target = func;
|
|
MARL_ASSERT(out->fiber != nullptr, "CreateFiberEx() failed with error 0x%x",
|
|
int(GetLastError()));
|
|
return out;
|
|
}
|
|
|
|
void OSFiber::switchTo(OSFiber* to) {
|
|
SwitchToFiber(to->fiber);
|
|
}
|
|
|
|
void WINAPI OSFiber::run(void* self) {
|
|
std::function<void()> func;
|
|
std::swap(func, reinterpret_cast<OSFiber*>(self)->target);
|
|
func();
|
|
}
|
|
|
|
} // namespace marl
|