Skip to content

Document ordering bwetween future drop and JoinHandle completion#7825

Merged
Darksonn merged 1 commit intotokio-rs:masterfrom
stepancheg:completion-ordering
Jan 3, 2026
Merged

Document ordering bwetween future drop and JoinHandle completion#7825
Darksonn merged 1 commit intotokio-rs:masterfrom
stepancheg:completion-ordering

Conversation

@stepancheg
Copy link
Copy Markdown
Contributor

Motivation

Consider this code

struct MyFuture;

impl Drop for MyFuture {
  fn drop(&mut self) { println!("A"); }
}

impl Future for MyFuture { ... }


let join_handle = tokio::spawn(MyFuture);
let _ = join_handle.await; // Can be completion or abort
println!("B");

Is it guaranteed that B is always printed after A?

Use case can be this:

let task = spawn({ some work });
task.await;
let task2 = spawn({ some work on the same data });

It would be good to know that destructor of data in the first future (e.g. sending some final message from the destructor) does not intersect with the second task.

After reading the code of tokio, I came to conclusion, it is guaranteed.

Solution

Let's make it formal, so users can rely on this behavior.

@Darksonn Darksonn added T-docs Topic: documentation A-tokio Area: The main tokio crate M-task Module: tokio/task labels Jan 3, 2026
@Darksonn
Copy link
Copy Markdown
Member

Darksonn commented Jan 3, 2026

Thanks for the PR!

@stepancheg stepancheg force-pushed the completion-ordering branch from 96d2aac to 12e1729 Compare January 3, 2026 01:01
@github-actions github-actions bot added R-loom-current-thread Run loom current-thread tests on this PR R-loom-multi-thread Run loom multi-thread tests on this PR labels Jan 3, 2026
@stepancheg
Copy link
Copy Markdown
Contributor Author

Updated the PR:

  • moved to JoinHandle
  • mentioned spawned function in addition to spawned future

Comment on lines +25 to +27
/// It is guaranteed that the spawned future's or function's destructor has finished
/// before the task completion is observed, for example,
/// via [`JoinHandle`] `await` or [`JoinHandle::is_finished`].
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A few wording nits:

  • You always spawn a future.
  • The mentioned methods are the only way to observe task completion.
Suggested change
/// It is guaranteed that the spawned future's or function's destructor has finished
/// before the task completion is observed, for example,
/// via [`JoinHandle`] `await` or [`JoinHandle::is_finished`].
/// It is guaranteed that the destructor of the future has finished
/// before the task completion is observed via [`JoinHandle`] `await` or
/// [`JoinHandle::is_finished`].

Copy link
Copy Markdown
Contributor Author

@stepancheg stepancheg Jan 3, 2026

Choose a reason for hiding this comment

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

You always spawn a future.

In spawn_blocking, the function is spawned, but the same JoinHandle.

The mentioned methods are the only way to observe task completion.

There's also AbortHandle::is_finished. Probably, I should mention it too.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Fair enough there is also spawn_blocking. Perhaps we just say "the destructor of the task".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added reference to AbortHandle::is_finished.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Replaced future or function with task.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

spawned task's destructor -> the destructor of the spawned task

@stepancheg stepancheg force-pushed the completion-ordering branch 3 times, most recently from b933452 to 845cfb8 Compare January 3, 2026 12:50
/// It is guaranteed that the destructor of the spawned task has finished
/// before the task completion is observed
/// via [`JoinHandle`] `await`, [`JoinHandle::is_finished`] or
/// [`AbortHandle::is_finished](crate::task::AbortHandle::is_finished).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If you import AbortHandle into this module, you don't need the explicit link.

Suggested change
/// [`AbortHandle::is_finished](crate::task::AbortHandle::is_finished).
/// [`AbortHandle::is_finished`](crate::task::AbortHandle::is_finished).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed backtick; if it is imported, unused code warning appears.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You can fix the warning by replacing super::AbortHandle with AbortHandle on the relevant method.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done!

@stepancheg stepancheg force-pushed the completion-ordering branch 3 times, most recently from f73cf4f to c20aa0e Compare January 3, 2026 13:04
@stepancheg
Copy link
Copy Markdown
Contributor Author

Removed redundant link to JoinHandle.

Copy link
Copy Markdown
Member

@Darksonn Darksonn left a comment

Choose a reason for hiding this comment

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

We can reflow the text a bit nicer. With this, LGTM.

Comment on lines +25 to +27
/// It is guaranteed that the destructor of the spawned task has finished
/// before the task completion is observed
/// via `JoinHandle` `await`, [`JoinHandle::is_finished`] or [`AbortHandle::is_finished`].
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
/// It is guaranteed that the destructor of the spawned task has finished
/// before the task completion is observed
/// via `JoinHandle` `await`, [`JoinHandle::is_finished`] or [`AbortHandle::is_finished`].
/// It is guaranteed that the destructor of the spawned task has finished
/// before task completion is observed via `JoinHandle` `await`,
/// [`JoinHandle::is_finished`] or [`AbortHandle::is_finished`].

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

@stepancheg stepancheg force-pushed the completion-ordering branch from c20aa0e to 87e4e30 Compare January 3, 2026 13:23
@stepancheg stepancheg force-pushed the completion-ordering branch from 87e4e30 to d9503e0 Compare January 3, 2026 13:23
@Darksonn Darksonn enabled auto-merge (squash) January 3, 2026 13:24
@Darksonn Darksonn merged commit 4d4870f into tokio-rs:master Jan 3, 2026
91 checks passed
jimsynz pushed a commit to jimsynz/neonfs that referenced this pull request Mar 6, 2026
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [tokio](https://tokio.rs) ([source](https://github.com/tokio-rs/tokio)) | dependencies | minor | `1.49.0` → `1.50.0` |

---

### Release Notes

<details>
<summary>tokio-rs/tokio (tokio)</summary>

### [`v1.50.0`](https://github.com/tokio-rs/tokio/releases/tag/tokio-1.50.0): Tokio v1.50.0

[Compare Source](tokio-rs/tokio@tokio-1.49.0...tokio-1.50.0)

### 1.50.0 (Mar 3rd, 2026)

##### Added

- net: add `TcpStream::set_zero_linger` ([#&#8203;7837])
- rt: add `is_rt_shutdown_err` ([#&#8203;7771])

##### Changed

- io: add optimizer hint that `memchr` returns in-bounds pointer ([#&#8203;7792])
- io: implement vectored writes for `write_buf` ([#&#8203;7871])
- runtime: panic when `event_interval` is set to 0 ([#&#8203;7838])
- runtime: shorten default thread name to fit in Linux limit ([#&#8203;7880])
- signal: remember the result of `SetConsoleCtrlHandler` ([#&#8203;7833])
- signal: specialize windows `Registry` ([#&#8203;7885])

##### Fixed

- io: always cleanup `AsyncFd` registration list on deregister ([#&#8203;7773])
- macros: remove (most) local `use` declarations in `tokio::select!` ([#&#8203;7929])
- net: fix `GET_BUF_SIZE` constant for `target_os = "android"` ([#&#8203;7889])
- runtime: avoid redundant unpark in current\_thread scheduler ([#&#8203;7834])
- runtime: don't park in `current_thread` if `before_park` defers waker ([#&#8203;7835])
- io: fix write readiness on ESP32 on short writes ([#&#8203;7872])
- runtime: wake deferred tasks before entering `block_in_place` ([#&#8203;7879])
- sync: drop rx waker when oneshot receiver is dropped ([#&#8203;7886])
- runtime: fix double increment of `num_idle_threads` on shutdown ([#&#8203;7910], [#&#8203;7918], [#&#8203;7922])

##### Unstable

- fs: check for io-uring opcode support ([#&#8203;7815])
- runtime: avoid lock acquisition after uring init ([#&#8203;7850])

##### Documented

- docs: update outdated unstable features section ([#&#8203;7839])
- io: clarify the behavior of `AsyncWriteExt::shutdown()` ([#&#8203;7908])
- io: explain how to flush stdout/stderr ([#&#8203;7904])
- io: fix incorrect and confusing `AsyncWrite` documentation ([#&#8203;7875])
- rt: clarify the documentation of `Runtime::spawn` ([#&#8203;7803])
- rt: fix missing quotation in docs ([#&#8203;7925])
- runtime: correct the default thread name in docs ([#&#8203;7896])
- runtime: fix `event_interval` doc ([#&#8203;7932])
- sync: clarify RwLock fairness documentation ([#&#8203;7919])
- sync: clarify that `recv` returns `None` once closed and no more messages ([#&#8203;7920])
- task: clarify when to use `spawn_blocking` vs dedicated threads ([#&#8203;7923])
- task: doc that task drops before `JoinHandle` completion ([#&#8203;7825])
- signal: guarantee that listeners never return `None` ([#&#8203;7869])
- task: fix task module feature flags in docs ([#&#8203;7891])
- task: fix two typos ([#&#8203;7913])
- task: improve the docs of `Builder::spawn_local` ([#&#8203;7828])
- time: add docs about auto-advance and when to use sleep ([#&#8203;7858])
- util: fix typo in docs ([#&#8203;7926])

[#&#8203;7771]: tokio-rs/tokio#7771

[#&#8203;7773]: tokio-rs/tokio#7773

[#&#8203;7792]: tokio-rs/tokio#7792

[#&#8203;7803]: tokio-rs/tokio#7803

[#&#8203;7815]: tokio-rs/tokio#7815

[#&#8203;7825]: tokio-rs/tokio#7825

[#&#8203;7828]: tokio-rs/tokio#7828

[#&#8203;7833]: tokio-rs/tokio#7833

[#&#8203;7834]: tokio-rs/tokio#7834

[#&#8203;7835]: tokio-rs/tokio#7835

[#&#8203;7837]: tokio-rs/tokio#7837

[#&#8203;7838]: tokio-rs/tokio#7838

[#&#8203;7839]: tokio-rs/tokio#7839

[#&#8203;7850]: tokio-rs/tokio#7850

[#&#8203;7858]: tokio-rs/tokio#7858

[#&#8203;7869]: tokio-rs/tokio#7869

[#&#8203;7871]: tokio-rs/tokio#7871

[#&#8203;7872]: tokio-rs/tokio#7872

[#&#8203;7875]: tokio-rs/tokio#7875

[#&#8203;7879]: tokio-rs/tokio#7879

[#&#8203;7880]: tokio-rs/tokio#7880

[#&#8203;7885]: tokio-rs/tokio#7885

[#&#8203;7886]: tokio-rs/tokio#7886

[#&#8203;7889]: tokio-rs/tokio#7889

[#&#8203;7891]: tokio-rs/tokio#7891

[#&#8203;7896]: tokio-rs/tokio#7896

[#&#8203;7904]: tokio-rs/tokio#7904

[#&#8203;7908]: tokio-rs/tokio#7908

[#&#8203;7910]: tokio-rs/tokio#7910

[#&#8203;7913]: tokio-rs/tokio#7913

[#&#8203;7918]: tokio-rs/tokio#7918

[#&#8203;7919]: tokio-rs/tokio#7919

[#&#8203;7920]: tokio-rs/tokio#7920

[#&#8203;7922]: tokio-rs/tokio#7922

[#&#8203;7923]: tokio-rs/tokio#7923

[#&#8203;7925]: tokio-rs/tokio#7925

[#&#8203;7926]: tokio-rs/tokio#7926

[#&#8203;7929]: tokio-rs/tokio#7929

[#&#8203;7932]: tokio-rs/tokio#7932

</details>

---

### Configuration

📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) in timezone Pacific/Auckland, Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) in timezone Pacific/Auckland.

🚦 **Automerge**: Disabled because a matching PR was automerged previously.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41Mi4xIiwidXBkYXRlZEluVmVyIjoiNDMuNTIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsicmVub3ZhdGUiXX0=-->

Reviewed-on: https://harton.dev/project-neon/neonfs/pulls/53
Co-authored-by: Renovate Bot <bot@harton.nz>
Co-committed-by: Renovate Bot <bot@harton.nz>
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 M-task Module: tokio/task R-loom-current-thread Run loom current-thread tests on this PR R-loom-multi-thread Run loom multi-thread tests on this PR T-docs Topic: documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants