2014-05-25 19:12:43 -05:00
|
|
|
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
|
2013-12-12 19:47:48 -06:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
//! Homing I/O implementation
|
|
|
|
//!
|
|
|
|
//! In libuv, whenever a handle is created on an I/O loop it is illegal to use
|
|
|
|
//! that handle outside of that I/O loop. We use libuv I/O with our green
|
|
|
|
//! scheduler, and each green scheduler corresponds to a different I/O loop on a
|
|
|
|
//! different OS thread. Green tasks are also free to roam among schedulers,
|
|
|
|
//! which implies that it is possible to create an I/O handle on one event loop
|
|
|
|
//! and then attempt to use it on another.
|
|
|
|
//!
|
|
|
|
//! In order to solve this problem, this module implements the notion of a
|
|
|
|
//! "homing operation" which will transplant a task from its currently running
|
|
|
|
//! scheduler back onto the original I/O loop. This is accomplished entirely at
|
|
|
|
//! the librustuv layer with very little cooperation from the scheduler (which
|
|
|
|
//! we don't even know exists technically).
|
|
|
|
//!
|
|
|
|
//! These homing operations are completed by first realizing that we're on the
|
|
|
|
//! wrong I/O loop, then descheduling ourselves, sending ourselves to the
|
|
|
|
//! correct I/O loop, and then waking up the I/O loop in order to process its
|
|
|
|
//! local queue of tasks which need to run.
|
|
|
|
//!
|
|
|
|
//! This enqueueing is done with a concurrent queue from libstd, and the
|
|
|
|
//! signalling is achieved with an async handle.
|
|
|
|
|
2014-03-21 20:05:05 -05:00
|
|
|
#![allow(dead_code)]
|
2013-12-13 23:14:08 -06:00
|
|
|
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
use std::mem;
|
2013-12-12 19:47:48 -06:00
|
|
|
use std::rt::local::Local;
|
|
|
|
use std::rt::rtio::LocalIo;
|
|
|
|
use std::rt::task::{Task, BlockedTask};
|
|
|
|
|
|
|
|
use ForbidUnwind;
|
|
|
|
use queue::{Queue, QueuePool};
|
|
|
|
|
|
|
|
/// A handle to a remote libuv event loop. This handle will keep the event loop
|
|
|
|
/// alive while active in order to ensure that a homing operation can always be
|
|
|
|
/// completed.
|
|
|
|
///
|
|
|
|
/// Handles are clone-able in order to derive new handles from existing handles
|
|
|
|
/// (very useful for when accepting a socket from a server).
|
|
|
|
pub struct HomeHandle {
|
2014-03-28 12:27:14 -05:00
|
|
|
queue: Queue,
|
|
|
|
id: uint,
|
2013-12-12 19:47:48 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl HomeHandle {
|
|
|
|
pub fn new(id: uint, pool: &mut QueuePool) -> HomeHandle {
|
|
|
|
HomeHandle { queue: pool.queue(), id: id }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn send(&mut self, task: BlockedTask) {
|
|
|
|
self.queue.push(task);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Clone for HomeHandle {
|
|
|
|
fn clone(&self) -> HomeHandle {
|
|
|
|
HomeHandle {
|
|
|
|
queue: self.queue.clone(),
|
|
|
|
id: self.id,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-18 12:14:44 -06:00
|
|
|
pub fn local_id() -> uint {
|
|
|
|
let mut io = match LocalIo::borrow() {
|
|
|
|
Some(io) => io, None => return 0,
|
|
|
|
};
|
|
|
|
let io = io.get();
|
|
|
|
unsafe {
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
let (_vtable, ptr): (uint, uint) = mem::transmute(io);
|
2013-12-18 12:14:44 -06:00
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-29 11:58:09 -05:00
|
|
|
#[doc(hidden)]
|
2013-12-12 19:47:48 -06:00
|
|
|
pub trait HomingIO {
|
|
|
|
fn home<'r>(&'r mut self) -> &'r mut HomeHandle;
|
|
|
|
|
|
|
|
/// This function will move tasks to run on their home I/O scheduler. Note
|
|
|
|
/// that this function does *not* pin the task to the I/O scheduler, but
|
|
|
|
/// rather it simply moves it to running on the I/O scheduler.
|
2014-05-25 19:12:43 -05:00
|
|
|
fn go_to_io_home(&mut self) -> uint {
|
2013-12-12 19:47:48 -06:00
|
|
|
let _f = ForbidUnwind::new("going home");
|
|
|
|
|
2013-12-18 12:14:44 -06:00
|
|
|
let cur_loop_id = local_id();
|
|
|
|
let destination = self.home().id;
|
2013-12-12 19:47:48 -06:00
|
|
|
|
|
|
|
// Try at all costs to avoid the homing operation because it is quite
|
|
|
|
// expensive. Hence, we only deschedule/send if we're not on the correct
|
|
|
|
// event loop. If we're already on the home event loop, then we're good
|
|
|
|
// to go (remember we have no preemption, so we're guaranteed to stay on
|
|
|
|
// this event loop as long as we avoid the scheduler).
|
2013-12-18 12:14:44 -06:00
|
|
|
if cur_loop_id != destination {
|
2014-05-05 20:56:44 -05:00
|
|
|
let cur_task: Box<Task> = Local::take();
|
2013-12-12 19:47:48 -06:00
|
|
|
cur_task.deschedule(1, |task| {
|
|
|
|
self.home().send(task);
|
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
|
|
|
|
// Once we wake up, assert that we're in the right location
|
2013-12-18 12:14:44 -06:00
|
|
|
assert_eq!(local_id(), destination);
|
2013-12-12 19:47:48 -06:00
|
|
|
}
|
2013-12-18 12:14:44 -06:00
|
|
|
|
|
|
|
return destination;
|
2013-12-12 19:47:48 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Fires a single homing missile, returning another missile targeted back
|
|
|
|
/// at the original home of this task. In other words, this function will
|
|
|
|
/// move the local task to its I/O scheduler and then return an RAII wrapper
|
|
|
|
/// which will return the task home.
|
|
|
|
fn fire_homing_missile(&mut self) -> HomingMissile {
|
2014-05-25 19:12:43 -05:00
|
|
|
HomingMissile { io_home: self.go_to_io_home() }
|
2013-12-12 19:47:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// After a homing operation has been completed, this will return the current
|
|
|
|
/// task back to its appropriate home (if applicable). The field is used to
|
|
|
|
/// assert that we are where we think we are.
|
2014-01-22 21:32:16 -06:00
|
|
|
pub struct HomingMissile {
|
2014-03-28 12:27:14 -05:00
|
|
|
io_home: uint,
|
2013-12-12 19:47:48 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl HomingMissile {
|
|
|
|
/// Check at runtime that the task has *not* transplanted itself to a
|
|
|
|
/// different I/O loop while executing.
|
|
|
|
pub fn check(&self, msg: &'static str) {
|
2013-12-18 12:14:44 -06:00
|
|
|
assert!(local_id() == self.io_home, "{}", msg);
|
2013-12-12 19:47:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for HomingMissile {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
let _f = ForbidUnwind::new("leaving home");
|
|
|
|
|
|
|
|
// It would truly be a sad day if we had moved off the home I/O
|
|
|
|
// scheduler while we were doing I/O.
|
|
|
|
self.check("task moved away from the home scheduler");
|
|
|
|
}
|
|
|
|
}
|
2013-12-13 13:30:59 -06:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2013-12-13 18:56:50 -06:00
|
|
|
use green::sched;
|
|
|
|
use green::{SchedPool, PoolConfig};
|
|
|
|
use std::rt::rtio::RtioUdpSocket;
|
2014-06-04 02:00:59 -05:00
|
|
|
use std::rt::task::TaskOpts;
|
2013-12-13 18:56:50 -06:00
|
|
|
|
|
|
|
use net::UdpWatcher;
|
|
|
|
use super::super::local_loop;
|
|
|
|
|
2013-12-13 13:30:59 -06:00
|
|
|
// On one thread, create a udp socket. Then send that socket to another
|
|
|
|
// thread and destroy the socket on the remote thread. This should make sure
|
|
|
|
// that homing kicks in for the socket to go back home to the original
|
|
|
|
// thread, close itself, and then come back to the last thread.
|
2013-12-13 18:56:50 -06:00
|
|
|
#[test]
|
|
|
|
fn test_homing_closes_correctly() {
|
2014-03-09 16:58:32 -05:00
|
|
|
let (tx, rx) = channel();
|
2013-12-13 20:28:18 -06:00
|
|
|
let mut pool = SchedPool::new(PoolConfig {
|
|
|
|
threads: 1,
|
2014-03-24 12:40:36 -05:00
|
|
|
event_loop_factory: ::event_loop,
|
2013-12-13 20:28:18 -06:00
|
|
|
});
|
2013-12-13 18:56:50 -06:00
|
|
|
|
2014-01-26 21:57:42 -06:00
|
|
|
pool.spawn(TaskOpts::new(), proc() {
|
2014-06-04 02:00:59 -05:00
|
|
|
let listener = UdpWatcher::bind(local_loop(), ::next_test_ip4());
|
2014-03-09 16:58:32 -05:00
|
|
|
tx.send(listener.unwrap());
|
2014-01-26 21:57:42 -06:00
|
|
|
});
|
2013-12-13 13:30:59 -06:00
|
|
|
|
2014-01-26 21:57:42 -06:00
|
|
|
let task = pool.task(TaskOpts::new(), proc() {
|
2014-03-09 16:58:32 -05:00
|
|
|
drop(rx.recv());
|
2014-01-26 21:57:42 -06:00
|
|
|
});
|
2013-12-13 18:56:50 -06:00
|
|
|
pool.spawn_sched().send(sched::TaskFromFriend(task));
|
2013-12-13 13:30:59 -06:00
|
|
|
|
2013-12-13 18:56:50 -06:00
|
|
|
pool.shutdown();
|
|
|
|
}
|
2013-12-13 13:30:59 -06:00
|
|
|
|
2013-12-13 18:56:50 -06:00
|
|
|
#[test]
|
|
|
|
fn test_homing_read() {
|
2014-03-09 16:58:32 -05:00
|
|
|
let (tx, rx) = channel();
|
2013-12-13 20:28:18 -06:00
|
|
|
let mut pool = SchedPool::new(PoolConfig {
|
|
|
|
threads: 1,
|
2014-03-24 12:40:36 -05:00
|
|
|
event_loop_factory: ::event_loop,
|
2013-12-13 20:28:18 -06:00
|
|
|
});
|
2013-12-13 18:56:50 -06:00
|
|
|
|
2014-01-26 21:57:42 -06:00
|
|
|
pool.spawn(TaskOpts::new(), proc() {
|
2014-06-04 02:00:59 -05:00
|
|
|
let addr1 = ::next_test_ip4();
|
|
|
|
let addr2 = ::next_test_ip4();
|
2013-12-13 18:56:50 -06:00
|
|
|
let listener = UdpWatcher::bind(local_loop(), addr2);
|
2014-03-09 16:58:32 -05:00
|
|
|
tx.send((listener.unwrap(), addr1));
|
2013-12-13 18:56:50 -06:00
|
|
|
let mut listener = UdpWatcher::bind(local_loop(), addr1).unwrap();
|
2014-06-04 02:00:59 -05:00
|
|
|
listener.sendto([1, 2, 3, 4], addr2).ok().unwrap();
|
2014-01-26 21:57:42 -06:00
|
|
|
});
|
2013-12-13 13:30:59 -06:00
|
|
|
|
2014-01-26 21:57:42 -06:00
|
|
|
let task = pool.task(TaskOpts::new(), proc() {
|
2014-03-09 16:58:32 -05:00
|
|
|
let (mut watcher, addr) = rx.recv();
|
2013-12-13 18:56:50 -06:00
|
|
|
let mut buf = [0, ..10];
|
2014-06-04 02:00:59 -05:00
|
|
|
assert!(watcher.recvfrom(buf).ok().unwrap() == (4, addr));
|
2014-01-26 21:57:42 -06:00
|
|
|
});
|
2013-12-13 18:56:50 -06:00
|
|
|
pool.spawn_sched().send(sched::TaskFromFriend(task));
|
2013-12-13 13:30:59 -06:00
|
|
|
|
2013-12-13 18:56:50 -06:00
|
|
|
pool.shutdown();
|
|
|
|
}
|
2013-12-13 13:30:59 -06:00
|
|
|
}
|