pub trait Listener<M>: Debug {
// Required method
fn receive(&self, messages: &[M]) -> bool;
// Provided methods
fn filter<MI, F>(self, function: F) -> Filter<F, Self, 1>
where Self: Sized,
F: for<'a> Fn(&'a MI) -> Option<M> { ... }
fn gate(self) -> (Gate, GateListener<Self>)
where Self: Sized { ... }
}Expand description
A receiver of messages (typically from something implementing Listen) which can
indicate when it is no longer interested in them (typically because the associated
recipient has been dropped).
Listeners are typically used in trait object form, which may be created via the
IntoListener trait in addition to the usual coercions;
this is done automatically by Listen, but calling .into_listener() earlier may be useful
to minimize the number of separately allocated clones of the listener when the same listener is
to be registered with multiple message sources.
If you are implementing Listener, note the requirements set out in Listener::receive().
Instead of writing new implementations, consider the following alternatives when you need a
listener:
- Use an existing implementation such as
FlagListenerfromFlag. - Implement
Storeinstead, and useStoreLockinstead.StoreLockprovides the weak reference and mutex that are needed in the most common kind of use ofListener. - Implement
StoreReffor an interior mutable data structure, then wrap it inWeakto make it a listener. (This works via via the built-in implementationimpl<T: StoreRef<M>, M> Listener<M> for Weak<T>).
§Generic parameters
Mis the type of message that can be received.
Required Methods§
Sourcefn receive(&self, messages: &[M]) -> bool
fn receive(&self, messages: &[M]) -> bool
Process and store the given series of messages.
Returns true if the listener is still interested in further messages (“alive”),
and false if it should be dropped because these and all future messages would have
no observable effect.
A call of the form .receive(&[]) may be performed to query aliveness without
delivering any messages.
§Requirements on implementors
-
Messages are provided in a batch for efficiency of dispatch. Each message in the provided slice should be processed exactly the same as if it were the only message provided. If the slice is empty, there should be no observable effect.
-
Do not panic under any possible incoming message stream, in order to ensure the sender’s other work is not interfered with. For example, if the listener accesses a poisoned mutex, it should do nothing or clear the poison, rather than panicking.
-
Do not acquire any locks except ones which are used only for the state of the listener itself.
-
Do not perform any blocking operation except for such locks.
-
Do not access thread-local state, since this may be called from whichever thread(s) the sender is using.
§Advice for implementors
Note that, since this method takes &Self, a Listener must use interior
mutability of some variety to store the message. As a Listener may be called
from various contexts, and in particular while the sender is still performing
its work, that mutability should in general be limited to setting dirty flags
or inserting into message queues — not attempting to directly perform further
game state changes, and particularly not taking any locks that are not solely
used by the Listener and its destination, as that could result in deadlock.
The typical pattern is for a listener to contain a Weak<Mutex<...>> or similar
multiply-owned mutable structure to aggregate incoming messages, which will
then be read and cleared by a later task; see StoreLock for assistance in
implementing this pattern.
Note that a Notifier might call .receive(&[]) at any time, particularly when
listeners are added. Be careful not to cause a deadlock in this case; it may be
necessary to avoid locking in the case where there are no messages to be delivered.
Provided Methods§
Sourcefn filter<MI, F>(self, function: F) -> Filter<F, Self, 1>
fn filter<MI, F>(self, function: F) -> Filter<F, Self, 1>
Wraps self so to apply a map/filter function (similar to Iterator::filter_map())
to incoming messages, to discard uninteresting messages and transform interesting ones.
Note: By default, this filter breaks up all message batching into batches of 1.
In order to avoid this and have more efficient message delivery, use
Filter::with_stack_buffer().
This is unnecessary if size_of::<M>() == 0; the buffer is automatically unbounded in
that case.
§Example
use nosy::{unsync::Notifier, Flag, Listen as _, Listener as _};
let notifier = Notifier::new();
let flag = Flag::new(false);
notifier.listen(flag.listener().filter(|&msg: &i32| {
if msg >= 0 {
Some(())
} else {
None
}
}));
// This message is filtered out.
notifier.notify(&-1);
assert_eq!(flag.get_and_clear(), false);
// This message is passed through:
notifier.notify(&2);
assert_eq!(flag.get_and_clear(), true);Sourcefn gate(self) -> (Gate, GateListener<Self>)where
Self: Sized,
fn gate(self) -> (Gate, GateListener<Self>)where
Self: Sized,
Wraps self to pass messages only until the returned Gate, and any clones
of it, are dropped.
This may be used to stop forwarding messages when a dependency no longer exists.
use nosy::{Listen as _, Listener as _};
let log = nosy::Log::new();
let (gate, gated) = log.listener().gate();
gated.receive(&["kept1"]);
assert_eq!(log.drain(), vec!["kept1"]);
gated.receive(&["kept2"]);
drop(gate);
gated.receive(&["discarded"]);
assert_eq!(log.drain(), vec!["kept2"]);Implementations on Foreign Types§
Source§impl<M, L1, L2> Listener<M> for (L1, L2)
Tuples of listeners may be used to distribute messages to multiple listeners with static
dispatch.
impl<M, L1, L2> Listener<M> for (L1, L2)
Tuples of listeners may be used to distribute messages to multiple listeners with static dispatch.