calog/libs/calogPubsub.h
2026-07-03 02:13:23 -05:00

41 lines
2.3 KiB
C

// calogPubsub.h -- calog publish/subscribe library: deliver a message to every subscriber
// of a topic, across contexts and engines.
//
// Registers natives so a script can broadcast to any number of listeners by topic name:
// subscribe(topic, fn) -> id register fn to receive messages published on topic
// unsubscribe(id) -> nil drop the subscription with that id
// publish(topic, msg) -> count deliver a copy of msg to every subscriber of topic,
// returning how many subscribers were invoked
//
// A subscriber is an ordinary calog function value, so each delivery runs in the
// subscriber's OWN context/thread (marshalled like any callable) and receives its own deep
// copy of the message -- publishing binary-safe data or nested aggregates is fine. Topics are
// matched by exact bytes (binary-safe), so a topic may itself contain embedded NULs.
//
// Delivery is SYNCHRONOUS: publish does not return until every matching subscriber has run.
// Because a subscriber runs on its owner context's thread, a subscriber that (directly or
// transitively) publishes back onto a topic it is itself subscribed to can DEADLOCK -- the
// publishing thread blocks in the callback, which cannot complete until the first publish
// returns. Keep publish graphs acyclic. A publish after a subscriber's owner context is gone
// simply skips that subscriber and does not count it.
//
// The registry is process-wide and reference-counted across runtimes.
#ifndef CALOG_PUBSUB_H
#define CALOG_PUBSUB_H
#include "calog.h"
// Register the pubsub natives (subscribe, unsubscribe, publish) on a runtime. Idempotent
// across runtimes (shared registry).
int32_t calogPubsubRegister(CalogT *calog);
// Release every subscribed function (once the last registered runtime unregisters). Like the
// export library, call this while the subscribing contexts are still ALIVE -- before you
// close them and before calogDestroy -- because a subscription is a live reference into its
// owner's interpreter; releasing it after that context is gone would touch freed memory. The
// static registry bookkeeping itself is intentionally never freed (the natives stay callable
// until calogDestroy), so this is safe to call and re-register across runtimes.
void calogPubsubShutdown(void);
#endif