kernel: publish ordered events with reserved lifecycle capacity
d84aebbccc0a1bc31157fffcf54cf07a805002e796bffa665bb39461eebff80b
Verified: make -C kernel check with the machine-capable emulator; all pass.
1 parent
53c5af21
kernel/Makefile
+6 -5
| 3 | 3 | HOST_EMU ?= $(EMU) |
|
| 4 | 4 | COMPILER := ../bin/radiance.rv64.dev |
|
| 5 | 5 | COMPILE := $(HOST_EMU) -memory-size=385024 -data-size=348160 -stack-size=512 -run $(COMPILER) |
|
| 6 | 6 | MODULES := core/fdt.rad core/platform.rad core/frames.rad core/abi.rad core/handles.rad \ |
|
| 7 | 7 | core/events.rad core/domains.rad core/resources.rad core/capabilities.rad \ |
|
| 8 | - | core/memory.rad core/state.rad core/pages.rad |
|
| 9 | - | CORE := -pkg core -mod core.rad $(addprefix -mod ,$(MODULES)) |
|
| 8 | + | core/memory.rad core/state.rad core/pages.rad core/atomic.rad |
|
| 9 | + | CORE_ASM := arch/atomic.ras |
|
| 10 | + | CORE := -pkg core -mod core.rad $(addprefix -mod ,$(MODULES) $(CORE_ASM)) |
|
| 10 | 11 | CHECK_MODULES := check/boot.rad check/fixture.rad check/frames.rad check/handles.rad \ |
|
| 11 | - | check/domains.rad check/capabilities.rad check/pages.rad |
|
| 12 | + | check/domains.rad check/capabilities.rad check/pages.rad check/events.rad |
|
| 12 | 13 | ||
| 13 | 14 | .PHONY: all check clean compiler-check |
|
| 14 | 15 | all: kernel.rv64 |
|
| 15 | 16 | ||
| 16 | 17 | compiler-check: |
|
| 17 | 18 | ||
| 18 | 19 | $(COMPILER): compiler-check |
|
| 19 | 20 | $(MAKE) -C .. RAD_EMULATOR=$(abspath $(shell command -v $(HOST_EMU))) |
|
| 20 | 21 | ||
| 21 | - | kernel.rv64: main.rad arch/entry.ras core.rad $(MODULES) $(COMPILER) |
|
| 22 | + | kernel.rv64: main.rad arch/entry.ras core.rad $(MODULES) $(CORE_ASM) $(COMPILER) |
|
| 22 | 23 | $(COMPILE) $(CORE) -pkg kernel -start arch/entry.ras -mod main.rad -entry kernel -o $@ |
|
| 23 | 24 | ||
| 24 | - | check.rv64: check.rad core.rad $(MODULES) $(CHECK_MODULES) $(COMPILER) |
|
| 25 | + | check.rv64: check.rad core.rad $(MODULES) $(CORE_ASM) $(CHECK_MODULES) $(COMPILER) |
|
| 25 | 26 | $(COMPILE) $(CORE) -pkg check -mod check.rad $(addprefix -mod ,$(CHECK_MODULES)) -entry check -o $@ |
|
| 26 | 27 | ||
| 27 | 28 | check: all check.rv64 |
|
| 28 | 29 | $(HOST_EMU) -run check.rv64 |
|
| 29 | 30 | $(EMU) -machine -no-guard-stack -max-steps=100000000 -count-instructions -run kernel.rv64 |
kernel/NOTES.md
+18 -3
| 1 | 1 | # Kernel implementation decisions |
|
| 2 | 2 | ||
| 3 | 3 | The specification at https://radiant.computer/system/kernel takes precedence |
|
| 4 | 4 | for fixed call numbers, handle layout, rights, and object behavior. These notes |
|
| 5 | - | record the contracts established through step 8 of the 22-step plan. |
|
| 5 | + | record the contracts established through step 9 of the 22-step plan. |
|
| 6 | 6 | ||
| 7 | 7 | ## Source and trust boundary |
|
| 8 | 8 | ||
| 9 | 9 | - Kernel mechanisms use freestanding Radiance; RAS owns machine entry, register |
|
| 10 | 10 | state, atomics, and MMIO. Hosted checks exercise the same mechanism modules. |
| 63 | 63 | Active domain identities. |
|
| 64 | 64 | - Zero as a self-sentinel identifies the caller but supplies no resource right. |
|
| 65 | 65 | Allocate and Create require a live capability to that caller with the right. |
|
| 66 | 66 | A newly created domain has only its installed Read|Write Events handle. |
|
| 67 | 67 | - Accepting a child reserves one terminal event. At most 128 credits can be |
|
| 68 | - | outstanding in a 256-entry Events queue. |
|
| 68 | + | outstanding in a 256-entry Events queue. Ordinary capacity is limited to |
|
| 69 | + | 128 entries. |
|
| 69 | 70 | ||
| 70 | 71 | ## Capability transactions |
|
| 71 | 72 | ||
| 72 | 73 | - Grant intersects requested rights with source rights. A grant to another |
|
| 73 | 74 | domain requires Grant; a self-grant may attenuate without that right. |
| 99 | 100 | disappear. Clear a quiescent domain's bitmap before reusing its identity. |
|
| 100 | 101 | - Allocation preflights capability/object slots before taking RAM. PageSplit |
|
| 101 | 102 | checks exclusivity and both destination slots before changing the source. |
|
| 102 | 103 | Readable coverage may span adjacent grants; a gap never authorizes access. |
|
| 103 | 104 | ||
| 105 | + | ## Event publication and consumption |
|
| 106 | + | ||
| 107 | + | - The 4112-byte shared ring contains 256 complete Event records followed by u32 |
|
| 108 | + | head, tail, mask, and consumer lock at offsets 4096, 4100, 4104, and 4108. |
|
| 109 | + | Kernel producers do not acquire the user consumer lock. |
|
| 110 | + | - Write data before release-publishing tail. Consumers acquire tail, copy an |
|
| 111 | + | entry, then release-publish head. Private positions and slot marks validate |
|
| 112 | + | progress and remain authoritative despite user-writable event contents. |
|
| 113 | + | - Ordinary delivery returns Busy at its capacity limit. Terminal delivery uses |
|
| 114 | + | relationship credits; consumption releases the credit. At most one entry per |
|
| 115 | + | IRQ source is outstanding, and validated consumption permits its next event. |
|
| 116 | + | - Boundary checks cover full capacity, u32 wraparound, malformed head progress, |
|
| 117 | + | lifecycle credit integrity, and coalescing through IRQ source 127. |
|
| 118 | + | ||
| 104 | 119 | ## Validation |
|
| 105 | 120 | ||
| 106 | 121 | Use the current machine-capable sibling emulator. Set `RAD_EMULATOR`, pass |
|
| 107 | 122 | `EMU` to the kernel Make invocation, or put `emulator` on PATH. The kernel build |
|
| 108 | 123 | checks compiler dependencies. From the repository root, run: |
|
| 109 | 124 | ||
| 110 | 125 | ```sh |
|
| 111 | 126 | make -C kernel check |
|
| 112 | 127 | ``` |
|
| 113 | 128 | ||
| 114 | - | Exercise split exclusivity, failed allocation/split transactions, pointers retained after handle drop, and reclamation after the final recipient lifetime. |
|
| 129 | + | Exercise all ring slots, ordinary backpressure, reserved delivery, IRQ coalescing, wraparound, and invalid consumer progress. |
kernel/arch/atomic.ras
added
+12 -0
| 1 | + | // Ordered, naturally aligned shared-memory accesses. |
|
| 2 | + | .text; |
|
| 3 | + | .export @"core::atomic::load"; |
|
| 4 | + | @"core::atomic::load" |
|
| 5 | + | lwu %a0 0(%a0); |
|
| 6 | + | fence; |
|
| 7 | + | ret; |
|
| 8 | + | .export @"core::atomic::store"; |
|
| 9 | + | @"core::atomic::store" |
|
| 10 | + | fence; |
|
| 11 | + | sw %a1 0(%a0); |
|
| 12 | + | ret; |
kernel/check.rad
+2 -0
| 5 | 5 | mod frames; |
|
| 6 | 6 | mod handles; |
|
| 7 | 7 | mod domains; |
|
| 8 | 8 | mod capabilities; |
|
| 9 | 9 | mod pages; |
|
| 10 | + | mod events; |
|
| 10 | 11 | ||
| 11 | 12 | /// Run the available kernel mechanism checks. |
|
| 12 | 13 | @default fn main() -> u32 { |
|
| 13 | 14 | frames::run(); |
|
| 14 | 15 | boot::run(); |
|
| 15 | 16 | handles::run(); |
|
| 16 | 17 | domains::run(); |
|
| 17 | 18 | capabilities::run(); |
|
| 18 | 19 | pages::run(); |
|
| 20 | + | events::run(); |
|
| 19 | 21 | return 0; |
|
| 20 | 22 | } |
kernel/check/events.rad
added
+103 -0
| 1 | + | //! Notification ordering, reserved delivery, and untrusted consumer progress. |
|
| 2 | + | ||
| 3 | + | use core::abi; |
|
| 4 | + | use core::atomic; |
|
| 5 | + | use core::events; |
|
| 6 | + | ||
| 7 | + | /// Read one complete entry and publish consumer progress with release ordering. |
|
| 8 | + | fn pop(queue: *mut events::Queue) -> events::Event { |
|
| 9 | + | let head = atomic::load(&queue.ring.head); |
|
| 10 | + | assert head <> atomic::load(&queue.ring.tail); |
|
| 11 | + | let event = queue.ring.data[head & queue.ring.mask]; |
|
| 12 | + | atomic::store(&mut queue.ring.head, ((head as u64 + 1) & 0xffffffff) as u32); |
|
| 13 | + | return event; |
|
| 14 | + | } |
|
| 15 | + | ||
| 16 | + | /// Reject an ordinary notification when its bounded capacity is occupied. |
|
| 17 | + | fn busy(queue: *mut events::Queue, event: events::Event) { |
|
| 18 | + | try events::push(queue, event, events::Class::Ordinary) catch error { |
|
| 19 | + | assert error == abi::Error::Busy; |
|
| 20 | + | return; |
|
| 21 | + | }; |
|
| 22 | + | panic "busy: full ordinary ring accepted a notification"; |
|
| 23 | + | } |
|
| 24 | + | ||
| 25 | + | /// Reject a relationship when all lifecycle credits are occupied. |
|
| 26 | + | fn exhausted(queue: *mut events::Queue) { |
|
| 27 | + | try events::reserve(queue) catch error { |
|
| 28 | + | assert error == abi::Error::Exhausted; |
|
| 29 | + | return; |
|
| 30 | + | }; |
|
| 31 | + | panic "exhausted: full lifecycle reservations accepted a child"; |
|
| 32 | + | } |
|
| 33 | + | ||
| 34 | + | /// Reject backward or out-of-range consumer positions. |
|
| 35 | + | fn invalid(queue: *mut events::Queue) { |
|
| 36 | + | try events::refresh(queue) catch error { |
|
| 37 | + | assert error == abi::Error::InvalidArg; |
|
| 38 | + | return; |
|
| 39 | + | }; |
|
| 40 | + | panic "invalid: corrupt consumer position was accepted"; |
|
| 41 | + | } |
|
| 42 | + | ||
| 43 | + | /// Exercise delivery through saturation, wraparound, coalescing, and corruption. |
|
| 44 | + | export fn run() { |
|
| 45 | + | let mut queue: events::Queue = undefined; |
|
| 46 | + | events::init(&mut queue); |
|
| 47 | + | let ordinary = events::Event { kind: 5, reserved: 0, code: 0, value: 0 }; |
|
| 48 | + | for i in 0..events::CRITICAL { try! events::reserve(&mut queue); } |
|
| 49 | + | exhausted(&mut queue); |
|
| 50 | + | for i in 0..events::CAPACITY - events::CRITICAL { |
|
| 51 | + | try! events::push(&mut queue, events::Event { kind: 5, reserved: 0, code: i, value: i as u64 }, events::Class::Ordinary); |
|
| 52 | + | } |
|
| 53 | + | busy(&mut queue, ordinary); |
|
| 54 | + | for i in 0..events::CRITICAL { |
|
| 55 | + | try! events::push(&mut queue, events::Event { kind: 4, reserved: 0, code: i, value: i as u64 + 128 }, events::Class::Critical); |
|
| 56 | + | } |
|
| 57 | + | for i in 0..events::CAPACITY { |
|
| 58 | + | let event = pop(&mut queue); |
|
| 59 | + | assert event.value == i as u64; |
|
| 60 | + | if i < 128 { assert event.kind == 5; } |
|
| 61 | + | else { assert event.kind == 4; } |
|
| 62 | + | } |
|
| 63 | + | try! events::refresh(&mut queue); |
|
| 64 | + | assert not events::available(&queue); |
|
| 65 | + | for i in 0..events::CRITICAL { try! events::reserve(&mut queue); } |
|
| 66 | + | exhausted(&mut queue); |
|
| 67 | + | ||
| 68 | + | events::init(&mut queue); |
|
| 69 | + | let interrupt = events::Event { kind: 1, reserved: 0, code: 127, value: 0 }; |
|
| 70 | + | try! events::push(&mut queue, interrupt, events::Class::Interrupt(127)); |
|
| 71 | + | try! events::push(&mut queue, interrupt, events::Class::Interrupt(127)); |
|
| 72 | + | assert pop(&mut queue).code == 127; |
|
| 73 | + | try! events::refresh(&mut queue); |
|
| 74 | + | assert not events::available(&queue); |
|
| 75 | + | try! events::push(&mut queue, interrupt, events::Class::Interrupt(127)); |
|
| 76 | + | assert pop(&mut queue).code == 127; |
|
| 77 | + | try! events::refresh(&mut queue); |
|
| 78 | + | ||
| 79 | + | set queue.head = 0xfffffff0; |
|
| 80 | + | set queue.tail = 0xfffffff0; |
|
| 81 | + | atomic::store(&mut queue.ring.head, 0xfffffff0); |
|
| 82 | + | atomic::store(&mut queue.ring.tail, 0xfffffff0); |
|
| 83 | + | for i in 0..32 { |
|
| 84 | + | try! events::push(&mut queue, events::Event { kind: 5, reserved: 0, code: i, value: i as u64 }, events::Class::Ordinary); |
|
| 85 | + | } |
|
| 86 | + | for i in 0..32 { assert pop(&mut queue).code == i; } |
|
| 87 | + | try! events::refresh(&mut queue); |
|
| 88 | + | assert not events::available(&queue); |
|
| 89 | + | atomic::store(&mut queue.ring.head, 17); |
|
| 90 | + | invalid(&mut queue); |
|
| 91 | + | atomic::store(&mut queue.ring.head, 15); |
|
| 92 | + | invalid(&mut queue); |
|
| 93 | + | atomic::store(&mut queue.ring.head, 16); |
|
| 94 | + | try! events::refresh(&mut queue); |
|
| 95 | + | ||
| 96 | + | try! events::reserve(&mut queue); |
|
| 97 | + | try! events::push(&mut queue, events::Event { kind: 4, reserved: 0, code: 0, value: 1 }, events::Class::Critical); |
|
| 98 | + | set queue.ring.data[16].kind = 5; |
|
| 99 | + | let _event = pop(&mut queue); |
|
| 100 | + | try! events::refresh(&mut queue); |
|
| 101 | + | for i in 0..events::CRITICAL { try! events::reserve(&mut queue); } |
|
| 102 | + | exhausted(&mut queue); |
|
| 103 | + | } |
kernel/core.rad
+1 -0
| 10 | 10 | export mod resources; |
|
| 11 | 11 | export mod capabilities; |
|
| 12 | 12 | export mod memory; |
|
| 13 | 13 | export mod state; |
|
| 14 | 14 | export mod pages; |
|
| 15 | + | export mod atomic; |
kernel/core/atomic.rad
added
+7 -0
| 1 | + | //! Acquire and release accesses used by shared notification memory. |
|
| 2 | + | ||
| 3 | + | /// Load one naturally aligned u32 with acquire ordering. |
|
| 4 | + | export fn load(pointer: *u32) -> u32; |
|
| 5 | + | ||
| 6 | + | /// Store one naturally aligned u32 with release ordering. |
|
| 7 | + | export fn store(pointer: *mut u32, value: u32); |
kernel/core/domains.rad
+1 -2
| 145 | 145 | ||
| 146 | 146 | /// Create a pending domain after the caller has authorized image admission. |
|
| 147 | 147 | /// Reserve terminal event capacity before accepting the parent relationship. |
|
| 148 | 148 | export fn create(domains: *mut [Domain], creator: abi::Object, image: u32) -> abi::Object throws (abi::Error) { |
|
| 149 | 149 | if not live(domains, creator) { throw abi::Error::BadHandle; } |
|
| 150 | - | if domains[creator.index].events.reserved == events::CRITICAL { throw abi::Error::Exhausted; } |
|
| 151 | 150 | for index in 0..domains.len { |
|
| 152 | 151 | let domain = &domains[index]; |
|
| 153 | 152 | if domain.state == Lifecycle::Dead and domain.epoch < 0xffffffff |
|
| 154 | 153 | and domain.handles.entries[1].generation <> 0 { |
|
| 154 | + | try events::reserve(&mut domains[creator.index].events); |
|
| 155 | 155 | let result = prepare(domains, index, creator, image); |
|
| 156 | - | set domains[creator.index].events.reserved += 1; |
|
| 157 | 156 | return result; |
|
| 158 | 157 | } |
|
| 159 | 158 | } |
|
| 160 | 159 | throw abi::Error::Exhausted; |
|
| 161 | 160 | } |
kernel/core/events.rad
+100 -3
| 1 | 1 | //! Fixed per-domain notification memory and producer state. |
|
| 2 | 2 | ||
| 3 | + | use core::abi; |
|
| 4 | + | use core::atomic; |
|
| 5 | + | ||
| 3 | 6 | /// Shared notification ring depth; a power of two. |
|
| 4 | 7 | export constant CAPACITY: u32 = 256; |
|
| 5 | 8 | /// Notification slots reserved for terminal lifecycle events. |
|
| 6 | 9 | export constant CRITICAL: u32 = 128; |
|
| 10 | + | /// Largest interrupt source that can have an outstanding ring entry. |
|
| 11 | + | export constant MAX_IRQ: u32 = 127; |
|
| 12 | + | /// Private marker for a lifecycle entry. |
|
| 13 | + | constant LIFECYCLE: u16 = 256; |
|
| 14 | + | ||
| 15 | + | /// Notification capacity and coalescing policy. |
|
| 16 | + | export union Class: Copy { |
|
| 17 | + | /// Ordinary notification subject to backpressure. |
|
| 18 | + | Ordinary, |
|
| 19 | + | /// One outstanding entry for a physical interrupt source. |
|
| 20 | + | Interrupt(u32), |
|
| 21 | + | /// Terminal lifecycle notification with a prior reservation. |
|
| 22 | + | Critical, |
|
| 23 | + | } |
|
| 7 | 24 | ||
| 8 | 25 | /// A kernel-to-domain asynchronous notification. |
|
| 9 | 26 | export record Event: Copy { |
|
| 10 | 27 | /// Interrupt=1, Timeout=2, Fault=3, ChildExit=4, Wakeup=5. |
|
| 11 | 28 | kind: u16, |
| 25 | 42 | head: u32, |
|
| 26 | 43 | /// Producer progress, published by the kernel. |
|
| 27 | 44 | tail: u32, |
|
| 28 | 45 | /// CAPACITY - 1, supplied by the kernel. |
|
| 29 | 46 | mask: u32, |
|
| 47 | + | /// User-space consumer lock; zero means unlocked. |
|
| 48 | + | consumer: u32, |
|
| 30 | 49 | } |
|
| 31 | 50 | ||
| 32 | 51 | /// Private kernel state for a domain's ring. |
|
| 33 | 52 | export record Queue: Copy { |
|
| 34 | 53 | /// Shared ABI storage exposed to this domain. |
| 39 | 58 | tail: u32, |
|
| 40 | 59 | /// Number of outstanding ordinary notifications. |
|
| 41 | 60 | ordinary: u32, |
|
| 42 | 61 | /// Live-child and queued-lifecycle delivery reservations. |
|
| 43 | 62 | reserved: u32, |
|
| 44 | - | /// Slots that release a lifecycle reservation when consumed. |
|
| 45 | - | critical: [bool; CAPACITY], |
|
| 63 | + | /// Zero for ordinary entries, source for IRQs, LIFECYCLE for terminal events. |
|
| 64 | + | marks: [u16; CAPACITY], |
|
| 65 | + | /// Interrupt sources with outstanding notifications. |
|
| 66 | + | pending: [u64; 2], |
|
| 46 | 67 | } |
|
| 47 | 68 | ||
| 48 | 69 | /// Clear event memory before it is exposed to a domain incarnation. |
|
| 49 | 70 | export fn init(queue: *mut Queue) { |
|
| 50 | 71 | for i in 0..CAPACITY { |
|
| 51 | 72 | set queue.ring.data[i] = Event { kind: 0, reserved: 0, code: 0, value: 0 }; |
|
| 52 | - | set queue.critical[i] = false; |
|
| 73 | + | set queue.marks[i] = 0; |
|
| 53 | 74 | } |
|
| 54 | 75 | set queue.ring.head = 0; |
|
| 55 | 76 | set queue.ring.tail = 0; |
|
| 56 | 77 | set queue.ring.mask = CAPACITY - 1; |
|
| 78 | + | set queue.ring.consumer = 0; |
|
| 57 | 79 | set queue.head = 0; |
|
| 58 | 80 | set queue.tail = 0; |
|
| 59 | 81 | set queue.ordinary = 0; |
|
| 60 | 82 | set queue.reserved = 0; |
|
| 83 | + | set queue.pending = [0; 2]; |
|
| 84 | + | } |
|
| 85 | + | ||
| 86 | + | /// Difference between wrapping u32 positions, evaluated without overflow. |
|
| 87 | + | fn distance(a: u32, b: u32) -> u32 { |
|
| 88 | + | return ((a as u64 + 0x100000000 - b as u64) & 0xffffffff) as u32; |
|
| 89 | + | } |
|
| 90 | + | ||
| 91 | + | /// Validate consumer progress and reclaim consumed capacity. |
|
| 92 | + | export fn refresh(queue: *mut Queue) throws (abi::Error) { |
|
| 93 | + | let head = atomic::load(&queue.ring.head); |
|
| 94 | + | let count = distance(head, queue.head); |
|
| 95 | + | if count > distance(queue.tail, queue.head) { throw abi::Error::InvalidArg; } |
|
| 96 | + | for n in 0..count { |
|
| 97 | + | let i = ((queue.head as u64 + n as u64) & (CAPACITY - 1) as u64) as u32; |
|
| 98 | + | let mark = queue.marks[i]; |
|
| 99 | + | if mark == LIFECYCLE { set queue.reserved -= 1; } |
|
| 100 | + | else { |
|
| 101 | + | set queue.ordinary -= 1; |
|
| 102 | + | if mark > 0 { |
|
| 103 | + | let source = mark as u32; |
|
| 104 | + | set queue.pending[source / 64] &= ~(1 << (source as u64 % 64)); |
|
| 105 | + | } |
|
| 106 | + | } |
|
| 107 | + | set queue.marks[i] = 0; |
|
| 108 | + | } |
|
| 109 | + | set queue.head = head; |
|
| 110 | + | } |
|
| 111 | + | ||
| 112 | + | /// Reserve a delivery credit before installing a parent relationship. |
|
| 113 | + | export fn reserve(queue: *mut Queue) throws (abi::Error) { |
|
| 114 | + | try refresh(queue); |
|
| 115 | + | if queue.reserved == CRITICAL { throw abi::Error::Exhausted; } |
|
| 116 | + | set queue.reserved += 1; |
|
| 117 | + | } |
|
| 118 | + | ||
| 119 | + | /// Publish complete event data before the tail; coalesce outstanding IRQs. |
|
| 120 | + | /// The kernel serializes calls that change private producer state. |
|
| 121 | + | export fn push(queue: *mut Queue, event: Event, class: Class) throws (abi::Error) { |
|
| 122 | + | try refresh(queue); |
|
| 123 | + | let count = distance(queue.tail, queue.head); |
|
| 124 | + | let mut mark: u16 = 0; |
|
| 125 | + | match class { |
|
| 126 | + | case Class::Ordinary => {}, |
|
| 127 | + | case Class::Interrupt(source) => { |
|
| 128 | + | if source == 0 or source > MAX_IRQ { throw abi::Error::InvalidArg; } |
|
| 129 | + | if (queue.pending[source / 64] & (1 << (source as u64 % 64))) <> 0 { return; } |
|
| 130 | + | set mark = source as u16; |
|
| 131 | + | }, |
|
| 132 | + | case Class::Critical => { |
|
| 133 | + | assert queue.reserved > count - queue.ordinary; |
|
| 134 | + | set mark = LIFECYCLE; |
|
| 135 | + | }, |
|
| 136 | + | } |
|
| 137 | + | if mark <> LIFECYCLE and queue.ordinary == CAPACITY - CRITICAL { |
|
| 138 | + | throw abi::Error::Busy; |
|
| 139 | + | } |
|
| 140 | + | assert count < CAPACITY and event.reserved == 0; |
|
| 141 | + | let i = queue.tail & (CAPACITY - 1); |
|
| 142 | + | set queue.ring.data[i] = event; |
|
| 143 | + | set queue.marks[i] = mark; |
|
| 144 | + | if mark <> LIFECYCLE { |
|
| 145 | + | set queue.ordinary += 1; |
|
| 146 | + | if mark > 0 { |
|
| 147 | + | let source = mark as u32; |
|
| 148 | + | set queue.pending[source / 64] |= 1 << (source as u64 % 64); |
|
| 149 | + | } |
|
| 150 | + | } |
|
| 151 | + | set queue.tail = ((queue.tail as u64 + 1) & 0xffffffff) as u32; |
|
| 152 | + | atomic::store(&mut queue.ring.tail, queue.tail); |
|
| 153 | + | } |
|
| 154 | + | ||
| 155 | + | /// Test the authoritative queue after consumer progress has been validated. |
|
| 156 | + | export fn available(queue: *Queue) -> bool { |
|
| 157 | + | return queue.head <> queue.tail; |
|
| 61 | 158 | } |