Skip to content

Commit 426b025

Browse files
Pauankoute
authored andcommitted
Add DiscardOnDrop and make Promise::done auto-cancel on drop (#127)
1 parent b9ed6a2 commit 426b025

File tree

9 files changed

+264
-29
lines changed

9 files changed

+264
-29
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ description = "A standard library for the client-side Web"
1414
build = "build.rs"
1515

1616
[dependencies]
17+
discard = "1.0.3"
1718
serde = { version = "1", optional = true }
1819
serde_json = { version = "1", optional = true }
1920
futures = { version = "0.1.18", optional = true }

src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ extern crate futures;
141141
#[macro_use]
142142
extern crate stdweb_derive;
143143

144+
extern crate discard;
145+
144146
#[macro_use]
145147
mod webcore;
146148
mod webapi;
@@ -173,8 +175,10 @@ pub use webcore::instance_of::InstanceOf;
173175
pub use webcore::reference_type::ReferenceType;
174176
pub use webcore::serialization::JsSerialize;
175177

178+
pub use webcore::discard::DiscardOnDrop;
179+
176180
#[cfg(feature = "experimental_features_which_may_break_on_minor_version_bumps")]
177-
pub use webcore::promise::Promise;
181+
pub use webcore::promise::{Promise, DoneHandle};
178182

179183
#[cfg(all(
180184
feature = "futures",

src/webapi/event_target.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,16 @@ impl fmt::Debug for EventListenerHandle {
2020
}
2121

2222
impl EventListenerHandle {
23-
/// Removes the handler from the [IEventTarget](trait.IEventTarget.html) on
23+
/// Removes the listener from the [IEventTarget](trait.IEventTarget.html) on
2424
/// which it was previously registered.
2525
///
2626
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener)
2727
// https://dom.spec.whatwg.org/#ref-for-dom-eventtarget-removeeventlistener%E2%91%A0
2828
pub fn remove( self ) {
2929
js! { @(no_return)
30-
var self = @{self.reference};
31-
var event_type = @{self.event_type};
32-
var listener = @{self.listener_reference};
30+
var listener = @{&self.listener_reference};
31+
@{&self.reference}.removeEventListener( @{self.event_type}, listener );
3332
listener.drop();
34-
self.removeEventListener( event_type, listener );
3533
}
3634
}
3735
}
@@ -42,7 +40,7 @@ impl EventListenerHandle {
4240
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
4341
// https://dom.spec.whatwg.org/#eventtarget
4442
pub trait IEventTarget: ReferenceType {
45-
/// Adds given event handler to the list the list of event listeners for
43+
/// Adds given event handler to the list of event listeners for
4644
/// the specified `EventTarget` on which it's called.
4745
///
4846
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
@@ -51,6 +49,7 @@ pub trait IEventTarget: ReferenceType {
5149
where T: ConcreteEvent, F: FnMut( T ) + 'static
5250
{
5351
let reference = self.as_ref();
52+
5453
let listener_reference = js! {
5554
var listener = @{listener};
5655
@{reference}.addEventListener( @{T::EVENT_TYPE}, listener );

src/webapi/mutation_observer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ impl std::ops::Deref for MutationObserverHandle {
171171
type Target = MutationObserver;
172172

173173
#[inline]
174-
fn deref(&self) -> &Self::Target {
174+
fn deref( &self ) -> &Self::Target {
175175
&self.mutation_observer
176176
}
177177
}

src/webapi/window.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ impl RequestAnimationFrameHandle {
1616
/// Cancels an animation frame request.
1717
///
1818
/// [(Javascript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Window/cancelAnimationFrame)
19-
pub fn cancel(self) {
20-
js!{
21-
var val = @{self.0};
19+
pub fn cancel( self ) {
20+
js! { @(no_return)
21+
var val = @{&self.0};
2222
val.window.cancelAnimationFrame(val.request);
2323
val.callback.drop();
24-
};
24+
}
2525
}
2626
}
2727

src/webcore/discard.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
use discard;
2+
use discard::Discard;
3+
use std::ops::{Deref, DerefMut};
4+
5+
6+
/// If you have a value which implements [`Discard`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html), you can use
7+
/// [`DiscardOnDrop::new(value)`](struct.DiscardOnDrop.html#method.new) which will wrap the value.
8+
/// When the wrapper is dropped it will automatically call [`value.discard()`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html#tymethod.discard).
9+
///
10+
/// You can use the [`leak`](#method.leak) method to unwrap it (which returns `value`). This causes
11+
/// it to no longer call [`discard`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html#tymethod.discard) when it is dropped, which
12+
/// means it will usually leak memory unless you manually call [`discard`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html#tymethod.discard).
13+
#[must_use = "
14+
15+
The DiscardOnDrop is unused, which causes it to be immediately discarded.
16+
You probably don't want that to happen.
17+
18+
How to fix this:
19+
20+
* Store the DiscardOnDrop in a variable or data structure.
21+
22+
* Or use the leak() method which will cause it to not be
23+
discarded (this will usually leak memory!)
24+
25+
See the documentation for more details.
26+
"]
27+
#[derive(Debug)]
28+
pub struct DiscardOnDrop< A: Discard >( discard::DiscardOnDrop< A > );
29+
30+
impl< A: Discard > DiscardOnDrop< A > {
31+
/// Creates a new `DiscardOnDrop`.
32+
///
33+
/// When the `DiscardOnDrop` is dropped it will automatically call [`discarder.discard()`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html#tymethod.discard).
34+
#[inline]
35+
pub fn new( discarder: A ) -> Self {
36+
DiscardOnDrop( discard::DiscardOnDrop::new( discarder ) )
37+
}
38+
39+
/// Returns the wrapped `discarder`.
40+
///
41+
/// It will no longer automatically call [`discarder.discard()`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html#tymethod.discard), so this will usually leak
42+
/// memory unless you manually call [`discarder.discard()`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html#tymethod.discard).
43+
#[inline]
44+
pub fn leak( self ) -> A {
45+
discard::DiscardOnDrop::leak( self.0 )
46+
}
47+
}
48+
49+
impl< A: Discard > Deref for DiscardOnDrop< A > {
50+
type Target = A;
51+
52+
#[inline]
53+
fn deref( &self ) -> &Self::Target {
54+
&*self.0
55+
}
56+
}
57+
58+
impl< A: Discard > DerefMut for DiscardOnDrop< A > {
59+
#[inline]
60+
fn deref_mut( &mut self ) -> &mut Self::Target {
61+
&mut *self.0
62+
}
63+
}
64+
65+
66+
#[cfg(test)]
67+
mod tests {
68+
use discard::Discard;
69+
use super::DiscardOnDrop;
70+
use std::rc::Rc;
71+
use std::cell::Cell;
72+
73+
struct Foo( Rc< Cell< bool > > );
74+
75+
impl Foo {
76+
fn new() -> Self {
77+
Foo( Rc::new( Cell::new( false ) ) )
78+
}
79+
80+
fn dropped( &self ) -> Rc< Cell< bool > > {
81+
self.0.clone()
82+
}
83+
84+
fn as_mut( &mut self ) -> &mut Self {
85+
self
86+
}
87+
}
88+
89+
impl Discard for Foo {
90+
fn discard( self ) {
91+
self.0.set( true );
92+
}
93+
}
94+
95+
96+
#[test]
97+
fn unused() {
98+
Foo::new();
99+
}
100+
101+
#[test]
102+
fn unused_discard_on_drop() {
103+
DiscardOnDrop::new( Foo::new() );
104+
}
105+
106+
#[test]
107+
fn discard() {
108+
let foo = Foo::new();
109+
110+
let dropped = foo.dropped();
111+
112+
assert_eq!( dropped.get(), false );
113+
foo.discard();
114+
assert_eq!( dropped.get(), true );
115+
}
116+
117+
#[test]
118+
fn no_discard() {
119+
let foo = Foo::new();
120+
121+
let dropped = foo.dropped();
122+
123+
assert_eq!( dropped.get(), false );
124+
drop( foo );
125+
assert_eq!( dropped.get(), false );
126+
}
127+
128+
#[test]
129+
fn discard_on_drop() {
130+
let foo = DiscardOnDrop::new( Foo::new() );
131+
132+
let dropped = foo.dropped();
133+
134+
assert_eq!( dropped.get(), false );
135+
drop( foo );
136+
assert_eq!( dropped.get(), true );
137+
}
138+
139+
#[test]
140+
fn leak() {
141+
let foo = DiscardOnDrop::new(Foo::new());
142+
143+
let dropped = foo.dropped();
144+
145+
assert_eq!( dropped.get(), false );
146+
drop( foo.leak() );
147+
assert_eq!( dropped.get(), false );
148+
}
149+
150+
#[test]
151+
fn deref_mut() {
152+
let mut foo = DiscardOnDrop::new( Foo::new() );
153+
154+
let dropped = foo.as_mut().dropped();
155+
156+
assert_eq!( dropped.get(), false );
157+
drop( foo.leak() );
158+
assert_eq!( dropped.get(), false );
159+
}
160+
}

src/webcore/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod once;
1717
pub mod instance_of;
1818
pub mod reference_type;
1919
pub mod promise;
20+
pub mod discard;
2021

2122
#[cfg(feature = "futures")]
2223
pub mod promise_future;

0 commit comments

Comments
 (0)