Skip to content

Implement Thread::os_id - #160219

Merged
rust-bors[bot] merged 7 commits into
rust-lang:mainfrom
valentynkit:thread-os-id
Sep 9, 2026
Merged

Implement Thread::os_id#160219
rust-bors[bot] merged 7 commits into
rust-lang:mainfrom
valentynkit:thread-os-id

Conversation

@valentynkit

@valentynkit valentynkit commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

View all comments

Implements Thread::os_id as an unstable feature, per the accepted ACP rust-lang/libs-team#635.

Tracking issue: #160215

os_id returns the OS-level thread id, so Rust programs can tie their own logs to system-level logs (the ACP's stated motivation).

  • Using the existing current_os_id is much simpler than pulling the id off imp::Thread in spawn_unchecked. That needs a per-platform arm, and the child still has to fill it in on platforms without a by handle query, so it'd be extra on top of this rather than instead of it.
  • Thread is handed to user code by spawn hooks before the native thread exists, so the id can only be filled in later. There's no spare u64 value to mean "not set yet", so a OnceLock is chosen as a simple primitive to use for this purpose.

r? libs

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jul 30, 2026
Comment thread library/std/src/thread/thread.rs Outdated
use thread_name_string::ThreadNameString;

// The handle of a spawned thread exists before the thread does, so the thread
// stores its own id once it starts running, hence the atomic. 0 means "not known".

@joboet joboet Jul 30, 2026

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.

Nothing guarantees that the OS's ID is non-zero, we really shouldn't use zero as a sentinel.

View changes since the review

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.

Switched to OnceLock: write-once without a sentinel, and it needs no 64-bit atomic so the cfg_select is gone too.

Waiting would need the child to always store the value, so OnceLock<Option>.
I'm also not sure how it works out for spawn hooks, which get &Thread on the parent before the thread exists.
You mentioned you have an implementation, so if you've already worked that out I'd rather build on it than guess.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 30, 2026
@rustbot

rustbot commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@valentynkit

Copy link
Copy Markdown
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jul 30, 2026
@joboet

joboet commented Jul 30, 2026

Copy link
Copy Markdown
Member

A meta comment: I appreciate your recent contributions to the project, you are clearly interested in helping out and invest time and thought into it. At the same time, especially the description of this PR makes it very obvious that you are using a LLM to aid you in your contributions, both in writing code and comments. These kinds of cases have been very intensely discussed within the project, and the result of that discussion is our LLM policy that will come into effect next week. Under that policy your LLM usage is deemed forbidden, and failing to declare it (like you have been doing) may result in moderation actions against you.

I'm not a moderator, and this is not a moderation warning, just some friendly advice that I want to give you, stemming from years of working on the standard library:

In my experience, the hard part about working on the standard library is not actually writing the code, but in

  1. Doing the research and taking the thinking time to verify that the changes are truly correct. The standard library ships on literally billions of devices, we must try very hard to ensure that it works as intended.
  2. Communicating that work to other people so that not just you, but the person reviewing your work (in this case, me) and everyone else can follow along.

An LLM can do neither those things. While they are admittedly good at doing the mechanical task of writing the code, they are awfully unreliable when it comes to providing evidence, have little to no intrinsic capability to exercise good judgement and are fundamentally just not you.

As an example, the current description of this PR is mostly just a very detailed summary of its changes. I can see those myself, thank you very much, that's what the "Files changed" tab is for! The much more interesting questions in this case are e.g. why OnceLock is correct in this case (both you and me need to think this through in any case, the LLM cannot lift that responsibility off our shoulders), or how one might do better (here you'd need e.g. links to platform documentation – which is rather difficult for an LLM to find especially in the curious case of pthread_gettid_np), or why this PR is still a good point to start from (that's a matter of taste and opinion, I see why one might argue that it is). And if you've thought about all these questions, then writing these kinds of PRs entirely on your own is actually easier than proofreading the LLMs output and explaining all your thoughts to it.

Thus, please, remember to take time to think and research and be the author, not just the editor, of your communication and your code. If you want to learn how best to use (or not use) LLMs, feel free to join our LLM-mentoring channel on Zulip.

As for me, I'm not interested in some stochastic parrot's output, but in other people's, since that's what I learn and thrive from. That's why I invest my time in this project anyway, and am very happy to help other people out if they get stuck. I must insist however that the responsibility of doing the thinking and research doesn't fall on my shoulders alone.

@rustbot author
until you update the PR description with something entirely of your own.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 30, 2026
@valentynkit

Copy link
Copy Markdown
Contributor Author

In my experience, the hard part about working on the standard library is not actually writing the code, but in

1. Doing the research and taking the thinking time to verify that the changes are truly correct. The standard library ships on literally _billions_ of devices, we must try very hard to ensure that it works as intended.

2. Communicating that work to other people so that not just you, but the person reviewing your work (in this case, me) and everyone else can follow along.

Thanks for your advice, I totally agree with inappropriate use of LLM in this PR, I usually trying to take more time preparing the PR and understanding all the nuances, this one was bad and too heavily relied on AI.

Thanks and I will take it into consideration.

As for this PR, I will take some time to really reason about it and also consider other approaches.
Especially about using OnceLock or alternative approaches.

Sorry for that. :)

Comment thread library/std/src/thread/thread.rs Outdated
/// use std::thread;
///
/// let spawned = thread::spawn(|| thread::current().os_id());
/// println!("spawned thread ran as {:?}", spawned.join().unwrap());

@tgross35 tgross35 Jul 30, 2026

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.

Could this do the assert_ne! test here? I think that demos relevant properties a bit better.

View changes since the review

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.

Thanks, applied the changes but guarded it so it will not fail on other platforms that do not support os_id and could return None making it fail.

Comment thread library/std/src/thread/thread.rs Outdated
Comment on lines +229 to +233
/// The operating system may hand the same id to a later thread once this one
/// exits, so it does not name a thread uniquely over the life of the
/// process. It may also no longer refer to this thread at all, since any
/// thread but the current one can exit at any point. For anything other than
/// the current thread, logging is the only safe use.

@tgross35 tgross35 Jul 30, 2026

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.

It's usable for any thread that is running, not just the current right?

I think the property to convey is that OS TIDs uniquely represent a thread among other running threads, which effectively means that if a thread isn't known to be running then ID can only be used in cases where non-uniqueness is okay (e.g. logging). And then one way to know the thread is running is if you're looking at the current thread's ID.

Not sure how best to put this into words.

View changes since the review

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.

So there are conditions under which it is safe to use os_id and it refers to correct thread, however in other conditions it should be used only for cases when stale os_id reference is harmless, like logging.

I have tried to reword this section to better communicate this, thank. Let me know if you see any better way to put this into words.

Comment thread library/std/src/thread/thread.rs Outdated
///
/// This is the id that shows up in tools like `ps` and `top`, debuggers and
/// crash logs, unlike [`ThreadId`], which has no guaranteed relationship to
/// it. `None` means the platform has no such id or offers no way to read it,

@teor2345 teor2345 Jul 31, 2026

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.

Some sandboxes or containers allow per-function filtering, so it is also possible the thread ID exists with an API to read it, but the process can't do that.

Suggested change
/// it. `None` means the platform has no such id or offers no way to read it,
/// it. `None` means the platform has no such id or it can't be read,

View changes since the review

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.

Thanks, applied your suggestion to reword it slightly.

Comment thread library/std/src/thread/current.rs Outdated
Comment on lines +249 to +250
let thread = Thread::new(id::get_or_init(), None);
thread.set_os_id_to_current();

@Darksonn Darksonn Jul 31, 2026

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.

Why doesn't Thread::new just call set_os_id_to_current internally?

If there are callers where this doesn't work, maybe we should have two constructors?

  • Thread::new_current uses current OS id
  • Thread::new_remote takes OS id as paramter

I think this would also avoid the OnceLock.

View changes since the review

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.

I added Thread::new_current just for the current path, however I think we couldn't get rid of sync primitives for os_id because of the spawn_unchecked path, in that case when we create the Thread, we are still running on parent thread, so using set_os_id_to_current would assign parent os_id to child thread that it is trying to spawn.

  1. Thread::new lifecycle.rs:48 (imp::current_os_id() returns the parent's TID)
  2. imp::Thread::new lifecycle.rs:116

@valentynkit

Copy link
Copy Markdown
Contributor Author

@rustbot ready

I have addressed suggested refinements.

PR body was updated to better explain decisions for the implementation and further possible refinements, it is pretty long and detailed but I think those nuances worth explaining properly.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 2, 2026
@Darksonn

Darksonn commented Aug 5, 2026

Copy link
Copy Markdown
Member

Sorry I'm at capacity for the next two weeks.

@rustbot reroll

@rustbot rustbot assigned Mark-Simulacrum and unassigned Darksonn Aug 5, 2026
@valentynkit

Copy link
Copy Markdown
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 10, 2026

@nia-e nia-e left a comment

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.

left a couple nits. one is probably fine with just a doc change, the other i'm unsure of - if there's no way to make this resilient across forks without just querying the thread id every time then that's Sad but may be justifiable perf-wise. either way, impl looks mostly good.

@rustbot author

View changes since this review

/// spawned thread does this itself once it starts running, since its handle
/// already exists by then.
pub(crate) fn set_os_id_to_current(&self) {
if let Some(os_id) = imp::current_os_id() {

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.

So, on SGX we just get the thread address? I guess that's justifiable but it means that the meaning of os_id is platform-specific; this thread ID won't show up in a debugger or the likes.

Then again I don't know if anyone cares to use debuggers in SGX, so /shrug I wouldn't consider this blocking but it could use a doc note

#[unstable(feature = "thread_os_id", issue = "160215")]
#[must_use]
pub fn os_id(&self) -> Option<u64> {
self.inner.os_id.get().copied()

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.

this will be stale after a fork, and i think it is a reasonable want of an api consumer to wish to be able to use this in conjunction with forking. unsure if there's a nice way to check whether we have forked, though

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.

Yes, after fork the stored os_id will be stale. It may be fixed by querying the current_os_id when it's current thread (no way for querying non current thread because we are not storing pthread_t), so something like these could be added:
if self.id() == current_id() { imp::current_os_id() } else { cached }
Or detecting a fork, but we will have to store extra fields, and making additional syscall, we could store stored_pid alongside os_id at start.:
if getpid() != stored_pid
To be honest nothing else comes to my mind, and not sure if it's worth it?

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 3, 2026
diff --git a/library/std/src/thread/thread.rs b/library/std/src/thread/thread.rs
index ff6affa..d70c244 100644
--- a/library/std/src/thread/thread.rs
+++ b/library/std/src/thread/thread.rs
@@ -125,7 +125,8 @@ pub(crate) fn new_current(id: ThreadId) -> Thread {
         thread
     }

-    /// Records the OS id of the calling thread in this handle.
+    /// Records the calling thread's OS id, as reported by
+    /// `imp::current_os_id`, in this handle.
     ///
     /// May only be called from the thread to which this handle belongs. A
     /// spawned thread does this itself once it starts running, since its handle
@@ -240,12 +241,17 @@ pub fn id(&self) -> ThreadId {
     ///
     /// This is the id that shows up in tools like `ps` and `top`, debuggers and
     /// crash logs, unlike [`ThreadId`], which has no guaranteed relationship to
-    /// it. `None` means the platform has no such id, the thread has not started
-    /// running yet, or the id could not be read.
+    /// it. On a platform with no OS-visible thread id, such as SGX, the value
+    /// may be some other per-thread value (there, the thread's address), which
+    /// such tools will not recognize. `None` means no id could be recorded: the
+    /// thread has not started running yet, or the platform has no way to read
+    /// one.
     ///
     /// The operating system may reuse the id of a thread that has exited, and a
-    /// `Thread` handle can outlive the thread it refers to. Use the id only
-    /// where a reused id is harmless, such as logging.
+    /// `Thread` handle can outlive the thread it refers to. After a `fork`, the
+    /// id recorded in the child process still refers to the parent's thread; it
+    /// is not re-read. Use the id only where a reused or stale id is harmless,
+    /// such as logging.
     ///
     /// # Examples
     ///
@rustbot

rustbot commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

⚠️ Warning ⚠️

@valentynkit

Copy link
Copy Markdown
Contributor Author

@rustbot ready
I have addressed the review feedback.
It doesn't look for me that there are a wise way of making it resilient across forks, because it could only be captured on current thread (or during the creation of thread by parent), when we still have pthread_t.
We could detect stale os_id, by storing additional field stored_pid , than checking getpid() != stored_pid .
But its additional fields + syscall, so not sure if it's worth it, or there are a better way.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 7, 2026
@nia-e

nia-e commented Sep 8, 2026

Copy link
Copy Markdown
Member

looks good! head commit is a bit stale so let's test it. i'll r+ if this passes.

@bors try

@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Sep 8, 2026
@rust-bors

rust-bors Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 42b96d7 (42b96d768d3871394290c56dc4390952ca35c3b4)
Base parent: b505807 (b505807a88bdb0dca9c968155f2167a927dddb34)

@nia-e

nia-e commented Sep 8, 2026

Copy link
Copy Markdown
Member

@bors r+

@rust-bors

rust-bors Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📌 Commit 1af640e has been approved by nia-e

It is now in the queue for this repository.

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 8, 2026
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Sep 8, 2026
Implement `Thread::os_id`

Implements `Thread::os_id` as an unstable feature, per the accepted ACP rust-lang/libs-team#635.

Tracking issue: rust-lang#160215

`os_id` returns the OS-level thread id, so Rust programs can tie their own logs to system-level logs (the ACP's stated motivation).

- Using the existing `current_os_id` is much simpler than pulling the id off `imp::Thread` in `spawn_unchecked`. That needs a per-platform arm, and the child still has to fill it in on platforms without a by handle query, so it'd be extra on top of this rather than instead of it.
- `Thread` is handed to user code by spawn hooks before the native thread exists, so the id can only be filled in later. There's no spare `u64` value to mean "not set yet", so a OnceLock is chosen as a simple primitive to use for this purpose.

r? libs
rust-bors Bot pushed a commit that referenced this pull request Sep 8, 2026
…uwer

Rollup of 9 pull requests

Successful merges:

 - #157738 (Support move expressions in coroutine closures)
 - #160219 (Implement `Thread::os_id`)
 - #162449 (Prefer removing a redundant shared reference over reborrow)
 - #162494 (Ignore `self-in-const-generics` test for parallel frontend)
 - #162495 (Reserve items in `Extend` implementations)
 - #162238 (trait solver: Include implied outlives assumptions)
 - #162473 (Small `x perf` improvements)
 - #162489 (Clean up on upvar_tys)
 - #162500 (Move the `expect-item-after-attribute.rs` test to the correct directory)
@rust-bors
rust-bors Bot merged commit 815492b into rust-lang:main Sep 9, 2026
14 checks passed
rust-bors Bot pushed a commit that referenced this pull request Sep 9, 2026
Rollup merge of #160219 - valentynkit:thread-os-id, r=nia-e

Implement `Thread::os_id`

Implements `Thread::os_id` as an unstable feature, per the accepted ACP rust-lang/libs-team#635.

Tracking issue: #160215

`os_id` returns the OS-level thread id, so Rust programs can tie their own logs to system-level logs (the ACP's stated motivation).

- Using the existing `current_os_id` is much simpler than pulling the id off `imp::Thread` in `spawn_unchecked`. That needs a per-platform arm, and the child still has to fill it in on platforms without a by handle query, so it'd be extra on top of this rather than instead of it.
- `Thread` is handed to user code by spawn hooks before the native thread exists, so the id can only be filled in later. There's no spare `u64` value to mean "not set yet", so a OnceLock is chosen as a simple primitive to use for this purpose.

r? libs
@rustbot rustbot added this to the 1.100.0 milestone Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants