-
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
. Maintain that new M{..}
should be avoided. This requires the use of temporaries or std::move
.
// Temporary for MsgPtr&
MsgPtr<M> msg = makeMessage<M>{..};
theMsg()->sendMsg(dest, /* MsgPtr<M>& */ msg);
// std::move for lvalue-to-MsgPtr&&
theMsg()->sendMsg(dest, std::move(makeMessage<M>{..}));
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}
.