Error initialzing u64 property

I have a RefCell variable STATE inside thread_local! macro which has the type State when I try to deploy and request access to STATE it gives me an error "State Not Initialized". As soon as I commented min_delay it worked fine. I want to know why u64 types cannot be initialized inside RefCell.

Thanks!


#[derive(Serialize, Deserialize)]
pub struct State {
    #[serde(skip, default = "init_stable_states")]
    pub stable_state: StableStates,
    pub heap_state: HeapStates,
}

#[derive(Serialize, Deserialize)]
pub struct HeapStates {
    pub ledger_ids: BTreeMap<Asset, Principal>,
    // pub min_delay: u64,
}

Can you show the code that's printing the error?

This isn't a thing. If a certain type didn't work inside RefCell, it would be a compile error.

1 Like

The code builds but does not initialize the state, and when trying to access it give an error

To access I used a custom-defined macros function


   const __STATE_ALREADY_INITIALIZED: &str = "State has already been initialized";
        const __STATE_NOT_INITIALIZED: &str = "State has not been initialized";

 fn read_state<F, R>(f: F) -> R
        where
            F: FnOnce(&$type) -> R,
        {
            __STATE.with_borrow(|s| f(s.as_ref().expect(__STATE_NOT_INITIALIZED)))
        }

The custom macro works fine in other builds, but gives an error in current crate

I don't know the reason why after removing min_delay it works fine as other builds/crates

Smells like a race condition. Are you using threads and unsafe to access the RefCell (which is not thread-safe)?

I am not using unsafe

This looks like the type is not State but rather Option<State>. Thread-locals already have initialization logic so you shouldn't need the Option.

1 Like

Great, then it's at least probably not UB. Please show us some more context: how and where are you exactly calling this code?

I have another crates with same initialize type and that works fine, in this crate I introduced another property min_delay in the HeapStates struct it produces an error

custom macro

#[macro_export]
macro_rules! crate_state {
    ($type:ty) => {
        thread_local! {
            static __STATE: std::cell::RefCell<Option<$type>> = std::cell::RefCell::default();
        }

        const __STATE_ALREADY_INITIALIZED: &str = "State has already been initialized";
        const __STATE_NOT_INITIALIZED: &str = "State has not been initialized";

        fn init_state(state: $type) {
            __STATE.with_borrow_mut(|s| {
                if s.is_some() {
                    panic!("{}", __STATE_ALREADY_INITIALIZED);
                } else {
                    *s = Some(state);
                }
            });
        }

        fn replace_state(state: $type) -> $type {
            __STATE.replace(Some(state)).expect(__STATE_NOT_INITIALIZED)
        }

        fn take_state() -> $type {
            __STATE.take().expect(__STATE_NOT_INITIALIZED)
        }

        fn read_state<F, R>(f: F) -> R
        where
            F: FnOnce(&$type) -> R,
        {
            __STATE.with_borrow(|s| f(s.as_ref().expect(__STATE_NOT_INITIALIZED)))
        }

        fn mutate_state<F, R>(f: F) -> R
        where
            F: FnOnce(&mut $type) -> R,
        {
            __STATE.with_borrow_mut(|s| f(s.as_mut().expect(__STATE_NOT_INITIALIZED)))
        }

        fn can_borrow_state() -> bool {
            __STATE.with(|s| s.try_borrow().is_ok())
        }
    };
}
///lib .rs

crate_state!(State);

/// method 

fn init(args: InitArgs) {
    println!("Init Start ");

    init_state(State::default());

    mutate_state(|s| {
        let res: &mut StableStates = &mut s.stable_state;
        let _ = res.init.set(args);
    });

   println!("Init End ");
}

I follow the same pattern for other crates and those are working fine, but this crate gives me an error because of the introduction of the new property min_delay inside HeapStates.

It works fine. Rust Playground

2 Likes

To be 100% clear, I know with certainty that adding u64 to that struct isn't the issue.

It is possible to make compilation fail by adding a field, since auto traits are automatically inherited, but u64 implements all the auto traits, and you described your error as a runtime error, not a compilation error.

It is also uncommon but possible to cause a runtime error by introducing a field, since it changes the result of needs_drop[1], size_of, and derive macros, but these would only happen if code elsewhere is incorrectly written or being misused.

If you have anything time-aware in your code, then that could be the issue, but time-related bugs are rare in single-threaded context.

My best guess is that you made the call to init_state dependent on deserialization succeeding, aren't handling deserialization errors, and have an unconditional call to mutate_state.


  1. not for u64 though ↩ī¸Ž

1 Like

Thanks for the explanation. Now I understand the error, In State::Default() I define the Default config function for min_delay, rather than passing constant value I passed the set_min_delay() function which ultimately accesses the HeapState which is not yet initialized

1 Like

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.