// 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: // psSubscribe(topic, fn) -> id register fn to receive messages published on topic // psUnsubscribe(id) -> nil drop the subscription with that id // psPublish(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 (psSubscribe, psUnsubscribe, psPublish) 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