-
Notifications
You must be signed in to change notification settings - Fork 9
Message Ownership
Messages in VT are allocated off-stack and have internally managed reference counts. As such, they must be handled correctly to avoid memory leaks. The MsgPtr
(in 1.0 this is named MsgSharedPtr
) type acts a a shared-ref wrapper to ensure proper lifetime management.
Note: The method of message creation is currently under some updates. The original method uses makeSharedMessage
which returns a MsgT*
. VT takes ownership of the messages these raw pointers represent in send
and broadcast
calls. A message is created with makeSharedMessage
MUST either be guaranteed to be passed to VT for transmission or wrapped inside a MsgPtr
(via promoteMsg
in 1.0).
Good (1.0):
M* msg = makeSharedMessage<M>(..);
theMsg->sendMsg(dest, msg);
theMsg->sendMsg(dest, makeSharedMessage<M>(..));
MsgPtr<M> msg = makeMessage<M>(..);
theMsg->sendMsg(dest, msg.get()) // MsgPtr<M>.get() -> M*
theMsg->sendMsg(dest, makeMessage<M>(..).get())
Bad (1.0):
// Messages must be created via appropriate methods.
M* msg = new M{..};
// Message is leaked when it is not supplied to sendMsg.
// Only create messages as they are immediately going to be used.
M* msg = makeSharedMessage<M>(..);
if (maybe_send) {
theMsg->sendMsg(dest, msg);
}
1.1 Proposal A: Allow direct new
calls, with the same caveats as makeSharedMessage
and discourage use of makeSharedMessage
.
// Inline usages of message creation is encouraged for send/broadcast calls.
theMsg()->sendMsg(dest, new M{..});
// Still prone to memory leaks if lifetime ownership is not established.
M* msg = new M{..};
if (maybe_send) {
theMsg()->sendMsg(dest, msg);
}
1.1 Proposal B: Accept MsgPtr&
/MsgPtr&&
in send/broadcast and discourage the use of makeSharedMessage
. Usages of new M{..}
are considered coding errors. This requires the use of temporaries or std::move
.
// Temporary for MsgPtr&
MsgPtr<M> msg = makeMessage<M>{..};
theMsg()->sendMsg(dest, msg); // MsgPtr& param
// std::move for lvalue-to-MsgPtr&&
theMsg()->sendMsg(dest, std::move(makeMessage<M>{..})); // MsgPtr&& param
1.1 Propose B-Extended: Messages should be created with MsgPtr<T>{..}
, which has a templated perfect-forwarding overloaded. This allows usage as an xref directly. Usages of makeSharedMessage
and makeMessage
are discouraged. Usages of new M{..}
are considered coding errors. Usages of a temporary (and MsgPtr&
parameters) are not affected.
theMsg()->sendMsg(dest, /* MsgPtr&& */ MsgPtr<M>{..}); // MsgPtr&& param (xref)
auto msg = MsgPtr<M>{..};
theMsg()->sendMsg(dest, /* MsgPtr& */ msg); // MsgPtr& param
This has a negative effect of possible confusing error messages. It has a positive side-effect of being incompatible with lambda-copy captures (a separate internal type can be exposed for this case).
Messages in VT can be sent at most once. It is an error if the same message is sent multiple times.
Messages received from send/broadcast callbacks are considered sent and cannot be directly sent again. A new message must be constructed.
Invalid code:
T* msg = makeSharedMessage<T>(..);
theMsg()->sendMsg(dest, msg);
// Invalid: message has already been sent
theMsg()->sendMsg(dest, msg);
T* msg = makeSharedMessage<T>(..);
theMsg()->sendMsg(dest, msg, [](T* cb_message) {
// Invalid: messages from callbacks cannot be re-sent.
theMsg()->sendMsg(dest, cb_message);
});
1.1 Proposal: Active message supports a Copy Constructor that resets the transmission state of the new message such that the following is valid:
T* msg = makeSharedMessage<T>(..);
theMsg()->sendMsg(dest, msg, [](T* cb_message) {
// Valid: a copy of the message without transmission state is created and sent.
theMsg()->sendMsg(dest, makeSharedMessage(*cb_message));
});
The new message has a new lifetime as well, subject to all other lifetime rules. In degenerate cases this might result in excessive data copying.
Callbacks are given a M*
value once a message is received. The lifetime of message is only for the duration of the callback. If the message needs to extend beyond the callback lifetime, wrap it inside a MsgPtr
. For 1.0 this is done using promoteMsg<M>(rawMsgPtr)
which returns a MsgPtr<M>
.
1.1 Proposal: Create a MsgPtr with MsgPtr<M>{rawMsgPtr}
.