feat: BREAKING force all zig actors to declare a destructor

Actors may be destroyed without ever calling their receivers. This is
regular behavior when an actor is killed by an exit message. C++ actors
cleanup automatically via their destructors. Up to now zig actor had to
enable trapping and cleanup on the exit message. This was a big foot gun
and cumbersome.

Now all zig actors are required to pass a deinit function to
thespian.receive. This simplifies clean up and prevents the foot gun
entirely.
This commit is contained in:
CJ van den Berg 2026-03-04 19:32:11 +01:00
parent b16a47efae
commit 5a729b6d06
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
3 changed files with 34 additions and 19 deletions

View file

@ -7,11 +7,19 @@ using std::string_view;
extern "C" {
void thespian_receive(thespian_receiver r, thespian_behaviour_state s) {
thespian::receive([r, s](auto from, cbor::buffer msg) -> thespian::result {
void thespian_receive(thespian_receiver r, thespian_behaviour_state s,
thespian_receiver_dtor dtor) {
struct receiver_wrapper {
thespian_receiver r;
thespian_behaviour_state s;
thespian_receiver_dtor dtor;
~receiver_wrapper() { dtor(s); }
};
auto wrapper = std::make_shared<receiver_wrapper>(r, s, dtor);
thespian::receive([wrapper](auto from, cbor::buffer msg) -> thespian::result {
thespian_handle from_handle = reinterpret_cast<thespian_handle>( // NOLINT
&from);
auto *ret = r(s, from_handle, {msg.data(), msg.size()});
auto *ret = wrapper->r(wrapper->s, from_handle, {msg.data(), msg.size()});
if (ret) {
auto err = cbor::buffer();
const uint8_t *data = ret->base;