Skip to content

Commit a719540

Browse files
authored
Change thread_id's return type to be an Option. (#97)
If `thread_id` is called after a thread has exited, the thread no longer has an ID, so return an `Option` indicating whether the ID is present.
1 parent 18be6bf commit a719540

7 files changed

Lines changed: 172 additions & 22 deletions

File tree

src/thread/linux_raw.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,10 @@ pub(super) fn initialize_startup_thread_info() {
231231
current_phdr = current_phdr.cast::<u8>().add(phent).cast();
232232

233233
match phdr.p_type {
234-
// Compute the offset from the static virtual addresses
235-
// in the `p_vaddr` fields to the dynamic addresses. We don't
236-
// always get a `PT_PHDR` or `PT_DYNAMIC` header, so use
237-
// whichever one we get.
234+
// Compute the offset from the static virtual addresses in the
235+
// `p_vaddr` fields to the dynamic addresses. We don't always
236+
// get a `PT_PHDR` or `PT_DYNAMIC` header, so use whichever one
237+
// we get.
238238
PT_PHDR => offset = first_phdr.addr().wrapping_sub(phdr.p_vaddr),
239239
PT_DYNAMIC => offset = dynamic_addr.addr().wrapping_sub(phdr.p_vaddr),
240240

@@ -950,9 +950,17 @@ pub fn current_thread_id() -> ThreadId {
950950
// SAFETY: All threads have been initialized, including the main thread
951951
// with `initialize_main_thread`, so `current_thread()` returns a valid
952952
// pointer.
953-
let tid = unsafe { thread_id(current_thread()) };
954-
debug_assert_eq!(tid, gettid(), "`current_thread_id` disagrees with `gettid`");
955-
tid
953+
unsafe {
954+
// Don't use the `thread_id` function here because it returns an
955+
// `Option` to handle the case where the thread has exited. We're
956+
// querying the current thread which we know is still running because
957+
// we're on it.
958+
let raw = current_thread().0.as_ref().thread_id.load(SeqCst);
959+
debug_assert!(raw > 0);
960+
let tid = ThreadId::from_raw_unchecked(raw);
961+
debug_assert_eq!(tid, gettid(), "`current_thread_id` disagrees with `gettid`");
962+
tid
963+
}
956964
}
957965

958966
/// Set the current thread id, after a `fork`.
@@ -1015,7 +1023,7 @@ pub fn current_thread_tls_addr(offset: usize) -> *mut c_void {
10151023
}
10161024
}
10171025

1018-
/// Return the id of a thread.
1026+
/// Return the id of a thread, or `None` if the thread has exited.
10191027
///
10201028
/// This is the same as [`rustix::thread::gettid`], but loads the value from a
10211029
/// field in the runtime rather than making a system call.
@@ -1024,10 +1032,9 @@ pub fn current_thread_tls_addr(offset: usize) -> *mut c_void {
10241032
///
10251033
/// `thread` must point to a valid thread record.
10261034
#[inline]
1027-
pub unsafe fn thread_id(thread: Thread) -> ThreadId {
1035+
pub unsafe fn thread_id(thread: Thread) -> Option<ThreadId> {
10281036
let raw = thread.0.as_ref().thread_id.load(SeqCst);
1029-
debug_assert!(raw > 0);
1030-
ThreadId::from_raw_unchecked(raw)
1037+
ThreadId::from_raw(raw)
10311038
}
10321039

10331040
/// Return the current thread's stack address (lowest address), size, and guard
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
name = "tls"
2+
name = "origin-start-tests"
33
version = "0.0.0"
44
edition = "2021"
55
publish = false
@@ -9,6 +9,13 @@ origin = { path = "../..", default-features = false, features = ["origin-thread"
99
atomic-dbg = { version = "0.1.8", default-features = false }
1010
rustix-dlmalloc = { version = "0.1.0", features = ["global"] }
1111
compiler_builtins = { version = "0.1.101", features = ["mem"] }
12+
rustix = { version = "0.38", default-features = false, features = ["thread"] }
13+
rustix-futex-sync = { version = "0.1.5", default-features = false }
1214

1315
# This is just a test crate, and not part of the origin workspace.
1416
[workspace]
17+
18+
[profile.release]
19+
debug = true
20+
debug-assertions = true
21+
overflow-checks = true
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//! Test `thread_id` and `current_thread_id`.
2+
3+
#![no_std]
4+
#![no_main]
5+
#![allow(internal_features)]
6+
#![feature(lang_items)]
7+
#![feature(core_intrinsics)]
8+
9+
extern crate alloc;
10+
extern crate compiler_builtins;
11+
12+
use alloc::boxed::Box;
13+
use alloc::sync::Arc;
14+
use atomic_dbg::dbg;
15+
use core::ffi::c_void;
16+
use core::ptr::{null_mut, NonNull};
17+
use origin::program::*;
18+
use origin::thread::*;
19+
use rustix_futex_sync::{Condvar, Mutex};
20+
21+
#[panic_handler]
22+
fn panic(panic: &core::panic::PanicInfo<'_>) -> ! {
23+
dbg!(panic);
24+
core::intrinsics::abort();
25+
}
26+
27+
#[lang = "eh_personality"]
28+
extern "C" fn eh_personality() {}
29+
30+
#[global_allocator]
31+
static GLOBAL_ALLOCATOR: rustix_dlmalloc::GlobalDlmalloc = rustix_dlmalloc::GlobalDlmalloc;
32+
33+
#[derive(Eq, PartialEq, Debug)]
34+
enum Id {
35+
Parent(ThreadId),
36+
Child(ThreadId),
37+
}
38+
39+
#[no_mangle]
40+
unsafe fn origin_main(_argc: usize, _argv: *mut *mut u8, _envp: *mut *mut u8) -> i32 {
41+
assert_eq!(current_thread_id(), thread_id(current_thread()).unwrap());
42+
at_exit(Box::new(|| {
43+
assert_eq!(current_thread_id(), thread_id(current_thread()).unwrap())
44+
}));
45+
at_thread_exit(Box::new(|| {
46+
assert_eq!(current_thread_id(), thread_id(current_thread()).unwrap());
47+
}));
48+
49+
let cond = Arc::new((Mutex::new(None), Condvar::new()));
50+
let cond_clone = NonNull::new(Box::into_raw(Box::new(Arc::clone(&cond))).cast());
51+
52+
let thread = create_thread(
53+
move |args| {
54+
assert_eq!(current_thread_id(), thread_id(current_thread()).unwrap());
55+
at_thread_exit(Box::new(|| {
56+
assert_eq!(current_thread_id(), thread_id(current_thread()).unwrap());
57+
}));
58+
59+
let unpack = |x: Option<NonNull<c_void>>| match x {
60+
Some(p) => p.as_ptr().cast::<Arc<(Mutex<Option<Id>>, Condvar)>>(),
61+
None => null_mut(),
62+
};
63+
let cond = Box::from_raw(unpack(args[0]));
64+
65+
// Wait for the parent to tell the child its id, and check it.
66+
let (lock, cvar) = &**cond;
67+
{
68+
let mut id = lock.lock();
69+
while (*id).is_none() {
70+
id = cvar.wait(id);
71+
}
72+
assert_ne!(Id::Parent(current_thread_id()), *id.as_ref().unwrap());
73+
}
74+
75+
// Tell the parent the child's id.
76+
{
77+
let mut id = lock.lock();
78+
*id = Some(Id::Child(current_thread_id()));
79+
cvar.notify_one();
80+
}
81+
82+
assert_eq!(current_thread_id(), thread_id(current_thread()).unwrap());
83+
None
84+
},
85+
&[cond_clone],
86+
default_stack_size(),
87+
default_guard_size(),
88+
)
89+
.unwrap();
90+
91+
// While the child is still running, examine its id.
92+
let child_thread_id_from_main = thread_id(thread).unwrap();
93+
94+
// Tell the child the main thread id.
95+
let (lock, cvar) = &*cond;
96+
{
97+
let mut id = lock.lock();
98+
*id = Some(Id::Parent(current_thread_id()));
99+
cvar.notify_one();
100+
}
101+
102+
// Wait for the child to tell the main thread its id, and check it.
103+
{
104+
let mut id = lock.lock();
105+
loop {
106+
if let Some(Id::Child(id)) = *id {
107+
assert_eq!(child_thread_id_from_main, id);
108+
break;
109+
}
110+
id = cvar.wait(id);
111+
}
112+
}
113+
114+
// At some point the child thread should exit, at which point `thread_id`
115+
// will return `None`.
116+
while thread_id(thread).is_some() {
117+
let _ = rustix::thread::nanosleep(&rustix::thread::Timespec {
118+
tv_sec: 0,
119+
tv_nsec: 100_000_000,
120+
});
121+
}
122+
123+
join_thread(thread);
124+
125+
assert_eq!(current_thread_id(), thread_id(current_thread()).unwrap());
126+
exit(201);
127+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
//! Like `origin-start`, but add a `#[thread_local]` variable and asserts
2-
//! to ensure that it works.
1+
//! Like the `origin-start` example, but add a `#[thread_local]` variable and
2+
//! asserts to ensure that it works.
33
44
#![no_std]
55
#![no_main]

test-crates/tls/README.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

tests/test_crates.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,21 @@ fn test_crate(
1818

1919
#[test]
2020
fn test_crate_tls() {
21-
test_crate("tls", &["--release"], &[], "", "", Some(200));
21+
test_crate(
22+
"origin-start",
23+
&["--bin=tls", "--release"],
24+
&[],
25+
"",
26+
"",
27+
Some(200),
28+
);
2229
}
2330

2431
#[test]
2532
fn test_crate_tls_crt_static() {
2633
test_crate(
27-
"tls",
28-
&["--features=origin/experimental-relocate"],
34+
"origin-start",
35+
&["--bin=tls", "--features=origin/experimental-relocate"],
2936
&[("RUSTFLAGS", "-C target-feature=+crt-static")],
3037
"",
3138
"",
@@ -36,8 +43,8 @@ fn test_crate_tls_crt_static() {
3643
#[test]
3744
fn test_crate_tls_crt_static_relocation_static() {
3845
test_crate(
39-
"tls",
40-
&[],
46+
"origin-start",
47+
&["--bin=tls"],
4148
&[(
4249
"RUSTFLAGS",
4350
"-C target-feature=+crt-static -C relocation-model=static",
@@ -47,3 +54,8 @@ fn test_crate_tls_crt_static_relocation_static() {
4754
Some(200),
4855
);
4956
}
57+
58+
#[test]
59+
fn test_crate_thread_id() {
60+
test_crate("origin-start", &["--bin=thread-id"], &[], "", "", Some(201));
61+
}

0 commit comments

Comments
 (0)