@@ -93,10 +93,10 @@ use std::thread;
93
93
use std:: time:: Duration ;
94
94
95
95
use async_channel:: { bounded, Receiver } ;
96
+ use async_lock:: OnceCell ;
96
97
use async_task:: Runnable ;
97
98
use atomic_waker:: AtomicWaker ;
98
99
use futures_lite:: { future, prelude:: * , ready} ;
99
- use once_cell:: sync:: Lazy ;
100
100
101
101
#[ doc( no_inline) ]
102
102
pub use async_task:: Task ;
@@ -113,17 +113,6 @@ const MAX_MAX_THREADS: usize = 10000;
113
113
/// Env variable that allows to override default value for max threads.
114
114
const MAX_THREADS_ENV : & str = "BLOCKING_MAX_THREADS" ;
115
115
116
- /// Lazily initialized global executor.
117
- static EXECUTOR : Lazy < Executor > = Lazy :: new ( || Executor {
118
- inner : Mutex :: new ( Inner {
119
- idle_count : 0 ,
120
- thread_count : 0 ,
121
- queue : VecDeque :: new ( ) ,
122
- } ) ,
123
- cvar : Condvar :: new ( ) ,
124
- thread_limit : Executor :: max_threads ( ) ,
125
- } ) ;
126
-
127
116
/// The blocking executor.
128
117
struct Executor {
129
118
/// Inner state of the executor.
@@ -162,11 +151,31 @@ impl Executor {
162
151
Err ( _) => DEFAULT_MAX_THREADS ,
163
152
}
164
153
}
154
+
165
155
/// Spawns a future onto this executor.
166
156
///
167
157
/// Returns a [`Task`] handle for the spawned task.
168
158
fn spawn < T : Send + ' static > ( future : impl Future < Output = T > + Send + ' static ) -> Task < T > {
169
- let ( runnable, task) = async_task:: spawn ( future, |r| EXECUTOR . schedule ( r) ) ;
159
+ static EXECUTOR : OnceCell < Executor > = OnceCell :: new ( ) ;
160
+
161
+ let ( runnable, task) = async_task:: spawn ( future, |r| {
162
+ // Initialize the executor if we haven't already.
163
+ let executor = EXECUTOR . get_or_init_blocking ( || {
164
+ let thread_limit = Self :: max_threads ( ) ;
165
+ Executor {
166
+ inner : Mutex :: new ( Inner {
167
+ idle_count : 0 ,
168
+ thread_count : 0 ,
169
+ queue : VecDeque :: new ( ) ,
170
+ } ) ,
171
+ cvar : Condvar :: new ( ) ,
172
+ thread_limit,
173
+ }
174
+ } ) ;
175
+
176
+ // Schedule the task on our executor.
177
+ executor. schedule ( r)
178
+ } ) ;
170
179
runnable. schedule ( ) ;
171
180
task
172
181
}
@@ -223,8 +232,7 @@ impl Executor {
223
232
fn grow_pool ( & ' static self , mut inner : MutexGuard < ' static , Inner > ) {
224
233
// If runnable tasks greatly outnumber idle threads and there aren't too many threads
225
234
// already, then be aggressive: wake all idle threads and spawn one more thread.
226
- while inner. queue . len ( ) > inner. idle_count * 5 && inner. thread_count < EXECUTOR . thread_limit
227
- {
235
+ while inner. queue . len ( ) > inner. idle_count * 5 && inner. thread_count < self . thread_limit {
228
236
// The new thread starts in idle state.
229
237
inner. idle_count += 1 ;
230
238
inner. thread_count += 1 ;
0 commit comments