Skip to content
This repository has been archived by the owner on Nov 5, 2018. It is now read-only.

Implement default_handle() #28

Closed
wants to merge 1 commit into from
Closed

Implement default_handle() #28

wants to merge 1 commit into from

Conversation

jeehoonkang
Copy link
Contributor

Closes #25

default_handle() is unsafe because when it is called in another TLS object's drop(), HANDLE may have already been dropped.

@joshlf
Copy link

joshlf commented Oct 16, 2017

Ah, good point. Maybe we should make it default_handle() -> Option<Handle> so it can be safe? That basically converts a crash condition into a None, and code that knows it won't be called from TLS can safely just .unwrap().

/// It should not be called in another TLS storage's `drop()`, because `HANDLE` may already be
/// droped.
pub unsafe fn default_handle() -> &'static Handle {
&*HANDLE.with(|handle| handle as *const _)
Copy link
Member

Choose a reason for hiding this comment

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

The documentation for with is a bit ambiguous, but looking at the source it should always panic if the key has been destroyed. Since we return a reference, it can't escape its scope and so HANDLE won't be destroyed while it exists. Hence, this seems safe (but I could be wrong of course).

Copy link

Choose a reason for hiding this comment

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

It's unsafe because this function allows you to obtain a &'static Handle reference and then use it even after HANDLE gets destroyed, without any additional checks.

Copy link

Choose a reason for hiding this comment

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

@Vtec234 Destruction in the context of TLS is somewhat different than destruction in the rest of Rust unfortunately. When a thread exits, all of the registered TLS variables get destroyed - it doesn't have to do with scope. The problem arises when the Drop implementation for a TLS variable accesses another TLS variable which has already been dropped. @ezrosent and I have run into this issue, for example, when our own allocator caches (stored in TLS) were dropped after Crossbeam's TLS was dropped, but we relied on Crossbeam in order to implement Drop.

Copy link
Member

@Vtec234 Vtec234 Oct 16, 2017

Choose a reason for hiding this comment

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

In that case another option would be to forward with and expose something like with_default_handle<F: FnOnce(&Handle) -> R>(f: F) -> R.

Copy link

@joshlf joshlf Oct 16, 2017

Choose a reason for hiding this comment

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

That's true, but that might lead to callback spaghetti. It's just as easy to do default_handle() -> Option<&Handle>, and somewhat more ergonomic.

Copy link

Choose a reason for hiding this comment

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

And what's the issue with lifetimes when returning Option<&Handle>? Or is the lifetime problem only an issue when returning Option<Handle> (ie, not a reference)?

Copy link
Member

@Vtec234 Vtec234 Oct 16, 2017

Choose a reason for hiding this comment

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

@joshlf: Without a lifetime-binding argument (like in with) the lifetime of &Handle will have to be 'static, and that can in turn be leaked until after the TLS drops since Rust allows passing 'static references around freely.

Copy link

Choose a reason for hiding this comment

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

Ah I see. Fair enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A problem with fn default_handle() -> Option<&Handle> is we cannot implement it in Rust stable. I think we need to use HANDLE.try_with(), which is provided only in Rust nightly.

Copy link

Choose a reason for hiding this comment

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

Ah good point. Would you guys be cool with just having it be feature-gated on nightly?

@ghost
Copy link

ghost commented Oct 16, 2017

What's the benefit of having a default_handle() function over just making HANDLE public?

thread_local! { pub static HANDLE: Handle = COLLECTOR.handle(); }

@joshlf
Copy link

joshlf commented Oct 16, 2017

@stjepang IMO, exposing HANDLE directly is brittle and makes for a more complicated interface. Users shouldn't have to go read the TLS documentation (which is pretty complicated) just to get a reference to the local handle. Exposing it is also brittle in the sense that it would preclude future changes to the internal implementation.

@jeehoonkang
Copy link
Contributor Author

Closing (in favor of #31)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants