@@ -18,8 +18,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
1818use std:: sync:: { Arc , Condvar , Mutex } ;
1919use std:: thread;
2020
21- use futures:: prelude:: * ;
22-
2321use crate :: options:: NetworkOption ;
2422use crate :: { error, FdbResult } ;
2523use foundationdb_sys as fdb_sys;
@@ -92,9 +90,9 @@ impl Default for FdbApiBuilder {
9290/// use foundationdb::api::FdbApiBuilder;
9391///
9492/// 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 );
9896/// ```
9997pub struct NetworkBuilder {
10098 _private : ( ) ,
@@ -110,7 +108,7 @@ impl NetworkBuilder {
110108 /// Finalizes the initialization of the Network
111109 ///
112110 /// 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.
114112 ///
115113 /// In order to start the network you have to call the unsafe `NetworkRunner::run()` method.
116114 /// This method starts the foundationdb network runloop, once started, the `NetworkStop::stop()`
@@ -144,90 +142,59 @@ impl NetworkBuilder {
144142 Ok ( ( NetworkRunner { cond : cond. clone ( ) } , NetworkWait { cond } ) )
145143 }
146144
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.
148158 ///
149159 /// # Examples
150160 ///
151161 /// ```rust
152162 /// use foundationdb::api::FdbApiBuilder;
153163 ///
154164 /// 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 );
158168 /// ```
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
186169 ///
187170 /// ```rust
188171 /// use foundationdb::api::FdbApiBuilder;
189172 ///
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() };
192177 /// // do some interesting things with the API...
193- /// });
178+ /// drop(network);
179+ /// }
194180 /// ```
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 > {
200182 let ( runner, cond) = self . build ( ) ?;
201183
202- let net_thread = thread:: spawn ( move || {
203- unsafe { runner. run ( ) } . expect ( "failed to run" ) ;
204- } ) ;
184+ let net_thread = runner. spawn ( ) ;
205185
206186 let network = cond. wait ( ) ;
207187
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+ } )
225192 }
226193}
227194
228195/// A foundationDB network event loop runner
229196///
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 ()`.
231198pub struct NetworkRunner {
232199 cond : Arc < ( Mutex < bool > , Condvar ) > ,
233200}
@@ -257,11 +224,17 @@ impl NetworkRunner {
257224
258225 error:: eval ( unsafe { fdb_sys:: fdb_run_network ( ) } )
259226 }
227+
228+ unsafe fn spawn ( self ) -> thread:: JoinHandle < ( ) > {
229+ thread:: spawn ( move || {
230+ self . run ( ) . expect ( "failed to run network thread" ) ;
231+ } )
232+ }
260233}
261234
262235/// A condition object that can wait for the associated `NetworkRunner` to actually run.
263236///
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 ()`.
265238pub struct NetworkWait {
266239 cond : Arc < ( Mutex < bool > , Condvar ) > ,
267240}
@@ -284,7 +257,7 @@ impl NetworkWait {
284257
285258/// Allow to stop the associated and running `NetworkRunner`.
286259///
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 ()`.
288261pub struct NetworkStop {
289262 _private : ( ) ,
290263}
@@ -296,6 +269,26 @@ impl NetworkStop {
296269 }
297270}
298271
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+
299292#[ cfg( test) ]
300293mod tests {
301294 use super :: * ;
0 commit comments