diff options
| author | bors <bors@rust-lang.org> | 2013-08-20 09:42:00 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2013-08-20 09:42:00 -0700 |
| commit | 67c954e365970e4c2cd06f0c50724656d7010f45 (patch) | |
| tree | d3b73e89a241ab0210e53d80b1846394139bf693 /src/libstd/rt/uv | |
| parent | 7f268128954fef84dcbcb7c9fe77e2a107e0bf69 (diff) | |
| parent | e5ccf13668ed7b66d6efd9a1a03926e98546705d (diff) | |
| download | rust-67c954e365970e4c2cd06f0c50724656d7010f45.tar.gz rust-67c954e365970e4c2cd06f0c50724656d7010f45.zip | |
auto merge of #8566 : toddaaro/rust/idle-opt+cleaning, r=catamorphism,brson
Instead of a furious storm of idle callbacks we just have one. This is a major performance gain - around 40% on my machine for the ping pong bench. Also in this PR is a cleanup commit for the scheduler code. Was previously up as a separate PR, but bors load + imminent merge hell led me to roll them together. Was #8549.
Diffstat (limited to 'src/libstd/rt/uv')
| -rw-r--r-- | src/libstd/rt/uv/idle.rs | 14 | ||||
| -rw-r--r-- | src/libstd/rt/uv/uvio.rs | 89 |
2 files changed, 96 insertions, 7 deletions
diff --git a/src/libstd/rt/uv/idle.rs b/src/libstd/rt/uv/idle.rs index b73be9f7250..a21146620ca 100644 --- a/src/libstd/rt/uv/idle.rs +++ b/src/libstd/rt/uv/idle.rs @@ -48,6 +48,20 @@ impl IdleWatcher { } } + pub fn restart(&mut self) { + unsafe { + assert!(0 == uvll::idle_start(self.native_handle(), idle_cb)) + }; + + extern fn idle_cb(handle: *uvll::uv_idle_t, status: c_int) { + let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); + let data = idle_watcher.get_watcher_data(); + let cb: &IdleCallback = data.idle_cb.get_ref(); + let status = status_to_maybe_uv_error(idle_watcher, status); + (*cb)(idle_watcher, status); + } + } + pub fn stop(&mut self) { // NB: Not resetting the Rust idle_cb to None here because `stop` is // likely called from *within* the idle callback, causing a use after diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index d4794da9b0f..078dce4f0c8 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -117,6 +117,15 @@ impl EventLoop for UvEventLoop { } } + fn pausible_idle_callback(&mut self) -> ~PausibleIdleCallback { + let idle_watcher = IdleWatcher::new(self.uvio.uv_loop()); + return ~UvPausibleIdleCallback { + watcher: idle_watcher, + idle_flag: false, + closed: false + }; + } + fn callback_ms(&mut self, ms: u64, f: ~fn()) { let mut timer = TimerWatcher::new(self.uvio.uv_loop()); do timer.start(ms, 0) |timer, status| { @@ -135,6 +144,44 @@ impl EventLoop for UvEventLoop { } } +pub struct UvPausibleIdleCallback { + watcher: IdleWatcher, + idle_flag: bool, + closed: bool +} + +impl UvPausibleIdleCallback { + #[inline] + pub fn start(&mut self, f: ~fn()) { + do self.watcher.start |_idle_watcher, _status| { + f(); + }; + self.idle_flag = true; + } + #[inline] + pub fn pause(&mut self) { + if self.idle_flag == true { + self.watcher.stop(); + self.idle_flag = false; + } + } + #[inline] + pub fn resume(&mut self) { + if self.idle_flag == false { + self.watcher.restart(); + self.idle_flag = true; + } + } + #[inline] + pub fn close(&mut self) { + self.pause(); + if !self.closed { + self.closed = true; + self.watcher.close(||()); + } + } +} + #[test] fn test_callback_run_once() { do run_in_bare_thread { @@ -163,14 +210,39 @@ impl UvRemoteCallback { let exit_flag_clone = exit_flag.clone(); let async = do AsyncWatcher::new(loop_) |watcher, status| { assert!(status.is_none()); + + // The synchronization logic here is subtle. To review, + // the uv async handle type promises that, after it is + // triggered the remote callback is definitely called at + // least once. UvRemoteCallback needs to maintain those + // semantics while also shutting down cleanly from the + // dtor. In our case that means that, when the + // UvRemoteCallback dtor calls `async.send()`, here `f` is + // always called later. + + // In the dtor both the exit flag is set and the async + // callback fired under a lock. Here, before calling `f`, + // we take the lock and check the flag. Because we are + // checking the flag before calling `f`, and the flag is + // set under the same lock as the send, then if the flag + // is set then we're guaranteed to call `f` after the + // final send. + + // If the check was done after `f()` then there would be a + // period between that call and the check where the dtor + // could be called in the other thread, missing the final + // callback while still destroying the handle. + + let should_exit = unsafe { + exit_flag_clone.with_imm(|&should_exit| should_exit) + }; + f(); - unsafe { - do exit_flag_clone.with_imm |&should_exit| { - if should_exit { - watcher.close(||()); - } - } + + if should_exit { + watcher.close(||()); } + }; UvRemoteCallback { async: async, @@ -219,7 +291,10 @@ mod test_remote { let tube_clone = tube_clone.clone(); let tube_clone_cell = Cell::new(tube_clone); let remote = do sched.event_loop.remote_callback { - tube_clone_cell.take().send(1); + // This could be called multiple times + if !tube_clone_cell.is_empty() { + tube_clone_cell.take().send(1); + } }; remote_cell.put_back(remote); } |
