Recursive Mutexes and Notion of Concurrency Unit Id

I’m currently implementing a sqlite3 vfs on top of zig 0.16’s Io interface mostly because it would be very neat if you could make the actual sqlite C library use the Evented implementation and get async sqlite for free(-ish). Unfortunately, sqlite requires a recursive mutex implementation while the Io interface only has a non-recursive implementation. Now, in principle it’s fairly easy to write a recursive mutex using just a regular mutex by tracking the owner thread id (e.g. the 0.15.2 implementation) however for the Io interface this obviously would only work for the Threaded implementation.

As far as I can tell the Io interface doesn’t have a notion of identifying the currently executing concurrency unit, so this post is for discussion on whether it would be something worth adding to Io (or whether there’s a better way of implementing a recursive mutex).

The function prototype could look something like this:

// name is bikesheddable
concurrencyId: *const fn (?*anyopaque) usize,

Besides recursive mutexes, it could be used to implement something equivalent to thread_local but per concurrency unit:

const Io = @import("std").Io;

const max_concurrency = 128;

var cu_local_state: [max_concurrency]State = undefined;


// ... in some function
const io: Io = ...;
const index = getIndex(io.concurrencyId()); // some way of mapping id to index
const local = &cu_local_state[index];
2 Likes

Considering ziglibc is implementing pthreads and pthreads require recursive mutex, there has to be way for it at least (whether it requiring std changes or not is other concern).

2 Likes

Getting an identifier for a unit of concurrency is also useful to implement other primitives, for example something like “task-local” storage.

Open question: What would this mean for stackless coroutines?

You don’t need to know the unit of concurrency for “task-local” storage, in fact, for stackless coroutines you explicitly don’t want that since they could jump to different units of concurrency!

If you want “task local” storage, that is not local variables, you can already do that your self with your own ID’s

That makes sense.

Arguably, but this is not exactly a composable mechanism.