@@ -17,37 +17,45 @@ use std::fmt::Debug;
17
17
use once_cell:: sync:: OnceCell ;
18
18
use state:: Container ;
19
19
20
+ /// SINGLETON_TYPE is the global singleton type.
21
+ static SINGLETON_TYPE : OnceCell < SingletonType > = OnceCell :: new ( ) ;
22
+
23
+ /// GLOBAL is a static type that holding all global data.
24
+ static GLOBAL : OnceCell < Container ! [ Send + Sync ] > = OnceCell :: new ( ) ;
25
+
26
+ #[ cfg( debug_assertions) ]
27
+ /// LOCAL is a static type that holding all global data only for local tests.
28
+ static LOCAL : OnceCell <
29
+ parking_lot:: RwLock < std:: collections:: HashMap < String , Container ! [ Send + Sync ] > > ,
30
+ > = OnceCell :: new ( ) ;
31
+
20
32
/// Singleton is a wrapper enum for `Container![Send + Sync]`.
21
33
///
22
34
/// - `Production` is used in our production code.
23
35
/// - `Testing` is served for test only and gated under `debug_assertions`.
24
- pub enum Singleton {
25
- Production ( Container ! [ Send + Sync ] ) ,
36
+ pub enum SingletonType {
37
+ Production ,
26
38
27
39
#[ cfg( debug_assertions) ]
28
- Testing ( parking_lot :: Mutex < std :: collections :: HashMap < String , Container ! [ Send + Sync ] > > ) ,
40
+ Testing ,
29
41
}
30
42
31
- unsafe impl Send for Singleton { }
32
- unsafe impl Sync for Singleton { }
33
-
34
- impl Singleton {
43
+ impl SingletonType {
35
44
fn get < T : Clone + ' static > ( & self ) -> T {
36
45
match self {
37
- Singleton :: Production ( c ) => {
38
- let v: & T = c . get ( ) ;
46
+ SingletonType :: Production => {
47
+ let v: & T = GLOBAL . wait ( ) . get ( ) ;
39
48
v. clone ( )
40
49
}
41
50
#[ cfg( debug_assertions) ]
42
- Singleton :: Testing ( c) => {
43
- let thread = std:: thread:: current ( ) ;
44
- let thread_name = match thread. name ( ) {
45
- Some ( name) => name,
46
- None => panic ! ( "thread doesn't have name" ) ,
47
- } ;
48
- let guard = c. lock ( ) ;
51
+ SingletonType :: Testing => {
52
+ let thread_name = std:: thread:: current ( )
53
+ . name ( )
54
+ . expect ( "thread doesn't have name" )
55
+ . to_string ( ) ;
56
+ let guard = LOCAL . wait ( ) . read ( ) ;
49
57
let v: & T = guard
50
- . get ( thread_name)
58
+ . get ( & thread_name)
51
59
. unwrap_or_else ( || panic ! ( "thread {thread_name} is not initiated" ) )
52
60
. get ( ) ;
53
61
v. clone ( )
@@ -57,37 +65,35 @@ impl Singleton {
57
65
58
66
fn set < T : Send + Sync + ' static > ( & self , value : T ) -> bool {
59
67
match self {
60
- Singleton :: Production ( c ) => c . set ( value) ,
68
+ SingletonType :: Production => GLOBAL . wait ( ) . set ( value) ,
61
69
#[ cfg( debug_assertions) ]
62
- Singleton :: Testing ( c) => {
63
- let thread = std:: thread:: current ( ) ;
64
- let thread_name = match thread. name ( ) {
65
- Some ( name) => name,
66
- None => panic ! ( "thread doesn't have name" ) ,
67
- } ;
68
- let mut guard = c. lock ( ) ;
69
- let c = guard. entry ( thread_name. to_string ( ) ) . or_default ( ) ;
70
- c. set ( value)
70
+ SingletonType :: Testing => {
71
+ let thread_name = std:: thread:: current ( )
72
+ . name ( )
73
+ . expect ( "thread doesn't have name" )
74
+ . to_string ( ) ;
75
+ let guard = LOCAL . wait ( ) . read ( ) ;
76
+ guard
77
+ . get ( & thread_name)
78
+ . unwrap_or_else ( || panic ! ( "thread {thread_name} is not initiated" ) )
79
+ . set ( value)
71
80
}
72
81
}
73
82
}
74
83
}
75
84
76
- impl Debug for Singleton {
85
+ impl Debug for SingletonType {
77
86
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
78
87
f. debug_struct ( "Singleton" )
79
88
. field ( "type" , & match self {
80
- Self :: Production ( _ ) => "Production" ,
89
+ Self :: Production => "Production" ,
81
90
#[ cfg( debug_assertions) ]
82
- Self :: Testing ( _ ) => "Testing" ,
91
+ Self :: Testing => "Testing" ,
83
92
} )
84
93
. finish ( )
85
94
}
86
95
}
87
96
88
- /// GLOBAL is a static type that holding all global data.
89
- static GLOBAL : OnceCell < Singleton > = OnceCell :: new ( ) ;
90
-
91
97
/// Global is an empty struct that only used to carry associated functions.
92
98
pub struct GlobalInstance ;
93
99
@@ -96,44 +102,48 @@ impl GlobalInstance {
96
102
///
97
103
/// Should only be initiated once.
98
104
pub fn init_production ( ) {
99
- let _ = GLOBAL . set ( Singleton :: Production ( <Container ! [ Send + Sync ] >:: new ( ) ) ) ;
105
+ let _ = SINGLETON_TYPE . set ( SingletonType :: Production ) ;
106
+ let _ = GLOBAL . set ( <Container ! [ Send + Sync ] >:: new ( ) ) ;
100
107
}
101
108
102
109
/// init testing global data registry.
103
110
///
104
111
/// Should only be initiated once and only used in testing.
105
112
#[ cfg( debug_assertions) ]
106
- pub fn init_testing ( ) {
107
- let _ = GLOBAL . set ( Singleton :: Testing ( parking_lot:: Mutex :: default ( ) ) ) ;
113
+ pub fn init_testing ( thread_name : & str ) {
114
+ let _ = SINGLETON_TYPE . set ( SingletonType :: Testing ) ;
115
+ let _ = LOCAL . set ( parking_lot:: RwLock :: default ( ) ) ;
116
+
117
+ let v = LOCAL
118
+ . wait ( )
119
+ . write ( )
120
+ . insert ( thread_name. to_string ( ) , <Container ! [ Send + Sync ] >:: new ( ) ) ;
121
+ assert ! (
122
+ v. is_none( ) ,
123
+ "thread {thread_name} has been initiated before"
124
+ )
108
125
}
109
126
110
127
/// drop testing global data by thread name.
111
128
///
112
129
/// Should only be used in testing code.
113
130
#[ cfg( debug_assertions) ]
114
131
pub fn drop_testing ( thread_name : & str ) {
115
- match GLOBAL . wait ( ) {
116
- Singleton :: Production ( _) => {
117
- unreachable ! ( "drop_testing should never be called on production global" )
118
- }
119
- Singleton :: Testing ( c) => {
120
- let mut guard = c. lock ( ) ;
121
- let _ = guard. remove ( thread_name) ;
122
- }
123
- }
132
+ // Make sure the write lock is released before alling drop.
133
+ let _ = { LOCAL . wait ( ) . write ( ) . remove ( thread_name) } ;
124
134
}
125
135
126
136
/// Get data from global data registry.
127
137
pub fn get < T : Clone + ' static > ( ) -> T {
128
- GLOBAL
138
+ SINGLETON_TYPE
129
139
. get ( )
130
140
. expect ( "global data registry must be initiated" )
131
141
. get ( )
132
142
}
133
143
134
144
/// Set data into global data registry.
135
145
pub fn set < T : Send + Sync + ' static > ( value : T ) {
136
- let set = GLOBAL
146
+ let set = SINGLETON_TYPE
137
147
. get ( )
138
148
. expect ( "global data registry must be initiated" )
139
149
. set ( value) ;
0 commit comments