Skip to content

Commit fc8987a

Browse files
authored
Merge pull request #9242 from Xuanwo/fix-testing-singletion-drop
fix: Address unit test hang on test_insert
2 parents 8cbb7f7 + 48be2b5 commit fc8987a

File tree

14 files changed

+541
-546
lines changed

14 files changed

+541
-546
lines changed

src/common/base/src/base/singleton_instance.rs

Lines changed: 57 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -17,37 +17,45 @@ use std::fmt::Debug;
1717
use once_cell::sync::OnceCell;
1818
use state::Container;
1919

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+
2032
/// Singleton is a wrapper enum for `Container![Send + Sync]`.
2133
///
2234
/// - `Production` is used in our production code.
2335
/// - `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,
2638

2739
#[cfg(debug_assertions)]
28-
Testing(parking_lot::Mutex<std::collections::HashMap<String, Container![Send + Sync]>>),
40+
Testing,
2941
}
3042

31-
unsafe impl Send for Singleton {}
32-
unsafe impl Sync for Singleton {}
33-
34-
impl Singleton {
43+
impl SingletonType {
3544
fn get<T: Clone + 'static>(&self) -> T {
3645
match self {
37-
Singleton::Production(c) => {
38-
let v: &T = c.get();
46+
SingletonType::Production => {
47+
let v: &T = GLOBAL.wait().get();
3948
v.clone()
4049
}
4150
#[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();
4957
let v: &T = guard
50-
.get(thread_name)
58+
.get(&thread_name)
5159
.unwrap_or_else(|| panic!("thread {thread_name} is not initiated"))
5260
.get();
5361
v.clone()
@@ -57,37 +65,35 @@ impl Singleton {
5765

5866
fn set<T: Send + Sync + 'static>(&self, value: T) -> bool {
5967
match self {
60-
Singleton::Production(c) => c.set(value),
68+
SingletonType::Production => GLOBAL.wait().set(value),
6169
#[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)
7180
}
7281
}
7382
}
7483
}
7584

76-
impl Debug for Singleton {
85+
impl Debug for SingletonType {
7786
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7887
f.debug_struct("Singleton")
7988
.field("type", &match self {
80-
Self::Production(_) => "Production",
89+
Self::Production => "Production",
8190
#[cfg(debug_assertions)]
82-
Self::Testing(_) => "Testing",
91+
Self::Testing => "Testing",
8392
})
8493
.finish()
8594
}
8695
}
8796

88-
/// GLOBAL is a static type that holding all global data.
89-
static GLOBAL: OnceCell<Singleton> = OnceCell::new();
90-
9197
/// Global is an empty struct that only used to carry associated functions.
9298
pub struct GlobalInstance;
9399

@@ -96,44 +102,48 @@ impl GlobalInstance {
96102
///
97103
/// Should only be initiated once.
98104
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());
100107
}
101108

102109
/// init testing global data registry.
103110
///
104111
/// Should only be initiated once and only used in testing.
105112
#[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+
)
108125
}
109126

110127
/// drop testing global data by thread name.
111128
///
112129
/// Should only be used in testing code.
113130
#[cfg(debug_assertions)]
114131
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) };
124134
}
125135

126136
/// Get data from global data registry.
127137
pub fn get<T: Clone + 'static>() -> T {
128-
GLOBAL
138+
SINGLETON_TYPE
129139
.get()
130140
.expect("global data registry must be initiated")
131141
.get()
132142
}
133143

134144
/// Set data into global data registry.
135145
pub fn set<T: Send + Sync + 'static>(value: T) {
136-
let set = GLOBAL
146+
let set = SINGLETON_TYPE
137147
.get()
138148
.expect("global data registry must be initiated")
139149
.set(value);

src/query/service/src/global_services.rs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,11 @@ pub struct GlobalServices;
3636

3737
impl GlobalServices {
3838
pub async fn init(config: Config) -> Result<()> {
39-
GlobalServices::init_with(config, true).await
39+
GlobalInstance::init_production();
40+
GlobalServices::init_with(config).await
4041
}
4142

42-
pub async fn init_with(config: Config, prodcution: bool) -> Result<()> {
43-
#[cfg(debug_assertions)]
44-
if prodcution {
45-
GlobalInstance::init_production();
46-
} else {
47-
GlobalInstance::init_testing();
48-
}
49-
#[cfg(not(debug_assertions))]
50-
if prodcution {
51-
GlobalInstance::init_production();
52-
} else {
53-
unreachable!("release build must init with production global")
54-
}
55-
43+
pub async fn init_with(config: Config) -> Result<()> {
5644
// The order of initialization is very important
5745
GlobalConfig::init(config.clone())?;
5846

0 commit comments

Comments
 (0)