// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! This is a basic event loop implementation not meant for any "real purposes" //! other than testing the scheduler and proving that it's possible to have a //! pluggable event loop. //! //! This implementation is also used as the fallback implementation of an event //! loop if no other one is provided (and M:N scheduling is desired). use std::cast; use std::mem::replace; use std::rt::rtio::{EventLoop, IoFactory, RemoteCallback, PausableIdleCallback, Callback}; use std::unstable::sync::Exclusive; /// This is the only exported function from this module. pub fn event_loop() -> ~EventLoop { ~BasicLoop::new() as ~EventLoop } struct BasicLoop { work: ~[proc()], // pending work idle: Option<*mut BasicPausable>, // only one is allowed remotes: ~[(uint, ~Callback)], next_remote: uint, messages: Exclusive<~[Message]>, } enum Message { RunRemote(uint), RemoveRemote(uint) } impl BasicLoop { fn new() -> BasicLoop { BasicLoop { work: ~[], idle: None, next_remote: 0, remotes: ~[], messages: Exclusive::new(~[]), } } /// Process everything in the work queue (continually) fn work(&mut self) { while self.work.len() > 0 { for work in replace(&mut self.work, ~[]).move_iter() { work(); } } } fn remote_work(&mut self) { let messages = unsafe { self.messages.with(|messages| { if messages.len() > 0 { Some(replace(messages, ~[])) } else { None } }) }; let messages = match messages { Some(m) => m, None => return }; for message in messages.iter() { self.message(*message); } } fn message(&mut self, message: Message) { match message { RunRemote(i) => { match self.remotes.mut_iter().find(|& &(id, _)| id == i) { Some(&(_, ref mut f)) => f.call(), None => unreachable!() } } RemoveRemote(i) => { match self.remotes.iter().position(|&(id, _)| id == i) { Some(i) => { self.remotes.remove(i).unwrap(); } None => unreachable!() } } } } /// Run the idle callback if one is registered fn idle(&mut self) { unsafe { match self.idle { Some(idle) => { if (*idle).active { (*idle).work.call(); } } None => {} } } } fn has_idle(&self) -> bool { unsafe { self.idle.is_some() && (**self.idle.get_ref()).active } } } impl EventLoop for BasicLoop { fn run(&mut self) { // Not exactly efficient, but it gets the job done. while self.remotes.len() > 0 || self.work.len() > 0 || self.has_idle() { self.work(); self.remote_work(); if self.has_idle() { self.idle(); continue } unsafe { // We block here if we have no messages to process and we may // receive a message at a later date self.messages.hold_and_wait(|messages| { self.remotes.len() > 0 && messages.len() == 0 && self.work.len() == 0 }) } } } fn callback(&mut self, f: proc()) { self.work.push(f); } // FIXME: Seems like a really weird requirement to have an event loop provide. fn pausable_idle_callback(&mut self, cb: ~Callback) -> ~PausableIdleCallback { let callback = ~BasicPausable::new(self, cb); rtassert!(self.idle.is_none()); unsafe { let cb_ptr: &*mut BasicPausable = cast::transmute(&callback); self.idle = Some(*cb_ptr); } return callback as ~PausableIdleCallback; } fn remote_callback(&mut self, f: ~Callback) -> ~RemoteCallback { let id = self.next_remote; self.next_remote += 1; self.remotes.push((id, f)); ~BasicRemote::new(self.messages.clone(), id) as ~RemoteCallback } fn io<'a>(&'a mut self) -> Option<&'a mut IoFactory> { None } fn has_active_io(&self) -> bool { false } } struct BasicRemote { queue: Exclusive<~[Message]>, id: uint, } impl BasicRemote { fn new(queue: Exclusive<~[Message]>, id: uint) -> BasicRemote { BasicRemote { queue: queue, id: id } } } impl RemoteCallback for BasicRemote { fn fire(&mut self) { unsafe { self.queue.hold_and_signal(|queue| { queue.push(RunRemote(self.id)); }) } } } impl Drop for BasicRemote { fn drop(&mut self) { unsafe { self.queue.hold_and_signal(|queue| { queue.push(RemoveRemote(self.id)); }) } } } struct BasicPausable { eloop: *mut BasicLoop, work: ~Callback, active: bool, } impl BasicPausable { fn new(eloop: &mut BasicLoop, cb: ~Callback) -> BasicPausable { BasicPausable { active: false, work: cb, eloop: eloop, } } } impl PausableIdleCallback for BasicPausable { fn pause(&mut self) { self.active = false; } fn resume(&mut self) { self.active = true; } } impl Drop for BasicPausable { fn drop(&mut self) { unsafe { (*self.eloop).idle = None; } } } #[cfg(test)] mod test { use std::task::TaskOpts; use basic; use PoolConfig; use SchedPool; fn pool() -> SchedPool { SchedPool::new(PoolConfig { threads: 1, event_loop_factory: Some(basic::event_loop), }) } fn run(f: proc()) { let mut pool = pool(); pool.spawn(TaskOpts::new(), f); pool.shutdown(); } #[test] fn smoke() { run(proc() {}); } #[test] fn some_channels() { run(proc() { let (p, c) = Chan::new(); spawn(proc() { c.send(()); }); p.recv(); }); } #[test] fn multi_thread() { let mut pool = SchedPool::new(PoolConfig { threads: 2, event_loop_factory: Some(basic::event_loop), }); for _ in range(0, 20) { pool.spawn(TaskOpts::new(), proc() { let (p, c) = Chan::new(); spawn(proc() { c.send(()); }); p.recv(); }); } pool.shutdown(); } }