std1.0.0-beta-7 SafeC docs

Source async/future.sc

View in the repository

// SafeC Standard Library — async: Waker, Context, WakerSlot, WaitList, YieldNow
#pragma once
#include <std/async/future.h>

namespace std {

// ── atomics ────────────────────────────────────────────────────────────────

int async_load_(const &int p) { unsafe { return atomic_load((const _Atomic int*)p); } }
void async_store_(&int p, int v) { unsafe { atomic_store((_Atomic int*)p, v); } }
int async_xchg_(&int p, int v) { unsafe { return atomic_exchange((_Atomic int*)p, v); } }
int async_fetch_or_(&int p, int bits) { unsafe { return atomic_fetch_or((_Atomic int*)p, bits); } }
int async_fetch_and_(&int p, int bits) { unsafe { return atomic_fetch_and((_Atomic int*)p, bits); } }

// ── Waker ──────────────────────────────────────────────────────────────────

static void waker_noop_fn_(?&int data, unsigned long tag) { }

struct Waker Waker::empty_() {
    struct Waker w;
    w.wake_  = null;
    w.clone_ = null;
    w.drop_  = null;
    w.data_  = null;
    w.tag_   = 0UL;
    return w;
}

struct Waker Waker::noop() {
    return Waker::new(waker_noop_fn_, (?&int)null, 0UL);
}

void Waker::wake_by_ref() const {
    match (self.wake_) {
        case some(f): f(self.data_, self.tag_);
        case null: { }
    }
}

void Waker::wake() {
    self.wake_by_ref();
    self.drop();
}

struct Waker Waker::clone() const {
    match (self.clone_) {
        case some(f): f(self.data_, self.tag_);
        case null: { }
    }
    unsafe { return *self; }   // a bitwise copy: clone_ took the reference it holds
}

void Waker::drop() {
    match (self.drop_) {
        case some(f): f(self.data_, self.tag_);
        case null: { }
    }
    // A dropped waker wakes nothing (and drops nothing) again.
    self.wake_ = null;
    self.drop_ = null;
    self.clone_ = null;
}

int Waker::will_wake(const &Waker other) const {
    unsafe {
        return (self.data_ == other.data_ && self.wake_ == other.wake_ &&
                self.tag_ == other.tag_) ? 1 : 0;
    }
}

int Waker::is_noop() const {
    fn void(?&int, unsigned long) n = waker_noop_fn_;
    unsafe { return (self.wake_ == (?fn void(?&Erased, unsigned long))n || self.wake_.is_null()) ? 1 : 0; }
}

// ── Context ────────────────────────────────────────────────────────────────

struct Context Context::from_waker(struct Waker w) {
    struct Context cx;
    cx.waker  = w;
    cx.timers = null;
    cx.io_    = null;
    cx.exec_  = null;
    cx.spawn_ = null;
    return cx;
}

struct Context Context::noop() {
    return Context::from_waker(Waker::noop());
}

void Context::drop() { self.waker.drop(); }

void Context::wake_by_ref() const {
    self.waker.wake_by_ref();
}

// ── WakerSlot ──────────────────────────────────────────────────────────────

struct WakerSlot WakerSlot::new() {
    struct WakerSlot s;
    s.w = Waker::empty_();
    s.set = 0;
    return s;
}

void WakerSlot::register_(const &Waker w) {
    if (self.set != 0) {
        if (self.w.will_wake(w) != 0) return;
    }
    std::replace(&self.w, w.clone()).drop();   // the old waker, or the empty one
    self.set = 1;
}

int WakerSlot::wake() {
    if (self.set == 0) return 0;
    self.set = 0;
    struct Waker w = std::replace(&self.w, Waker::empty_());
    w.wake();
    return 1;
}

void WakerSlot::drop() {
    if (self.set != 0) { self.set = 0; self.w.drop(); }
}

void WakerSlot::clear() {
    if (self.set == 0) return;
    self.set = 0;
    std::replace(&self.w, Waker::noop()).drop();
}

// ── WaitList ───────────────────────────────────────────────────────────────
// id = serial * 64 + slot + 1 (ASYNC_WAITERS <= 63).

static int waitlist_slot_(int id) { return (id & 63) - 1; }

struct WaitList WaitList::new() {
    struct WaitList l;
    int i = 0;
    while (i < ASYNC_WAITERS) {
        l.w[i] = Waker::empty_();
        l.serial[i] = 0;
        i = i + 1;
    }
    l.count = 0;
    l.next_serial = 1;
    l.next_ = 0U;
    return l;
}

int WaitList::contains(int id) const {
    if (id <= 0) return 0;
    int slot = waitlist_slot_(id);
    if (slot < 0 || slot >= ASYNC_WAITERS) return 0;
    return (self.serial[slot] != 0 && self.serial[slot] == id / 64) ? 1 : 0;
}

int WaitList::add(int id, const &Waker w) {
    if (self.contains(id) != 0) {
        int slot = waitlist_slot_(id);
        if (self.w[slot].will_wake(w) == 0) std::replace(&self.w[slot], w.clone()).drop();
        return id;
    }
    int i = 0;
    while (i < ASYNC_WAITERS) {
        if (self.serial[i] == 0) {
            int ser = self.next_serial;
            self.next_serial = self.next_serial >= 16777215 ? 1 : self.next_serial + 1;
            self.serial[i] = ser;
            self.w[i] = w.clone();
            self.count = self.count + 1;
            return ser * 64 + i + 1;
        }
        i = i + 1;
    }
    return 0;
}

void WaitList::remove(int id) {
    if (self.contains(id) == 0) return;
    int slot = waitlist_slot_(id);
    self.serial[slot] = 0;
    std::replace(&self.w[slot], Waker::noop()).drop();
    self.count = self.count - 1;
}

int WaitList::wake_one() {
    if (self.count == 0) return 0;
    int k = 0;
    while (k < ASYNC_WAITERS) {
        int i = (int)((self.next_ + (unsigned int)k) % (unsigned int)ASYNC_WAITERS);
        if (self.serial[i] != 0) {
            self.serial[i] = 0;
            self.count = self.count - 1;
            self.next_ = (unsigned int)(i + 1);
            struct Waker w = std::replace(&self.w[i], Waker::noop());
            w.wake();
            return 1;
        }
        k = k + 1;
    }
    return 0;
}

int WaitList::wake_all() {
    int n = 0;
    int i = 0;
    while (i < ASYNC_WAITERS && self.count > 0) {
        if (self.serial[i] != 0) {
            self.serial[i] = 0;
            self.count = self.count - 1;
            struct Waker w = std::replace(&self.w[i], Waker::noop());
            w.wake();
            n = n + 1;
        }
        i = i + 1;
    }
    return n;
}

int WaitList::len() const { return self.count; }

void WaitList::drop() {
    int i = 0;
    while (i < ASYNC_WAITERS) {
        if (self.serial[i] != 0) { self.serial[i] = 0; self.w[i].drop(); }
        i = i + 1;
    }
    self.count = 0;
}

// ── YieldNow ───────────────────────────────────────────────────────────────

struct YieldNow YieldNow::new() {
    struct YieldNow y;
    y.yielded = 0;
    return y;
}

struct Poll<Unit> YieldNow::poll(&Context cx) {
    if (self.yielded != 0) return Poll::ready(UNIT);
    self.yielded = 1;
    cx.wake_by_ref();
    return Poll::pending();
}

void YieldNow::drop() { self.yielded = 1; }

} // namespace std