Channel table + common channel, inert (B1) -- FABRIC-3.6.md task 3.2
Adds SkHermesChannel: a channel is an index into a boot-time, stadium_max_vm_count()-sized table (same sizing pattern task 3.1 established for the switch table -- no separate numeric rule was ruled for this table, so task 3.1's bound is extended directly, flagged as such rather than restated as a new ruling). No name field, mirroring messaging.4th's own nameless CH-ARENA. The common channel (index 0) is created at boot and permanent. Hera subscribes explicitly in kernel_main.c (she is the one VM never born through capsule_birth_baby()); every other VM -- Tripod fleet and future WIREBIND identities alike -- subscribes inside capsule_birth_baby() itself, the single choke point every other birth already passes through. Inert: no publish, no dispatch, no ACK/NACK, no ACL hook (tasks 3.3, 3.6, 3.7). Verified live on all three architectures: channel table sized to 50/202/50 slots (matching switch-signal's own per-arch sizing), common-channel fleet self-test confirms all four Tripod members are members, and a synthetic create/subscribe/unsubscribe/ destroy round-trip against a private topic passes, including refusing to destroy the common channel. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
c19ef365fe
commit
a4afdfa591
+29
-1
@@ -798,10 +798,38 @@ ruling]** cannot be written precisely until Captain Bob settles the named sub-it
|
||||
attempts (sectors 20/8/8 and 3224/5496/5504); boot recovered and reached the prompt
|
||||
regardless. `virtio_blk.c` was not touched by this task. Reporting per standing rule,
|
||||
not investigating further here.
|
||||
- [ ] **3.2** — **Channel table + common channel**, inert (B1). Dynamic table of topics, each
|
||||
- [x] **3.2** — **Channel table + common channel**, inert (B1). Dynamic table of topics, each
|
||||
with its own `SkHermesMembership`; the common channel exists from boot; every VM is
|
||||
subscribed at birth. Wired to nothing. *Check:* boot byte-identical; every born VM appears
|
||||
in the common channel's membership; create/destroy a synthetic private topic.
|
||||
2026-09-22 · `logs/20260922-000447/amd64/`, `logs/20260922-000954/aarch64/`,
|
||||
`logs/20260922-001511/riscv64/` — all three reach `[zuse@Hera] ok>`, zero `UNKNOWN WORD`,
|
||||
`mkcapsule --lint capsules/` clean (38 files, 0 violations, unchanged -- no capsule
|
||||
touched). New `SkHermesChannel` table (kernel_hermes.h/.c): a channel is an index into a
|
||||
boot-time, `stadium_max_vm_count()`-sized array, no name field (mirrors messaging.4th's
|
||||
own nameless `CH-ARENA`). Sizing note: SXLV.1 says the channel table has "no fixed channel
|
||||
maximum, same reasoning as SXLV.2" but names no separate numeric rule -- extending task
|
||||
3.1's already-ruled `stadium_max_vm_count()` bound directly (same kmalloc-at-boot shape)
|
||||
is the smallest choice consistent with what was ruled; flagged in the header comment as an
|
||||
engineering extrapolation, not restated as a separate Captain Bob ruling. Common channel
|
||||
(index `SK_HERMES_CHANNEL_COMMON` = 0) created in `sk_hermes_channels_boot_init()`, called
|
||||
from `kernel_main.c` right after the switch-signal boot init; Hera subscribed explicitly
|
||||
right after `stadium_birth_hera()` (she is the one VM never born through
|
||||
`capsule_birth_baby()`); every other VM (Tripod fleet and future WIREBIND identities
|
||||
alike) subscribed inside `capsule_birth_baby()` itself, at its `VM_STATE_LIVE` completion
|
||||
point -- the single choke point every non-Hera birth already passes through, confirmed by
|
||||
grep (`mama_forth_words.c`, `capsule_runcap.c`, `capsule_console.c` all call it). New
|
||||
console lines confirmed live on all three architectures: `Kernel-Hermes: N channel slots`
|
||||
(**50 amd64, 202 aarch64, 50 riscv64** -- tracks switch-signal's own per-arch sizing
|
||||
exactly, as expected since both derive from the same `stadium_max_vm_count()`), then after
|
||||
all four fleet births, `Kernel-Hermes common-channel fleet self-test: PASS` (`common
|
||||
channel members=4`, one per Hera/Hermes/Hestia/Artemis) and `Kernel-Hermes synthetic
|
||||
private-topic self-test: PASS` (create/subscribe/is-member/unsubscribe/destroy round-trip
|
||||
against a synthetic VM id, plus confirms a destroyed channel refuses further ops and the
|
||||
common channel itself refuses `sk_hermes_channel_destroy()`) -- both PASS on all three
|
||||
architectures. `dict_hash` for Hermes (`0xc95ef2d92fa0781f`) and Hestia
|
||||
(`0xfbde9fe105fd3b3d`) identical across all three architectures, unmoved from task 3.1's
|
||||
values (this task touches no capsule).
|
||||
- [ ] **3.3** — **Publish path, no dispatch** (§XLIII.3). Publish allocates via
|
||||
`sk_hermes_alloc()`, enqueues onto each subscriber's own pending queue, and records the
|
||||
fact; it dispatches nothing. Self-test only. *Check:* ledger audit and
|
||||
|
||||
@@ -279,6 +279,101 @@ uint64_t sk_hermes_audit_failure_count(void);
|
||||
uint64_t sk_hermes_scan_held(size_t *live_count);
|
||||
int sk_hermes_scan_check(void);
|
||||
|
||||
/*
|
||||
* Channel table (FABRIC-3.6.md task 3.2, B1 / FABRIC-3.5.md SXLV.1): B1
|
||||
* overrules SXXXIII.5's single flat broadcast list -- messaging is
|
||||
* publish/subscribe, one permanent common channel every VM joins at
|
||||
* birth, private channels created by request/grant/deny (task 3.6, not
|
||||
* this one). SkHermesMembership (task 2.1) becomes per-channel: one
|
||||
* instance per SkHermesChannel table entry instead of one global list.
|
||||
*
|
||||
* INERT, same posture as task 2.1: this task wires channel existence and
|
||||
* membership bookkeeping only. No publish, no dispatch, no ACK/NACK, no
|
||||
* ACL hook (tasks 3.3, 3.6, 3.7) -- creating/destroying/subscribing a
|
||||
* channel here changes no VM's dictionary and delivers no message.
|
||||
*
|
||||
* Sizing: SXLV.1 says the channel table has "no fixed channel maximum,
|
||||
* same reasoning as SXLV.2" -- SXLV.2/task 3.1's own ruling is boot-time,
|
||||
* RAM-derived sizing (Stadium's stadium_max_vm_count_val pattern), not a
|
||||
* literal unbounded/growable table. No separate numeric sizing rule was
|
||||
* ruled for the channel table specifically (task 3.0's five sub-items
|
||||
* covered ACK cadence/ACL-hook-location/switch-table-sizing/chunk-
|
||||
* framing/drain-cadence, not this). Extending task 3.1's already-ruled
|
||||
* pattern directly -- same stadium_max_vm_count() bound, same
|
||||
* kmalloc-at-boot shape -- is the smallest choice consistent with what
|
||||
* was ruled, flagged here as an engineering extrapolation, not restated
|
||||
* as a separate Captain Bob ruling.
|
||||
*/
|
||||
|
||||
/* SK_HERMES_CHANNEL_COMMON - the permanent common channel's fixed index.
|
||||
* Exists (in_use, empty membership) from sk_hermes_channels_boot_init()
|
||||
* itself, before any VM is born -- every VM joins it at birth (task 3.2's
|
||||
* own kernel_main.c/capsule_birth.c wiring), never destroyed. */
|
||||
#define SK_HERMES_CHANNEL_COMMON 0
|
||||
|
||||
/*
|
||||
* SkHermesChannel - one channel/topic: its membership plus an occupancy
|
||||
* flag for the dynamic table below. No name field -- messaging.4th's own
|
||||
* channel abstraction (CH-ARENA) has none either; a channel is identified
|
||||
* by its table index, the same way a Stadium quota slot or a switch-
|
||||
* signal slot is identified by index, not by string.
|
||||
*/
|
||||
typedef struct {
|
||||
SkHermesMembership membership;
|
||||
int in_use;
|
||||
} SkHermesChannel;
|
||||
|
||||
/* Boot-time allocation: kmalloc's the channel table to
|
||||
* stadium_max_vm_count() entries (see this section's own sizing note
|
||||
* above) and creates the common channel (index SK_HERMES_CHANNEL_COMMON,
|
||||
* in_use, empty membership -- members join at birth, not pre-populated
|
||||
* here). Must run after stadium_boot_init() (that bound is 0, and this
|
||||
* fails, until Stadium has computed it). Soft failure -- returns -1 and
|
||||
* leaves the table unallocated (capacity 0, so every call below simply
|
||||
* refuses) rather than halting boot, same posture as
|
||||
* stadium_boot_init()/session_boot_init()/sk_vm_switch_signal_boot_init().
|
||||
* Idempotent-unsafe: calling twice leaks the first allocation, so callers
|
||||
* must call it exactly once. */
|
||||
int sk_hermes_channels_boot_init(void);
|
||||
|
||||
/* sk_hermes_channel_create - allocate a new, empty, inert channel.
|
||||
* Returns its table index, or -1 if the table is full or
|
||||
* sk_hermes_channels_boot_init() was never called/failed. */
|
||||
int sk_hermes_channel_create(void);
|
||||
|
||||
/* sk_hermes_channel_destroy - tear down a channel created by
|
||||
* sk_hermes_channel_create(). Refuses (-1, no effect) if channel_id is
|
||||
* SK_HERMES_CHANNEL_COMMON (the common channel is permanent, SXLV.1),
|
||||
* out of range, or not in use. Clears membership. */
|
||||
int sk_hermes_channel_destroy(int channel_id);
|
||||
|
||||
/* sk_hermes_channel_subscribe - add vm_id to channel_id's membership.
|
||||
* Refuses (-1, no effect) if channel_id is invalid/not in use, vm_id is
|
||||
* already a member, or the channel's membership is already at
|
||||
* SK_HERMES_MEMBER_MAX. Idempotent in effect (a second call with the
|
||||
* same args refuses rather than duplicating), not in return value. */
|
||||
int sk_hermes_channel_subscribe(int channel_id, VMUuid vm_id);
|
||||
|
||||
/* sk_hermes_channel_unsubscribe - remove vm_id from channel_id's
|
||||
* membership (compacts the list, same shape as
|
||||
* sk_vm_switch_signal_unregister()). Refuses (-1, no effect) if
|
||||
* channel_id is invalid/not in use or vm_id is not a member. */
|
||||
int sk_hermes_channel_unsubscribe(int channel_id, VMUuid vm_id);
|
||||
|
||||
/* sk_hermes_channel_is_member - nonzero iff vm_id is currently a member
|
||||
* of channel_id. Zero (not an error signal) if channel_id is invalid/not
|
||||
* in use. */
|
||||
int sk_hermes_channel_is_member(int channel_id, VMUuid vm_id);
|
||||
|
||||
/* sk_hermes_channel_member_count - current membership size of channel_id,
|
||||
* or -1 if channel_id is invalid/not in use. */
|
||||
int sk_hermes_channel_member_count(int channel_id);
|
||||
|
||||
/* sk_hermes_channel_capacity - the table's boot-time-computed capacity
|
||||
* (0 if sk_hermes_channels_boot_init() was never called or failed) --
|
||||
* DoE/test observability, mirrors sk_vm_switch_signal_slot_capacity(). */
|
||||
int sk_hermes_channel_capacity(void);
|
||||
|
||||
#endif /* __STARKERNEL__ */
|
||||
|
||||
#endif /* STARKERNEL_VM_KERNEL_HERMES_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,7 @@
|
||||
#include "starkernel/session.h" /* session_register()/session_set_pinned() -- FABRIC-2.md §H.12 step 5 */
|
||||
#include "starkernel/timer.h" /* timer_calibration_record()->vm_mode -- FABRIC-2.md §I.5 CONTRIB trust tier */
|
||||
#include "starkernel/capsule_vm_switch_signal.h" /* FABRIC-3.md §XXVIII Stage 4 -- capsule_vm_force_reap() */
|
||||
#include "starkernel/vm/kernel_hermes.h" /* FABRIC-3.6.md task 3.2 -- sk_hermes_channel_subscribe() */
|
||||
#include "word_source/include/framebuffer_words.h" /* FABRIC-3.6.md task 1.8 -- register_framebuffer_words() */
|
||||
#include "vm.h"
|
||||
#include "platform_alloc.h"
|
||||
@@ -857,6 +858,16 @@ CapsuleRunResult capsule_birth_baby(
|
||||
entry->state = VM_STATE_LIVE;
|
||||
entry->birth_dict_hash = dict_hash;
|
||||
|
||||
/* FABRIC-3.6.md task 3.2 (B1 / FABRIC-3.5.md §XLV.1): every VM joins
|
||||
* the common channel at birth. This is the single choke point every
|
||||
* non-Hera birth passes through (BIRTH, WIREBIND identities, DoE
|
||||
* workload births alike) -- Hera herself is subscribed explicitly in
|
||||
* kernel_main.c, the one birth path that never reaches here. Soft
|
||||
* failure, same posture as every other call in this function -- a
|
||||
* refused subscription (table exhausted) is logged nowhere special
|
||||
* and does not fail the birth itself. */
|
||||
(void)sk_hermes_channel_subscribe(SK_HERMES_CHANNEL_COMMON, vm_id);
|
||||
|
||||
capsule_parity_log_birth(vm_id, cap->capsule_id, cap->content_hash, dict_hash);
|
||||
|
||||
/* §H.12 step 11: applied AFTER dict_hash/parity logging above, not
|
||||
|
||||
@@ -535,12 +535,29 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
* (capacity 0) rather than treating this as fatal. */
|
||||
(void)sk_vm_switch_signal_boot_init();
|
||||
|
||||
/* Kernel-Hermes channel table: boot-time allocation (FABRIC-3.6.md
|
||||
* task 3.2, B1 / FABRIC-3.5.md §XLV.1), sized from
|
||||
* stadium_max_vm_count() -- same ordering requirement and soft-failure
|
||||
* posture as the allocations immediately above. Creates the permanent
|
||||
* common channel (empty); every VM joins it at birth (Hera explicitly
|
||||
* below, every baby VM via capsule_birth_baby()'s own task 3.2
|
||||
* wiring). */
|
||||
(void)sk_hermes_channels_boot_init();
|
||||
|
||||
/* item 4.1, FABRIC-0.md item 3.6/§17.7: actually enforce "Hera is patron
|
||||
* zero" before anything else can land on cell 0 via the free list, then
|
||||
* bring up the word layer's map. Both must happen before the first word
|
||||
* ever dispatches -- capsule birth below runs init.4th, which dispatches
|
||||
* words. */
|
||||
(void)stadium_birth_hera();
|
||||
|
||||
/* Task 3.2: Hera is the one VM never born through capsule_birth_baby()
|
||||
* (she is Mama, registered directly in capsule_vm_registry_init() and
|
||||
* granted her quota by stadium_birth_hera() just above) -- so her
|
||||
* common-channel subscription is explicit here rather than reached
|
||||
* through the shared baby-birth hook below. */
|
||||
(void)sk_hermes_channel_subscribe(SK_HERMES_CHANNEL_COMMON, vm_uuid_hera());
|
||||
|
||||
stadium_words_init();
|
||||
stadium_blocks_init(); /* FABRIC-2.md §B: block-patron layer, same ordering as words */
|
||||
|
||||
@@ -1277,6 +1294,63 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
}
|
||||
}
|
||||
|
||||
/* FABRIC-3.6.md task 3.2 (B1) self-test: confirm every live fleet
|
||||
* member is a common-channel member (the birth-time subscription just
|
||||
* exercised for real, above -- Hera explicitly, Hermes/Hestia/Artemis
|
||||
* through capsule_birth_baby()'s own task 3.2 hook), then create and
|
||||
* destroy a synthetic private topic against a synthetic VM id (lo=5,
|
||||
* distinct from every other synthetic id this file already uses --
|
||||
* lo=1 Stadium quota grant, lo=3 kernel-Hermes alloc/release). Diagnostic
|
||||
* only, same posture as every other self-test block in this function:
|
||||
* never used for anything else, never perturbs the real fleet. */
|
||||
{
|
||||
VMRegistryEntry hera_ck, hermes_ck, hestia_ck, artemis_ck;
|
||||
int fleet_ok = 1;
|
||||
|
||||
if (capsule_vm_find_by_name_nocase("Hera", &hera_ck) != 0 ||
|
||||
!sk_hermes_channel_is_member(SK_HERMES_CHANNEL_COMMON, hera_ck.vm_id)) fleet_ok = 0;
|
||||
if (capsule_vm_find_by_name_nocase("Hermes", &hermes_ck) != 0 ||
|
||||
hermes_ck.state != VM_STATE_LIVE ||
|
||||
!sk_hermes_channel_is_member(SK_HERMES_CHANNEL_COMMON, hermes_ck.vm_id)) fleet_ok = 0;
|
||||
if (capsule_vm_find_by_name_nocase("Hestia", &hestia_ck) != 0 ||
|
||||
hestia_ck.state != VM_STATE_LIVE ||
|
||||
!sk_hermes_channel_is_member(SK_HERMES_CHANNEL_COMMON, hestia_ck.vm_id)) fleet_ok = 0;
|
||||
if (capsule_vm_find_by_name_nocase("Artemis", &artemis_ck) != 0 ||
|
||||
artemis_ck.state != VM_STATE_LIVE ||
|
||||
!sk_hermes_channel_is_member(SK_HERMES_CHANNEL_COMMON, artemis_ck.vm_id)) fleet_ok = 0;
|
||||
|
||||
console_puts("Kernel-Hermes common-channel fleet self-test: ");
|
||||
console_println(fleet_ok ? "PASS" : "FAIL");
|
||||
print_uint(" common channel members=", (uint64_t)sk_hermes_channel_member_count(SK_HERMES_CHANNEL_COMMON));
|
||||
print_uint(" channel table capacity=", (uint64_t)sk_hermes_channel_capacity());
|
||||
|
||||
{
|
||||
VMUuid topic_test_id;
|
||||
int ch;
|
||||
int topic_ok = 1;
|
||||
|
||||
topic_test_id.hi = 0;
|
||||
topic_test_id.lo = 5;
|
||||
|
||||
ch = sk_hermes_channel_create();
|
||||
if (ch < 0) topic_ok = 0;
|
||||
if (topic_ok && sk_hermes_channel_is_member(ch, topic_test_id)) topic_ok = 0;
|
||||
if (topic_ok && sk_hermes_channel_subscribe(ch, topic_test_id) != 0) topic_ok = 0;
|
||||
if (topic_ok && !sk_hermes_channel_is_member(ch, topic_test_id)) topic_ok = 0;
|
||||
if (topic_ok && sk_hermes_channel_member_count(ch) != 1) topic_ok = 0;
|
||||
if (topic_ok && sk_hermes_channel_unsubscribe(ch, topic_test_id) != 0) topic_ok = 0;
|
||||
if (topic_ok && sk_hermes_channel_is_member(ch, topic_test_id)) topic_ok = 0;
|
||||
if (topic_ok && sk_hermes_channel_destroy(ch) != 0) topic_ok = 0;
|
||||
/* Destroyed channel must refuse every further op against it. */
|
||||
if (topic_ok && sk_hermes_channel_subscribe(ch, topic_test_id) == 0) topic_ok = 0;
|
||||
/* The common channel must never be destroyable. */
|
||||
if (topic_ok && sk_hermes_channel_destroy(SK_HERMES_CHANNEL_COMMON) == 0) topic_ok = 0;
|
||||
|
||||
console_puts("Kernel-Hermes synthetic private-topic self-test: ");
|
||||
console_println(topic_ok ? "PASS" : "FAIL");
|
||||
}
|
||||
}
|
||||
|
||||
/* Decided 2026-09-05: no console for the running system unless a
|
||||
* thumbdrive is present -- headless by default (EMERGENCY_CONSOLE_
|
||||
* ENABLED off), reusing that flag's own existing "does this build
|
||||
|
||||
@@ -48,8 +48,27 @@
|
||||
#include "starkernel/vm/kernel_hermes.h"
|
||||
#include "starkernel/vm/stadium.h"
|
||||
#include "starkernel/q48_16.h"
|
||||
#include "starkernel/kmalloc.h"
|
||||
#include "console.h"
|
||||
|
||||
/* Freestanding: no libc printf. Prints an unsigned decimal, no leading
|
||||
* zeros -- same small helper stadium.c/capsule_vm_switch_signal.c each
|
||||
* carry their own copy of. */
|
||||
static void console_put_u64(uint64_t v) {
|
||||
char buf[21];
|
||||
int i = 20;
|
||||
buf[20] = '\0';
|
||||
if (v == 0) {
|
||||
console_puts("0");
|
||||
return;
|
||||
}
|
||||
while (v > 0 && i > 0) {
|
||||
buf[--i] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
console_puts(&buf[i]);
|
||||
}
|
||||
|
||||
static SkHermesMessage sk_hermes_msgs[SK_HERMES_MSG_MAX];
|
||||
|
||||
/* Task 2.4 (item 28), FABRIC-3.5.md SXXXVII.3/SXL.4's four counters.
|
||||
@@ -247,4 +266,130 @@ int sk_hermes_decay(SkHermesMessage *msg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Task 3.2 (item 27, B1 / FABRIC-3.5.md SXLV.1): the channel table. See
|
||||
* kernel_hermes.h's own doc comment on this section for scope and sizing
|
||||
* reasoning. */
|
||||
static SkHermesChannel *sk_hermes_channels = (SkHermesChannel *)0;
|
||||
static int sk_hermes_channel_capacity_val = 0;
|
||||
|
||||
int sk_hermes_channels_boot_init(void) {
|
||||
size_t max_vm_count = stadium_max_vm_count();
|
||||
SkHermesChannel *table;
|
||||
size_t i;
|
||||
|
||||
if (max_vm_count == 0) return -1; /* Stadium not yet initialized */
|
||||
|
||||
table = (SkHermesChannel *)kmalloc(max_vm_count * sizeof(SkHermesChannel));
|
||||
if (!table) return -1;
|
||||
|
||||
for (i = 0; i < max_vm_count; i++) {
|
||||
memset(&table[i], 0, sizeof(SkHermesChannel));
|
||||
}
|
||||
|
||||
/* The common channel exists from boot (SXLV.1), empty -- VMs join it
|
||||
* at birth (kernel_main.c/capsule_birth.c task 3.2 wiring), not here. */
|
||||
table[SK_HERMES_CHANNEL_COMMON].in_use = 1;
|
||||
|
||||
sk_hermes_channels = table;
|
||||
sk_hermes_channel_capacity_val = (int)max_vm_count;
|
||||
|
||||
console_puts("Kernel-Hermes: ");
|
||||
console_put_u64((uint64_t)max_vm_count);
|
||||
console_println(" channel slots");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sk_hermes_channel_capacity(void) {
|
||||
return sk_hermes_channel_capacity_val;
|
||||
}
|
||||
|
||||
static int channel_valid(int channel_id) {
|
||||
return sk_hermes_channels &&
|
||||
channel_id >= 0 &&
|
||||
channel_id < sk_hermes_channel_capacity_val &&
|
||||
sk_hermes_channels[channel_id].in_use;
|
||||
}
|
||||
|
||||
int sk_hermes_channel_create(void) {
|
||||
int i;
|
||||
|
||||
if (!sk_hermes_channels) return -1;
|
||||
|
||||
/* Index 0 is always in_use (the common channel), so this scan never
|
||||
* returns it -- start at 1 purely as a scan-cost micro-optimisation,
|
||||
* not for correctness (the in_use check alone would already skip it). */
|
||||
for (i = 1; i < sk_hermes_channel_capacity_val; i++) {
|
||||
if (!sk_hermes_channels[i].in_use) {
|
||||
memset(&sk_hermes_channels[i], 0, sizeof(SkHermesChannel));
|
||||
sk_hermes_channels[i].in_use = 1;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sk_hermes_channel_destroy(int channel_id) {
|
||||
if (channel_id == SK_HERMES_CHANNEL_COMMON) return -1; /* permanent, SXLV.1 */
|
||||
if (!channel_valid(channel_id)) return -1;
|
||||
|
||||
memset(&sk_hermes_channels[channel_id], 0, sizeof(SkHermesChannel));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sk_hermes_channel_subscribe(int channel_id, VMUuid vm_id) {
|
||||
SkHermesMembership *m;
|
||||
size_t i;
|
||||
|
||||
if (!channel_valid(channel_id)) return -1;
|
||||
|
||||
m = &sk_hermes_channels[channel_id].membership;
|
||||
for (i = 0; i < m->count; i++) {
|
||||
if (vm_uuid_equal(m->members[i], vm_id)) return -1; /* already a member */
|
||||
}
|
||||
if (m->count >= SK_HERMES_MEMBER_MAX) return -1;
|
||||
|
||||
m->members[m->count] = vm_id;
|
||||
m->count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sk_hermes_channel_unsubscribe(int channel_id, VMUuid vm_id) {
|
||||
SkHermesMembership *m;
|
||||
size_t i;
|
||||
|
||||
if (!channel_valid(channel_id)) return -1;
|
||||
|
||||
m = &sk_hermes_channels[channel_id].membership;
|
||||
for (i = 0; i < m->count; i++) {
|
||||
if (vm_uuid_equal(m->members[i], vm_id)) {
|
||||
size_t j;
|
||||
for (j = i; j < m->count - 1; j++) {
|
||||
m->members[j] = m->members[j + 1];
|
||||
}
|
||||
m->count--;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1; /* not a member */
|
||||
}
|
||||
|
||||
int sk_hermes_channel_is_member(int channel_id, VMUuid vm_id) {
|
||||
SkHermesMembership *m;
|
||||
size_t i;
|
||||
|
||||
if (!channel_valid(channel_id)) return 0;
|
||||
|
||||
m = &sk_hermes_channels[channel_id].membership;
|
||||
for (i = 0; i < m->count; i++) {
|
||||
if (vm_uuid_equal(m->members[i], vm_id)) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sk_hermes_channel_member_count(int channel_id) {
|
||||
if (!channel_valid(channel_id)) return -1;
|
||||
return (int)sk_hermes_channels[channel_id].membership.count;
|
||||
}
|
||||
|
||||
#endif /* __STARKERNEL__ */
|
||||
|
||||
Reference in New Issue
Block a user