---
title: "Readers and writers"
canonical_url: https://developer.repebble.com/pebbleos/docs/samples/kernel/classic/readers_writers/
last_updated: 2026-11-13
sdk_version: 4.44
---
# Readers and writers

Source: https://developer.repebble.com/pebbleos/docs/samples/kernel/classic/readers_writers/

This sample builds a lock that lets any number of threads read a record, or one write it. The first reader in takes the room for all readers and the last one out gives it back, which is why the room is a semaphore: a mutex is unlocked by the thread that locked it.

This version favors readers: a writer waits as long as readers keep coming, which the readers here avoid by staying out for a while after each read.

Run:

`pbl samples samples/kernel/classic/readers_writers`

Boards:

every board

## Sources

src/main.c

```
#include \<pbl/kernel/delay.h\>#include \<pbl/kernel/mutex.h\>#include \<pbl/kernel/sem.h\>#include \<pbl/kernel/thread.h\>#include \<pbl/logging/logging.h\>#define NUM\_READERS 3#define READS 10#define WRITES 5/\* \* Any number of readers, or one writer. The room is a semaphore and not a mutex because the \* reader that leaves it last is not always the one that entered first. \*/struct rwlock {struct pbl\_mutex readers\_lock;unsigned int readers;struct pbl\_sem room;};struct record {unsigned int version;unsigned int copy\_of\_version;};static struct rwlock s\_lock = {.readers\_lock = PBL\_MUTEX\_INITIALIZER,.room = PBL\_SEM\_INITIALIZER(1, 1),};static struct record s\_record;/\* Readers share the room, so what they count needs a lock of its own. \*/static PBL\_MUTEX\_DEFINE(s\_stats\_lock);static unsigned int s\_reading;static unsigned int s\_max\_reading;static unsigned int s\_torn\_reads;static unsigned int s\_writes\_with\_readers;PBL\_THREAD\_STACK\_DEFINE(s\_stack\_0, 1024);PBL\_THREAD\_STACK\_DEFINE(s\_stack\_1, 1024);PBL\_THREAD\_STACK\_DEFINE(s\_stack\_2, 1024);PBL\_THREAD\_STACK\_DEFINE(s\_stack\_3, 1024);static struct pbl\_thread s\_threads[NUM\_READERS + 1];static void prv\_read\_lock(struct rwlock \*lock) {pbl\_mutex\_lock(&lock-\>readers\_lock, PBL\_FOREVER);if (++lock-\>readers == 1) {/\* The first reader in takes the room for all of them. \*/pbl\_sem\_take(&lock-\>room, PBL\_FOREVER);}pbl\_mutex\_unlock(&lock-\>readers\_lock);}static void prv\_read\_unlock(struct rwlock \*lock) {pbl\_mutex\_lock(&lock-\>readers\_lock, PBL\_FOREVER);if (--lock-\>readers == 0) {pbl\_sem\_give(&lock-\>room);}pbl\_mutex\_unlock(&lock-\>readers\_lock);}static void prv\_write\_lock(struct rwlock \*lock) {pbl\_sem\_take(&lock-\>room, PBL\_FOREVER);}static void prv\_write\_unlock(struct rwlock \*lock) {pbl\_sem\_give(&lock-\>room);}static void prv\_reader(void \*arg) {for (unsigned int i = 0; i \< READS; i++) {prv\_read\_lock(&s\_lock);pbl\_mutex\_lock(&s\_stats\_lock, PBL\_FOREVER);s\_reading++;if (s\_reading \> s\_max\_reading) {s\_max\_reading = s\_reading;}pbl\_mutex\_unlock(&s\_stats\_lock);unsigned int version = s\_record.version;pbl\_sleep\_ms(2);const bool torn = version != s\_record.copy\_of\_version;pbl\_mutex\_lock(&s\_stats\_lock, PBL\_FOREVER);if (torn) {s\_torn\_reads++;}s\_reading--;pbl\_mutex\_unlock(&s\_stats\_lock);prv\_read\_unlock(&s\_lock);/\* Out of the room for a while, or a writer would never find it empty. \*/pbl\_sleep\_ms(3);}}static void prv\_writer(void \*arg) {for (unsigned int i = 0; i \< WRITES; i++) {prv\_write\_lock(&s\_lock);pbl\_mutex\_lock(&s\_stats\_lock, PBL\_FOREVER);if (s\_reading != 0) {s\_writes\_with\_readers++;}pbl\_mutex\_unlock(&s\_stats\_lock);/\* Half written for a while, which no reader may see. \*/s\_record.version++;pbl\_sleep\_ms(2);s\_record.copy\_of\_version = s\_record.version;prv\_write\_unlock(&s\_lock);pbl\_sleep\_ms(5);}}int main(void) {void \*const stacks[] = {s\_stack\_0, s\_stack\_1, s\_stack\_2, s\_stack\_3};int ret;for (unsigned int i = 0; i \< NUM\_READERS + 1; i++) {const bool reader = i \< NUM\_READERS;const struct pbl\_thread\_attr attr = {.name = reader ? "reader" : "writer",.entry = reader ? prv\_reader : prv\_writer,.prio = CONFIG\_KERNEL\_MAIN\_THREAD\_PRIO - 1,.privileged = true,.stack = stacks[i],.stack\_size = sizeof(s\_stack\_0),};pbl\_thread\_create(&s\_threads[i], &attr);}for (unsigned int i = 0; i \< NUM\_READERS + 1; i++) {ret = PBL\_POLL\_SLEEP\_MS(pbl\_thread\_state(&s\_threads[i]) == PBL\_THREAD\_DEAD, 1, 2000);if (ret != 0) {PBL\_LOG\_ERR("main: %s did not end (%d)", i \< NUM\_READERS ? "a reader" : "the writer", ret);return ret;}}PBL\_LOG\_INFO("record: version %u after %u writes", s\_record.version, WRITES);PBL\_LOG\_INFO("record: up to %u readers at once, %u half written records read", s\_max\_reading,s\_torn\_reads);PBL\_LOG\_INFO("record: %u writes made with a reader in the room", s\_writes\_with\_readers);PBL\_LOG\_INFO("Sample done");return 0;}
```

## Output

```
record: version 5 after 5 writes
record: up to 3 readers at once, 0 half written records read
record: 0 writes made with a reader in the room
Sample done
```

Generated from PebbleOS [f4926c952](https://github.com/coredevices/pebbleos/commit/f4926c952bf9b82bce9e3cdb44645a59531c0ee9). This page is maintained in the pebbleos repository: [docs/samples/kernel/classic/readers\_writers.md](https://github.com/coredevices/pebbleos/blob/f4926c952bf9b82bce9e3cdb44645a59531c0ee9/docs/samples/kernel/classic/readers_writers.md).
