Repository: leptos-rs/leptos
File: reactive_graph/src/computed/inner.rs (Rust)

Code before:
            // Two locks are aquired, so order matters.
            let mut reactivity_lock = self.reactivity.write().or_poisoned();
            let mut value_lock = self.value.write().or_poisoned();
            *value_lock = Some(S::wrap(new_value));
            reactivity_lock.state = ReactiveNodeState::Clean;

            if changed {
                let subs = reactivity_lock.subscribers.clone();
                drop(value_lock);
                drop(reactivity_lock);
                for sub in subs {
                    // don't trigger reruns of effects/memos
                    // basically: if one of the observers has triggered this memo to
                    // run, it doesn't need to be re-triggered because of this change
                    if !Observer::is(&sub) {
                        sub.mark_dirty();
                    }
                }
            } else {
                drop(value_lock);
                drop(reactivity_lock);
            }

            changed
        } else {
            let mut lock = self.reactivity.try_write().unwrap(); // TODO: Make less agressive
            lock.state = ReactiveNodeState::Clean;
            false
        }
    }
}

impl<T: 'static, S> Source for MemoInner<T, S>
where
    S: Storage<T>,
{
    fn add_subscriber(&self, subscriber: AnySubscriber) {
        let mut lock = self.reactivity.try_write().unwrap(); // TODO: Make less agressive
        lock.subscribers.subscribe(subscriber);
    }

    fn remove_subscriber(&self, subscriber: &AnySubscriber) {
        self.reactivity
            .write()
            .or_poisoned()
            .subscribers
            .unsubscribe(subscriber);
    }

    fn clear_subscribers(&self) {
        self.reactivity.write().or_poisoned().subscribers.take();

Review comment (bug):
This and 162 switch from failing silently to panicking, with a TODO comment. Did you have an alternate idea for what can be done here? I am fine with a `.expect()` with a useful message, here, describing the circumstances in which this would panic.

Code after:
            // Two locks are aquired, so order matters.
            let mut reactivity_lock = self.reactivity.write().or_poisoned();
            {
                // Safety: Can block endlessly if the user is has a ReadGuard on the value
                let mut value_lock = self.value.write().or_poisoned();
                *value_lock = Some(S::wrap(new_value));
            }
            reactivity_lock.state = ReactiveNodeState::Clean;

            if changed {
                let subs = reactivity_lock.subscribers.clone();
                drop(reactivity_lock);
                for sub in subs {
                    // don't trigger reruns of effects/memos
                    // basically: if one of the observers has triggered this memo to
                    // run, it doesn't need to be re-triggered because of this change
                    if !Observer::is(&sub) {
                        sub.mark_dirty();
                    }
                }
            } else {
                drop(reactivity_lock);
            }

            changed
        } else {
            let mut lock = self.reactivity.write().or_poisoned();
            lock.state = ReactiveNodeState::Clean;
            false
        }
    }
}

impl<T: 'static, S> Source for MemoInner<T, S>
where
    S: Storage<T>,
{
    fn add_subscriber(&self, subscriber: AnySubscriber) {
        let mut lock = self.reactivity.write().or_poisoned();
        lock.subscribers.subscribe(subscriber);
    }

    fn remove_subscriber(&self, subscriber: &AnySubscriber) {
        self.reactivity
            .write()
            .or_poisoned()
            .subscribers
            .unsubscribe(subscriber);
    }

    fn clear_subscribers(&self) {