@@ -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,9 +90,9 @@ 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(|| {
96
- /// // do some work with foundationDB
97
- /// }).expect("fdb network to run" );
93
+ /// let guard = unsafe { network_builder.boot() };
94
+ /// // do some work with foundationDB
95
+ /// drop(guard );
98
96
/// ```
99
97
pub struct NetworkBuilder {
100
98
_private : ( ) ,
@@ -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()`
@@ -144,90 +142,59 @@ impl NetworkBuilder {
144
142
Ok ( ( NetworkRunner { cond : cond. clone ( ) } , NetworkWait { cond } ) )
145
143
}
146
144
147
- /// Execute `f` with the FoundationDB Client API ready, this can only be called once per process.
145
+ /// Initialize the FoundationDB Client API, this can only be called once per process.
146
+ ///
147
+ /// # Returns
148
+ ///
149
+ /// A `NetworkAutoStop` handle which must be dropped before the program exits.
150
+ ///
151
+ /// # Safety
152
+ ///
153
+ /// This method used to be safe in version `0.4`. But because `drop` on the returned object
154
+ /// might not be called before the program exits, it was found unsafe.
155
+ /// You should prefer the safe `run` variant.
156
+ /// If you still want to use this, you *MUST* ensure drop is called on the returned object
157
+ /// before the program exits. This is not required if the program is aborted.
148
158
///
149
159
/// # Examples
150
160
///
151
161
/// ```rust
152
162
/// use foundationdb::api::FdbApiBuilder;
153
163
///
154
164
/// let network_builder = FdbApiBuilder::default().build().expect("fdb api initialized");
155
- /// network_builder.boot(|| {
156
- /// // do some interesting things with the API...
157
- /// } );
165
+ /// let network = unsafe { network_builder.boot() };
166
+ /// // do some interesting things with the API...
167
+ /// drop(network );
158
168
/// ```
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 ( ) ;
167
-
168
- let res = panic:: catch_unwind ( f) ;
169
-
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" ) ;
176
-
177
- match res {
178
- Err ( payload) => panic:: resume_unwind ( payload) ,
179
- Ok ( v) => Ok ( v) ,
180
- }
181
- }
182
-
183
- /// Async execute `f` with the FoundationDB Client API ready, this can only be called once per process.
184
- ///
185
- /// # Examples
186
169
///
187
170
/// ```rust
188
171
/// use foundationdb::api::FdbApiBuilder;
189
172
///
190
- /// let network_builder = FdbApiBuilder::default().build().expect("fdb api initialized");
191
- /// network_builder.boot_async(|| async {
173
+ /// #[tokio::main]
174
+ /// async fn main() {
175
+ /// let network_builder = FdbApiBuilder::default().build().expect("fdb api initialized");
176
+ /// let network = unsafe { network_builder.boot() };
192
177
/// // do some interesting things with the API...
193
- /// });
178
+ /// drop(network);
179
+ /// }
194
180
/// ```
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
- {
181
+ pub unsafe fn boot ( self ) -> FdbResult < NetworkAutoStop > {
200
182
let ( runner, cond) = self . build ( ) ?;
201
183
202
- let net_thread = thread:: spawn ( move || {
203
- unsafe { runner. run ( ) } . expect ( "failed to run" ) ;
204
- } ) ;
184
+ let net_thread = runner. spawn ( ) ;
205
185
206
186
let network = cond. wait ( ) ;
207
187
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
- }
188
+ Ok ( NetworkAutoStop {
189
+ handle : Some ( net_thread) ,
190
+ network : Some ( network) ,
191
+ } )
225
192
}
226
193
}
227
194
228
195
/// A foundationDB network event loop runner
229
196
///
230
- /// Most of the time you should never need to use this directly and use `NetworkBuilder::boot ()`.
197
+ /// Most of the time you should never need to use this directly and use `NetworkBuilder::run ()`.
231
198
pub struct NetworkRunner {
232
199
cond : Arc < ( Mutex < bool > , Condvar ) > ,
233
200
}
@@ -257,11 +224,17 @@ impl NetworkRunner {
257
224
258
225
error:: eval ( unsafe { fdb_sys:: fdb_run_network ( ) } )
259
226
}
227
+
228
+ unsafe fn spawn ( self ) -> thread:: JoinHandle < ( ) > {
229
+ thread:: spawn ( move || {
230
+ self . run ( ) . expect ( "failed to run network thread" ) ;
231
+ } )
232
+ }
260
233
}
261
234
262
235
/// A condition object that can wait for the associated `NetworkRunner` to actually run.
263
236
///
264
- /// Most of the time you should never need to use this directly and use `NetworkBuilder::boot ()`.
237
+ /// Most of the time you should never need to use this directly and use `NetworkBuilder::run ()`.
265
238
pub struct NetworkWait {
266
239
cond : Arc < ( Mutex < bool > , Condvar ) > ,
267
240
}
@@ -284,7 +257,7 @@ impl NetworkWait {
284
257
285
258
/// Allow to stop the associated and running `NetworkRunner`.
286
259
///
287
- /// Most of the time you should never need to use this directly and use `NetworkBuilder::boot ()`.
260
+ /// Most of the time you should never need to use this directly and use `NetworkBuilder::run ()`.
288
261
pub struct NetworkStop {
289
262
_private : ( ) ,
290
263
}
@@ -296,6 +269,26 @@ impl NetworkStop {
296
269
}
297
270
}
298
271
272
+ /// Stop the associated `NetworkRunner` and thread if dropped
273
+ pub struct NetworkAutoStop {
274
+ network : Option < NetworkStop > ,
275
+ handle : Option < std:: thread:: JoinHandle < ( ) > > ,
276
+ }
277
+ impl Drop for NetworkAutoStop {
278
+ fn drop ( & mut self ) {
279
+ if let Err ( err) = self . network . take ( ) . unwrap ( ) . stop ( ) {
280
+ eprintln ! ( "failed to stop network: {}" , err) ;
281
+ // Not aborting can probably cause undefined behavior
282
+ std:: process:: abort ( ) ;
283
+ }
284
+ self . handle
285
+ . take ( )
286
+ . unwrap ( )
287
+ . join ( )
288
+ . expect ( "failed to join fdb thread" ) ;
289
+ }
290
+ }
291
+
299
292
#[ cfg( test) ]
300
293
mod tests {
301
294
use super :: * ;
0 commit comments