Skip to content

Commit 9e8c82a

Browse files
bors[bot]ltratt
andauthored
Merge #168
168: Don't create a malloc'd block each time we start tracing. r=vext01 a=ltratt When we detect a possibly closed loop in the end user's program, we need to determine whether the current thread is the one tracing it or not. We previously allocated a chunk of memory when we started tracing and used its address as a temporary thread ID. Ideally we would use the system provided thread ID to determine this, but that turns out to be a non-portable minefield. pthreads provides the pthread_t ID type but that's notionally opaque (on OpenBSD, as far as I can tell from a quick search, it's really an opaque struct about which no guarantees are provided, though some programs such as Chrome do seem to rely on it being equivalent to a usize). Some platforms (at least Linux and OpenBSD) use the PID ID type pid_t for threads too and that's guaranteed to be a signed integer though not it seems of a guaranteed size (though on both platforms it's actually a C int, so 32 bits in practise). Rust provides a ThreadID type and an unstable "as_64" function (rust-lang/rust#67939) but that provides no guarantees about how much of the 64-bit integer space is used. The challenge we have is that whatever ID we use it must fit into (numbits(usize) - numbits(PHASE_TAGS)) i.e. we have 62 bits for the ID on a 64-bit system. If we rely on the system thread IDs it feels like we're storing up a portability time bomb that might explode one day in the dim and distant future. At least for now, the (minimal) extra performance we might get from that doesn't seem worth the danger. This commit is thus a relatively simple change. Rather than allocating a malloc'd block every time we start tracing a thread, which means we have to be quite careful about freeing memory, we allocate it on thread construction. This simplifies the code slightly and (since it's an aligned address) we can feel fairly safe that it plays nicely with PHASE_TAGS. We could, if we wanted, allocate this block lazily the first time we trace but that feels like an unnecessary optimisation at this point. This is based on a suggestion from @vext01 and a comment from @bjorn3. Co-authored-by: Laurence Tratt <[email protected]>
2 parents ae5c029 + b5a3c60 commit 9e8c82a

File tree

1 file changed

+24
-18
lines changed

1 file changed

+24
-18
lines changed

ykrt/src/mt.rs

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#[cfg(test)]
22
use std::time::Duration;
33
use std::{
4+
alloc::{alloc, dealloc, Layout},
45
io, mem,
56
panic::{catch_unwind, resume_unwind, UnwindSafe},
67
rc::Rc,
@@ -238,17 +239,15 @@ impl MTThread {
238239
// count further.
239240
return None;
240241
}
241-
let loc_id = Box::into_raw(Box::new(0u8));
242-
let new_pack = loc_id as usize | PHASE_TRACING;
242+
let new_pack = self.inner.tid as usize | PHASE_TRACING;
243243
if loc.pack.compare_and_swap(lp, new_pack, Ordering::Release) == lp {
244244
Rc::get_mut(&mut self.inner).unwrap().tracer =
245-
Some((start_tracing(self.inner.tracing_kind), loc_id));
245+
Some((start_tracing(self.inner.tracing_kind), self.inner.tid));
246246
return None;
247-
} else {
248-
// We raced with another thread that's also trying to trace this
249-
// Location, so free the malloc'd block.
250-
unsafe { Box::from_raw(loc_id) };
251247
}
248+
// We raced with another thread that's (probably) trying to trace this
249+
// Location or (less likely) has already compiled it so we go around the
250+
// loop again to see what we should do.
252251
} else {
253252
let new_pack = PHASE_COUNTING | ((count + 1) << PHASE_NUM_BITS);
254253
if loc.pack.compare_and_swap(lp, new_pack, Ordering::Release) == lp {
@@ -259,18 +258,17 @@ impl MTThread {
259258
}
260259
}
261260
PHASE_TRACING => {
262-
let loc_id = if let Some((_, loc_id)) = self.inner.tracer {
261+
if let Some((_, tid)) = self.inner.tracer {
263262
// This thread is tracing something...
264-
if loc_id != ((lp & !PHASE_TAG) as *mut u8) {
263+
if tid != ((lp & !PHASE_TAG) as *mut u8) {
265264
// ...but we didn't start at the current Location.
266265
return None;
267266
}
268-
// ...and we started at this Location, so we've got a complete loop!
269-
loc_id
267+
// ...and we started at this Location, so we've got a complete loop!
270268
} else {
271269
// Another thread is tracing this location.
272270
return None;
273-
};
271+
}
274272

275273
let sir_trace = Rc::get_mut(&mut self.inner)
276274
.unwrap()
@@ -291,8 +289,6 @@ impl MTThread {
291289

292290
let new_pack = ptr | PHASE_COMPILED;
293291
loc.pack.store(new_pack, Ordering::Release);
294-
// Free the small block of memory we used as a Location ID.
295-
unsafe { Box::from_raw(loc_id) };
296292
Rc::get_mut(&mut self.inner).unwrap().tracer = None;
297293
return None;
298294
}
@@ -316,6 +312,11 @@ impl MTThread {
316312
/// The innards of a meta-tracer thread.
317313
struct MTThreadInner {
318314
mt: MT,
315+
/// A value that uniquely identifies a thread. Since this ID needs to be ORable with PHASE_TAG,
316+
/// we use a pointer to a malloc'd chunk of memory. We guarantee a) that chunk is aligned to a
317+
/// machine word b) that it is a non-zero chunk of memory (and thus guaranteed to be a unique
318+
/// pointer).
319+
tid: *mut u8,
319320
hot_threshold: HotThreshold,
320321
#[allow(dead_code)]
321322
tracing_kind: TracingKind,
@@ -329,8 +330,14 @@ impl MTThreadInner {
329330
fn init(mt: MT) -> MTThread {
330331
let hot_threshold = mt.hot_threshold();
331332
let tracing_kind = mt.tracing_kind();
333+
let tid = {
334+
let layout =
335+
Layout::from_size_align(mem::size_of::<usize>(), mem::size_of::<usize>()).unwrap();
336+
unsafe { alloc(layout) }
337+
};
332338
let inner = MTThreadInner {
333339
mt,
340+
tid,
334341
hot_threshold,
335342
tracing_kind,
336343
tracer: None,
@@ -343,10 +350,9 @@ impl MTThreadInner {
343350

344351
impl Drop for MTThreadInner {
345352
fn drop(&mut self) {
346-
if let Some((_, loc_id)) = self.tracer {
347-
// We were trying to trace something.
348-
unsafe { Box::from_raw(loc_id) };
349-
}
353+
let layout =
354+
Layout::from_size_align(mem::size_of::<usize>(), mem::size_of::<usize>()).unwrap();
355+
unsafe { dealloc(self.tid, layout) };
350356
}
351357
}
352358

0 commit comments

Comments
 (0)