Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change Global to use Once #752

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

change Global to use Once #752

wants to merge 1 commit into from

Conversation

bend-n
Copy link

@bend-n bend-n commented Jun 11, 2024

improves code quality

@lilizoey
Copy link
Member

lilizoey commented Jun 11, 2024

if you're gonna do this you should at least use the most recent master considering #750

but also you'll need to be certain that this doesn't hurt performance

i dont have time to look at this further rn tho

@bend-n bend-n force-pushed the wheels branch 2 times, most recently from 7871677 to 37df2af Compare June 11, 2024 05:08
@bend-n
Copy link
Author

bend-n commented Jun 11, 2024

I can run some benchmarks, i guess?

@bend-n
Copy link
Author

bend-n commented Jun 11, 2024

i ran a benchmark and the performance seems to be about the same.

extern crate test;
#[bench]
fn x(b: &mut test::Bencher) {
    b.iter(|| {
        let x = Global::<String>::default();
        for _ in 0..10 {
            *x.lock() += "304.78";
        }
    })
}

it could probably be a bit faster once get_mut_or_init drops.

@Bromeon Bromeon added quality-of-life No new functionality, but improves ergonomics/internals c: core Core components labels Jun 11, 2024
@Bromeon
Copy link
Member

Bromeon commented Jun 11, 2024

Thanks for the PR!

This uses Mutex<OnceLock<T>>, which requires two synchronizations instead of one. I honestly don't see how this is an improvement over the status quo, at best we get similar/same performance.

If you find concrete bugs with the current implementation, I'd prefer to address those directly and write regression tests for it, but I'm not really willing to introduce extra locking just so we can reuse standard facilities. Yes, I find it mindblowing too that something as basic as ergonomic global variables seems to be pioneering work in Rust 😉

@bend-n
Copy link
Author

bend-n commented Jun 11, 2024

wait i can swap it to OnceCell

@Bromeon
Copy link
Member

Bromeon commented Jun 11, 2024

Please provide a proper rationale for the change. Your initial message doesn't even have a description.

@bend-n
Copy link
Author

bend-n commented Jun 11, 2024

the rationale is to reduce the necessary maintenance surface, this makes the code much simpler.
its a refactor.
the old code was rather sketchy, this is much less so.

@Bromeon
Copy link
Member

Bromeon commented Jun 11, 2024

Some smaller disadvantages I see with your approach:

  • The function is still accessible after its use. It might be a bit harder if we e.g. wanted to use FnOnce in the future.
  • The type now occupies space for both the function and the value, while before this was in an enum. It's not a big deal, but it's a problem the current solution doesn't have.

Btw, regarding several of your comments, also on Discord:

can I fix Global

its kinda broken though
like it has a whole useless mutex thats just making api harder

Global: use the wheel, dont reinvent it

the old code was rather sketchy

I could appreciate your changes a lot more without the snarky comments. You may not notice it, but the way you word things comes across as pretty arrogant. I would recommend to familiarize yourself with the existing code and design behind it, before just assuming it's garbage.

Let's keep it technical from now on. Thanks.

@bend-n bend-n changed the title change Global to use OnceLock change Global to use Once Jun 11, 2024
@bend-n
Copy link
Author

bend-n commented Jun 14, 2024

i believe this change doesn't make FnOnce much harder to swap to? you can just put an Option around it (which keeps the type size the same).
The size of Global before and after remain the same (64).


My bad, will do.

godot-ffi/src/global.rs Outdated Show resolved Hide resolved
Comment on lines 29 to 31
pub struct Global<T, F = fn() -> T> {
// When needed, this could be changed to use RwLock and separate read/write guards.
value: Mutex<InitState<T>>,
value: Mutex<OnceCell<T>>,
init_fn: F,
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The F parameter is not necessary; new() in its current form can only be invoked with a function pointer. I'd keep this a pure refactoring without changing the public API, we can discuss new features separately if needed.

Comment on lines 64 to 67
pub fn lock(&self) -> GlobalGuard<'_, T> {
let mutex_guard = self
.value
.lock()
.expect("Global<T> poisoned; a thread has panicked while holding a lock to it");

let guard = Self::ensure_init(mutex_guard, true)
.unwrap_or_else(|| panic!("previous Global<T> initialization failed due to panic"));

guard
let guard = self.value.lock().unwrap_or_else(PoisonError::into_inner);
guard.get_or_init(self.init_fn);
// SAFETY: above line guarantees init
unsafe { GlobalGuard::new_unchecked(guard) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure the get_or_init() isn't more expensive than the previous Self::ensure_init()?

While it may not matter in average cases, it would be a pity to pessimize performance just to make our own life a bit easier.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick, please also leave an empty line before // comment blocks (here // SAFETY).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the get_or_init here boils down to

if self.get().is_some() { return };
let value = f();
let slot = unsafe { &mut *self.inner.get() };
slot.insert(value);

which seems like an improvement from the previous ensure_init fn.

and ive benchmarked it to be about the same speed.

godot-ffi/src/global.rs Outdated Show resolved Hide resolved
Comment on lines 86 to 88
std::panic::catch_unwind(|| guard.get_or_init(self.init_fn))
.map_err(|_| GlobalLockError::InitFailed)?;
Ok(unsafe { GlobalGuard::new_unchecked(guard) })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not obvious why this is called again here, could you elaborate?

Also, unsafe would need // SAFETY, can be kept short like above.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i hope ive clarified it now

Comment on lines 97 to 101
use std::{
cell::OnceCell,
ops::{Deref, DerefMut},
sync::MutexGuard,
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imports

Comment on lines +110 to 117
match mutex_guard.get() {
Some(_) => Some(Self { mutex_guard }),
_ => None,
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use map here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you try to avoid just saying "no" like this? it isnt very helpful most of the time and we need to ask follow up questions anyway to understand more clearly. why doesn't map work?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it results in an error because it moves the mutex guard into a FnOnce

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error[E0505]: cannot move out of `mutex_guard` because it is borrowed
   --> godot-ffi/src/global.rs:114:35
    |
113 |         pub(super) fn new(mutex_guard: MutexGuard<'a, OnceCell<T>>) -> Option<Self> {
    |                           ----------- binding `mutex_guard` declared here
114 |             mutex_guard.get().map(|_| Self { mutex_guard })
    |             -----------       --- ^^^        ----------- move occurs due to use in closure
    |             |                 |   |
    |             |                 |   move out of `mutex_guard` occurs here
    |             |                 borrow later used by call
    |             borrow of `mutex_guard` occurs here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can do this though if you prefer

mutex_guard.get().is_some().then_some(Self { mutex_guard })

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you see what i have right now is basically an eager map

godot-ffi/src/global.rs Outdated Show resolved Hide resolved
godot-ffi/src/global.rs Outdated Show resolved Hide resolved
@@ -283,6 +214,7 @@ mod tests {

#[test]
fn test_global_poison() {
std::panic::set_hook(Box::new(|_| ()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because cargo test was looking weird without it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how exactly? could you clarify a bit more?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CAN YOU PLEASE STOP RESOLVING CONVERSATIONS WHILE QUESTIONS ARE NOT YET ANSWERED?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

anyhow ive removed it but ill clarify a little more:
running cargo test -- --show-output was showing two panics which was being a little confusing

Copy link
Member

@Bromeon Bromeon Jun 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks. Could you add this explanation as a comment above set_hook?

Copy link
Author

@bend-n bend-n Jun 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well ive removed the set_hook so

@bend-n bend-n force-pushed the wheels branch 6 times, most recently from 8bf0b04 to be74d96 Compare June 27, 2024 04:22
@GodotRust
Copy link

API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-752

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: core Core components quality-of-life No new functionality, but improves ergonomics/internals
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants