Skip to content

Conversation

@aryan9600
Copy link

Add support for non Send+Sync closures for thread parking and unparking callbacks when using a LocalRuntime. Since a LocalRuntime will always run its tasks on the same thread, its safe to accept a non Send+Sync closure.

Motivation

Since LocalRuntime can run non Send+Sync functions, we should accept thread parking/unparking callbacks that do not implement Send and Sync.

Solution

Add two methods on LocalOptions, on_thread_park and on_thread_unpark that accept a Fn() + 'static. These callbacks are then converted into Fn() + Send + Sync + 'static. This requires unsafe code, but unsafe is acceptable here because of the behaviour of LocalRuntime

fixes #7370

@aryan9600 aryan9600 force-pushed the non-send-thread-park branch from be90205 to eeb45d3 Compare June 25, 2025 11:16
@aryan9600 aryan9600 force-pushed the non-send-thread-park branch 2 times, most recently from c124e08 to 4f8b562 Compare June 26, 2025 05:34
@mox692 mox692 added A-tokio Area: The main tokio crate M-runtime Module: tokio/runtime labels Jun 27, 2025
@ADD-SP ADD-SP added the C-enhancement Category: A PR with an enhancement or bugfix. label Jun 29, 2025
@ADD-SP ADD-SP added the R-loom-current-thread Run loom current-thread tests on this PR label Jun 29, 2025
@aryan9600 aryan9600 force-pushed the non-send-thread-park branch from 4f8b562 to fde0d15 Compare August 25, 2025 16:52
@github-actions github-actions bot removed the R-loom-current-thread Run loom current-thread tests on this PR label Aug 25, 2025
@aryan9600 aryan9600 force-pushed the non-send-thread-park branch from fde0d15 to 453dbd2 Compare August 25, 2025 17:26
@aryan9600 aryan9600 requested a review from ADD-SP August 25, 2025 17:28
@ADD-SP ADD-SP added the S-waiting-on-author Status: awaiting some action (such as code changes) from the PR or issue author. label Oct 15, 2025
Add support for non `Send`+`Sync` closures for thread parking and unparking
callbacks when using a `LocalRuntime`. Since a `LocalRuntime` will always
run its tasks on the same thread, its safe to accept a non `Send`+`Sync`
closure.

Signed-off-by: Sanskar Jaiswal <[email protected]>
@aryan9600 aryan9600 force-pushed the non-send-thread-park branch from 453dbd2 to 980ac53 Compare November 12, 2025 13:05
@ADD-SP ADD-SP changed the title rt: add support for non-send closures for thread (un)parking rt: add thread (un)parking hooks for LocalRuntime Nov 12, 2025
Comment on lines 20 to 21
/// Marker used to make this !Send and !Sync.
_phantom: PhantomData<*mut u8>,
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 unusual to place PhantomData as the first field — please move it to the end.

Comment on lines +1533 to +1534
self.before_park = opts.before_park;
self.after_unpark = opts.after_unpark;
Copy link
Member

Choose a reason for hiding this comment

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

Is adding the support for the following hooks also safe? If so, we should also add them.

  • on_after_task_poll
  • on_before_task_poll
  • on_task_spawn
  • on_task_terminate

/// [`Builder`]: crate::runtime::Builder
#[derive(Default)]
#[non_exhaustive]
#[allow(missing_debug_implementations)]
Copy link
Member

Choose a reason for hiding this comment

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

/// To run before the local runtime is parked.
pub(crate) before_park: Option<Callback>,

/// To run before the local runtime is spawned.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// To run before the local runtime is spawned.
/// To run after the local runtime is unparked.

});

// assert that the callbacks were not called - `Handle::block_on` can not drive IO or timer
// drivers on a current-thread runtime, so the park/unpark callbacks should not be invoked.
Copy link
Member

Choose a reason for hiding this comment

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

The comment above is not clear to me.
Where is the difference with the code for test_on_thread_park_unpark_in_runtime() ?
Until line https://github.com/tokio-rs/tokio/pull/7420/files#diff-3c0fef031dc5f261b671919a47345cbbc60841e2ffcd3287df279290b7297933R79 they are exactly the same. They differ in the bodies of block_on().


let tid = std::thread::current().id();

self.before_park = opts.before_park;
Copy link
Member

Choose a reason for hiding this comment

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

IMO this should be done only if opts.on_xyz_abc is Some.

Use case:

let opts = LocalOptions::default();
// no on_before_park/on_after_unpark for opts !

Builder::new_current_thread()
    .enable_time()
    .on_before_park(...)  // 1
    .build_local(opts)    // 2
    .unwrap();

Currently the user application sets on_before_park callback at 1) and then 2) will silently wipe them

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-tokio Area: The main tokio crate C-enhancement Category: A PR with an enhancement or bugfix. M-runtime Module: tokio/runtime S-waiting-on-author Status: awaiting some action (such as code changes) from the PR or issue author.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Non-Send version of callbacks such as on_thread_park

5 participants