Yarn: Implement fiber context switching for Windows, macOS, Linux and Android.

Windows uses OSFiber_windows.hpp.
macOS and Linux uses OSFiber_asm_* or OSFiber_ucontext.hpp
Android has no support for makecontext / swapcontext, and so relies on the OSFiber_asm_* implementations.

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 yarn scheduler requires fibers to be executed on a single thread.

Bug: b/139010488
Change-Id: I5f890ffe4f069361a7eeae64567a77be3a4ba008
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/34771
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Yarn/OSFiber.hpp b/src/Yarn/OSFiber.hpp
new file mode 100644
index 0000000..d52de3e
--- /dev/null
+++ b/src/Yarn/OSFiber.hpp
@@ -0,0 +1,21 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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(_WIN32)
+#include "OSFiber_windows.hpp"
+#elif defined(YARN_FIBERS_USE_UCONTEXT)
+#include "OSFiber_ucontext.hpp"
+#else
+#include "OSFiber_asm.hpp"
+#endif
diff --git a/src/Yarn/OSFiber_asm.hpp b/src/Yarn/OSFiber_asm.hpp
new file mode 100644
index 0000000..e0fa508
--- /dev/null
+++ b/src/Yarn/OSFiber_asm.hpp
@@ -0,0 +1,103 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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 yarn scheduler requires fibers to be executed on a single
+// thread.
+
+#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"
+#else
+#error "Unsupported target"
+#endif
+
+#include <functional>
+#include <memory>
+
+extern "C"
+{
+
+extern void yarn_fiber_set_target(yarn_fiber_context*, void* stack, uint32_t stack_size, void(*target)(void*), void* arg);
+extern void yarn_fiber_swap(yarn_fiber_context* from, const yarn_fiber_context* to);
+
+} // extern "C"
+
+namespace yarn {
+
+class OSFiber
+{
+public:
+    // createFiberFromCurrentThread() returns a fiber created from the current
+    // thread.
+    static inline OSFiber* createFiberFromCurrentThread();
+
+    // 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 OSFiber* createFiber(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 run(OSFiber* self);
+
+    yarn_fiber_context context;
+    std::function<void()> target;
+    std::unique_ptr<uint8_t[]> stack;
+};
+
+OSFiber* OSFiber::createFiberFromCurrentThread()
+{
+    auto out = new OSFiber();
+    out->context = {};
+    return out;
+}
+
+OSFiber* OSFiber::createFiber(size_t stackSize, const std::function<void()>& func)
+{
+    auto out = new OSFiber();
+    out->context = {};
+    out->target = func;
+    out->stack = std::unique_ptr<uint8_t[]>(new uint8_t[stackSize]);
+    yarn_fiber_set_target(&out->context, out->stack.get(), stackSize, reinterpret_cast<void (*)(void*)>(&OSFiber::run), out);
+    return out;
+}
+
+void OSFiber::run(OSFiber* self)
+{
+    std::function<void()> func;
+    std::swap(func, self->target);
+    func();
+}
+
+void OSFiber::switchTo(OSFiber* fiber)
+{
+    yarn_fiber_swap(&context, &fiber->context);
+}
+
+}  // namespace yarn
diff --git a/src/Yarn/OSFiber_asm_aarch64.c b/src/Yarn/OSFiber_asm_aarch64.c
new file mode 100644
index 0000000..7b09c58
--- /dev/null
+++ b/src/Yarn/OSFiber_asm_aarch64.c
@@ -0,0 +1,33 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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(__aarch64__)
+
+#include "OSFiber_asm_aarch64.h"
+
+void yarn_fiber_trampoline(void(*target)(void*), void* arg)
+{
+    target(arg);
+}
+
+void yarn_fiber_set_target(struct yarn_fiber_context* ctx, void* stack, uint32_t stack_size, void(*target)(void*), void* arg)
+{
+    uintptr_t* stack_top = (uintptr_t*)((uint8_t*)(stack) + stack_size);
+    ctx->LR = (uintptr_t)&yarn_fiber_trampoline;
+    ctx->r0 = (uintptr_t)target;
+    ctx->r1 = (uintptr_t)arg;
+    ctx->SP = ((uintptr_t)stack_top) & ~(uintptr_t)15;
+}
+
+#endif // defined(__aarch64__)
diff --git a/src/Yarn/OSFiber_asm_aarch64.h b/src/Yarn/OSFiber_asm_aarch64.h
new file mode 100644
index 0000000..d6b99ea
--- /dev/null
+++ b/src/Yarn/OSFiber_asm_aarch64.h
@@ -0,0 +1,118 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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.
+
+#define YARN_REG_r0  0x00
+#define YARN_REG_r1  0x08
+#define YARN_REG_r16 0x10
+#define YARN_REG_r17 0x18
+#define YARN_REG_r18 0x20
+#define YARN_REG_r19 0x28
+#define YARN_REG_r20 0x30
+#define YARN_REG_r21 0x38
+#define YARN_REG_r22 0x40
+#define YARN_REG_r23 0x48
+#define YARN_REG_r24 0x50
+#define YARN_REG_r25 0x58
+#define YARN_REG_r26 0x60
+#define YARN_REG_r27 0x68
+#define YARN_REG_r28 0x70
+#define YARN_REG_v8  0x78
+#define YARN_REG_v9  0x80
+#define YARN_REG_v10 0x88
+#define YARN_REG_v11 0x90
+#define YARN_REG_v12 0x98
+#define YARN_REG_v13 0xa0
+#define YARN_REG_v14 0xa8
+#define YARN_REG_v15 0xb0
+#define YARN_REG_SP  0xb8
+#define YARN_REG_LR  0xc0
+
+#if defined(__APPLE__)
+#define YARN_ASM_SYMBOL(x) _##x
+#else
+#define YARN_ASM_SYMBOL(x) x
+#endif
+
+#ifndef BUILD_ASM
+
+#include <stdint.h>
+
+// Procedure Call Standard for the ARM 64-bit Architecture
+// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
+struct yarn_fiber_context
+{
+    // parameter registers
+    uintptr_t r0;
+    uintptr_t r1;
+
+    // special purpose registers
+    uintptr_t r16;
+    uintptr_t r17;
+    uintptr_t r18; // platform specific (maybe inter-procedural state)
+
+    // callee-saved registers
+    uintptr_t r19;
+    uintptr_t r20;
+    uintptr_t r21;
+    uintptr_t r22;
+    uintptr_t r23;
+    uintptr_t r24;
+    uintptr_t r25;
+    uintptr_t r26;
+    uintptr_t r27;
+    uintptr_t r28;
+
+    uintptr_t v8;
+    uintptr_t v9;
+    uintptr_t v10;
+    uintptr_t v11;
+    uintptr_t v12;
+    uintptr_t v13;
+    uintptr_t v14;
+    uintptr_t v15;
+
+    uintptr_t SP; // stack pointer
+    uintptr_t LR; // link register (R30)
+};
+
+#ifdef __cplusplus
+#include <cstddef>
+static_assert(offsetof(yarn_fiber_context, r0)  == YARN_REG_r0,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r1)  == YARN_REG_r1,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r16) == YARN_REG_r16, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r17) == YARN_REG_r17, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r18) == YARN_REG_r18, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r19) == YARN_REG_r19, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r20) == YARN_REG_r20, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r21) == YARN_REG_r21, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r22) == YARN_REG_r22, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r23) == YARN_REG_r23, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r24) == YARN_REG_r24, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r25) == YARN_REG_r25, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r26) == YARN_REG_r26, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r27) == YARN_REG_r27, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r28) == YARN_REG_r28, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v8)  == YARN_REG_v8,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v9)  == YARN_REG_v9,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v10) == YARN_REG_v10, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v11) == YARN_REG_v11, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v12) == YARN_REG_v12, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v13) == YARN_REG_v13, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v14) == YARN_REG_v14, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v15) == YARN_REG_v15, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, SP)  == YARN_REG_SP,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, LR)  == YARN_REG_LR,  "Bad register offset");
+#endif // __cplusplus
+
+#endif // BUILD_ASM
diff --git a/src/Yarn/OSFiber_asm_aarch64.s b/src/Yarn/OSFiber_asm_aarch64.s
new file mode 100644
index 0000000..95abb06
--- /dev/null
+++ b/src/Yarn/OSFiber_asm_aarch64.s
@@ -0,0 +1,101 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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(__aarch64__)
+
+#include "OSFiber_asm_aarch64.h"
+
+// void yarn_fiber_swap(yarn_fiber_context* from, const yarn_fiber_context* to)
+// x0: from
+// x1: to
+.text
+.global YARN_ASM_SYMBOL(yarn_fiber_swap)
+.align 4
+YARN_ASM_SYMBOL(yarn_fiber_swap):
+
+    // Save context 'from'
+    // TODO: pairs of str can be combined with stp.
+
+    // Store special purpose registers
+    str x16, [x0, #YARN_REG_r16]
+    str x17, [x0, #YARN_REG_r17]
+    str x18, [x0, #YARN_REG_r18]
+
+    // Store callee-preserved registers
+    str x19, [x0, #YARN_REG_r19]
+    str x20, [x0, #YARN_REG_r20]
+    str x21, [x0, #YARN_REG_r21]
+    str x22, [x0, #YARN_REG_r22]
+    str x23, [x0, #YARN_REG_r23]
+    str x24, [x0, #YARN_REG_r24]
+    str x25, [x0, #YARN_REG_r25]
+    str x26, [x0, #YARN_REG_r26]
+    str x27, [x0, #YARN_REG_r27]
+    str x28, [x0, #YARN_REG_r28]
+
+    str d8,  [x0, #YARN_REG_v8]
+    str d9,  [x0, #YARN_REG_v9]
+    str d10, [x0, #YARN_REG_v10]
+    str d11, [x0, #YARN_REG_v11]
+    str d12, [x0, #YARN_REG_v12]
+    str d13, [x0, #YARN_REG_v13]
+    str d14, [x0, #YARN_REG_v14]
+    str d15, [x0, #YARN_REG_v15]
+
+    // Store sp and lr
+    mov x2, sp
+    str x2,  [x0, #YARN_REG_SP]
+    str x30, [x0, #YARN_REG_LR]
+
+    // Load context 'to'
+    mov x7, x1
+
+    // Load special purpose registers
+    ldr x16, [x7, #YARN_REG_r16]
+    ldr x17, [x7, #YARN_REG_r17]
+    ldr x18, [x7, #YARN_REG_r18]
+
+    // Load callee-preserved registers
+    ldr x19, [x7, #YARN_REG_r19]
+    ldr x20, [x7, #YARN_REG_r20]
+    ldr x21, [x7, #YARN_REG_r21]
+    ldr x22, [x7, #YARN_REG_r22]
+    ldr x23, [x7, #YARN_REG_r23]
+    ldr x24, [x7, #YARN_REG_r24]
+    ldr x25, [x7, #YARN_REG_r25]
+    ldr x26, [x7, #YARN_REG_r26]
+    ldr x27, [x7, #YARN_REG_r27]
+    ldr x28, [x7, #YARN_REG_r28]
+
+    ldr d8,  [x7, #YARN_REG_v8]
+    ldr d9,  [x7, #YARN_REG_v9]
+    ldr d10, [x7, #YARN_REG_v10]
+    ldr d11, [x7, #YARN_REG_v11]
+    ldr d12, [x7, #YARN_REG_v12]
+    ldr d13, [x7, #YARN_REG_v13]
+    ldr d14, [x7, #YARN_REG_v14]
+    ldr d15, [x7, #YARN_REG_v15]
+
+    // Load parameter registers
+    ldr x0, [x7, #YARN_REG_r0]
+    ldr x1, [x7, #YARN_REG_r1]
+
+    // Load sp and lr
+    ldr x30, [x7, #YARN_REG_LR]
+    ldr x2,  [x7, #YARN_REG_SP]
+    mov sp, x2
+
+    ret
+
+#endif // defined(__aarch64__)
diff --git a/src/Yarn/OSFiber_asm_arm.c b/src/Yarn/OSFiber_asm_arm.c
new file mode 100644
index 0000000..4a6b78e
--- /dev/null
+++ b/src/Yarn/OSFiber_asm_arm.c
@@ -0,0 +1,33 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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(__arm__)
+
+#include "OSFiber_asm_arm.h"
+
+void yarn_fiber_trampoline(void(*target)(void*), void* arg)
+{
+    target(arg);
+}
+
+void yarn_fiber_set_target(struct yarn_fiber_context* ctx, void* stack, uint32_t stack_size, void(*target)(void*), void* arg)
+{
+    uintptr_t* stack_top = (uintptr_t*)((uint8_t*)(stack) + stack_size);
+    ctx->LR = (uintptr_t)&yarn_fiber_trampoline;
+    ctx->r0 = (uintptr_t)target;
+    ctx->r1 = (uintptr_t)arg;
+    ctx->SP = ((uintptr_t)stack_top) & ~(uintptr_t)15;
+}
+
+#endif // defined(__arm__)
diff --git a/src/Yarn/OSFiber_asm_arm.h b/src/Yarn/OSFiber_asm_arm.h
new file mode 100644
index 0000000..d40c8d4
--- /dev/null
+++ b/src/Yarn/OSFiber_asm_arm.h
@@ -0,0 +1,99 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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.
+
+#define YARN_REG_r0  0x00
+#define YARN_REG_r1  0x04
+#define YARN_REG_r12 0x08
+#define YARN_REG_r4  0x0c
+#define YARN_REG_r5  0x10
+#define YARN_REG_r6  0x14
+#define YARN_REG_r7  0x18
+#define YARN_REG_r8  0x1c
+#define YARN_REG_r9  0x20
+#define YARN_REG_r10 0x24
+#define YARN_REG_r11 0x28
+#define YARN_REG_v8  0x2c
+#define YARN_REG_v9  0x30
+#define YARN_REG_v10 0x34
+#define YARN_REG_v11 0x38
+#define YARN_REG_v12 0x3c
+#define YARN_REG_v13 0x40
+#define YARN_REG_v14 0x44
+#define YARN_REG_v15 0x48
+#define YARN_REG_SP  0x4c
+#define YARN_REG_LR  0x50
+
+#ifndef BUILD_ASM
+#include <stdint.h>
+
+// Procedure Call Standard for the ARM 64-bit Architecture
+// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
+struct yarn_fiber_context
+{
+    // parameter registers
+    uintptr_t r0;
+    uintptr_t r1;
+
+    // special purpose registers
+    uintptr_t r12; // Intra-Procedure-call
+
+    // callee-saved registers
+    uintptr_t r4;
+    uintptr_t r5;
+    uintptr_t r6;
+    uintptr_t r7;
+    uintptr_t r8;
+    uintptr_t r9;
+    uintptr_t r10;
+    uintptr_t r11;
+
+    uintptr_t v8;
+    uintptr_t v9;
+    uintptr_t v10;
+    uintptr_t v11;
+    uintptr_t v12;
+    uintptr_t v13;
+    uintptr_t v14;
+    uintptr_t v15;
+
+    uintptr_t SP; // stack pointer (r13)
+    uintptr_t LR; // link register (r14)
+};
+
+#ifdef __cplusplus
+#include <cstddef>
+static_assert(offsetof(yarn_fiber_context, r0)  == YARN_REG_r0,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r1)  == YARN_REG_r1,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r12) == YARN_REG_r12, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r4)  == YARN_REG_r4,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r5)  == YARN_REG_r5,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r6)  == YARN_REG_r6,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r7)  == YARN_REG_r7,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r8)  == YARN_REG_r8,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r9)  == YARN_REG_r9,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r10) == YARN_REG_r10, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, r11) == YARN_REG_r11, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v8)  == YARN_REG_v8,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v9)  == YARN_REG_v9,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v10) == YARN_REG_v10, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v11) == YARN_REG_v11, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v12) == YARN_REG_v12, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v13) == YARN_REG_v13, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v14) == YARN_REG_v14, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, v15) == YARN_REG_v15, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, SP)  == YARN_REG_SP,  "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, LR)  == YARN_REG_LR,  "Bad register offset");
+#endif // __cplusplus
+
+#endif // BUILD_ASM
diff --git a/src/Yarn/OSFiber_asm_arm.s b/src/Yarn/OSFiber_asm_arm.s
new file mode 100644
index 0000000..9162f57
--- /dev/null
+++ b/src/Yarn/OSFiber_asm_arm.s
@@ -0,0 +1,73 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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(__arm__)
+
+#include "OSFiber_asm_arm.h"
+
+// void yarn_fiber_swap(yarn_fiber_context* from, const yarn_fiber_context* to)
+// x0: from
+// x1: to
+.text
+.global yarn_fiber_swap
+.align 4
+yarn_fiber_swap:
+
+    // Save context 'from'
+    // TODO: multiple registers can be stored in a single instruction with: stm rA, {rB-rC}
+
+    // Store special purpose registers
+    str r12, [r0, #YARN_REG_r12]
+
+    // Store callee-preserved registers
+    str r4, [r0, #YARN_REG_r4]
+    str r5, [r0, #YARN_REG_r5]
+    str r6, [r0, #YARN_REG_r6]
+    str r7, [r0, #YARN_REG_r7]
+    str r8, [r0, #YARN_REG_r8]
+    str r9, [r0, #YARN_REG_r9]
+    str r10, [r0, #YARN_REG_r10]
+    str r11, [r0, #YARN_REG_r11]
+
+    // Store sp, lr and pc
+    str sp, [r0, #YARN_REG_SP]
+    str lr, [r0, #YARN_REG_LR]
+
+    // Load context 'to'
+    // TODO: multiple registers can be loaded in a single instruction with: ldm rA, {rB-rC}
+    mov r3, r1
+
+    // Load special purpose registers
+    ldr r12, [r3, #YARN_REG_r12]
+
+    // Load callee-preserved registers
+    ldr r4, [r3, #YARN_REG_r4]
+    ldr r5, [r3, #YARN_REG_r5]
+    ldr r6, [r3, #YARN_REG_r6]
+    ldr r7, [r3, #YARN_REG_r7]
+    ldr r8, [r3, #YARN_REG_r8]
+    ldr r9, [r3, #YARN_REG_r9]
+    ldr r10, [r3, #YARN_REG_r10]
+    ldr r11, [r3, #YARN_REG_r11]
+
+    // Load parameter registers
+    ldr r0, [r3, #YARN_REG_r0]
+    ldr r1, [r3, #YARN_REG_r1]
+
+    // Load sp, lr and pc
+    ldr sp, [r3, #YARN_REG_SP]
+    ldr lr, [r3, #YARN_REG_LR]
+    mov pc, lr
+
+#endif // defined(__arm__)
diff --git a/src/Yarn/OSFiber_asm_x64.c b/src/Yarn/OSFiber_asm_x64.c
new file mode 100644
index 0000000..0675053
--- /dev/null
+++ b/src/Yarn/OSFiber_asm_x64.c
@@ -0,0 +1,34 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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(__x86_64__)
+
+#include "OSFiber_asm_x64.h"
+
+void yarn_fiber_trampoline(void(*target)(void*), void* arg)
+{
+    target(arg);
+}
+
+void yarn_fiber_set_target(struct yarn_fiber_context* ctx, void* stack, uint32_t stack_size, void(*target)(void*), void* arg)
+{
+    uintptr_t* stack_top = (uintptr_t*)((uint8_t*)(stack) + stack_size);
+    ctx->RIP = (uintptr_t)&yarn_fiber_trampoline;
+    ctx->RDI = (uintptr_t)target;
+    ctx->RSI = (uintptr_t)arg;
+    ctx->RSP = (uintptr_t)&stack_top[-3];
+    stack_top[-2] = 0; // No return target.
+}
+
+#endif // defined(__x86_64__)
diff --git a/src/Yarn/OSFiber_asm_x64.h b/src/Yarn/OSFiber_asm_x64.h
new file mode 100644
index 0000000..420d578
--- /dev/null
+++ b/src/Yarn/OSFiber_asm_x64.h
@@ -0,0 +1,69 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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.
+
+#define YARN_REG_RBX 0x00
+#define YARN_REG_RBP 0x08
+#define YARN_REG_R12 0x10
+#define YARN_REG_R13 0x18
+#define YARN_REG_R14 0x20
+#define YARN_REG_R15 0x28
+#define YARN_REG_RDI 0x30
+#define YARN_REG_RSI 0x38
+#define YARN_REG_RSP 0x40
+#define YARN_REG_RIP 0x48
+
+#if defined(__APPLE__)
+#define YARN_ASM_SYMBOL(x) _##x
+#else
+#define YARN_ASM_SYMBOL(x) x
+#endif
+
+#ifndef BUILD_ASM
+
+#include <stdint.h>
+
+struct yarn_fiber_context
+{
+    // callee-saved registers
+    uintptr_t RBX;
+    uintptr_t RBP;
+    uintptr_t R12;
+    uintptr_t R13;
+    uintptr_t R14;
+    uintptr_t R15;
+
+    // parameter registers
+    uintptr_t RDI;
+    uintptr_t RSI;
+
+    // stack and instruction registers
+    uintptr_t RSP;
+    uintptr_t RIP;
+};
+
+#ifdef __cplusplus
+#include <cstddef>
+static_assert(offsetof(yarn_fiber_context, RBX) == YARN_REG_RBX, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, RBP) == YARN_REG_RBP, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, R12) == YARN_REG_R12, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, R13) == YARN_REG_R13, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, R14) == YARN_REG_R14, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, R15) == YARN_REG_R15, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, RDI) == YARN_REG_RDI, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, RSI) == YARN_REG_RSI, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, RSP) == YARN_REG_RSP, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, RIP) == YARN_REG_RIP, "Bad register offset");
+#endif // __cplusplus
+
+#endif // BUILD_ASM
diff --git a/src/Yarn/OSFiber_asm_x64.s b/src/Yarn/OSFiber_asm_x64.s
new file mode 100644
index 0000000..47af304
--- /dev/null
+++ b/src/Yarn/OSFiber_asm_x64.s
@@ -0,0 +1,64 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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(__x86_64__)
+
+#include "OSFiber_asm_x64.h"
+
+// void yarn_fiber_swap(yarn_fiber_context* from, const yarn_fiber_context* to)
+// rdi: from
+// rsi: to
+.text
+.global YARN_ASM_SYMBOL(yarn_fiber_swap)
+.align 4
+YARN_ASM_SYMBOL(yarn_fiber_swap):
+
+    // Save context 'from'
+
+    // Store callee-preserved registers
+    movq        %rbx, YARN_REG_RBX(%rdi)
+    movq        %rbp, YARN_REG_RBP(%rdi)
+    movq        %r12, YARN_REG_R12(%rdi)
+    movq        %r13, YARN_REG_R13(%rdi)
+    movq        %r14, YARN_REG_R14(%rdi)
+    movq        %r15, YARN_REG_R15(%rdi)
+
+    movq        (%rsp), %rcx             /* call stores the return address on the stack before jumping */
+    movq        %rcx, YARN_REG_RIP(%rdi)
+    leaq        8(%rsp), %rcx            /* skip the pushed return address */
+    movq        %rcx, YARN_REG_RSP(%rdi)
+
+    // Load context 'to'
+    movq        %rsi, %r8
+
+    // Load callee-preserved registers
+    movq        YARN_REG_RBX(%r8), %rbx
+    movq        YARN_REG_RBP(%r8), %rbp
+    movq        YARN_REG_R12(%r8), %r12
+    movq        YARN_REG_R13(%r8), %r13
+    movq        YARN_REG_R14(%r8), %r14
+    movq        YARN_REG_R15(%r8), %r15
+
+    // Load first two call parameters
+    movq        YARN_REG_RDI(%r8), %rdi
+    movq        YARN_REG_RSI(%r8), %rsi
+
+    // Load stack pointer
+    movq        YARN_REG_RSP(%r8), %rsp
+
+    // Load instruction pointer, and jump
+    movq        YARN_REG_RIP(%r8), %rcx
+    jmp         *%rcx
+
+#endif // defined(__x86_64__)
diff --git a/src/Yarn/OSFiber_asm_x86.c b/src/Yarn/OSFiber_asm_x86.c
new file mode 100644
index 0000000..2bfe082
--- /dev/null
+++ b/src/Yarn/OSFiber_asm_x86.c
@@ -0,0 +1,34 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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(__i386__)
+
+#include "OSFiber_asm_x86.h"
+
+void yarn_fiber_trampoline(void(*target)(void*), void* arg)
+{
+    target(arg);
+}
+
+void yarn_fiber_set_target(struct yarn_fiber_context* ctx, void* stack, uint32_t stack_size, void(*target)(void*), void* arg)
+{
+    uintptr_t* stack_top = (uintptr_t*)((uint8_t*)(stack) + stack_size);
+    ctx->EIP = (uintptr_t)&yarn_fiber_trampoline;
+    ctx->ESP = (uintptr_t)&stack_top[-3];
+    stack_top[-1] = (uintptr_t)arg;
+    stack_top[-2] = (uintptr_t)target;
+    stack_top[-3] = 0; // No return target.
+}
+
+#endif // defined(__i386__)
diff --git a/src/Yarn/OSFiber_asm_x86.h b/src/Yarn/OSFiber_asm_x86.h
new file mode 100644
index 0000000..503bf8c
--- /dev/null
+++ b/src/Yarn/OSFiber_asm_x86.h
@@ -0,0 +1,50 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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.
+
+#define YARN_REG_EBX 0x00
+#define YARN_REG_EBP 0x04
+#define YARN_REG_ESI 0x08
+#define YARN_REG_EDI 0x0c
+#define YARN_REG_ESP 0x10
+#define YARN_REG_EIP 0x14
+
+#ifndef BUILD_ASM
+#include <stdint.h>
+
+// Assumes cdecl calling convention.
+// Registers EAX, ECX, and EDX are caller-saved, and the rest are callee-saved.
+struct yarn_fiber_context
+{
+    // callee-saved registers
+    uintptr_t EBX;
+    uintptr_t EBP;
+    uintptr_t ESI;
+    uintptr_t EDI;
+
+    // stack and instruction registers
+    uintptr_t ESP;
+    uintptr_t EIP;
+};
+
+#ifdef __cplusplus
+#include <cstddef>
+static_assert(offsetof(yarn_fiber_context, EBX) == YARN_REG_EBX, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, EBP) == YARN_REG_EBP, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, ESI) == YARN_REG_ESI, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, EDI) == YARN_REG_EDI, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, ESP) == YARN_REG_ESP, "Bad register offset");
+static_assert(offsetof(yarn_fiber_context, EIP) == YARN_REG_EIP, "Bad register offset");
+#endif // __cplusplus
+
+#endif // BUILD_ASM
diff --git a/src/Yarn/OSFiber_asm_x86.s b/src/Yarn/OSFiber_asm_x86.s
new file mode 100644
index 0000000..a57d1d6
--- /dev/null
+++ b/src/Yarn/OSFiber_asm_x86.s
@@ -0,0 +1,56 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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(__i386__)
+
+#include "OSFiber_asm_x86.h"
+
+// void yarn_fiber_swap(yarn_fiber_context* from, const yarn_fiber_context* to)
+// esp+4: from
+// esp+8: to
+.text
+.global yarn_fiber_swap
+.align 4
+yarn_fiber_swap:
+    // Save context 'from'
+    movl        4(%esp), %eax
+
+    // Store callee-preserved registers
+    movl        %ebx, YARN_REG_EBX(%eax)
+    movl        %ebp, YARN_REG_EBP(%eax)
+    movl        %esi, YARN_REG_ESI(%eax)
+    movl        %edi, YARN_REG_EDI(%eax)
+
+    movl        (%esp), %ecx             /* call stores the return address on the stack before jumping */
+    movl        %ecx, YARN_REG_EIP(%eax)
+    lea         4(%esp), %ecx            /* skip the pushed return address */
+    movl        %ecx, YARN_REG_ESP(%eax)
+
+    // Load context 'to'
+    movl        8(%esp), %ecx
+
+    // Load callee-preserved registers
+    movl        YARN_REG_EBX(%ecx), %ebx
+    movl        YARN_REG_EBP(%ecx), %ebp
+    movl        YARN_REG_ESI(%ecx), %esi
+    movl        YARN_REG_EDI(%ecx), %edi
+
+    // Load stack pointer
+    movl        YARN_REG_ESP(%ecx), %esp
+
+    // Load instruction pointer, and jump
+    movl        YARN_REG_EIP(%ecx), %ecx
+    jmp         *%ecx
+
+#endif // defined(__i386__)
diff --git a/src/Yarn/OSFiber_test.cpp b/src/Yarn/OSFiber_test.cpp
new file mode 100644
index 0000000..a4afb6b
--- /dev/null
+++ b/src/Yarn/OSFiber_test.cpp
@@ -0,0 +1,44 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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 "OSFiber.hpp"
+
+#include "Yarn_test.hpp"
+
+TEST(WithoutBoundScheduler, OSFiber)
+{
+    std::string str;
+    auto constexpr fiberStackSize = 8 * 1024;
+    auto main = std::unique_ptr<yarn::OSFiber>(yarn::OSFiber::createFiberFromCurrentThread());
+    std::unique_ptr<yarn::OSFiber> fiberA, fiberB, fiberC;
+    fiberC = std::unique_ptr<yarn::OSFiber>(yarn::OSFiber::createFiber(fiberStackSize, [&]
+    {
+        str += "C";
+        fiberC->switchTo(fiberB.get());
+    }));
+    fiberB = std::unique_ptr<yarn::OSFiber>(yarn::OSFiber::createFiber(fiberStackSize, [&]
+    {
+        str += "B";
+        fiberB->switchTo(fiberA.get());
+    }));
+    fiberA = std::unique_ptr<yarn::OSFiber>(yarn::OSFiber::createFiber(fiberStackSize, [&]
+    {
+        str += "A";
+        fiberA->switchTo(main.get());
+    }));
+
+    main->switchTo(fiberC.get());
+
+    ASSERT_EQ(str, "CBA");
+}
diff --git a/src/Yarn/OSFiber_ucontext.hpp b/src/Yarn/OSFiber_ucontext.hpp
new file mode 100644
index 0000000..6ab1698
--- /dev/null
+++ b/src/Yarn/OSFiber_ucontext.hpp
@@ -0,0 +1,114 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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 "Debug.hpp"
+
+#include <functional>
+#include <memory>
+
+#include <ucontext.h>
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif // defined(__clang__)
+
+namespace yarn {
+
+class OSFiber
+{
+public:
+    // createFiberFromCurrentThread() returns a fiber created from the current
+    // thread.
+    static inline OSFiber* createFiberFromCurrentThread();
+
+    // 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 OSFiber* createFiber(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:
+	std::unique_ptr<uint8_t[]> stack;
+	ucontext_t context;
+	std::function<void()> target;
+};
+
+OSFiber* OSFiber::createFiberFromCurrentThread()
+{
+	auto out = new OSFiber();
+	out->context = {};
+	getcontext(&out->context);
+	return out;
+}
+
+OSFiber* OSFiber::createFiber(size_t stackSize, const std::function<void()>& 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;
+			std::function<void()> func;
+			std::swap(func, u.self->target);
+			func();
+		}
+	};
+
+	auto out = new OSFiber();
+	out->context = {};
+	out->stack = std::unique_ptr<uint8_t[]>(new uint8_t[stackSize]);
+	out->target = func;
+
+	auto alignmentOffset = 15 - (reinterpret_cast<uintptr_t>(out->stack.get() + 15) & 15);
+	auto res = getcontext(&out->context);
+	YARN_ASSERT(res == 0, "getcontext() returned %d", int(res));
+	out->context.uc_stack.ss_sp = out->stack.get() + alignmentOffset;
+	out->context.uc_stack.ss_size = stackSize - alignmentOffset;
+	out->context.uc_link = nullptr;
+
+	Args args;
+	args.self = out;
+	makecontext(&out->context, reinterpret_cast<void(*)()>(&Target::Main), 2, args.a, args.b);
+
+	return out;
+}
+
+void OSFiber::switchTo(OSFiber* fiber)
+{
+	auto res = swapcontext(&context, &fiber->context);
+	YARN_ASSERT(res == 0, "swapcontext() returned %d", int(res));
+}
+
+}  // namespace yarn
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif // defined(__clang__)
diff --git a/src/Yarn/OSFiber_windows.hpp b/src/Yarn/OSFiber_windows.hpp
new file mode 100644
index 0000000..af07649
--- /dev/null
+++ b/src/Yarn/OSFiber_windows.hpp
@@ -0,0 +1,90 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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
+//
+//    http://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 <functional>
+#include <memory>
+
+#include <Windows.h>
+
+namespace yarn {
+
+class OSFiber
+{
+public:
+	inline ~OSFiber();
+
+    // createFiberFromCurrentThread() returns a fiber created from the current
+    // thread.
+    static inline OSFiber* createFiberFromCurrentThread();
+
+    // 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 OSFiber* createFiber(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);
+		}
+	}
+}
+
+OSFiber* OSFiber::createFiberFromCurrentThread()
+{
+	auto out = new OSFiber();
+	out->fiber = ConvertThreadToFiber(nullptr);
+	out->isFiberFromThread = true;
+	return out;
+}
+
+OSFiber* OSFiber::createFiber(size_t stackSize, const std::function<void()>& func)
+{
+	auto out = new OSFiber();
+	out->fiber = CreateFiber(stackSize, &OSFiber::run, out);
+	out->target = func;
+	return out;
+}
+
+void OSFiber::switchTo(OSFiber* fiber)
+{
+	SwitchToFiber(fiber->fiber);
+}
+
+void WINAPI OSFiber::run(void* self)
+{
+	std::function<void()> func;
+	std::swap(func, reinterpret_cast<OSFiber*>(self)->target);
+	func();
+}
+
+}  // namespace yarn