---
title: "Bounded buffer"
canonical_url: https://developer.repebble.com/pebbleos/docs/samples/kernel/classic/bounded_buffer/
last_updated: 2026-11-13
sdk_version: 4.44
---
# Bounded buffer

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

This sample builds a queue of four items by hand, with a semaphore counting the free slots, another counting the items and a mutex for the indices, and has two producers and two slower consumers go through a hundred items.

This is what [a message queue](https://developer.repebble.com/pebbleos/docs/samples/kernel/msgq.md) is inside, and the queue is what to use. Note the order: wait for the slot first, lock second. The other way round, a producer waiting with the lock held stops every consumer.

Run:

`pbl samples samples/kernel/classic/bounded_buffer`

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 CAPACITY 4#define ITEMS\_PER\_PRODUCER 50#define NUM\_PRODUCERS 2#define NUM\_CONSUMERS 2/\* \* What a message queue does inside: one semaphore counts the free slots, another the items, and \* a mutex guards the indices. Prefer pbl\_msgq, which also works from interrupts. \*/struct buffer {unsigned int items[CAPACITY];unsigned int head;unsigned int tail;unsigned int used;unsigned int max\_used;struct pbl\_sem free\_slots;struct pbl\_sem queued\_items;struct pbl\_mutex lock;};static struct buffer s\_buffer = {.free\_slots = PBL\_SEM\_INITIALIZER(CAPACITY, CAPACITY),.queued\_items = PBL\_SEM\_INITIALIZER(0, CAPACITY),.lock = PBL\_MUTEX\_INITIALIZER,};static unsigned int s\_sums[NUM\_CONSUMERS];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\_PRODUCERS + NUM\_CONSUMERS];static void prv\_put(struct buffer \*buffer, unsigned int item) {/\* Waits for a free slot before the lock, never with it. \*/pbl\_sem\_take(&buffer-\>free\_slots, PBL\_FOREVER);pbl\_mutex\_lock(&buffer-\>lock, PBL\_FOREVER);buffer-\>items[buffer-\>tail] = item;buffer-\>tail = (buffer-\>tail + 1) % CAPACITY;buffer-\>used++;if (buffer-\>used \> buffer-\>max\_used) {buffer-\>max\_used = buffer-\>used;}pbl\_mutex\_unlock(&buffer-\>lock);pbl\_sem\_give(&buffer-\>queued\_items);}static unsigned int prv\_get(struct buffer \*buffer) {unsigned int item;pbl\_sem\_take(&buffer-\>queued\_items, PBL\_FOREVER);pbl\_mutex\_lock(&buffer-\>lock, PBL\_FOREVER);item = buffer-\>items[buffer-\>head];buffer-\>head = (buffer-\>head + 1) % CAPACITY;buffer-\>used--;pbl\_mutex\_unlock(&buffer-\>lock);pbl\_sem\_give(&buffer-\>free\_slots);return item;}static void prv\_producer(void \*arg) {for (unsigned int item = 1; item \<= ITEMS\_PER\_PRODUCER; item++) {prv\_put(&s\_buffer, item);}}static void prv\_consumer(void \*arg) {unsigned int \*sum = arg;for (unsigned int i = 0; i \< ITEMS\_PER\_PRODUCER \* NUM\_PRODUCERS / NUM\_CONSUMERS; i++) {\*sum += prv\_get(&s\_buffer);/\* Slower than the producers, so the buffer fills up. \*/pbl\_thread\_yield();}}int main(void) {void \*const stacks[] = {s\_stack\_0, s\_stack\_1, s\_stack\_2, s\_stack\_3};const unsigned int expected = NUM\_PRODUCERS \* (ITEMS\_PER\_PRODUCER \* (ITEMS\_PER\_PRODUCER + 1) / 2);unsigned int sum = 0;int ret;for (unsigned int i = 0; i \< NUM\_PRODUCERS + NUM\_CONSUMERS; i++) {const bool producer = i \< NUM\_PRODUCERS;const struct pbl\_thread\_attr attr = {.name = producer ? "producer" : "consumer",.entry = producer ? prv\_producer : prv\_consumer,.arg = producer ? NULL : &s\_sums[i - NUM\_PRODUCERS],.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\_PRODUCERS + NUM\_CONSUMERS; i++) {ret = PBL\_POLL\_SLEEP\_MS(pbl\_thread\_state(&s\_threads[i]) == PBL\_THREAD\_DEAD, 1, 1000);if (ret != 0) {PBL\_LOG\_ERR("main: %s did not end (%d)", i \< NUM\_PRODUCERS ? "a producer" : "a consumer",ret);return ret;}}for (unsigned int i = 0; i \< NUM\_CONSUMERS; i++) {sum += s\_sums[i];}PBL\_LOG\_INFO("buffer: %u items produced, consumed items add up to %u of %u",NUM\_PRODUCERS \* ITEMS\_PER\_PRODUCER, sum, expected);PBL\_LOG\_INFO("buffer: held at most %u of %u items, %u left", s\_buffer.max\_used, CAPACITY,s\_buffer.used);PBL\_LOG\_INFO("Sample done");return 0;}
```

## Output

```
buffer: 100 items produced, consumed items add up to 2550 of 2550
buffer: held at most \d of 4 items, 0 left
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/bounded\_buffer.md](https://github.com/coredevices/pebbleos/blob/f4926c952bf9b82bce9e3cdb44645a59531c0ee9/docs/samples/kernel/classic/bounded_buffer.md).
