queue
In-memory ring buffer queue with configurable overflow strategies. Transport-agnostic — use it as a buffer between data producers (sensors, MQTT, CAN bus) and consumers (HTTP POST, TCP send, logging).
|
|
| Version |
v0.1 |
| Platform |
PC, ESP32 |
| Type |
Native (C) |
star.mod
require dev-libs/queue v0.1
Usage
Functions
Create / Destroy
| Function |
Signature |
Description |
queue.new(cap) |
(i32) -> i32 |
Create queue with capacity, returns handle |
queue.destroy(q) |
(i32) -> void |
Free queue resources |
var q: i32 = queue.new(100)
# ... use queue ...
queue.destroy(q)
Maximum 32 queues can be active at the same time.
Push / Pop
| Function |
Signature |
Description |
queue.push(q, val) |
(i32, value) -> void |
Push value. On a full block queue it parks the task (backpressure) until a slot frees |
queue.tryPush(q, val) |
(i32, value) -> bool |
Non-blocking push. Returns false if a full block queue would reject; never parks |
queue.pop(q) |
(i32) -> value |
Pop front value (FIFO); wakes one parked producer |
queue.peek(q) |
(i32) -> value |
Read front value without removing |
queue.popBatch(q, n) |
(i32, i32) -> array |
Pop up to N values at once; wakes parked producers |
Queues carry the dynamic value type, so any value (number, string, struct, array, dict) can be queued. Use checked narrowing when reading back a typed value (see Type System).
var q: i32 = queue.new(10)
queue.push(q, 42)
queue.push(q, 99)
var first: number = queue.pop(q) # 42 (FIFO)
var next: number = queue.peek(q) # 99 (still in queue)
var batch: array = queue.popBatch(q, 5) # up to 5 items
Status
| Function |
Signature |
Description |
queue.len(q) |
(i32) -> i32 |
Current item count |
queue.cap(q) |
(i32) -> i32 |
Maximum capacity |
queue.full(q) |
(i32) -> bool |
Is queue at capacity? |
queue.empty(q) |
(i32) -> bool |
Is queue empty? |
queue.clear(q) |
(i32) -> void |
Remove all items |
Overflow Control
| Function |
Signature |
Description |
queue.setOverflow(q, mode) |
(i32, string) -> void |
Set overflow strategy |
queue.setOverflow(q, "drop_oldest")
| Mode |
Behavior |
"block" |
Default. Zero loss. queue.push parks the producer task until a consumer frees a slot (backpressure-as-yield); queue.tryPush returns false instead of parking |
"drop_oldest" |
Oldest item is dropped to make room for the new item (push never parks) |
"drop_newest" |
New item is discarded when full (push never parks) |
Backpressure (block mode)
In block mode a full queue does not drop and does not spin — the producing task is parked and the scheduler runs other tasks. A consumer's pop/popBatch/clear frees slots and wakes one parked producer per freed slot, which re-runs its push. This is the data-plane mechanism that gives zero loss without a busy-wait.
If the queue is full and no other task can drain it (the producer is the only runnable task), push raises fault code 13 (queue deadlock) rather than parking forever. Use tryPush when you want to handle fullness yourself instead of blocking.
Statistics
| Function |
Signature |
Description |
queue.stats(q) |
(i32) -> dict |
Get queue statistics |
var s: dict = queue.stats(q)
# s["pushed"] — total items pushed
# s["popped"] — total items popped
# s["dropped"] — total items dropped (overflow)
# s["len"] — current item count
Pattern: Store and Forward
package main
import "queue"
var outbox: i32 = 0
fn sensor_reader():void {
# simulated high-frequency sensor data
var i: i32 = 0
for (i = 0; i < 100; i = i + 1) {
queue.push(outbox, i * 10)
runtime.yield()
}
}
fn http_sender():void {
# drain queue and send batches
for (var tick: i32 = 0; tick < 20; tick = tick + 1) {
if (!queue.empty(outbox)) {
var batch: array = queue.popBatch(outbox, 10)
# http.post(endpoint, json.stringify(batch))
console.log(batch.len())
}
runtime.yield()
}
}
fn main():void {
outbox = queue.new(200)
queue.setOverflow(outbox, "drop_oldest")
go sensor_reader()
go http_sender()
runtime.keepAlive()
queue.destroy(outbox)
}
Notes
- Queue is a FIFO ring buffer — O(1) push/pop
popBatch returns fewer items than requested if the queue doesn't have enough
pop on an empty queue returns nil
- Each queue is identified by an
i32 handle
- Stats track lifetime totals, not just current state — under
block mode dropped stays 0 by design
- Queue contents are GC roots: values held in a queue survive collection
- The queue is the data plane — you choose its retention policy. For reliable control-plane delivery that must never drop, use events instead
- On ESP32, queue capacity should be sized to available RAM