I have Fn
's that need to be called from other Fn
closures.
I'm currently using RwLock
's in such places, but this code causes a deadlock here:
when running this test:
cargo test --test flatten -- --nocapture --test it_flattens_a_two_layer_finite_pullable_sources
This is because there are read locks (RwLockReadGuard
's) that are still held due to nested function calls.
Is there a way to atomically get a shared reference to a Fn
closure, without holding onto a read lock to it?
alice
November 23, 2021, 3:28pm
2
You must hold an RwLockReadGuard
for the full duration of your use of the value, but why do you have an Fn
in an RwLock
in the first place?
1 Like
I guess that's kinda my question. What is the right thing to do here, given the above requirements?
once_cell::sync::OnceCell
looks promising, but it has the limitation of only allowing a single assignment.
alice
November 23, 2021, 3:34pm
4
So it sounds like the answer to my question is that you want to be able to change the function?
1 Like
In certain cases, yes, it's necessary to be able to change which function is being called.
As an example in the above test case, the flatten
operator would pull from the outer source once the current inner source has terminated:
sink(Message::Terminate);
return;
}
{
let mut inner_talkback =
inner_talkback.write().unwrap();
*inner_talkback = None;
}
let outer_talkback = outer_talkback.read().unwrap();
let outer_talkback = outer_talkback.as_ref().unwrap();
outer_talkback(Message::Pull);
}
})
.into(),
));
}
Message::Pull => {
panic!("source must not pull");
}
Message::Error(error) => {
if let Some(inner_talkback) = &*inner_talkback.read().unwrap() {
which would later lead to a new inner_talkback
being assigned when the outer source emits a new data item (which is actually the new inner source):
Sequence diagram for this test case (view without word wrapping):
alice
November 23, 2021, 7:18pm
7
You could perhaps use the arc-swap crate instead of an rwlock
1 Like
Thank you! ArcSwapOption
works great for my use case.
I'm not sure it'll help in my case, but I've also come across GhostCell
which seems very interesting:
https://plv.mpi-sws.org/rustbelt/ghostcell/
As a follow-up question:
What can I use in cases where I need a mutable reference to the held value? For example, to call next
on an Iterator
:
let res_done = res_done.clone();
move || {
in_loop.store(true, AtomicOrdering::Release);
while got_pull.load(AtomicOrdering::Acquire)
&& !completed.load(AtomicOrdering::Acquire)
{
got_pull.store(false, AtomicOrdering::Release);
{
let iter = &mut *iter.write().unwrap();
let mut res = res.write().unwrap();
*res = iter.next();
res_done.store(res.is_none(), AtomicOrdering::Release);
}
if res_done.load(AtomicOrdering::Acquire) {
sink(Message::Terminate);
break;
} else {
let res = {
let res = &mut *res.write().unwrap();
res.take().unwrap()
};
Or is that not possible without locking?
alice
November 24, 2021, 3:33pm
12
Different answers to those questions result in different solutions.
1 Like
Right... I tried a combination of ArcSwapAny::into_inner
/ ArcSwapAny::swap
+ Arc::try_unwrap
but it caused a panic. Probably I'm running into one of those scenarios as you've described...
But yeah, at this point I've no idea what's the right way to handle them, so I think I'm sticking to RwLock
for those.
system
Closed
February 22, 2022, 3:39pm
14
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.