@@ -18,8 +18,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
18
18
use std:: sync:: { Arc , Condvar , Mutex } ;
19
19
use std:: thread;
20
20
21
- use futures:: prelude:: * ;
22
-
23
21
use crate :: options:: NetworkOption ;
24
22
use crate :: { error, FdbResult } ;
25
23
use foundationdb_sys as fdb_sys;
@@ -92,7 +90,7 @@ impl Default for FdbApiBuilder {
92
90
/// use foundationdb::api::FdbApiBuilder;
93
91
///
94
92
/// let network_builder = FdbApiBuilder::default().build().expect("fdb api initialized");
95
- /// network_builder.boot (|| {
93
+ /// network_builder.run (|| {
96
94
/// // do some work with foundationDB
97
95
/// }).expect("fdb network to run");
98
96
/// ```
@@ -110,7 +108,7 @@ impl NetworkBuilder {
110
108
/// Finalizes the initialization of the Network
111
109
///
112
110
/// It's not recommended to use this method unless you really know what you are doing.
113
- /// Otherwise, the `boot ` method is the **safe** and easiest way to do it.
111
+ /// Otherwise, the `run ` method is the **safe** and easiest way to do it.
114
112
///
115
113
/// In order to start the network you have to call the unsafe `NetworkRunner::run()` method.
116
114
/// This method starts the foundationdb network runloop, once started, the `NetworkStop::stop()`
@@ -145,89 +143,82 @@ impl NetworkBuilder {
145
143
}
146
144
147
145
/// Execute `f` with the FoundationDB Client API ready, this can only be called once per process.
146
+ /// Once this method returns, the FoundationDB Client API is stopped and cannot be restarted.
148
147
///
149
148
/// # Examples
150
149
///
151
150
/// ```rust
152
151
/// use foundationdb::api::FdbApiBuilder;
153
152
///
154
153
/// let network_builder = FdbApiBuilder::default().build().expect("fdb api initialized");
155
- /// network_builder.boot (|| {
154
+ /// network_builder.run (|| {
156
155
/// // do some interesting things with the API...
157
156
/// });
158
157
/// ```
159
- pub fn boot < T > ( self , f : impl ( FnOnce ( ) -> T ) + panic:: UnwindSafe ) -> FdbResult < T > {
160
- let ( runner, cond) = self . build ( ) ?;
161
-
162
- let net_thread = thread:: spawn ( move || {
163
- unsafe { runner. run ( ) } . expect ( "failed to run" ) ;
164
- } ) ;
165
-
166
- let network = cond. wait ( ) ;
158
+ pub fn run < T > ( self , f : impl FnOnce ( ) -> T ) -> FdbResult < T > {
159
+ // Safe because the returned object is dropped before this function returns
160
+ let must_drop = unsafe { self . boot ( ) ? } ;
167
161
168
- let res = panic :: catch_unwind ( f ) ;
162
+ let t = f ( ) ;
169
163
170
- if let Err ( err) = network. stop ( ) {
171
- eprintln ! ( "failed to stop network: {}" , err) ;
172
- // Not aborting can probably cause undefined behavior
173
- std:: process:: abort ( ) ;
174
- }
175
- net_thread. join ( ) . expect ( "failed to join fdb thread" ) ;
164
+ drop ( must_drop) ;
176
165
177
- match res {
178
- Err ( payload) => panic:: resume_unwind ( payload) ,
179
- Ok ( v) => Ok ( v) ,
180
- }
166
+ Ok ( t)
181
167
}
182
168
183
- /// Async execute `f` with the FoundationDB Client API ready, this can only be called once per process.
169
+ /// Initialize the FoundationDB Client API, this can only be called once per process.
170
+ ///
171
+ /// # Returns
172
+ ///
173
+ /// A `NetworkAutoStop` handle which must be dropped before the program exits.
174
+ ///
175
+ /// # Safety
176
+ ///
177
+ /// This method used to be safe in version `0.4`. But because `drop` on the returned object
178
+ /// might not be called before the program exits, it was found unsafe.
179
+ /// You should prefer the safe `run` variant.
180
+ /// If you still want to use this, you *MUST* ensure drop is called on the returned object
181
+ /// before the program exits. This is not required if the program is aborted.
184
182
///
185
183
/// # Examples
186
184
///
187
185
/// ```rust
188
186
/// use foundationdb::api::FdbApiBuilder;
189
187
///
190
188
/// let network_builder = FdbApiBuilder::default().build().expect("fdb api initialized");
191
- /// network_builder.boot_async(|| async {
189
+ /// let network = unsafe { network_builder.boot() }.expect("fdb network running");
190
+ /// // do some interesting things with the API...
191
+ /// drop(network);
192
+ /// ```
193
+ ///
194
+ /// ```rust
195
+ /// use foundationdb::api::FdbApiBuilder;
196
+ ///
197
+ /// #[tokio::main]
198
+ /// async fn main() {
199
+ /// let network_builder = FdbApiBuilder::default().build().expect("fdb api initialized");
200
+ /// let network = unsafe { network_builder.boot() }.expect("fdb network running");
192
201
/// // do some interesting things with the API...
193
- /// });
202
+ /// drop(network);
203
+ /// }
194
204
/// ```
195
- pub async fn boot_async < F , Fut , T > ( self , f : F ) -> FdbResult < T >
196
- where
197
- F : ( FnOnce ( ) -> Fut ) + panic:: UnwindSafe ,
198
- Fut : Future < Output = T > + panic:: UnwindSafe ,
199
- {
205
+ pub unsafe fn boot ( self ) -> FdbResult < NetworkAutoStop > {
200
206
let ( runner, cond) = self . build ( ) ?;
201
207
202
- let net_thread = thread:: spawn ( move || {
203
- unsafe { runner. run ( ) } . expect ( "failed to run" ) ;
204
- } ) ;
208
+ let net_thread = runner. spawn ( ) ;
205
209
206
210
let network = cond. wait ( ) ;
207
211
208
- let res = panic:: catch_unwind ( f) ;
209
- let res = match res {
210
- Ok ( fut) => fut. catch_unwind ( ) . await ,
211
- Err ( err) => Err ( err) ,
212
- } ;
213
-
214
- if let Err ( err) = network. stop ( ) {
215
- eprintln ! ( "failed to stop network: {}" , err) ;
216
- // Not aborting can probably cause undefined behavior
217
- std:: process:: abort ( ) ;
218
- }
219
- net_thread. join ( ) . expect ( "failed to join fdb thread" ) ;
220
-
221
- match res {
222
- Err ( payload) => panic:: resume_unwind ( payload) ,
223
- Ok ( v) => Ok ( v) ,
224
- }
212
+ Ok ( NetworkAutoStop {
213
+ handle : Some ( net_thread) ,
214
+ network : Some ( network) ,
215
+ } )
225
216
}
226
217
}
227
218
228
219
/// A foundationDB network event loop runner
229
220
///
230
- /// Most of the time you should never need to use this directly and use `NetworkBuilder::boot ()`.
221
+ /// Most of the time you should never need to use this directly and use `NetworkBuilder::run ()`.
231
222
pub struct NetworkRunner {
232
223
cond : Arc < ( Mutex < bool > , Condvar ) > ,
233
224
}
@@ -257,11 +248,17 @@ impl NetworkRunner {
257
248
258
249
error:: eval ( unsafe { fdb_sys:: fdb_run_network ( ) } )
259
250
}
251
+
252
+ unsafe fn spawn ( self ) -> thread:: JoinHandle < ( ) > {
253
+ thread:: spawn ( move || {
254
+ self . run ( ) . expect ( "failed to run network thread" ) ;
255
+ } )
256
+ }
260
257
}
261
258
262
259
/// A condition object that can wait for the associated `NetworkRunner` to actually run.
263
260
///
264
- /// Most of the time you should never need to use this directly and use `NetworkBuilder::boot ()`.
261
+ /// Most of the time you should never need to use this directly and use `NetworkBuilder::run ()`.
265
262
pub struct NetworkWait {
266
263
cond : Arc < ( Mutex < bool > , Condvar ) > ,
267
264
}
@@ -284,7 +281,7 @@ impl NetworkWait {
284
281
285
282
/// Allow to stop the associated and running `NetworkRunner`.
286
283
///
287
- /// Most of the time you should never need to use this directly and use `NetworkBuilder::boot ()`.
284
+ /// Most of the time you should never need to use this directly and use `NetworkBuilder::run ()`.
288
285
pub struct NetworkStop {
289
286
_private : ( ) ,
290
287
}
@@ -296,6 +293,26 @@ impl NetworkStop {
296
293
}
297
294
}
298
295
296
+ /// Stop the associated `NetworkRunner` and thread if dropped
297
+ pub struct NetworkAutoStop {
298
+ network : Option < NetworkStop > ,
299
+ handle : Option < std:: thread:: JoinHandle < ( ) > > ,
300
+ }
301
+ impl Drop for NetworkAutoStop {
302
+ fn drop ( & mut self ) {
303
+ if let Err ( err) = self . network . take ( ) . unwrap ( ) . stop ( ) {
304
+ eprintln ! ( "failed to stop network: {}" , err) ;
305
+ // Not aborting can probably cause undefined behavior
306
+ std:: process:: abort ( ) ;
307
+ }
308
+ self . handle
309
+ . take ( )
310
+ . unwrap ( )
311
+ . join ( )
312
+ . expect ( "failed to join fdb thread" ) ;
313
+ }
314
+ }
315
+
299
316
#[ cfg( test) ]
300
317
mod tests {
301
318
use super :: * ;
0 commit comments