// Build with: clang++ --std=c++20 -g -O1 schedtest.cpp
|
|
|
|
#include <array>
|
|
#include <span>
|
|
#include <memory>
|
|
#include <list>
|
|
#include <functional>
|
|
#include <iostream>
|
|
|
|
#if defined(__x86_64__)
|
|
#if defined(__GNUG__)
|
|
constexpr bool is_x86_64_linux = true;
|
|
#else
|
|
#if defined(__clang__)
|
|
constexpr bool is_x86_64_linux = true;
|
|
#else
|
|
constexpr bool is_x86_64_linux = false;
|
|
#endif
|
|
#endif
|
|
#else
|
|
constexpr bool is_x86_64_linux = false;
|
|
#endif
|
|
|
|
static_assert(is_x86_64_linux, "This code probably only works on x86_64 GNU Extensions C++");
|
|
|
|
struct platform_data {
|
|
platform_data() = default;
|
|
|
|
platform_data(std::span<char> stack_str)
|
|
: stack_ptr(&*(stack_str.end()-16))
|
|
, base_ptr(stack_ptr)
|
|
{}
|
|
|
|
uint64_t rbx, r12, r13, r14, r15;
|
|
|
|
void* stack_ptr;
|
|
void* base_ptr;
|
|
|
|
void pull() __attribute__((always_inline))
|
|
{
|
|
__asm__ __volatile__(
|
|
"movq %%rsp, %0\n"
|
|
"movq %%rbp, %1\n"
|
|
"movq %%rbx, %2\n"
|
|
"movq %%r12, %3\n"
|
|
"movq %%r13, %4\n"
|
|
"movq %%r14, %5\n"
|
|
"movq %%r15, %6\n"
|
|
: "=m"(stack_ptr)
|
|
, "=m"(base_ptr)
|
|
, "=m"(rbx)
|
|
, "=m"(r12)
|
|
, "=m"(r13)
|
|
, "=m"(r14)
|
|
, "=m"(r15)
|
|
);
|
|
}
|
|
|
|
void* push(void* location) __attribute__((always_inline))
|
|
{
|
|
volatile void* volatile tmp = static_cast<char*>(stack_ptr) - sizeof(void*);
|
|
*static_cast<volatile void* volatile * volatile>(tmp) = location;
|
|
__asm__ __volatile__(
|
|
"movq %1, %%rsp\n"
|
|
"movq %2, %%rbp\n"
|
|
"movq %3, %%rbx\n"
|
|
"movq %4, %%r12\n"
|
|
"movq %5, %%r13\n"
|
|
"movq %6, %%r14\n"
|
|
"movq %7, %%r15\n"
|
|
"popq %0\n"
|
|
: "+r"(location)
|
|
: "m"(tmp)
|
|
, "m"(base_ptr)
|
|
, "m"(rbx)
|
|
, "m"(r12)
|
|
, "m"(r13)
|
|
, "m"(r14)
|
|
, "m"(r15)
|
|
: "memory"
|
|
);
|
|
return location;
|
|
}
|
|
};
|
|
|
|
enum class process_status {
|
|
inactive = 0,
|
|
running = 1,
|
|
waiting = 2,
|
|
finished = 3,
|
|
zombie = 4
|
|
};
|
|
|
|
struct process {
|
|
char* stack;
|
|
size_t sz;
|
|
platform_data scheduling_swapper;
|
|
process_status state = process_status::inactive;
|
|
std::function<void()> fn;
|
|
|
|
process(std::function<void()> _fn, size_t _sz = 16384)
|
|
: stack(new char[_sz])
|
|
, sz(_sz)
|
|
, scheduling_swapper(std::span<char>(stack, sz))
|
|
, fn(_fn)
|
|
{}
|
|
|
|
process(char* stack_base)
|
|
: stack(stack_base)
|
|
, sz(0)
|
|
{}
|
|
|
|
process(const process&) = delete;
|
|
|
|
~process() {
|
|
if(sz) delete[] stack;
|
|
}
|
|
};
|
|
|
|
|
|
__attribute__((noinline)) struct system* spawner (struct system* sys);
|
|
|
|
struct system {
|
|
static system sys;
|
|
|
|
std::list<std::unique_ptr<process>>
|
|
running,
|
|
waiting,
|
|
naughty;
|
|
|
|
std::unique_ptr<process> previous;
|
|
std::unique_ptr<process> current;
|
|
|
|
std::unique_ptr<process> one() {
|
|
auto v = std::move(running.back());
|
|
running.pop_back();
|
|
return v;
|
|
}
|
|
|
|
void rid(std::unique_ptr<process> current) {
|
|
switch(current->state) {
|
|
case process_status::inactive:
|
|
case process_status::running:
|
|
running.push_front(std::move(current));
|
|
break;
|
|
case process_status::finished:
|
|
clean(std::move(current));
|
|
break;
|
|
case process_status::zombie:
|
|
naughty.push_front(std::move(current));
|
|
break;
|
|
case process_status::waiting:
|
|
waiting.push_front(std::move(current));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void clean(std::unique_ptr<process>) {}
|
|
|
|
void yield_to(std::unique_ptr<process> target) noexcept {
|
|
current->scheduling_swapper.pull();
|
|
sys.rid(std::move(current));
|
|
current = std::move(target);
|
|
current->scheduling_swapper.push(this);
|
|
spawner(&sys);
|
|
}
|
|
|
|
|
|
void yield() noexcept {
|
|
current->scheduling_swapper.pull();
|
|
sys.rid(std::move(current));
|
|
current = one();
|
|
current->scheduling_swapper.push(this);
|
|
spawner(&sys);
|
|
}
|
|
};
|
|
|
|
|
|
// Needs to return the system one way or another
|
|
__attribute__((noinline)) struct system* spawner (struct system* sys) {
|
|
auto& proc = *system::sys.current;
|
|
if(proc.state == process_status::inactive) {
|
|
proc.state = process_status::running;
|
|
proc.fn();
|
|
proc.state = process_status::finished;
|
|
sys->current->scheduling_swapper.pull();
|
|
sys->yield();
|
|
}
|
|
return sys;
|
|
}
|
|
|
|
struct system system::sys;
|
|
|
|
int main() {
|
|
char c;
|
|
system::sys.current = std::make_unique<process>(&c);
|
|
std::cout << "1";
|
|
system::sys.current->state = process_status::running;
|
|
system::sys.yield_to(std::make_unique<process>([](){
|
|
std::cout << "2";
|
|
}));
|
|
std::cout << "3\n";
|
|
return 0;
|
|
}
|