Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
mutex.h
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <lxsameer@lxsameer.com>
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21/** @file
22 * A fiber blocking mutex.
23 *
24 * This lock suspends the calling *fiber* while the lock is held elsewhere and lets the worker run
25 * other fibers meanwhile. It is therefore only valid to call from INSIDE A RUNNING FIBER. Calling
26 * it from the worker thread, a signal handler, or any other non-fiber context is an error, since
27 * there would be no fiber to suspend.
28 *
29 * The lock is not recursive, a fiber that locks a mutex it already holds deadlocks against itself.
30 * `state` carries no owner identity to detect it.
31 *
32 * Waiters are served in FIFO order, so no waiter starves under sustained contention. We achieve
33 * that by having the `state` of a like to act as a stack and unlucky fibers add themselves to the
34 * stack and use `srn_fiber_t.link` to point to the next fiber in the stack. Then a lucky fiber, on
35 * the unlocking time, will walk the stack, reverse the order to make it a FIFO queue and populates
36 * the `waiters_head` of the lock. This way the next time a fiber will unlock the lock, it will hand
37 * the lock to the oldest waiting fiber.
38 */
39
40#include <stdatomic.h>
41
42#include "serene/rt/fiber.h"
43
44typedef struct srn_fiber_mutex_t {
45 /// A pointer to the waiter's stack head. nullptr == unlocked. Contending fibers push onto the
46 /// stack lock free, and the holder drains it (it's basically a treiber stack). So having a waiter
47 /// implies the lock is acquired by someone.
49
50 /// A private (In the sense that it only touched only by the current lock holder during unlock)
51 /// pointer to the head of the waiters in order. Since it is private it needs not to be atomic.
52 /// The holder fills it in one reversed batch when it drains the incoming stack, then pops one
53 /// fiber from its head per unlock. Threaded through `srn_fiber_t.link`.
56
57/**
58 * Initialise `m` to the unlocked state.
59 */
61
62/**
63 * Acquire `m`. If the lock is free it is taken at once, otherwise the calling
64 * fiber is suspended and resumes owning the lock, handed off directly by the
65 * unlocking fiber. Must be called from a running fiber.
66 */
68
69/**
70 * Release `m`. If any fibers are waiting, the longest waiting one is handed the
71 * lock and made ready, otherwise the lock is dropped. Must be called by the
72 * fiber that currently holds `m`.
73 */
AI Generated (🤦) Fiber subsystem overview.
void srn_fiber_mutex_init(srn_fiber_mutex_t *m)
Initialise m to the unlocked state.
Definition mutex.c:76
void srn_fiber_mutex_unlock(srn_fiber_mutex_t *m)
Release m.
Definition mutex.c:99
void srn_fiber_mutex_lock(srn_fiber_mutex_t *m)
Acquire m.
Definition mutex.c:81
srn_fiber_t * waiters_head
A private (In the sense that it only touched only by the current lock holder during unlock) pointer t...
Definition mutex.h:54
_Atomic(srn_fiber_t *) state
A pointer to the waiter's stack head.