Skip to content

wait

include/thread/wait.h View source View on GitHub
struct thread_wait_header (2 fields) source
§ struct spinlock lock
§ struct list_head waiters
struct thread_wait_block (6 fields) source
§ struct list_node obj_node
TODO

we’re using a doubly linked list here because the block list under a thread can grow arbitrarily long. Perhaps someday we can consider something like Windows’ MAXIMUM_WAIT_OBJECTS, so we can go for a singularly linked list, save 8 bytes, at the cost of an absolute maximum + maybe minor slowdowns (?)

§ struct thread *thread
§ void *object
§ uint16_t key
§ struct thread_wait_header *header
struct thread_wait_object (3 fields) source
§ struct thread_wait_header *header
§ void *object
§ bool (*try_acquire)(void *object)

Optional function. This runs under header->lock right before we link the wait block, and if it returns true, the wait is satisfied at registration time and any remaining objects never get linked

struct thread_wait_result (3 fields) source
§ enum thread_wait_status status
§ uint16_t key
§ enum thread_resume_reason reason
enum thread_wait_block_state (3 variants) source
§ THREAD_WAIT_BLOCK_INACTIVE
§ THREAD_WAIT_BLOCK_ACTIVE
§ THREAD_WAIT_BLOCK_SATISFIED
void thread_wait_header_init(struct thread_wait_header *header)
source
void thread_wait_init(struct thread *thread)
source
void thread_wait_prepare(
    const struct thread_wait_object *objects,
    size_t count,
    struct thread_wait_block *storage,
    enum thread_wait_type type,
    enum thread_block_reason reason
)
source

If storage == NULL, we use the thread’s embedded blocks. No allocation ever happens in here, however, the caller remains runnable

void thread_wait_prepare_one(
    struct thread_wait_header *header,
    void *object,
    enum thread_wait_type type,
    enum thread_block_reason reason
)
source
void thread_wait_prepare_to_sleep(
    struct thread_wait_header *header,
    void *object,
    enum thread_wait_type type
)
source
bool thread_wait_satisfy(
    struct thread_wait_block *block,
    enum thread_resume_reason reason,
    void (*callback)(struct thread *)
)
source

Satisfy unlinks ALL blocks before the waiter becomes runnable. The callback runs just for the winner

bool thread_wait_satisfy_epoch(
    struct thread_wait_block *block,
    uint64_t epoch,
    enum thread_resume_reason reason,
    void (*cb)(struct thread *)
)
source

For callbacks that took a snapshot of thread->wait_epoch

struct thread * thread_wait_header_satisfy(
    struct thread_wait_header *hdr,
    enum thread_resume_reason reason,
    void (*cb)(struct thread *)
)
source
struct thread * thread_wait_header_satisfy_one(
    struct thread_wait_header *hdr,
    enum thread_resume_reason reason,
    void (*cb)(struct thread *),
    void (*empty_locked)(void *),
    void *arg
)
source

Like thread_wait_header_satisfy, but if nothing remains to satisfy, empty_locked runs under hdr->lock

size_t thread_wait_header_satisfy_all(
    struct thread_wait_header *hdr,
    enum thread_resume_reason reason,
    void (*callback)(struct thread *),
    void (*before_locked)(void *),
    void *arg
)
source

Satisfies the waiters linked when before_locked runs, under hdr->lock

void thread_alert(struct thread *cc_nonnull_ptr)
source

Alert is a “level-triggered” singular event. We only end interruptible waits with alerts, but otherwise, the alert will merely pend until the next interruptible wait/park happens

void thread_park(void)
source
struct thread_wait_result thread_wait_complete(void)
source

The actual “go wait” action, yield and whatnot

struct thread_wait_result thread_wait_complete_timeout(time_ms_t timeout_ms)
source
void thread_wait_cancel(void)
source
#define THREAD_WAIT_BLOCK_SYNC 0
source
#define THREAD_NUM_WAIT_BLOCKS 4
source
#define THREAD_WAIT_KEY_NONE UINT16_MAX
source
#define THREAD_WAIT_HEADER_INIT(name_)
source
Expansion
#define THREAD_WAIT_HEADER_INIT(name_) \
    {.lock = SPINLOCK_INIT, .waiters = LIST_HEAD_INIT((name_).waiters)}