@@ -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 :: RwLock < 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. read ( ) ;
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,39 +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 guard = c. read ( ) ;
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 ( ) ;
69
76
guard
70
- . get ( thread_name)
77
+ . get ( & thread_name)
71
78
. unwrap_or_else ( || panic ! ( "thread {thread_name} is not initiated" ) )
72
79
. set ( value)
73
80
}
74
81
}
75
82
}
76
83
}
77
84
78
- impl Debug for Singleton {
85
+ impl Debug for SingletonType {
79
86
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
80
87
f. debug_struct ( "Singleton" )
81
88
. field ( "type" , & match self {
82
- Self :: Production ( _ ) => "Production" ,
89
+ Self :: Production => "Production" ,
83
90
#[ cfg( debug_assertions) ]
84
- Self :: Testing ( _ ) => "Testing" ,
91
+ Self :: Testing => "Testing" ,
85
92
} )
86
93
. finish ( )
87
94
}
88
95
}
89
96
90
- /// GLOBAL is a static type that holding all global data.
91
- static GLOBAL : OnceCell < Singleton > = OnceCell :: new ( ) ;
92
-
93
97
/// Global is an empty struct that only used to carry associated functions.
94
98
pub struct GlobalInstance ;
95
99
@@ -98,48 +102,47 @@ impl GlobalInstance {
98
102
///
99
103
/// Should only be initiated once.
100
104
pub fn init_production ( ) {
101
- let _ = GLOBAL . set ( Singleton :: Production ( <Container ! [ Send + Sync ] >:: new ( ) ) ) ;
105
+ let _ = SINGLETON_TYPE . set ( SingletonType :: Production ) ;
106
+ let _ = GLOBAL . set ( <Container ! [ Send + Sync ] >:: new ( ) ) ;
102
107
}
103
108
104
109
/// init testing global data registry.
105
110
///
106
111
/// Should only be initiated once and only used in testing.
107
112
#[ cfg( debug_assertions) ]
108
113
pub fn init_testing ( thread_name : & str ) {
109
- let _ = GLOBAL . set ( Singleton :: Testing ( parking_lot:: RwLock :: default ( ) ) ) ;
110
- if let Singleton :: Testing ( v) = GLOBAL . wait ( ) {
111
- let mut guard = v. write ( ) ;
112
- guard. insert ( thread_name. to_string ( ) , <Container ! [ Send + Sync ] >:: new ( ) ) ;
113
- }
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
+ )
114
125
}
115
126
116
127
/// drop testing global data by thread name.
117
128
///
118
129
/// Should only be used in testing code.
119
130
#[ cfg( debug_assertions) ]
120
131
pub fn drop_testing ( thread_name : & str ) {
121
- match GLOBAL . wait ( ) {
122
- Singleton :: Production ( _) => {
123
- unreachable ! ( "drop_testing should never be called on production global" )
124
- }
125
- Singleton :: Testing ( c) => {
126
- let mut guard = c. write ( ) ;
127
- guard. remove ( thread_name) ;
128
- }
129
- }
132
+ LOCAL . wait ( ) . write ( ) . remove ( thread_name) ;
130
133
}
131
134
132
135
/// Get data from global data registry.
133
136
pub fn get < T : Clone + ' static > ( ) -> T {
134
- GLOBAL
137
+ SINGLETON_TYPE
135
138
. get ( )
136
139
. expect ( "global data registry must be initiated" )
137
140
. get ( )
138
141
}
139
142
140
143
/// Set data into global data registry.
141
144
pub fn set < T : Send + Sync + ' static > ( value : T ) {
142
- let set = GLOBAL
145
+ let set = SINGLETON_TYPE
143
146
. get ( )
144
147
. expect ( "global data registry must be initiated" )
145
148
. set ( value) ;
0 commit comments