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

Allow to check if sync::Once is already initialized #53027

Merged
merged 2 commits into from Sep 5, 2018

Conversation

@matklad
Member

matklad commented Aug 3, 2018

Hi!

I propose to expose a way to check if a Once instance is initialized.

I need it in once_cell. OnceCell is effetively a pair of (Once, UnsafeCell<Option<T>>), which can set the T only once. Because I can't check if Once is initialized, I am forced to add an indirection and check the value of ptr instead:

https://github.com/matklad/once_cell/blob/8127a81976c3f2f4c0860562c3f14647ebc025c0/src/lib.rs#L423-L429

https://github.com/matklad/once_cell/blob/8127a81976c3f2f4c0860562c3f14647ebc025c0/src/lib.rs#L457-L461

The parking_lot's version of Once exposes the state as an enum: https://docs.rs/parking_lot/0.6.3/parking_lot/struct.Once.html#method.state.

I suggest, for now, just to add a simple bool function: this fits my use-case perfectly, exposes less implementation details, and is forward-compatible with more fine-grained state checking.

@rust-highfive

This comment has been minimized.

Show comment
Hide comment
@rust-highfive

rust-highfive Aug 3, 2018

Collaborator

r? @shepmaster

(rust_highfive has picked a reviewer for you, use r? to override)

Collaborator

rust-highfive commented Aug 3, 2018

r? @shepmaster

(rust_highfive has picked a reviewer for you, use r? to override)

/// assert!(handle.join().is_err());
/// assert_eq!(INIT.is_completed(), false);
/// ```
#[unstable(feature = "once_is_completed", issue = "42")]

This comment has been minimized.

@mzji

mzji Aug 3, 2018

Isn't this issue number wrong? Issue #42 seems totally irrelevant to this code.

@mzji

mzji Aug 3, 2018

Isn't this issue number wrong? Issue #42 seems totally irrelevant to this code.

This comment has been minimized.

@matklad

matklad Aug 3, 2018

Member

It's a placeholder, there's no tracking issue for this yet

@matklad

matklad Aug 3, 2018

Member

It's a placeholder, there's no tracking issue for this yet

This comment has been minimized.

@mzji

mzji Aug 6, 2018

Well, I just found that 42 is the answer to everything, however I still hope it could be replaced by the relevant issue ^v^

@mzji

mzji Aug 6, 2018

Well, I just found that 42 is the answer to everything, however I still hope it could be replaced by the relevant issue ^v^

This comment has been minimized.

@matklad

matklad Aug 6, 2018

Member

As soon as @rust-lang/libs decides that we indeed want this feature and creates a tracking issue, I'll update the PR.

@matklad

matklad Aug 6, 2018

Member

As soon as @rust-lang/libs decides that we indeed want this feature and creates a tracking issue, I'll update the PR.

This comment has been minimized.

@mzji

mzji Aug 6, 2018

Thanks for the explanation!

@mzji

mzji Aug 6, 2018

Thanks for the explanation!

@matklad

This comment has been minimized.

Show comment
Hide comment
@matklad

matklad Aug 4, 2018

Member

Here's a somewhat better comparison which explains why .is_completed would be neat.

parking lot std
Member

matklad commented Aug 4, 2018

Here's a somewhat better comparison which explains why .is_completed would be neat.

parking lot std

@kennytm kennytm added the T-libs label Aug 5, 2018

@shepmaster

This feels like a highly abusable / misusable feature from the outside looking in. With such a function, I can see people wrapping their usages of Once with an extra guard "for the fast path". I don't know if that's a real concern or even if it would be an antipattern, but maybe you could chime in on that a bit?

///
/// static INIT: Once = Once::new();
///
/// assert_eq!(INIT.is_completed(), false);

This comment has been minimized.

@shepmaster

shepmaster Aug 6, 2018

Member

Think these would normally be written as assert!(INIT.is_completed()) / assert!(!INIT.is_completed())

@shepmaster

shepmaster Aug 6, 2018

Member

Think these would normally be written as assert!(INIT.is_completed()) / assert!(!INIT.is_completed())

This comment has been minimized.

@matklad

matklad Aug 6, 2018

Member

I think for boolean-returning methods writing an assert_eq! in docs specifically helps a bit with readability. Here's an example from result:

https://doc.rust-lang.org/std/result/enum.Result.html#method.is_ok

@matklad

matklad Aug 6, 2018

Member

I think for boolean-returning methods writing an assert_eq! in docs specifically helps a bit with readability. Here's an example from result:

https://doc.rust-lang.org/std/result/enum.Result.html#method.is_ok

Show outdated Hide outdated src/libstd/sync/once.rs
@shepmaster

This comment has been minimized.

Show comment
Hide comment
@shepmaster

shepmaster Aug 6, 2018

Member

Passing on to someone smarter...

r? @Kimundi

Member

shepmaster commented Aug 6, 2018

Passing on to someone smarter...

r? @Kimundi

@rust-highfive rust-highfive assigned Kimundi and unassigned shepmaster Aug 6, 2018

@matklad

This comment has been minimized.

Show comment
Hide comment
@matklad

matklad Aug 6, 2018

Member

With such a function, I can see people wrapping their usages of Once with an extra guard "for the fast path". I don't know if that's a real concern or even if it would be an antipattern, but maybe you could chime in on that a bit?

Interesting point! It is indeed would be an anti pattern! However, I believe there are cases where you really need to check if once is initialized, without actually initializing it.

For example, a Once may guard initialization of some resource, and some part of code might want to assume that the resources is initialized, without the ability to initialized the resource itself. For example, you might want to have a global logger, initialized in main from command-line arguments. To be able to use the logger in the rest of the program safely, you'll need to get an Option<Logger> somehow, and then unwrap it. And to do be able to differentiate between Some and None, you'll need either is_completed, or to maintain your own atomic state variable, which is error-prone.

Member

matklad commented Aug 6, 2018

With such a function, I can see people wrapping their usages of Once with an extra guard "for the fast path". I don't know if that's a real concern or even if it would be an antipattern, but maybe you could chime in on that a bit?

Interesting point! It is indeed would be an anti pattern! However, I believe there are cases where you really need to check if once is initialized, without actually initializing it.

For example, a Once may guard initialization of some resource, and some part of code might want to assume that the resources is initialized, without the ability to initialized the resource itself. For example, you might want to have a global logger, initialized in main from command-line arguments. To be able to use the logger in the rest of the program safely, you'll need to get an Option<Logger> somehow, and then unwrap it. And to do be able to differentiate between Some and None, you'll need either is_completed, or to maintain your own atomic state variable, which is error-prone.

@Kimundi

This comment has been minimized.

Show comment
Hide comment
@Kimundi

Kimundi Aug 8, 2018

Member

I've also got feature requests for such functionality in the past in lazy_static, so it seems like a useful addition.

@rfcbot fcp merge

Member

Kimundi commented Aug 8, 2018

I've also got feature requests for such functionality in the past in lazy_static, so it seems like a useful addition.

@rfcbot fcp merge

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Aug 8, 2018

Team member @Kimundi has proposed to merge this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

rfcbot commented Aug 8, 2018

Team member @Kimundi has proposed to merge this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@SimonSapin

This comment has been minimized.

Show comment
Hide comment
@SimonSapin

SimonSapin Aug 8, 2018

Contributor

@rfcbot document the race condition

I think there is a potential race condition:

  • is_completed does its atomic load, and returns false
  • Then another thread completes initialization
  • Then the first thread acts on the boolean return value, which is now outdated.

This race is probably fine in some cases. I think it is in the use case described by #53027 (comment). But still, this should be mentioned in the doc-comment to try and make callers aware of it.

Contributor

SimonSapin commented Aug 8, 2018

@rfcbot document the race condition

I think there is a potential race condition:

  • is_completed does its atomic load, and returns false
  • Then another thread completes initialization
  • Then the first thread acts on the boolean return value, which is now outdated.

This race is probably fine in some cases. I think it is in the use case described by #53027 (comment). But still, this should be mentioned in the doc-comment to try and make callers aware of it.

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Aug 9, 2018

🔔 This is now entering its final comment period, as per the review above. 🔔

rfcbot commented Aug 9, 2018

🔔 This is now entering its final comment period, as per the review above. 🔔

@matklad

This comment has been minimized.

Show comment
Hide comment
@matklad

matklad Aug 9, 2018

Member

But still, this should be mentioned in the doc-comment to try and make callers aware of it.

Yup, added this case to the doc. Ideally, these all should be formulated in terms of "happens before", but I can't find a short way to express that.

As a non-native speaker, I am also not sure whether it should be is_completed or is_complete.

Member

matklad commented Aug 9, 2018

But still, this should be mentioned in the doc-comment to try and make callers aware of it.

Yup, added this case to the doc. Ideally, these all should be formulated in terms of "happens before", but I can't find a short way to express that.

As a non-native speaker, I am also not sure whether it should be is_completed or is_complete.

@sfackler

This comment has been minimized.

Show comment
Hide comment
@sfackler

sfackler Aug 9, 2018

Member

has_completed?

Member

sfackler commented Aug 9, 2018

has_completed?

@RalfJung

This comment has been minimized.

Show comment
Hide comment
@RalfJung

RalfJung Aug 15, 2018

Member

Yup, added this case to the doc. Ideally, these all should be formulated in terms of "happens before", but I can't find a short way to express that.

In this case, the intuition I usually try to convey is that the return value is outdated. So people should think of is_completed as saying "at some point in the past (when I was checking), the Once was [not] initialized". That should hopefully make it clear that it might have been initialized since then.

Maybe it should be called was_completed? ;)

Member

RalfJung commented Aug 15, 2018

Yup, added this case to the doc. Ideally, these all should be formulated in terms of "happens before", but I can't find a short way to express that.

In this case, the intuition I usually try to convey is that the return value is outdated. So people should think of is_completed as saying "at some point in the past (when I was checking), the Once was [not] initialized". That should hopefully make it clear that it might have been initialized since then.

Maybe it should be called was_completed? ;)

@SimonSapin

This comment has been minimized.

Show comment
Hide comment
@SimonSapin

SimonSapin Aug 16, 2018

Contributor

Oops I messed up the rcfbot command to formally register my concern. But that’s ok, since my concern is resolved now. Thanks!

Contributor

SimonSapin commented Aug 16, 2018

Oops I messed up the rcfbot command to formally register my concern. But that’s ok, since my concern is resolved now. Thanks!

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Aug 19, 2018

The final comment period, with a disposition to merge, as per the review above, is now complete.

rfcbot commented Aug 19, 2018

The final comment period, with a disposition to merge, as per the review above, is now complete.

@pietroalbini

This comment has been minimized.

Show comment
Hide comment
@pietroalbini

pietroalbini Aug 27, 2018

Member

Ping from triage @Kimundi! The FCP ended.

Member

pietroalbini commented Aug 27, 2018

Ping from triage @Kimundi! The FCP ended.

@TimNN

This comment has been minimized.

Show comment
Hide comment
@TimNN

TimNN Sep 4, 2018

Contributor

Ping from triage @Kimundi / @rust-lang/libs: This PR requires your review.

Contributor

TimNN commented Sep 4, 2018

Ping from triage @Kimundi / @rust-lang/libs: This PR requires your review.

@alexcrichton

This comment has been minimized.

Show comment
Hide comment
@alexcrichton

alexcrichton Sep 4, 2018

Member

@bors: r+

I'm gonna go ahead and r+ this to merge but we can of course continue to bikeshed the name while it's unstable!

Member

alexcrichton commented Sep 4, 2018

@bors: r+

I'm gonna go ahead and r+ this to merge but we can of course continue to bikeshed the name while it's unstable!

@bors

This comment has been minimized.

Show comment
Hide comment
@bors

bors Sep 4, 2018

Contributor

📌 Commit e1bd0e7 has been approved by alexcrichton

Contributor

bors commented Sep 4, 2018

📌 Commit e1bd0e7 has been approved by alexcrichton

@bors

This comment has been minimized.

Show comment
Hide comment
@bors

bors Sep 5, 2018

Contributor

⌛️ Testing commit e1bd0e7 with merge f68b7cc...

Contributor

bors commented Sep 5, 2018

⌛️ Testing commit e1bd0e7 with merge f68b7cc...

bors added a commit that referenced this pull request Sep 5, 2018

Auto merge of #53027 - matklad:once_is_completed, r=alexcrichton
Allow to check if sync::Once is already initialized

Hi!

I propose to expose a way to check if a `Once` instance is initialized.

I need it in `once_cell`. `OnceCell` is effetively a pair of `(Once, UnsafeCell<Option<T>>)`, which can set the `T` only once. Because I can't check if `Once` is initialized, I am forced to add an indirection and check the value of ptr instead:

https://github.com/matklad/once_cell/blob/8127a81976c3f2f4c0860562c3f14647ebc025c0/src/lib.rs#L423-L429

https://github.com/matklad/once_cell/blob/8127a81976c3f2f4c0860562c3f14647ebc025c0/src/lib.rs#L457-L461

The `parking_lot`'s version of `Once` exposes the state as an enum: https://docs.rs/parking_lot/0.6.3/parking_lot/struct.Once.html#method.state.

I suggest, for now, just to add a simple `bool` function: this fits my use-case perfectly, exposes less implementation details, and is forward-compatible with more fine-grained state checking.
@bors

This comment has been minimized.

Show comment
Hide comment
@bors

bors Sep 5, 2018

Contributor

☀️ Test successful - status-appveyor, status-travis
Approved by: alexcrichton
Pushing f68b7cc to master...

Contributor

bors commented Sep 5, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: alexcrichton
Pushing f68b7cc to master...

@bors bors merged commit e1bd0e7 into rust-lang:master Sep 5, 2018

2 checks passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
homu Test successful
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment