33use crate :: stdlib:: {
44 fmt,
55 hash:: { Hash , Hasher } ,
6- sync:: Mutex ,
6+ ptr,
7+ sync:: {
8+ atomic:: { AtomicPtr , Ordering } ,
9+ Mutex , MutexGuard ,
10+ } ,
711 vec:: Vec ,
812} ;
913use crate :: {
@@ -13,61 +17,18 @@ use crate::{
1317} ;
1418
1519lazy_static ! {
16- static ref REGISTRY : Mutex <Registry > = Mutex :: new( Registry {
17- callsites: Vec :: new( ) ,
18- dispatchers: Vec :: new( ) ,
19- } ) ;
20- }
21-
22- struct Registry {
23- callsites : Vec < & ' static dyn Callsite > ,
24- dispatchers : Vec < dispatcher:: Registrar > ,
20+ static ref REGISTRY : Registry = Registry {
21+ callsites: LinkedList :: new( ) ,
22+ dispatchers: Mutex :: new( Vec :: new( ) ) ,
23+ } ;
2524}
2625
27- impl Registry {
28- fn rebuild_callsite_interest ( & self , callsite : & ' static dyn Callsite ) {
29- let meta = callsite. metadata ( ) ;
30-
31- // Iterate over the subscribers in the registry, and — if they are
32- // active — register the callsite with them.
33- let mut interests = self
34- . dispatchers
35- . iter ( )
36- . filter_map ( |registrar| registrar. try_register ( meta) ) ;
37-
38- // Use the first subscriber's `Interest` as the base value.
39- let interest = if let Some ( interest) = interests. next ( ) {
40- // Combine all remaining `Interest`s.
41- interests. fold ( interest, Interest :: and)
42- } else {
43- // If nobody was interested in this thing, just return `never`.
44- Interest :: never ( )
45- } ;
46-
47- callsite. set_interest ( interest)
48- }
49-
50- fn rebuild_interest ( & mut self ) {
51- let mut max_level = LevelFilter :: OFF ;
52- self . dispatchers . retain ( |registrar| {
53- if let Some ( dispatch) = registrar. upgrade ( ) {
54- // If the subscriber did not provide a max level hint, assume
55- // that it may enable every level.
56- let level_hint = dispatch. max_level_hint ( ) . unwrap_or ( LevelFilter :: TRACE ) ;
57- if level_hint > max_level {
58- max_level = level_hint;
59- }
60- true
61- } else {
62- false
63- }
64- } ) ;
26+ type Dispatchers = Vec < dispatcher:: Registrar > ;
27+ type Callsites = LinkedList ;
6528
66- self . callsites . iter ( ) . for_each ( |& callsite| {
67- self . rebuild_callsite_interest ( callsite) ;
68- } ) ;
69- LevelFilter :: set_max ( max_level) ;
70- }
29+ struct Registry {
30+ callsites : Callsites ,
31+ dispatchers : Mutex < Dispatchers > ,
7132}
7233
7334/// Trait implemented by callsites.
@@ -105,6 +66,18 @@ pub struct Identifier(
10566 pub & ' static dyn Callsite ,
10667) ;
10768
69+ /// A registration with the callsite registry.
70+ ///
71+ /// Every [`Callsite`] implementation must provide a `&'static Registration`
72+ /// when calling [`register`] to add itself to the global callsite registry.
73+ ///
74+ /// [`Callsite`]: crate::callsite::Callsite
75+ /// [`register`]: crate::callsite::register
76+ pub struct Registration < T = & ' static dyn Callsite > {
77+ callsite : T ,
78+ next : AtomicPtr < Registration < T > > ,
79+ }
80+
10881/// Clear and reregister interest on every [`Callsite`]
10982///
11083/// This function is intended for runtime reconfiguration of filters on traces
@@ -125,24 +98,76 @@ pub struct Identifier(
12598/// [`Interest::sometimes()`]: ../subscriber/struct.Interest.html#method.sometimes
12699/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
127100pub fn rebuild_interest_cache ( ) {
128- let mut registry = REGISTRY . lock ( ) . unwrap ( ) ;
129- registry. rebuild_interest ( ) ;
101+ let mut dispatchers = REGISTRY . dispatchers . lock ( ) . unwrap ( ) ;
102+ let callsites = & REGISTRY . callsites ;
103+ rebuild_interest ( callsites, & mut dispatchers) ;
130104}
131105
132106/// Register a new `Callsite` with the global registry.
133107///
134108/// This should be called once per callsite after the callsite has been
135109/// constructed.
136- pub fn register ( callsite : & ' static dyn Callsite ) {
137- let mut registry = REGISTRY . lock ( ) . unwrap ( ) ;
138- registry . rebuild_callsite_interest ( callsite) ;
139- registry . callsites . push ( callsite ) ;
110+ pub fn register ( registration : & ' static Registration ) {
111+ let mut dispatchers = REGISTRY . dispatchers . lock ( ) . unwrap ( ) ;
112+ rebuild_callsite_interest ( & mut dispatchers , registration . callsite ) ;
113+ REGISTRY . callsites . push ( registration ) ;
140114}
141115
142116pub ( crate ) fn register_dispatch ( dispatch : & Dispatch ) {
143- let mut registry = REGISTRY . lock ( ) . unwrap ( ) ;
144- registry. dispatchers . push ( dispatch. registrar ( ) ) ;
145- registry. rebuild_interest ( ) ;
117+ let mut dispatchers = REGISTRY . dispatchers . lock ( ) . unwrap ( ) ;
118+ let callsites = & REGISTRY . callsites ;
119+
120+ dispatchers. push ( dispatch. registrar ( ) ) ;
121+
122+ rebuild_interest ( callsites, & mut dispatchers) ;
123+ }
124+
125+ fn rebuild_callsite_interest (
126+ dispatchers : & mut MutexGuard < ' _ , Vec < dispatcher:: Registrar > > ,
127+ callsite : & ' static dyn Callsite ,
128+ ) {
129+ let meta = callsite. metadata ( ) ;
130+
131+ // Iterate over the subscribers in the registry, and — if they are
132+ // active — register the callsite with them.
133+ let mut interests = dispatchers
134+ . iter ( )
135+ . filter_map ( |registrar| registrar. try_register ( meta) ) ;
136+
137+ // Use the first subscriber's `Interest` as the base value.
138+ let interest = if let Some ( interest) = interests. next ( ) {
139+ // Combine all remaining `Interest`s.
140+ interests. fold ( interest, Interest :: and)
141+ } else {
142+ // If nobody was interested in this thing, just return `never`.
143+ Interest :: never ( )
144+ } ;
145+
146+ callsite. set_interest ( interest)
147+ }
148+
149+ fn rebuild_interest (
150+ callsites : & Callsites ,
151+ dispatchers : & mut MutexGuard < ' _ , Vec < dispatcher:: Registrar > > ,
152+ ) {
153+ let mut max_level = LevelFilter :: OFF ;
154+ dispatchers. retain ( |registrar| {
155+ if let Some ( dispatch) = registrar. upgrade ( ) {
156+ // If the subscriber did not provide a max level hint, assume
157+ // that it may enable every level.
158+ let level_hint = dispatch. max_level_hint ( ) . unwrap_or ( LevelFilter :: TRACE ) ;
159+ if level_hint > max_level {
160+ max_level = level_hint;
161+ }
162+ true
163+ } else {
164+ false
165+ }
166+ } ) ;
167+
168+ callsites. for_each ( |reg| rebuild_callsite_interest ( dispatchers, reg. callsite ) ) ;
169+
170+ LevelFilter :: set_max ( max_level) ;
146171}
147172
148173// ===== impl Identifier =====
@@ -169,3 +194,155 @@ impl Hash for Identifier {
169194 ( self . 0 as * const dyn Callsite ) . hash ( state)
170195 }
171196}
197+
198+ // ===== impl Registration =====
199+
200+ impl < T > Registration < T > {
201+ /// Construct a new `Registration` from some `&'static dyn Callsite`
202+ pub const fn new ( callsite : T ) -> Self {
203+ Self {
204+ callsite,
205+ next : AtomicPtr :: new ( ptr:: null_mut ( ) ) ,
206+ }
207+ }
208+ }
209+
210+ impl fmt:: Debug for Registration {
211+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
212+ f. debug_struct ( "Registration" )
213+ . field ( "callsite" , & format_args ! ( "{:p}" , self . callsite) )
214+ . field (
215+ "next" ,
216+ & format_args ! ( "{:p}" , self . next. load( Ordering :: Acquire ) ) ,
217+ )
218+ . finish ( )
219+ }
220+ }
221+
222+ // ===== impl LinkedList =====
223+
224+ /// An intrusive atomic push-only linked list.
225+ struct LinkedList {
226+ head : AtomicPtr < Registration > ,
227+ }
228+
229+ impl LinkedList {
230+ fn new ( ) -> Self {
231+ LinkedList {
232+ head : AtomicPtr :: new ( ptr:: null_mut ( ) ) ,
233+ }
234+ }
235+
236+ fn for_each ( & self , mut f : impl FnMut ( & ' static Registration ) ) {
237+ let mut head = self . head . load ( Ordering :: Acquire ) ;
238+
239+ while let Some ( reg) = unsafe { head. as_ref ( ) } {
240+ f ( reg) ;
241+
242+ head = reg. next . load ( Ordering :: Acquire ) ;
243+ }
244+ }
245+
246+ fn push ( & self , registration : & ' static Registration ) {
247+ let mut head = self . head . load ( Ordering :: Acquire ) ;
248+
249+ loop {
250+ registration. next . store ( head, Ordering :: Release ) ;
251+
252+ assert_ne ! (
253+ registration as * const _, head,
254+ "Attempted to register a `Callsite` that already exists! \
255+ This will cause an infinite loop when attempting to read from the \
256+ callsite cache. This is likely a bug! You should only need to call \
257+ `tracing-core::callsite::register` once per `Callsite`."
258+ ) ;
259+
260+ match self . head . compare_exchange (
261+ head,
262+ registration as * const _ as * mut _ ,
263+ Ordering :: AcqRel ,
264+ Ordering :: Acquire ,
265+ ) {
266+ Ok ( _) => {
267+ break ;
268+ }
269+ Err ( current) => head = current,
270+ }
271+ }
272+ }
273+ }
274+
275+ #[ cfg( test) ]
276+ mod tests {
277+ use super :: * ;
278+
279+ #[ derive( Eq , PartialEq ) ]
280+ struct Cs1 ;
281+ static CS1 : Cs1 = Cs1 ;
282+ static REG1 : Registration = Registration :: new ( & CS1 ) ;
283+
284+ impl Callsite for Cs1 {
285+ fn set_interest ( & self , _interest : Interest ) { }
286+ fn metadata ( & self ) -> & Metadata < ' _ > {
287+ unimplemented ! ( "not needed for this test" )
288+ }
289+ }
290+
291+ struct Cs2 ;
292+ static CS2 : Cs2 = Cs2 ;
293+ static REG2 : Registration = Registration :: new ( & CS2 ) ;
294+
295+ impl Callsite for Cs2 {
296+ fn set_interest ( & self , _interest : Interest ) { }
297+ fn metadata ( & self ) -> & Metadata < ' _ > {
298+ unimplemented ! ( "not needed for this test" )
299+ }
300+ }
301+
302+ #[ test]
303+ fn linked_list_push ( ) {
304+ let linked_list = LinkedList :: new ( ) ;
305+
306+ linked_list. push ( & REG1 ) ;
307+ linked_list. push ( & REG2 ) ;
308+
309+ let mut i = 0 ;
310+
311+ linked_list. for_each ( |reg| {
312+ if i == 0 {
313+ assert ! (
314+ ptr:: eq( reg, & REG2 ) ,
315+ "Registration pointers need to match REG2"
316+ ) ;
317+ } else {
318+ assert ! (
319+ ptr:: eq( reg, & REG1 ) ,
320+ "Registration pointers need to match REG1"
321+ ) ;
322+ }
323+
324+ i += 1 ;
325+ } ) ;
326+ }
327+
328+ #[ test]
329+ #[ should_panic]
330+ fn linked_list_repeated ( ) {
331+ let linked_list = LinkedList :: new ( ) ;
332+
333+ linked_list. push ( & REG1 ) ;
334+ // Pass in same reg and we should panic...
335+ linked_list. push ( & REG1 ) ;
336+
337+ linked_list. for_each ( |_| { } ) ;
338+ }
339+
340+ #[ test]
341+ fn linked_list_empty ( ) {
342+ let linked_list = LinkedList :: new ( ) ;
343+
344+ linked_list. for_each ( |_| {
345+ panic ! ( "List should be empty" ) ;
346+ } ) ;
347+ }
348+ }
0 commit comments