]>
git.proxmox.com Git - rustc.git/blob - vendor/tracing-subscriber/src/thread.rs
1 use crate::sync
::RwLock
;
2 use std
::sync
::atomic
::{AtomicUsize, Ordering}
;
4 cell
::{Cell, UnsafeCell}
,
8 pub(crate) struct Local
<T
> {
9 // TODO(eliza): this once used a `crossbeam_util::ShardedRwLock`. We may
10 // eventually wish to replace it with a sharded lock implementation on top
11 // of our internal `RwLock` wrapper type. If possible, we should profile
12 // this first to determine if it's necessary.
13 inner
: RwLock
<Inner
<T
>>,
16 type Inner
<T
> = Vec
<Option
<UnsafeCell
<T
>>>;
18 /// Uniquely identifies a thread.
20 #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
21 pub(crate) struct Id
{
23 _not_send
: PhantomData
<UnsafeCell
<()>>,
29 pub(crate) fn new() -> Self {
30 let len
= Id
::current().as_usize();
31 // Preallocate up to the current thread ID, so we don't have to inside
33 let mut data
= Vec
::with_capacity(len
);
34 data
.resize_with(len
, || None
);
36 inner
: RwLock
::new(data
),
40 pub(crate) fn with_or_else
<O
>(
42 new
: impl FnOnce() -> T
,
43 f
: impl FnOnce(&mut T
) -> O
,
45 let i
= Id
::current().as_usize();
47 self.try_with_index(i
, |item
| f
.take().expect("called twice")(item
))
49 self.new_thread(i
, new
);
50 self.try_with_index(i
, |item
| f
.take().expect("called twice")(item
))
54 fn try_with_index
<O
>(&self, i
: usize, f
: impl FnOnce(&mut T
) -> O
) -> Option
<O
> {
55 let lock
= try_lock
!(self.inner
.read(), else return None
);
56 let slot
= lock
.get(i
)?
.as_ref()?
;
57 let item
= unsafe { &mut *slot.get() }
;
62 fn new_thread(&self, i
: usize, new
: impl FnOnce() -> T
) {
63 let mut lock
= try_lock
!(self.inner
.write());
64 let this
= &mut *lock
;
65 this
.resize_with(i
+ 1, || None
);
66 this
[i
] = Some(UnsafeCell
::new(new()));
70 impl<T
: Default
> Local
<T
> {
72 pub(crate) fn with
<O
>(&self, f
: impl FnOnce(&mut T
) -> O
) -> Option
<O
> {
73 self.with_or_else(T
::default, f
)
77 unsafe impl<T
> Sync
for Local
<T
> {}
79 impl<T
: fmt
::Debug
> fmt
::Debug
for Local
<T
> {
80 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
81 let id
= Id
::current();
82 self.try_with_index(id
.as_usize(), |local
| {
83 f
.debug_struct("Local")
85 .field("local", &*local
)
89 f
.debug_struct("Local")
91 .field("local", &format_args
!("<uninitialized>"))
100 pub(crate) fn current() -> Self {
102 static MY_ID
: Cell
<Option
<Id
>> = Cell
::new(None
);
106 .try_with(|my_id
| my_id
.get().unwrap_or_else(|| Self::new_thread(my_id
)))
107 .unwrap_or_else(|_
| Self::poisoned())
110 pub(crate) fn as_usize(self) -> usize {
115 fn new_thread(local
: &Cell
<Option
<Id
>>) -> Self {
116 static NEXT_ID
: AtomicUsize
= AtomicUsize
::new(0);
117 let id
= NEXT_ID
.fetch_add(1, Ordering
::AcqRel
);
120 _not_send
: PhantomData
,
122 local
.set(Some(tid
));
127 fn poisoned() -> Self {
130 _not_send
: PhantomData
,
134 /// Returns true if the local thread ID was accessed while unwinding.
135 pub(crate) fn is_poisoned(self) -> bool
{
136 self.id
== std
::usize::MAX
140 impl fmt
::Debug
for Id
{
141 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
142 if self.is_poisoned() {
144 .field(&format_args
!("<poisoned>"))
147 f
.debug_tuple("Id").field(&self.id
).finish()