Skip to content

Commit 3e9630c

Browse files
committed
Adding in DoneHandle to clean up memory leaks
1 parent 77f5a2f commit 3e9630c

File tree

4 files changed

+74
-16
lines changed

4 files changed

+74
-16
lines changed

examples/promise/src/main.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ extern crate futures;
55
use futures::Future;
66
use stdweb::unstable::{TryInto};
77
use stdweb::web::error::Error;
8-
use stdweb::{Null, Promise, PromiseFuture};
8+
use stdweb::{Null, Promise, PromiseFuture, Value};
99
use std::rc::Rc;
1010
use std::cell::RefCell;
1111
use futures::{Poll, Async};
@@ -253,6 +253,32 @@ fn test_timeout() {
253253
}
254254

255255

256+
fn test_callback_drop() {
257+
let x: Promise = js!( return new Promise( function (yes, no) {} ) ).try_into().unwrap();
258+
259+
struct Foo( Value );
260+
261+
impl Drop for Foo {
262+
fn drop( &mut self ) {
263+
js! { @(no_return)
264+
console.log( "Callback dropped" );
265+
clearInterval( @{&self.0} );
266+
}
267+
}
268+
}
269+
270+
let y = Foo( js!(
271+
return setInterval( function () {
272+
console.log( "Callback drop interval" );
273+
}, 1000 );
274+
) );
275+
276+
x.done( move |_result: Result< (), () >| {
277+
let _z = y;
278+
} );
279+
}
280+
281+
256282
fn main() {
257283
stdweb::initialize();
258284

@@ -262,6 +288,7 @@ fn main() {
262288
test_notify();
263289
test_timeout();
264290
test_error_conversion();
291+
test_callback_drop();
265292

266293
stdweb::event_loop();
267294
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ pub use webcore::instance_of::InstanceOf;
131131
pub use webcore::reference_type::ReferenceType;
132132
pub use webcore::serialization::JsSerialize;
133133

134-
pub use webcore::promise::Promise;
134+
pub use webcore::promise::{Promise, DoneHandle};
135135

136136
#[cfg(feature = "futures")]
137137
pub use webcore::promise_future::PromiseFuture;

src/webcore/promise.rs

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,22 @@ use futures::unsync::oneshot::channel;
1010
use super::promise_future::PromiseFuture;
1111

1212

13+
#[derive( Debug, Clone )]
14+
pub struct DoneHandle {
15+
callback: Value,
16+
done: Value,
17+
}
18+
19+
impl Drop for DoneHandle {
20+
fn drop( &mut self ) {
21+
js! { @(no_return)
22+
@{&self.done}[0] = true;
23+
@{&self.callback}.drop();
24+
}
25+
}
26+
}
27+
28+
1329
/// A `Promise` object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
1430
///
1531
/// In most situations you shouldn't use this, use [`PromiseFuture`](struct.PromiseFuture.html) instead.
@@ -93,7 +109,7 @@ impl Promise {
93109
///
94110
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then)
95111
// https://www.ecma-international.org/ecma-262/6.0/#sec-performpromisethen
96-
pub fn done< A, B, F >( &self, callback: F )
112+
pub fn done< A, B, F >( &self, callback: F ) -> DoneHandle
97113
where A: TryFrom< Value >,
98114
B: TryFrom< Value >,
99115
// TODO these Debug constraints are only needed because of unwrap
@@ -106,6 +122,7 @@ impl Promise {
106122
// TODO figure out a way to avoid the unwrap
107123
let value: A = value.try_into().unwrap();
108124
Ok( value )
125+
109126
} else {
110127
// TODO figure out a way to avoid the unwrap
111128
let value: B = value.try_into().unwrap();
@@ -115,15 +132,29 @@ impl Promise {
115132
callback( value );
116133
};
117134

118-
js! { @(no_return)
119-
var callback = @{Once( callback )};
135+
let callback = js!( return @{Once( callback )}; );
136+
137+
let done = js!(
138+
var callback = @{&callback};
139+
var done = [ false ];
120140

121141
// TODO don't swallow any errors thrown inside callback
122142
@{self}.then( function ( value ) {
123-
callback( value, true );
143+
if ( !done[0] ) {
144+
callback( value, true );
145+
}
124146
}, function ( value ) {
125-
callback( value, false );
147+
if ( !done[0] ) {
148+
callback( value, false );
149+
}
126150
} );
151+
152+
return done;
153+
);
154+
155+
DoneHandle {
156+
callback,
157+
done
127158
}
128159
}
129160

@@ -148,16 +179,15 @@ impl Promise {
148179

149180
let ( sender, receiver ) = channel();
150181

151-
self.done( |value| {
152-
// TODO is this correct ?
153-
match sender.send( value ) {
154-
Ok( _ ) => {},
155-
Err( _ ) => {},
156-
};
157-
} );
158-
159182
PromiseFuture {
160183
future: receiver,
184+
_done_handle: self.done( |value| {
185+
// TODO is this correct ?
186+
match sender.send( value ) {
187+
Ok( _ ) => {},
188+
Err( _ ) => {},
189+
};
190+
} )
161191
}
162192
}
163193
}

src/webcore/promise_future.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use webapi::error;
55
use futures::{Future, Poll, Async};
66
use futures::unsync::oneshot::Receiver;
77
use webcore::promise_executor::spawn;
8-
use super::promise::Promise;
8+
use super::promise::{Promise, DoneHandle};
99

1010

1111
/// This allows you to use a JavaScript [`Promise`](struct.Promise.html) as if it is a Rust [`Future`](https://docs.rs/futures/0.1.18/futures/future/trait.Future.html).
@@ -21,6 +21,7 @@ use super::promise::Promise;
2121
/// ```
2222
pub struct PromiseFuture< Value, Error = error::Error > {
2323
pub(crate) future: Receiver< Result< Value, Error > >,
24+
pub(crate) _done_handle: DoneHandle,
2425
}
2526

2627
impl PromiseFuture< (), () > {

0 commit comments

Comments
 (0)