---
title: "Mutexes"
canonical_url: https://developer.repebble.com/pebbleos/docs/samples/kernel/mutex/
last_updated: 2026-11-13
sdk_version: 4.44
---
# Mutexes

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

This sample has two threads add to a balance in two steps, a read and a write, with the other thread getting in between. Without the mutex half of the deposits are lost. It then waits for a mutex held elsewhere with a time limit, and locks one twice.

Mutexes are recursive and inherit priority; [Priority inversion](https://developer.repebble.com/pebbleos/docs/samples/kernel/classic/priority_inversion.md) shows why the latter matters. They cannot be used from interrupts, where a [semaphore](https://developer.repebble.com/pebbleos/docs/samples/kernel/semaphore.md) is the tool.

Run:

`pbl samples samples/kernel/mutex`

Boards:

every board

## Sources

src/main.c

```
#include \<errno.h\>#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 DEPOSITS 100#define WORKER\_PRIO (CONFIG\_KERNEL\_MAIN\_THREAD\_PRIO - 1)struct account {struct pbl\_mutex lock;bool use\_lock;unsigned int balance;};static struct account s\_account = {.lock = PBL\_MUTEX\_INITIALIZER};static PBL\_SEM\_DEFINE(s\_holder\_release, 0, 1);PBL\_THREAD\_STACK\_DEFINE(s\_stack\_a, 1024);PBL\_THREAD\_STACK\_DEFINE(s\_stack\_b, 1024);static struct pbl\_thread s\_thread\_a;static struct pbl\_thread s\_thread\_b;static void prv\_deposit(struct account \*account) {if (account-\>use\_lock) {pbl\_mutex\_lock(&account-\>lock, PBL\_FOREVER);}unsigned int balance = account-\>balance;/\* Stands for being preempted between the read and the write. \*/pbl\_thread\_yield();account-\>balance = balance + 1;if (account-\>use\_lock) {pbl\_mutex\_unlock(&account-\>lock);}}static void prv\_depositor(void \*arg) {for (unsigned int i = 0; i \< DEPOSITS; i++) {prv\_deposit(arg);}}static void prv\_holder(void \*arg) {struct account \*account = arg;pbl\_mutex\_lock(&account-\>lock, PBL\_FOREVER);pbl\_sem\_take(&s\_holder\_release, PBL\_FOREVER);pbl\_mutex\_unlock(&account-\>lock);}static void prv\_start(struct pbl\_thread \*t, const char \*name, pbl\_thread\_entry\_t entry, void \*stack,size\_t stack\_size) {const struct pbl\_thread\_attr attr = {.name = name,.entry = entry,.arg = &s\_account,.prio = WORKER\_PRIO,.privileged = true,.stack = stack,.stack\_size = stack\_size,};pbl\_thread\_create(t, &attr);}static int prv\_join(const struct pbl\_thread \*t) {return PBL\_POLL\_SLEEP\_MS(pbl\_thread\_state(t) == PBL\_THREAD\_DEAD, 1, 1000);}static int prv\_run\_depositors(bool use\_lock) {int ret;s\_account.balance = 0;s\_account.use\_lock = use\_lock;prv\_start(&s\_thread\_a, "a", prv\_depositor, s\_stack\_a, sizeof(s\_stack\_a));prv\_start(&s\_thread\_b, "b", prv\_depositor, s\_stack\_b, sizeof(s\_stack\_b));ret = prv\_join(&s\_thread\_a);if (ret == 0) {ret = prv\_join(&s\_thread\_b);}if (ret != 0) {PBL\_LOG\_ERR("main: depositors did not end (%d)", ret);return ret;}PBL\_LOG\_INFO("main: %s the lock, balance is %u after %u deposits", use\_lock ? "with" : "without",s\_account.balance, 2 \* DEPOSITS);return 0;}int main(void) {int ret;ret = prv\_run\_depositors(false);if (ret != 0) {return ret;}ret = prv\_run\_depositors(true);if (ret != 0) {return ret;}/\* A lock held elsewhere for long is better waited for with a limit. \*/prv\_start(&s\_thread\_a, "holder", prv\_holder, s\_stack\_a, sizeof(s\_stack\_a));pbl\_sleep\_ms(10);ret = pbl\_mutex\_lock(&s\_account.lock, PBL\_MSEC(10));PBL\_LOG\_INFO("main: lock held by another thread, 10 ms wait returns %s",ret == -EAGAIN ? "-EAGAIN" : "something else");pbl\_sem\_give(&s\_holder\_release);ret = pbl\_mutex\_lock(&s\_account.lock, PBL\_FOREVER);if (ret != 0) {PBL\_LOG\_ERR("main: cannot take the released lock (%d)", ret);return ret;}/\* The owner may lock again, e.g. in a function called with the lock held. \*/pbl\_mutex\_lock(&s\_account.lock, PBL\_FOREVER);pbl\_mutex\_unlock(&s\_account.lock);PBL\_LOG\_INFO("main: locked twice, unlocked once, still the owner: %s",pbl\_mutex\_is\_owner(&s\_account.lock) ? "yes" : "no");pbl\_mutex\_unlock(&s\_account.lock);PBL\_LOG\_INFO("Sample done");return 0;}
```

## Output

```
main: without the lock, balance is \d+ after 200 deposits
main: with the lock, balance is 200 after 200 deposits
main: lock held by another thread, 10 ms wait returns -EAGAIN
main: locked twice, unlocked once, still the owner: yes
Sample done
```

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