Merge remote-tracking branch 'brson/rt'
Conflicts: src/libcore/rt/context.rs src/libcore/rt/sched.rs src/libcore/rt/thread.rs src/libcore/rt/uv.rs
This commit is contained in:
commit
30d4124a37
@ -238,7 +238,7 @@ $(foreach target,$(CFG_TARGET_TRIPLES),\
|
||||
|
||||
CORELIB_CRATE := $(S)src/libcore/core.rc
|
||||
CORELIB_INPUTS := $(wildcard $(addprefix $(S)src/libcore/, \
|
||||
core.rc *.rs */*.rs))
|
||||
core.rc *.rs */*.rs */*/*rs))
|
||||
|
||||
######################################################################
|
||||
# Standard library variables
|
||||
|
14
mk/tests.mk
14
mk/tests.mk
@ -244,21 +244,29 @@ $(foreach host,$(CFG_HOST_TRIPLES), \
|
||||
|
||||
define TEST_RUNNER
|
||||
|
||||
# If NO_REBUILD is set then break the dependencies on std so we can
|
||||
# test crates without rebuilding core and std first
|
||||
ifeq ($(NO_REBUILD),)
|
||||
STDTESTDEP_$(1)_$(2)_$(3) = $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_STDLIB_$(2))
|
||||
else
|
||||
STDTESTDEP_$(1)_$(2)_$(3) =
|
||||
endif
|
||||
|
||||
$(3)/test/coretest.stage$(1)-$(2)$$(X_$(2)): \
|
||||
$$(CORELIB_CRATE) $$(CORELIB_INPUTS) \
|
||||
$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_STDLIB_$(2))
|
||||
$$(STDTESTDEP_$(1)_$(2)_$(3))
|
||||
@$$(call E, compile_and_link: $$@)
|
||||
$$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test
|
||||
|
||||
$(3)/test/stdtest.stage$(1)-$(2)$$(X_$(2)): \
|
||||
$$(STDLIB_CRATE) $$(STDLIB_INPUTS) \
|
||||
$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_STDLIB_$(2))
|
||||
$$(STDTESTDEP_$(1)_$(2)_$(3))
|
||||
@$$(call E, compile_and_link: $$@)
|
||||
$$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test
|
||||
|
||||
$(3)/test/syntaxtest.stage$(1)-$(2)$$(X_$(2)): \
|
||||
$$(LIBSYNTAX_CRATE) $$(LIBSYNTAX_INPUTS) \
|
||||
$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_STDLIB_$(2))
|
||||
$$(STDTESTDEP_$(1)_$(2)_$(3))
|
||||
@$$(call E, compile_and_link: $$@)
|
||||
$$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test
|
||||
|
||||
|
@ -63,6 +63,9 @@ pub struct config {
|
||||
// Run tests using the JIT
|
||||
jit: bool,
|
||||
|
||||
// Run tests using the new runtime
|
||||
newrt: bool,
|
||||
|
||||
// Explain what's going on
|
||||
verbose: bool
|
||||
|
||||
|
@ -61,7 +61,8 @@ pub fn parse_config(args: ~[~str]) -> config {
|
||||
getopts::optopt(~"runtool"), getopts::optopt(~"rustcflags"),
|
||||
getopts::optflag(~"verbose"),
|
||||
getopts::optopt(~"logfile"),
|
||||
getopts::optflag(~"jit")];
|
||||
getopts::optflag(~"jit"),
|
||||
getopts::optflag(~"newrt")];
|
||||
|
||||
fail_unless!(!args.is_empty());
|
||||
let args_ = vec::tail(args);
|
||||
@ -95,6 +96,7 @@ pub fn parse_config(args: ~[~str]) -> config {
|
||||
runtool: getopts::opt_maybe_str(matches, ~"runtool"),
|
||||
rustcflags: getopts::opt_maybe_str(matches, ~"rustcflags"),
|
||||
jit: getopts::opt_present(matches, ~"jit"),
|
||||
newrt: getopts::opt_present(matches, ~"newrt"),
|
||||
verbose: getopts::opt_present(matches, ~"verbose")
|
||||
}
|
||||
}
|
||||
@ -114,6 +116,7 @@ pub fn log_config(config: config) {
|
||||
logv(c, fmt!("runtool: %s", opt_str(config.runtool)));
|
||||
logv(c, fmt!("rustcflags: %s", opt_str(config.rustcflags)));
|
||||
logv(c, fmt!("jit: %b", config.jit));
|
||||
logv(c, fmt!("newrt: %b", config.newrt));
|
||||
logv(c, fmt!("verbose: %b", config.verbose));
|
||||
logv(c, fmt!("\n"));
|
||||
}
|
||||
|
@ -484,9 +484,17 @@ fn compile_test_(config: config, props: TestProps,
|
||||
|
||||
fn exec_compiled_test(config: config, props: TestProps,
|
||||
testfile: &Path) -> ProcRes {
|
||||
|
||||
// If testing the new runtime then set the RUST_NEWRT env var
|
||||
let env = if config.newrt {
|
||||
props.exec_env + ~[(~"RUST_NEWRT", ~"1")]
|
||||
} else {
|
||||
props.exec_env
|
||||
};
|
||||
|
||||
compose_and_run(config, testfile,
|
||||
make_run_args(config, props, testfile),
|
||||
props.exec_env,
|
||||
env,
|
||||
config.run_lib_path, None)
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use option::*;
|
||||
use super::stack::StackSegment;
|
||||
use libc::c_void;
|
||||
use cast::{transmute, transmute_mut_unsafe,
|
||||
@ -16,17 +17,30 @@ use cast::{transmute, transmute_mut_unsafe,
|
||||
// XXX: Registers is boxed so that it is 16-byte aligned, for storing
|
||||
// SSE regs. It would be marginally better not to do this. In C++ we
|
||||
// use an attribute on a struct.
|
||||
pub struct Context(~Registers);
|
||||
// XXX: It would be nice to define regs as `~Option<Registers>` since
|
||||
// the registers are sometimes empty, but the discriminant would
|
||||
// then misalign the regs again.
|
||||
pub struct Context {
|
||||
/// The context entry point, saved here for later destruction
|
||||
start: Option<~~fn()>,
|
||||
/// Hold the registers while the task or scheduler is suspended
|
||||
regs: ~Registers
|
||||
}
|
||||
|
||||
pub impl Context {
|
||||
fn empty() -> Context {
|
||||
Context(new_regs())
|
||||
Context {
|
||||
start: None,
|
||||
regs: new_regs()
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new context that will resume execution by running ~fn()
|
||||
/// # Safety Note
|
||||
/// The `start` closure must remain valid for the life of the Task
|
||||
fn new(start: &~fn(), stack: &mut StackSegment) -> Context {
|
||||
fn new(start: ~fn(), stack: &mut StackSegment) -> Context {
|
||||
// XXX: Putting main into a ~ so it's a thin pointer and can
|
||||
// be passed to the spawn function. Another unfortunate
|
||||
// allocation
|
||||
let start = ~start;
|
||||
|
||||
// The C-ABI function that is the task entry point
|
||||
extern fn task_start_wrapper(f: &~fn()) { (*f)() }
|
||||
@ -40,21 +54,29 @@ pub impl Context {
|
||||
// which we will then modify to call the given function when restored
|
||||
let mut regs = new_regs();
|
||||
unsafe {
|
||||
swap_registers(transmute_mut_region(&mut *regs),
|
||||
transmute_region(&*regs))
|
||||
swap_registers(transmute_mut_region(&mut *regs), transmute_region(&*regs))
|
||||
};
|
||||
|
||||
initialize_call_frame(&mut *regs, fp, argp, sp);
|
||||
|
||||
return Context(regs);
|
||||
return Context {
|
||||
start: Some(start),
|
||||
regs: regs
|
||||
}
|
||||
}
|
||||
|
||||
/* Switch contexts
|
||||
|
||||
Suspend the current execution context and resume another by
|
||||
saving the registers values of the executing thread to a Context
|
||||
then loading the registers from a previously saved Context.
|
||||
*/
|
||||
fn swap(out_context: &mut Context, in_context: &Context) {
|
||||
let out_regs: &mut Registers = match out_context {
|
||||
&Context(~ref mut r) => r
|
||||
&Context { regs: ~ref mut r, _ } => r
|
||||
};
|
||||
let in_regs: &Registers = match in_context {
|
||||
&Context(~ref r) => r
|
||||
&Context { regs: ~ref r, _ } => r
|
||||
};
|
||||
|
||||
unsafe { swap_registers(out_regs, in_regs) };
|
||||
@ -84,11 +106,10 @@ fn new_regs() -> ~Registers {
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
fn initialize_call_frame(regs: &mut Registers,
|
||||
fptr: *c_void, arg: *c_void, sp: *mut uint) {
|
||||
fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) {
|
||||
|
||||
let sp = align_down(sp);
|
||||
let sp = mut_offset(sp, -4); // XXX: -4 words? Needs this be done at all?
|
||||
let sp = mut_offset(sp, -4);
|
||||
|
||||
unsafe { *sp = arg as uint; }
|
||||
let sp = mut_offset(sp, -1);
|
||||
@ -108,8 +129,7 @@ type Registers = [uint * 22];
|
||||
fn new_regs() -> ~Registers { ~[0, .. 22] }
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn initialize_call_frame(regs: &mut Registers,
|
||||
fptr: *c_void, arg: *c_void, sp: *mut uint) {
|
||||
fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) {
|
||||
|
||||
// Redefinitions from regs.h
|
||||
static RUSTRT_ARG0: uint = 3;
|
||||
@ -143,8 +163,7 @@ type Registers = [uint * 32];
|
||||
fn new_regs() -> ~Registers { ~[0, .. 32] }
|
||||
|
||||
#[cfg(target_arch = "arm")]
|
||||
fn initialize_call_frame(regs: &mut Registers,
|
||||
fptr: *c_void, arg: *c_void, sp: *mut uint) {
|
||||
fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) {
|
||||
let sp = mut_offset(sp, -1);
|
||||
|
||||
// The final return address. 0 indicates the bottom of the stack
|
||||
@ -162,8 +181,7 @@ type Registers = [uint * 32];
|
||||
fn new_regs() -> ~Registers { ~[0, .. 32] }
|
||||
|
||||
#[cfg(target_arch = "mips")]
|
||||
fn initialize_call_frame(regs: &mut Registers,
|
||||
fptr: *c_void, arg: *c_void, sp: *mut uint) {
|
||||
fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) {
|
||||
let sp = mut_offset(sp, -1);
|
||||
|
||||
// The final return address. 0 indicates the bottom of the stack
|
||||
|
45
src/libcore/rt/io/file.rs
Normal file
45
src/libcore/rt/io/file.rs
Normal file
@ -0,0 +1,45 @@
|
||||
// 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 <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.
|
||||
|
||||
use prelude::*;
|
||||
use super::super::sched::*;
|
||||
use super::super::rtio::*;
|
||||
use super::Stream;
|
||||
|
||||
pub struct FileStream;
|
||||
|
||||
pub impl FileStream {
|
||||
fn new(_path: Path) -> FileStream {
|
||||
fail!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for FileStream {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> uint {
|
||||
fail!()
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> bool {
|
||||
fail!()
|
||||
}
|
||||
|
||||
fn write(&mut self, _v: &const [u8]) {
|
||||
fail!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn super_simple_smoke_test_lets_go_read_some_files_and_have_a_good_time() {
|
||||
let message = "it's alright. have a good time";
|
||||
let filename = Path("test.txt");
|
||||
let mut outstream = FileStream::new(filename);
|
||||
outstream.write(message.to_bytes());
|
||||
}
|
45
src/libcore/rt/io/mod.rs
Normal file
45
src/libcore/rt/io/mod.rs
Normal file
@ -0,0 +1,45 @@
|
||||
// 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 <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.
|
||||
|
||||
use option::*;
|
||||
use comm::{GenericPort, GenericChan};
|
||||
|
||||
pub mod file;
|
||||
|
||||
// FIXME #5370 Strongly want this to be StreamError(&mut Stream)
|
||||
pub struct StreamError;
|
||||
|
||||
// XXX: Can't put doc comments on macros
|
||||
// Raised by `Stream` instances on error. Returning `true` from the handler
|
||||
// indicates that the `Stream` should continue, `false` that it should fail.
|
||||
condition! {
|
||||
stream_error: super::StreamError -> bool;
|
||||
}
|
||||
|
||||
pub trait Stream {
|
||||
/// Read bytes, up to the length of `buf` and place them in `buf`,
|
||||
/// returning the number of bytes read or an `IoError`. Reads
|
||||
/// 0 bytes on EOF.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Raises the `reader_error` condition on error
|
||||
fn read(&mut self, buf: &mut [u8]) -> uint;
|
||||
|
||||
/// Return whether the Reader has reached the end of the stream
|
||||
fn eof(&mut self) -> bool;
|
||||
|
||||
/// Write the given buffer
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Raises the `writer_error` condition on error
|
||||
fn write(&mut self, v: &const [u8]);
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use libc::c_char;
|
||||
|
||||
// Some basic logging
|
||||
macro_rules! rtdebug_ (
|
||||
@ -15,16 +16,10 @@ macro_rules! rtdebug_ (
|
||||
dumb_println(fmt!( $($arg),+ ));
|
||||
|
||||
fn dumb_println(s: &str) {
|
||||
use str::as_c_str;
|
||||
use libc::c_char;
|
||||
|
||||
extern {
|
||||
fn printf(s: *c_char);
|
||||
}
|
||||
|
||||
do as_c_str(s.to_str() + "\n") |s| {
|
||||
unsafe { printf(s); }
|
||||
}
|
||||
use io::WriterUtil;
|
||||
let dbg = ::libc::STDERR_FILENO as ::io::fd_t;
|
||||
dbg.write_str(s);
|
||||
dbg.write_str("\n");
|
||||
}
|
||||
|
||||
} )
|
||||
@ -36,9 +31,13 @@ macro_rules! rtdebug (
|
||||
)
|
||||
|
||||
mod sched;
|
||||
mod io;
|
||||
mod rtio;
|
||||
pub mod uvll;
|
||||
mod uvio;
|
||||
#[path = "uv/mod.rs"]
|
||||
mod uv;
|
||||
#[path = "io/mod.rs"]
|
||||
mod io;
|
||||
// FIXME #5248: The import in `sched` doesn't resolve unless this is pub!
|
||||
pub mod thread_local_storage;
|
||||
mod work_queue;
|
||||
@ -46,3 +45,23 @@ mod stack;
|
||||
mod context;
|
||||
mod thread;
|
||||
pub mod env;
|
||||
|
||||
pub fn start(main: *u8, _argc: int, _argv: *c_char, _crate_map: *u8) -> int {
|
||||
use self::sched::{Scheduler, Task};
|
||||
use self::uvio::UvEventLoop;
|
||||
|
||||
let loop_ = ~UvEventLoop::new();
|
||||
let mut sched = ~Scheduler::new(loop_);
|
||||
let main_task = ~do Task::new(&mut sched.stack_pool) {
|
||||
// XXX: Can't call a C function pointer from Rust yet
|
||||
unsafe { rust_call_nullary_fn(main) };
|
||||
};
|
||||
sched.task_queue.push_back(main_task);
|
||||
sched.run();
|
||||
return 0;
|
||||
|
||||
extern {
|
||||
fn rust_call_nullary_fn(f: *u8);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ use ptr::mut_null;
|
||||
|
||||
use super::work_queue::WorkQueue;
|
||||
use super::stack::{StackPool, StackSegment};
|
||||
use super::io::{EventLoop, EventLoopObject};
|
||||
use super::rtio::{EventLoop, EventLoopObject};
|
||||
use super::context::Context;
|
||||
use tls = super::thread_local_storage;
|
||||
|
||||
@ -70,7 +70,14 @@ enum CleanupJob {
|
||||
|
||||
pub impl Scheduler {
|
||||
|
||||
pub fn new(event_loop: ~EventLoopObject) -> Scheduler {
|
||||
fn new(event_loop: ~EventLoopObject) -> Scheduler {
|
||||
|
||||
// Lazily initialize the global state, currently the scheduler TLS key
|
||||
unsafe { rust_initialize_global_state(); }
|
||||
extern {
|
||||
fn rust_initialize_global_state();
|
||||
}
|
||||
|
||||
Scheduler {
|
||||
event_loop: event_loop,
|
||||
task_queue: WorkQueue::new(),
|
||||
@ -183,8 +190,7 @@ pub impl Scheduler {
|
||||
|
||||
let blocked_task = self.current_task.swap_unwrap();
|
||||
let f_fake_region = unsafe {
|
||||
transmute::<&fn(&mut Scheduler, ~Task),
|
||||
&fn(&mut Scheduler, ~Task)>(f)
|
||||
transmute::<&fn(&mut Scheduler, ~Task), &fn(&mut Scheduler, ~Task)>(f)
|
||||
};
|
||||
let f_opaque = HackAroundBorrowCk::from_fn(f_fake_region);
|
||||
self.enqueue_cleanup_job(GiveTask(blocked_task, f_opaque));
|
||||
@ -233,8 +239,7 @@ pub impl Scheduler {
|
||||
Context::swap(task_context, scheduler_context);
|
||||
}
|
||||
|
||||
priv fn swap_in_task_from_running_task(&mut self,
|
||||
running_task: &mut Task) {
|
||||
priv fn swap_in_task_from_running_task(&mut self, running_task: &mut Task) {
|
||||
let running_task_context = &mut running_task.saved_context;
|
||||
let next_context = &self.current_task.get_ref().saved_context;
|
||||
Context::swap(running_task_context, next_context);
|
||||
@ -285,8 +290,6 @@ pub impl Scheduler {
|
||||
static TASK_MIN_STACK_SIZE: uint = 10000000; // XXX: Too much stack
|
||||
|
||||
pub struct Task {
|
||||
/// The task entry point, saved here for later destruction
|
||||
priv start: ~~fn(),
|
||||
/// The segment of stack on which the task is currently running or,
|
||||
/// if the task is blocked, on which the task will resume execution
|
||||
priv current_stack_segment: StackSegment,
|
||||
@ -295,17 +298,13 @@ pub struct Task {
|
||||
priv saved_context: Context,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
pub fn new(stack_pool: &mut StackPool, start: ~fn()) -> Task {
|
||||
// XXX: Putting main into a ~ so it's a thin pointer and can
|
||||
// be passed to the spawn function. Another unfortunate
|
||||
// allocation
|
||||
let start = ~Task::build_start_wrapper(start);
|
||||
pub impl Task {
|
||||
fn new(stack_pool: &mut StackPool, start: ~fn()) -> Task {
|
||||
let start = Task::build_start_wrapper(start);
|
||||
let mut stack = stack_pool.take_segment(TASK_MIN_STACK_SIZE);
|
||||
// NB: Context holds a pointer to that ~fn
|
||||
let initial_context = Context::new(&*start, &mut stack);
|
||||
let initial_context = Context::new(start, &mut stack);
|
||||
return Task {
|
||||
start: start,
|
||||
current_stack_segment: stack,
|
||||
saved_context: initial_context,
|
||||
};
|
||||
@ -350,8 +349,7 @@ impl ThreadLocalScheduler {
|
||||
fn put_scheduler(&mut self, scheduler: ~Scheduler) {
|
||||
unsafe {
|
||||
let key = match self { &ThreadLocalScheduler(key) => key };
|
||||
let value: *mut c_void =
|
||||
transmute::<~Scheduler, *mut c_void>(scheduler);
|
||||
let value: *mut c_void = transmute::<~Scheduler, *mut c_void>(scheduler);
|
||||
tls::set(key, value);
|
||||
}
|
||||
}
|
||||
@ -363,8 +361,9 @@ impl ThreadLocalScheduler {
|
||||
fail_unless!(value.is_not_null());
|
||||
{
|
||||
let value_ptr = &mut value;
|
||||
let sched: &mut ~Scheduler =
|
||||
transmute::<&mut *mut c_void, &mut ~Scheduler>(value_ptr);
|
||||
let sched: &mut ~Scheduler = {
|
||||
transmute::<&mut *mut c_void, &mut ~Scheduler>(value_ptr)
|
||||
};
|
||||
let sched: &mut Scheduler = &mut **sched;
|
||||
return sched;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ pub impl StackSegment {
|
||||
}
|
||||
}
|
||||
|
||||
/// Point one word beyond the high end of the allocated stack
|
||||
fn end(&self) -> *uint {
|
||||
unsafe {
|
||||
vec::raw::to_ptr(self.buf).offset(self.buf.len()) as *uint
|
||||
|
@ -14,13 +14,13 @@ use ops::Drop;
|
||||
#[allow(non_camel_case_types)] // runtime type
|
||||
type raw_thread = libc::c_void;
|
||||
|
||||
struct Thread {
|
||||
pub struct Thread {
|
||||
main: ~fn(),
|
||||
raw_thread: *raw_thread
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
pub fn start(main: ~fn()) -> Thread {
|
||||
pub impl Thread {
|
||||
fn start(main: ~fn()) -> Thread {
|
||||
fn substart(main: &fn()) -> *raw_thread {
|
||||
unsafe { rust_raw_thread_start(&main) }
|
||||
}
|
||||
|
@ -1,919 +0,0 @@
|
||||
// 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 <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.
|
||||
|
||||
/*!
|
||||
|
||||
Bindings to libuv.
|
||||
|
||||
UV types consist of the event loop (Loop), Watchers, Requests and
|
||||
Callbacks.
|
||||
|
||||
Watchers and Requests encapsulate pointers to uv *handles*, which have
|
||||
subtyping relationships with each other. This subtyping is reflected
|
||||
in the bindings with explicit or implicit coercions. For example, an
|
||||
upcast from TcpWatcher to StreamWatcher is done with
|
||||
`tcp_watcher.as_stream()`. In other cases a callback on a specific
|
||||
type of watcher will be passed a watcher of a supertype.
|
||||
|
||||
Currently all use of Request types (connect/write requests) are
|
||||
encapsulated in the bindings and don't need to be dealt with by the
|
||||
caller.
|
||||
|
||||
# Safety note
|
||||
|
||||
Due to the complex lifecycle of uv handles, as well as compiler bugs,
|
||||
this module is not memory safe and requires explicit memory management,
|
||||
via `close` and `delete` methods.
|
||||
|
||||
*/
|
||||
|
||||
use option::*;
|
||||
use str::raw::from_c_str;
|
||||
use to_str::ToStr;
|
||||
use vec;
|
||||
use ptr;
|
||||
use libc::{c_void, c_int, size_t, malloc, free, ssize_t};
|
||||
use cast::{transmute, transmute_mut_region};
|
||||
use ptr::null;
|
||||
use sys::size_of;
|
||||
use unstable::uvll;
|
||||
use super::io::{IpAddr, Ipv4, Ipv6};
|
||||
|
||||
#[cfg(test)] use unstable::run_in_bare_thread;
|
||||
#[cfg(test)] use super::thread::Thread;
|
||||
#[cfg(test)] use cell::Cell;
|
||||
|
||||
fn ip4_to_uv_ip4(addr: IpAddr) -> uvll::sockaddr_in {
|
||||
match addr {
|
||||
Ipv4(a, b, c, d, p) => {
|
||||
unsafe {
|
||||
uvll::ip4_addr(fmt!("%u.%u.%u.%u",
|
||||
a as uint,
|
||||
b as uint,
|
||||
c as uint,
|
||||
d as uint), p as int)
|
||||
}
|
||||
}
|
||||
Ipv6 => fail!()
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for callbacks to implement. Provides a little extra type safety
|
||||
/// for generic, unsafe interop functions like `set_watcher_callback`.
|
||||
trait Callback { }
|
||||
|
||||
type NullCallback = ~fn();
|
||||
impl Callback for NullCallback { }
|
||||
|
||||
/// A type that wraps a native handle
|
||||
trait NativeHandle<T> {
|
||||
pub fn from_native_handle(T) -> Self;
|
||||
pub fn native_handle(&self) -> T;
|
||||
}
|
||||
|
||||
/// XXX: Loop(*handle) is buggy with destructors. Normal structs
|
||||
/// with dtors may not be destructured, but tuple structs can,
|
||||
/// but the results are not correct.
|
||||
pub struct Loop {
|
||||
handle: *uvll::uv_loop_t
|
||||
}
|
||||
|
||||
pub impl Loop {
|
||||
fn new() -> Loop {
|
||||
let handle = unsafe { uvll::loop_new() };
|
||||
fail_unless!(handle.is_not_null());
|
||||
NativeHandle::from_native_handle(handle)
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
unsafe { uvll::run(self.native_handle()) };
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
unsafe { uvll::loop_delete(self.native_handle()) };
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_loop_t> for Loop {
|
||||
fn from_native_handle(handle: *uvll::uv_loop_t) -> Loop {
|
||||
Loop { handle: handle }
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_loop_t {
|
||||
self.handle
|
||||
}
|
||||
}
|
||||
|
||||
/// The trait implemented by uv 'watchers' (handles). Watchers are
|
||||
/// non-owning wrappers around the uv handles and are not completely
|
||||
/// safe - there may be multiple instances for a single underlying
|
||||
/// handle. Watchers are generally created, then `start`ed, `stop`ed
|
||||
/// and `close`ed, but due to their complex life cycle may not be
|
||||
/// entirely memory safe if used in unanticipated patterns.
|
||||
trait Watcher {
|
||||
fn event_loop(&self) -> Loop;
|
||||
}
|
||||
|
||||
pub struct IdleWatcher(*uvll::uv_idle_t);
|
||||
|
||||
impl Watcher for IdleWatcher {
|
||||
fn event_loop(&self) -> Loop {
|
||||
loop_from_watcher(self)
|
||||
}
|
||||
}
|
||||
|
||||
type IdleCallback = ~fn(IdleWatcher, Option<UvError>);
|
||||
impl Callback for IdleCallback { }
|
||||
|
||||
pub impl IdleWatcher {
|
||||
fn new(loop_: &mut Loop) -> IdleWatcher {
|
||||
unsafe {
|
||||
let handle = uvll::idle_new();
|
||||
fail_unless!(handle.is_not_null());
|
||||
fail_unless!(0 == uvll::idle_init(loop_.native_handle(), handle));
|
||||
uvll::set_data_for_uv_handle(handle, null::<()>());
|
||||
NativeHandle::from_native_handle(handle)
|
||||
}
|
||||
}
|
||||
|
||||
fn start(&mut self, cb: IdleCallback) {
|
||||
|
||||
set_watcher_callback(self, cb);
|
||||
unsafe {
|
||||
fail_unless!(0 == uvll::idle_start(self.native_handle(), idle_cb))
|
||||
};
|
||||
|
||||
extern fn idle_cb(handle: *uvll::uv_idle_t, status: c_int) {
|
||||
let idle_watcher: IdleWatcher =
|
||||
NativeHandle::from_native_handle(handle);
|
||||
let cb: &IdleCallback =
|
||||
borrow_callback_from_watcher(&idle_watcher);
|
||||
let status = status_to_maybe_uv_error(handle, status);
|
||||
(*cb)(idle_watcher, status);
|
||||
}
|
||||
}
|
||||
|
||||
fn stop(&mut self) {
|
||||
unsafe { fail_unless!(0 == uvll::idle_stop(self.native_handle())); }
|
||||
}
|
||||
|
||||
fn close(self) {
|
||||
unsafe { uvll::close(self.native_handle(), close_cb) };
|
||||
|
||||
extern fn close_cb(handle: *uvll::uv_idle_t) {
|
||||
let mut idle_watcher = NativeHandle::from_native_handle(handle);
|
||||
drop_watcher_callback::<uvll::uv_idle_t,
|
||||
IdleWatcher,
|
||||
IdleCallback>(&mut idle_watcher);
|
||||
unsafe { uvll::idle_delete(handle) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_idle_t> for IdleWatcher {
|
||||
fn from_native_handle(handle: *uvll::uv_idle_t) -> IdleWatcher {
|
||||
IdleWatcher(handle)
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_idle_t {
|
||||
match self { &IdleWatcher(ptr) => ptr }
|
||||
}
|
||||
}
|
||||
|
||||
// uv_stream t is the parent class of uv_tcp_t, uv_pipe_t, uv_tty_t
|
||||
// and uv_file_t
|
||||
pub struct StreamWatcher(*uvll::uv_stream_t);
|
||||
|
||||
impl Watcher for StreamWatcher {
|
||||
fn event_loop(&self) -> Loop {
|
||||
loop_from_watcher(self)
|
||||
}
|
||||
}
|
||||
|
||||
type ReadCallback = ~fn(StreamWatcher, int, Buf, Option<UvError>);
|
||||
impl Callback for ReadCallback { }
|
||||
|
||||
// XXX: The uv alloc callback also has a *uv_handle_t arg
|
||||
pub type AllocCallback = ~fn(uint) -> Buf;
|
||||
impl Callback for AllocCallback { }
|
||||
|
||||
pub impl StreamWatcher {
|
||||
|
||||
fn read_start(&mut self, alloc: AllocCallback, cb: ReadCallback) {
|
||||
// XXX: Borrowchk problems
|
||||
let data = get_watcher_data(unsafe { transmute_mut_region(self) });
|
||||
data.alloc_cb = Some(alloc);
|
||||
data.read_cb = Some(cb);
|
||||
|
||||
let handle = self.native_handle();
|
||||
unsafe { uvll::read_start(handle, alloc_cb, read_cb); }
|
||||
|
||||
extern fn alloc_cb(stream: *uvll::uv_stream_t,
|
||||
suggested_size: size_t) -> Buf {
|
||||
let mut stream_watcher: StreamWatcher =
|
||||
NativeHandle::from_native_handle(stream);
|
||||
let data = get_watcher_data(&mut stream_watcher);
|
||||
let alloc_cb = data.alloc_cb.get_ref();
|
||||
return (*alloc_cb)(suggested_size as uint);
|
||||
}
|
||||
|
||||
extern fn read_cb(stream: *uvll::uv_stream_t,
|
||||
nread: ssize_t, ++buf: Buf) {
|
||||
rtdebug!("buf addr: %x", buf.base as uint);
|
||||
rtdebug!("buf len: %d", buf.len as int);
|
||||
let mut stream_watcher: StreamWatcher =
|
||||
NativeHandle::from_native_handle(stream);
|
||||
let data = get_watcher_data(&mut stream_watcher);
|
||||
let cb = data.read_cb.get_ref();
|
||||
let status = status_to_maybe_uv_error(stream, nread as c_int);
|
||||
(*cb)(stream_watcher, nread as int, buf, status);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_stop(&mut self) {
|
||||
// It would be nice to drop the alloc and read callbacks here,
|
||||
// but read_stop may be called from inside one of them and we
|
||||
// would end up freeing the in-use environment
|
||||
let handle = self.native_handle();
|
||||
unsafe { uvll::read_stop(handle); }
|
||||
}
|
||||
|
||||
// XXX: Needs to take &[u8], not ~[u8]
|
||||
fn write(&mut self, msg: ~[u8], cb: ConnectionCallback) {
|
||||
// XXX: Borrowck
|
||||
let data = get_watcher_data(unsafe { transmute_mut_region(self) });
|
||||
fail_unless!(data.write_cb.is_none());
|
||||
data.write_cb = Some(cb);
|
||||
|
||||
let req = WriteRequest::new();
|
||||
let buf = vec_to_uv_buf(msg);
|
||||
// XXX: Allocation
|
||||
let bufs = ~[buf];
|
||||
unsafe {
|
||||
fail_unless!(0 == uvll::write(req.native_handle(),
|
||||
self.native_handle(),
|
||||
&bufs, write_cb));
|
||||
}
|
||||
// XXX: Freeing immediately after write. Is this ok?
|
||||
let _v = vec_from_uv_buf(buf);
|
||||
|
||||
extern fn write_cb(req: *uvll::uv_write_t, status: c_int) {
|
||||
let write_request: WriteRequest =
|
||||
NativeHandle::from_native_handle(req);
|
||||
let mut stream_watcher = write_request.stream();
|
||||
write_request.delete();
|
||||
let cb = get_watcher_data(&mut stream_watcher)
|
||||
.write_cb.swap_unwrap();
|
||||
let status = status_to_maybe_uv_error(
|
||||
stream_watcher.native_handle(), status);
|
||||
cb(stream_watcher, status);
|
||||
}
|
||||
}
|
||||
|
||||
fn accept(&mut self, stream: StreamWatcher) {
|
||||
let self_handle = self.native_handle() as *c_void;
|
||||
let stream_handle = stream.native_handle() as *c_void;
|
||||
unsafe {
|
||||
fail_unless!(0 == uvll::accept(self_handle, stream_handle));
|
||||
}
|
||||
}
|
||||
|
||||
fn close(self, cb: NullCallback) {
|
||||
{
|
||||
let mut self = self;
|
||||
let data = get_watcher_data(&mut self);
|
||||
fail_unless!(data.close_cb.is_none());
|
||||
data.close_cb = Some(cb);
|
||||
}
|
||||
|
||||
unsafe { uvll::close(self.native_handle(), close_cb); }
|
||||
|
||||
extern fn close_cb(handle: *uvll::uv_stream_t) {
|
||||
let mut stream_watcher: StreamWatcher =
|
||||
NativeHandle::from_native_handle(handle);
|
||||
{
|
||||
let mut data = get_watcher_data(&mut stream_watcher);
|
||||
data.close_cb.swap_unwrap()();
|
||||
}
|
||||
drop_watcher_data(&mut stream_watcher);
|
||||
unsafe { free(handle as *c_void) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_stream_t> for StreamWatcher {
|
||||
fn from_native_handle(
|
||||
handle: *uvll::uv_stream_t) -> StreamWatcher {
|
||||
StreamWatcher(handle)
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_stream_t {
|
||||
match self { &StreamWatcher(ptr) => ptr }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TcpWatcher(*uvll::uv_tcp_t);
|
||||
|
||||
impl Watcher for TcpWatcher {
|
||||
fn event_loop(&self) -> Loop {
|
||||
loop_from_watcher(self)
|
||||
}
|
||||
}
|
||||
|
||||
type ConnectionCallback = ~fn(StreamWatcher, Option<UvError>);
|
||||
impl Callback for ConnectionCallback { }
|
||||
|
||||
pub impl TcpWatcher {
|
||||
fn new(loop_: &mut Loop) -> TcpWatcher {
|
||||
unsafe {
|
||||
let size = size_of::<uvll::uv_tcp_t>() as size_t;
|
||||
let handle = malloc(size) as *uvll::uv_tcp_t;
|
||||
fail_unless!(handle.is_not_null());
|
||||
fail_unless!(0 == uvll::tcp_init(loop_.native_handle(), handle));
|
||||
let mut watcher = NativeHandle::from_native_handle(handle);
|
||||
install_watcher_data(&mut watcher);
|
||||
return watcher;
|
||||
}
|
||||
}
|
||||
|
||||
fn bind(&mut self, address: IpAddr) {
|
||||
match address {
|
||||
Ipv4(*) => {
|
||||
let addr = ip4_to_uv_ip4(address);
|
||||
let result = unsafe {
|
||||
uvll::tcp_bind(self.native_handle(), &addr)
|
||||
};
|
||||
// XXX: bind is likely to fail. need real error handling
|
||||
fail_unless!(result == 0);
|
||||
}
|
||||
_ => fail!()
|
||||
}
|
||||
}
|
||||
|
||||
fn connect(&mut self, address: IpAddr, cb: ConnectionCallback) {
|
||||
unsafe {
|
||||
fail_unless!(get_watcher_data(self).connect_cb.is_none());
|
||||
get_watcher_data(self).connect_cb = Some(cb);
|
||||
|
||||
let mut connect_watcher = ConnectRequest::new();
|
||||
let connect_handle = connect_watcher.native_handle();
|
||||
match address {
|
||||
Ipv4(*) => {
|
||||
let addr = ip4_to_uv_ip4(address);
|
||||
rtdebug!("connect_t: %x", connect_handle as uint);
|
||||
fail_unless!(0 == uvll::tcp_connect(connect_handle,
|
||||
self.native_handle(),
|
||||
&addr, connect_cb));
|
||||
}
|
||||
_ => fail!()
|
||||
}
|
||||
|
||||
extern fn connect_cb(req: *uvll::uv_connect_t, status: c_int) {
|
||||
rtdebug!("connect_t: %x", req as uint);
|
||||
let connect_request: ConnectRequest =
|
||||
NativeHandle::from_native_handle(req);
|
||||
let mut stream_watcher = connect_request.stream();
|
||||
connect_request.delete();
|
||||
let cb: ConnectionCallback = {
|
||||
let data = get_watcher_data(&mut stream_watcher);
|
||||
data.connect_cb.swap_unwrap()
|
||||
};
|
||||
let status = status_to_maybe_uv_error(
|
||||
stream_watcher.native_handle(), status);
|
||||
cb(stream_watcher, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn listen(&mut self, cb: ConnectionCallback) {
|
||||
// XXX: Borrowck
|
||||
let data = get_watcher_data(unsafe { transmute_mut_region(self) });
|
||||
fail_unless!(data.connect_cb.is_none());
|
||||
data.connect_cb = Some(cb);
|
||||
|
||||
unsafe {
|
||||
static BACKLOG: c_int = 128; // XXX should be configurable
|
||||
// XXX: This can probably fail
|
||||
fail_unless!(0 == uvll::listen(self.native_handle(),
|
||||
BACKLOG, connection_cb));
|
||||
}
|
||||
|
||||
extern fn connection_cb(handle: *uvll::uv_stream_t, status: c_int) {
|
||||
rtdebug!("connection_cb");
|
||||
let mut stream_watcher: StreamWatcher =
|
||||
NativeHandle::from_native_handle(handle);
|
||||
let cb = get_watcher_data(&mut stream_watcher)
|
||||
.connect_cb.swap_unwrap();
|
||||
let status = status_to_maybe_uv_error(
|
||||
stream_watcher.native_handle(), status);
|
||||
cb(stream_watcher, status);
|
||||
}
|
||||
}
|
||||
|
||||
fn as_stream(&self) -> StreamWatcher {
|
||||
NativeHandle::from_native_handle(
|
||||
self.native_handle() as *uvll::uv_stream_t)
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_tcp_t> for TcpWatcher {
|
||||
fn from_native_handle(handle: *uvll::uv_tcp_t) -> TcpWatcher {
|
||||
TcpWatcher(handle)
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_tcp_t {
|
||||
match self { &TcpWatcher(ptr) => ptr }
|
||||
}
|
||||
}
|
||||
|
||||
trait Request { }
|
||||
|
||||
type ConnectCallback = ~fn(ConnectRequest, Option<UvError>);
|
||||
impl Callback for ConnectCallback { }
|
||||
|
||||
// uv_connect_t is a subclass of uv_req_t
|
||||
struct ConnectRequest(*uvll::uv_connect_t);
|
||||
|
||||
impl Request for ConnectRequest { }
|
||||
|
||||
impl ConnectRequest {
|
||||
|
||||
fn new() -> ConnectRequest {
|
||||
let connect_handle = unsafe {
|
||||
malloc(size_of::<uvll::uv_connect_t>() as size_t)
|
||||
};
|
||||
fail_unless!(connect_handle.is_not_null());
|
||||
let connect_handle = connect_handle as *uvll::uv_connect_t;
|
||||
ConnectRequest(connect_handle)
|
||||
}
|
||||
|
||||
fn stream(&self) -> StreamWatcher {
|
||||
unsafe {
|
||||
let stream_handle =
|
||||
uvll::get_stream_handle_from_connect_req(
|
||||
self.native_handle());
|
||||
NativeHandle::from_native_handle(stream_handle)
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(self) {
|
||||
unsafe { free(self.native_handle() as *c_void) }
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_connect_t> for ConnectRequest {
|
||||
fn from_native_handle(
|
||||
handle: *uvll:: uv_connect_t) -> ConnectRequest {
|
||||
ConnectRequest(handle)
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_connect_t {
|
||||
match self { &ConnectRequest(ptr) => ptr }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WriteRequest(*uvll::uv_write_t);
|
||||
|
||||
impl Request for WriteRequest { }
|
||||
|
||||
impl WriteRequest {
|
||||
|
||||
fn new() -> WriteRequest {
|
||||
let write_handle = unsafe {
|
||||
malloc(size_of::<uvll::uv_write_t>() as size_t)
|
||||
};
|
||||
fail_unless!(write_handle.is_not_null());
|
||||
let write_handle = write_handle as *uvll::uv_write_t;
|
||||
WriteRequest(write_handle)
|
||||
}
|
||||
|
||||
fn stream(&self) -> StreamWatcher {
|
||||
unsafe {
|
||||
let stream_handle =
|
||||
uvll::get_stream_handle_from_write_req(self.native_handle());
|
||||
NativeHandle::from_native_handle(stream_handle)
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(self) {
|
||||
unsafe { free(self.native_handle() as *c_void) }
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_write_t> for WriteRequest {
|
||||
fn from_native_handle(handle: *uvll:: uv_write_t) -> WriteRequest {
|
||||
WriteRequest(handle)
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_write_t {
|
||||
match self { &WriteRequest(ptr) => ptr }
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Need to define the error constants like EOF so they can be
|
||||
// compared to the UvError type
|
||||
|
||||
struct UvError(uvll::uv_err_t);
|
||||
|
||||
impl UvError {
|
||||
|
||||
fn name(&self) -> ~str {
|
||||
unsafe {
|
||||
let inner = match self { &UvError(ref a) => a };
|
||||
let name_str = uvll::err_name(inner);
|
||||
fail_unless!(name_str.is_not_null());
|
||||
from_c_str(name_str)
|
||||
}
|
||||
}
|
||||
|
||||
fn desc(&self) -> ~str {
|
||||
unsafe {
|
||||
let inner = match self { &UvError(ref a) => a };
|
||||
let desc_str = uvll::strerror(inner);
|
||||
fail_unless!(desc_str.is_not_null());
|
||||
from_c_str(desc_str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToStr for UvError {
|
||||
fn to_str(&self) -> ~str {
|
||||
fmt!("%s: %s", self.name(), self.desc())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_smoke_test() {
|
||||
let err = uvll::uv_err_t { code: 1, sys_errno_: 1 };
|
||||
let err: UvError = UvError(err);
|
||||
fail_unless!(err.to_str() == ~"EOF: end of file");
|
||||
}
|
||||
|
||||
|
||||
/// Given a uv handle, convert a callback status to a UvError
|
||||
// XXX: Follow the pattern below by parameterizing over T: Watcher, not T
|
||||
fn status_to_maybe_uv_error<T>(handle: *T, status: c_int) -> Option<UvError> {
|
||||
if status != -1 {
|
||||
None
|
||||
} else {
|
||||
unsafe {
|
||||
rtdebug!("handle: %x", handle as uint);
|
||||
let loop_ = uvll::get_loop_for_uv_handle(handle);
|
||||
rtdebug!("loop: %x", loop_ as uint);
|
||||
let err = uvll::last_error(loop_);
|
||||
Some(UvError(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the uv event loop from a Watcher
|
||||
pub fn loop_from_watcher<H, W: Watcher + NativeHandle<*H>>(
|
||||
watcher: &W) -> Loop {
|
||||
|
||||
let handle = watcher.native_handle();
|
||||
let loop_ = unsafe { uvll::get_loop_for_uv_handle(handle) };
|
||||
NativeHandle::from_native_handle(loop_)
|
||||
}
|
||||
|
||||
/// Set the custom data on a handle to a callback Note: This is only
|
||||
/// suitable for watchers that make just one type of callback. For
|
||||
/// others use WatcherData
|
||||
fn set_watcher_callback<H, W: Watcher + NativeHandle<*H>, CB: Callback>(
|
||||
watcher: &mut W, cb: CB) {
|
||||
|
||||
drop_watcher_callback::<H, W, CB>(watcher);
|
||||
// XXX: Boxing the callback so it fits into a
|
||||
// pointer. Unfortunate extra allocation
|
||||
let boxed_cb = ~cb;
|
||||
let data = unsafe { transmute::<~CB, *c_void>(boxed_cb) };
|
||||
unsafe { uvll::set_data_for_uv_handle(watcher.native_handle(), data) };
|
||||
}
|
||||
|
||||
/// Delete a callback from a handle's custom data
|
||||
fn drop_watcher_callback<H, W: Watcher + NativeHandle<*H>, CB: Callback>(
|
||||
watcher: &mut W) {
|
||||
|
||||
unsafe {
|
||||
let handle = watcher.native_handle();
|
||||
let handle_data: *c_void = uvll::get_data_for_uv_handle(handle);
|
||||
if handle_data.is_not_null() {
|
||||
// Take ownership of the callback and drop it
|
||||
let _cb = transmute::<*c_void, ~CB>(handle_data);
|
||||
// Make sure the pointer is zeroed
|
||||
uvll::set_data_for_uv_handle(
|
||||
watcher.native_handle(), null::<()>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Take a pointer to the callback installed as custom data
|
||||
fn borrow_callback_from_watcher<H, W: Watcher + NativeHandle<*H>,
|
||||
CB: Callback>(watcher: &W) -> &CB {
|
||||
|
||||
unsafe {
|
||||
let handle = watcher.native_handle();
|
||||
let handle_data: *c_void = uvll::get_data_for_uv_handle(handle);
|
||||
fail_unless!(handle_data.is_not_null());
|
||||
let cb = transmute::<&*c_void, &~CB>(&handle_data);
|
||||
return &**cb;
|
||||
}
|
||||
}
|
||||
|
||||
/// Take ownership of the callback installed as custom data
|
||||
fn take_callback_from_watcher<H, W: Watcher + NativeHandle<*H>, CB: Callback>(
|
||||
watcher: &mut W) -> CB {
|
||||
|
||||
unsafe {
|
||||
let handle = watcher.native_handle();
|
||||
let handle_data: *c_void = uvll::get_data_for_uv_handle(handle);
|
||||
fail_unless!(handle_data.is_not_null());
|
||||
uvll::set_data_for_uv_handle(handle, null::<()>());
|
||||
let cb: ~CB = transmute::<*c_void, ~CB>(handle_data);
|
||||
let cb = match cb { ~cb => cb };
|
||||
return cb;
|
||||
}
|
||||
}
|
||||
|
||||
/// Callbacks used by StreamWatchers, set as custom data on the foreign handle
|
||||
struct WatcherData {
|
||||
read_cb: Option<ReadCallback>,
|
||||
write_cb: Option<ConnectionCallback>,
|
||||
connect_cb: Option<ConnectionCallback>,
|
||||
close_cb: Option<NullCallback>,
|
||||
alloc_cb: Option<AllocCallback>
|
||||
}
|
||||
|
||||
fn install_watcher_data<H, W: Watcher + NativeHandle<*H>>(watcher: &mut W) {
|
||||
unsafe {
|
||||
let data = ~WatcherData {
|
||||
read_cb: None,
|
||||
write_cb: None,
|
||||
connect_cb: None,
|
||||
close_cb: None,
|
||||
alloc_cb: None
|
||||
};
|
||||
let data = transmute::<~WatcherData, *c_void>(data);
|
||||
uvll::set_data_for_uv_handle(watcher.native_handle(), data);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_watcher_data<H, W: Watcher + NativeHandle<*H>>(
|
||||
watcher: &'r mut W) -> &'r mut WatcherData {
|
||||
|
||||
unsafe {
|
||||
let data = uvll::get_data_for_uv_handle(watcher.native_handle());
|
||||
let data = transmute::<&*c_void, &mut ~WatcherData>(&data);
|
||||
return &mut **data;
|
||||
}
|
||||
}
|
||||
|
||||
fn drop_watcher_data<H, W: Watcher + NativeHandle<*H>>(watcher: &mut W) {
|
||||
unsafe {
|
||||
let data = uvll::get_data_for_uv_handle(watcher.native_handle());
|
||||
let _data = transmute::<*c_void, ~WatcherData>(data);
|
||||
uvll::set_data_for_uv_handle(watcher.native_handle(), null::<()>());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slice_to_uv_buf() {
|
||||
let slice = [0, .. 20];
|
||||
let buf = slice_to_uv_buf(slice);
|
||||
|
||||
fail_unless!(buf.len == 20);
|
||||
|
||||
unsafe {
|
||||
let base = transmute::<*u8, *mut u8>(buf.base);
|
||||
(*base) = 1;
|
||||
(*ptr::mut_offset(base, 1)) = 2;
|
||||
}
|
||||
|
||||
fail_unless!(slice[0] == 1);
|
||||
fail_unless!(slice[1] == 2);
|
||||
}
|
||||
|
||||
/// The uv buffer type
|
||||
pub type Buf = uvll::uv_buf_t;
|
||||
|
||||
/// Borrow a slice to a Buf
|
||||
pub fn slice_to_uv_buf(v: &[u8]) -> Buf {
|
||||
let data = unsafe { vec::raw::to_ptr(v) };
|
||||
unsafe { uvll::buf_init(data, v.len()) }
|
||||
}
|
||||
|
||||
// XXX: Do these conversions without copying
|
||||
|
||||
/// Transmute an owned vector to a Buf
|
||||
fn vec_to_uv_buf(v: ~[u8]) -> Buf {
|
||||
let data = unsafe { malloc(v.len() as size_t) } as *u8;
|
||||
fail_unless!(data.is_not_null());
|
||||
do vec::as_imm_buf(v) |b, l| {
|
||||
let data = data as *mut u8;
|
||||
unsafe { ptr::copy_memory(data, b, l) }
|
||||
}
|
||||
let buf = unsafe { uvll::buf_init(data, v.len()) };
|
||||
return buf;
|
||||
}
|
||||
|
||||
/// Transmute a Buf that was once a ~[u8] back to ~[u8]
|
||||
fn vec_from_uv_buf(buf: Buf) -> Option<~[u8]> {
|
||||
if !(buf.len == 0 && buf.base.is_null()) {
|
||||
let v = unsafe { vec::from_buf(buf.base, buf.len as uint) };
|
||||
unsafe { free(buf.base as *c_void) };
|
||||
return Some(v);
|
||||
} else {
|
||||
// No buffer
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_smoke_test() {
|
||||
do run_in_bare_thread {
|
||||
let mut loop_ = Loop::new();
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore(reason = "valgrind - loop destroyed before watcher?")]
|
||||
fn idle_new_then_close() {
|
||||
do run_in_bare_thread {
|
||||
let mut loop_ = Loop::new();
|
||||
let mut idle_watcher = { IdleWatcher::new(&mut loop_) };
|
||||
idle_watcher.close();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idle_smoke_test() {
|
||||
do run_in_bare_thread {
|
||||
let mut loop_ = Loop::new();
|
||||
let mut idle_watcher = { IdleWatcher::new(&mut loop_) };
|
||||
let mut count = 10;
|
||||
let count_ptr: *mut int = &mut count;
|
||||
do idle_watcher.start |idle_watcher, status| {
|
||||
let mut idle_watcher = idle_watcher;
|
||||
fail_unless!(status.is_none());
|
||||
if unsafe { *count_ptr == 10 } {
|
||||
idle_watcher.stop();
|
||||
idle_watcher.close();
|
||||
} else {
|
||||
unsafe { *count_ptr = *count_ptr + 1; }
|
||||
}
|
||||
}
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
fail_unless!(count == 10);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idle_start_stop_start() {
|
||||
do run_in_bare_thread {
|
||||
let mut loop_ = Loop::new();
|
||||
let mut idle_watcher = { IdleWatcher::new(&mut loop_) };
|
||||
do idle_watcher.start |idle_watcher, status| {
|
||||
let mut idle_watcher = idle_watcher;
|
||||
fail_unless!(status.is_none());
|
||||
idle_watcher.stop();
|
||||
do idle_watcher.start |idle_watcher, status| {
|
||||
fail_unless!(status.is_none());
|
||||
let mut idle_watcher = idle_watcher;
|
||||
idle_watcher.stop();
|
||||
idle_watcher.close();
|
||||
}
|
||||
}
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore(reason = "ffi struct issues")]
|
||||
fn connect_close() {
|
||||
do run_in_bare_thread() {
|
||||
let mut loop_ = Loop::new();
|
||||
let mut tcp_watcher = { TcpWatcher::new(&mut loop_) };
|
||||
// Connect to a port where nobody is listening
|
||||
let addr = Ipv4(127, 0, 0, 1, 2923);
|
||||
do tcp_watcher.connect(addr) |stream_watcher, status| {
|
||||
rtdebug!("tcp_watcher.connect!");
|
||||
fail_unless!(status.is_some());
|
||||
fail_unless!(status.get().name() == ~"ECONNREFUSED");
|
||||
stream_watcher.close(||());
|
||||
}
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore(reason = "need a server to connect to")]
|
||||
fn connect_read() {
|
||||
do run_in_bare_thread() {
|
||||
let mut loop_ = Loop::new();
|
||||
let mut tcp_watcher = { TcpWatcher::new(&mut loop_) };
|
||||
let addr = Ipv4(127, 0, 0, 1, 2924);
|
||||
do tcp_watcher.connect(addr) |stream_watcher, status| {
|
||||
let mut stream_watcher = stream_watcher;
|
||||
rtdebug!("tcp_watcher.connect!");
|
||||
fail_unless!(status.is_none());
|
||||
let alloc: AllocCallback = |size| {
|
||||
vec_to_uv_buf(vec::from_elem(size, 0))
|
||||
};
|
||||
do stream_watcher.read_start(alloc)
|
||||
|stream_watcher, nread, buf, status| {
|
||||
|
||||
let buf = vec_from_uv_buf(buf);
|
||||
rtdebug!("read cb!");
|
||||
if status.is_none() {
|
||||
let bytes = buf.unwrap();
|
||||
rtdebug!("%s", bytes.slice(0, nread as uint).to_str());
|
||||
} else {
|
||||
rtdebug!("status after read: %s", status.get().to_str());
|
||||
rtdebug!("closing");
|
||||
stream_watcher.close(||());
|
||||
}
|
||||
}
|
||||
}
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore(reason = "ffi struct issues")]
|
||||
fn listen() {
|
||||
do run_in_bare_thread() {
|
||||
static MAX: int = 10;
|
||||
let mut loop_ = Loop::new();
|
||||
let mut server_tcp_watcher = { TcpWatcher::new(&mut loop_) };
|
||||
let addr = Ipv4(127, 0, 0, 1, 2925);
|
||||
server_tcp_watcher.bind(addr);
|
||||
let loop_ = loop_;
|
||||
rtdebug!("listening");
|
||||
do server_tcp_watcher.listen |server_stream_watcher, status| {
|
||||
rtdebug!("listened!");
|
||||
fail_unless!(status.is_none());
|
||||
let mut server_stream_watcher = server_stream_watcher;
|
||||
let mut loop_ = loop_;
|
||||
let mut client_tcp_watcher = TcpWatcher::new(&mut loop_);
|
||||
let mut client_tcp_watcher = client_tcp_watcher.as_stream();
|
||||
server_stream_watcher.accept(client_tcp_watcher);
|
||||
let count_cell = Cell(0);
|
||||
let server_stream_watcher = server_stream_watcher;
|
||||
rtdebug!("starting read");
|
||||
let alloc: AllocCallback = |size| {
|
||||
vec_to_uv_buf(vec::from_elem(size, 0))
|
||||
};
|
||||
do client_tcp_watcher.read_start(alloc)
|
||||
|stream_watcher, nread, buf, status| {
|
||||
|
||||
rtdebug!("i'm reading!");
|
||||
let buf = vec_from_uv_buf(buf);
|
||||
let mut count = count_cell.take();
|
||||
if status.is_none() {
|
||||
rtdebug!("got %d bytes", nread);
|
||||
let buf = buf.unwrap();
|
||||
for buf.slice(0, nread as uint).each |byte| {
|
||||
fail_unless!(*byte == count as u8);
|
||||
rtdebug!("%u", *byte as uint);
|
||||
count += 1;
|
||||
}
|
||||
} else {
|
||||
fail_unless!(count == MAX);
|
||||
do stream_watcher.close {
|
||||
server_stream_watcher.close(||());
|
||||
}
|
||||
}
|
||||
count_cell.put_back(count);
|
||||
}
|
||||
}
|
||||
|
||||
let _client_thread = do Thread::start {
|
||||
rtdebug!("starting client thread");
|
||||
let mut loop_ = Loop::new();
|
||||
let mut tcp_watcher = { TcpWatcher::new(&mut loop_) };
|
||||
do tcp_watcher.connect(addr) |stream_watcher, status| {
|
||||
rtdebug!("connecting");
|
||||
fail_unless!(status.is_none());
|
||||
let mut stream_watcher = stream_watcher;
|
||||
let msg = ~[0, 1, 2, 3, 4, 5, 6 ,7 ,8, 9];
|
||||
do stream_watcher.write(msg) |stream_watcher, status| {
|
||||
rtdebug!("writing");
|
||||
fail_unless!(status.is_none());
|
||||
stream_watcher.close(||());
|
||||
}
|
||||
}
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
};
|
||||
|
||||
let mut loop_ = loop_;
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
}
|
||||
}
|
52
src/libcore/rt/uv/file.rs
Normal file
52
src/libcore/rt/uv/file.rs
Normal file
@ -0,0 +1,52 @@
|
||||
// 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 <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.
|
||||
|
||||
use prelude::*;
|
||||
use ptr::null;
|
||||
use libc::c_void;
|
||||
use super::{UvError, Callback, Request, NativeHandle, Loop};
|
||||
use super::super::uvll;
|
||||
use super::super::uvll::*;
|
||||
|
||||
pub type FsCallback = ~fn(FsRequest, Option<UvError>);
|
||||
impl Callback for FsCallback { }
|
||||
|
||||
pub struct FsRequest(*uvll::uv_fs_t);
|
||||
|
||||
impl Request for FsRequest;
|
||||
|
||||
impl FsRequest {
|
||||
fn new() -> FsRequest {
|
||||
let fs_req = unsafe { malloc_req(UV_FS) };
|
||||
fail_unless!(fs_req.is_not_null());
|
||||
let fs_req = fs_req as *uvll::uv_write_t;
|
||||
unsafe { uvll::set_data_for_req(fs_req, null::<()>()); }
|
||||
NativeHandle::from_native_handle(fs_req)
|
||||
}
|
||||
|
||||
fn delete(self) {
|
||||
unsafe { free_req(self.native_handle() as *c_void) }
|
||||
}
|
||||
|
||||
fn open(&mut self, _loop_: &Loop, _cb: FsCallback) {
|
||||
}
|
||||
|
||||
fn close(&mut self, _loop_: &Loop, _cb: FsCallback) {
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_fs_t> for FsRequest {
|
||||
fn from_native_handle(handle: *uvll:: uv_fs_t) -> FsRequest {
|
||||
FsRequest(handle)
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_fs_t {
|
||||
match self { &FsRequest(ptr) => ptr }
|
||||
}
|
||||
}
|
456
src/libcore/rt/uv/mod.rs
Normal file
456
src/libcore/rt/uv/mod.rs
Normal file
@ -0,0 +1,456 @@
|
||||
// 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 <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.
|
||||
|
||||
/*!
|
||||
|
||||
Bindings to libuv.
|
||||
|
||||
UV types consist of the event loop (Loop), Watchers, Requests and
|
||||
Callbacks.
|
||||
|
||||
Watchers and Requests encapsulate pointers to uv *handles*, which have
|
||||
subtyping relationships with each other. This subtyping is reflected
|
||||
in the bindings with explicit or implicit coercions. For example, an
|
||||
upcast from TcpWatcher to StreamWatcher is done with
|
||||
`tcp_watcher.as_stream()`. In other cases a callback on a specific
|
||||
type of watcher will be passed a watcher of a supertype.
|
||||
|
||||
Currently all use of Request types (connect/write requests) are
|
||||
encapsulated in the bindings and don't need to be dealt with by the
|
||||
caller.
|
||||
|
||||
# Safety note
|
||||
|
||||
Due to the complex lifecycle of uv handles, as well as compiler bugs,
|
||||
this module is not memory safe and requires explicit memory management,
|
||||
via `close` and `delete` methods.
|
||||
|
||||
*/
|
||||
|
||||
use option::*;
|
||||
use str::raw::from_c_str;
|
||||
use to_str::ToStr;
|
||||
use vec;
|
||||
use ptr;
|
||||
use libc::{c_void, c_int, size_t, malloc, free, ssize_t};
|
||||
use cast::{transmute, transmute_mut_region};
|
||||
use ptr::null;
|
||||
use sys::size_of;
|
||||
use super::uvll;
|
||||
use super::uvll::*;
|
||||
use unstable::finally::Finally;
|
||||
|
||||
#[cfg(test)] use unstable::run_in_bare_thread;
|
||||
#[cfg(test)] use super::thread::Thread;
|
||||
#[cfg(test)] use cell::Cell;
|
||||
|
||||
pub use self::file::{FsRequest, FsCallback};
|
||||
pub use self::net::{StreamWatcher, TcpWatcher};
|
||||
pub use self::net::{ReadCallback, AllocCallback, ConnectionCallback, ConnectCallback};
|
||||
|
||||
pub mod file;
|
||||
pub mod net;
|
||||
|
||||
/// A trait for callbacks to implement. Provides a little extra type safety
|
||||
/// for generic, unsafe interop functions like `set_watcher_callback`.
|
||||
pub trait Callback { }
|
||||
|
||||
pub trait Request { }
|
||||
|
||||
/// The trait implemented by uv 'watchers' (handles). Watchers are
|
||||
/// non-owning wrappers around the uv handles and are not completely
|
||||
/// safe - there may be multiple instances for a single underlying
|
||||
/// handle. Watchers are generally created, then `start`ed, `stop`ed
|
||||
/// and `close`ed, but due to their complex life cycle may not be
|
||||
/// entirely memory safe if used in unanticipated patterns.
|
||||
pub trait Watcher {
|
||||
fn event_loop(&self) -> Loop;
|
||||
}
|
||||
|
||||
pub type NullCallback = ~fn();
|
||||
impl Callback for NullCallback { }
|
||||
|
||||
/// A type that wraps a native handle
|
||||
pub trait NativeHandle<T> {
|
||||
pub fn from_native_handle(T) -> Self;
|
||||
pub fn native_handle(&self) -> T;
|
||||
}
|
||||
|
||||
/// XXX: Loop(*handle) is buggy with destructors. Normal structs
|
||||
/// with dtors may not be destructured, but tuple structs can,
|
||||
/// but the results are not correct.
|
||||
pub struct Loop {
|
||||
handle: *uvll::uv_loop_t
|
||||
}
|
||||
|
||||
pub impl Loop {
|
||||
fn new() -> Loop {
|
||||
let handle = unsafe { uvll::loop_new() };
|
||||
fail_unless!(handle.is_not_null());
|
||||
NativeHandle::from_native_handle(handle)
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
unsafe { uvll::run(self.native_handle()) };
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
unsafe { uvll::loop_delete(self.native_handle()) };
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_loop_t> for Loop {
|
||||
fn from_native_handle(handle: *uvll::uv_loop_t) -> Loop {
|
||||
Loop { handle: handle }
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_loop_t {
|
||||
self.handle
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IdleWatcher(*uvll::uv_idle_t);
|
||||
|
||||
impl Watcher for IdleWatcher {
|
||||
fn event_loop(&self) -> Loop {
|
||||
loop_from_watcher(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub type IdleCallback = ~fn(IdleWatcher, Option<UvError>);
|
||||
impl Callback for IdleCallback { }
|
||||
|
||||
pub impl IdleWatcher {
|
||||
fn new(loop_: &mut Loop) -> IdleWatcher {
|
||||
unsafe {
|
||||
let handle = uvll::idle_new();
|
||||
fail_unless!(handle.is_not_null());
|
||||
fail_unless!(0 == uvll::idle_init(loop_.native_handle(), handle));
|
||||
uvll::set_data_for_uv_handle(handle, null::<()>());
|
||||
NativeHandle::from_native_handle(handle)
|
||||
}
|
||||
}
|
||||
|
||||
fn start(&mut self, cb: IdleCallback) {
|
||||
|
||||
set_watcher_callback(self, cb);
|
||||
unsafe {
|
||||
fail_unless!(0 == uvll::idle_start(self.native_handle(), idle_cb))
|
||||
};
|
||||
|
||||
extern fn idle_cb(handle: *uvll::uv_idle_t, status: c_int) {
|
||||
let idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle);
|
||||
let cb: &IdleCallback = borrow_callback_from_watcher(&idle_watcher);
|
||||
let status = status_to_maybe_uv_error(handle, status);
|
||||
(*cb)(idle_watcher, status);
|
||||
}
|
||||
}
|
||||
|
||||
fn stop(&mut self) {
|
||||
unsafe { fail_unless!(0 == uvll::idle_stop(self.native_handle())); }
|
||||
}
|
||||
|
||||
fn close(self) {
|
||||
unsafe { uvll::close(self.native_handle(), close_cb) };
|
||||
|
||||
extern fn close_cb(handle: *uvll::uv_idle_t) {
|
||||
let mut idle_watcher = NativeHandle::from_native_handle(handle);
|
||||
drop_watcher_callback::<uvll::uv_idle_t, IdleWatcher, IdleCallback>(&mut idle_watcher);
|
||||
unsafe { uvll::idle_delete(handle) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_idle_t> for IdleWatcher {
|
||||
fn from_native_handle(handle: *uvll::uv_idle_t) -> IdleWatcher {
|
||||
IdleWatcher(handle)
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_idle_t {
|
||||
match self { &IdleWatcher(ptr) => ptr }
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Need to define the error constants like EOF so they can be
|
||||
// compared to the UvError type
|
||||
|
||||
pub struct UvError(uvll::uv_err_t);
|
||||
|
||||
pub impl UvError {
|
||||
|
||||
fn name(&self) -> ~str {
|
||||
unsafe {
|
||||
let inner = match self { &UvError(ref a) => a };
|
||||
let name_str = uvll::err_name(inner);
|
||||
fail_unless!(name_str.is_not_null());
|
||||
from_c_str(name_str)
|
||||
}
|
||||
}
|
||||
|
||||
fn desc(&self) -> ~str {
|
||||
unsafe {
|
||||
let inner = match self { &UvError(ref a) => a };
|
||||
let desc_str = uvll::strerror(inner);
|
||||
fail_unless!(desc_str.is_not_null());
|
||||
from_c_str(desc_str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToStr for UvError {
|
||||
fn to_str(&self) -> ~str {
|
||||
fmt!("%s: %s", self.name(), self.desc())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_smoke_test() {
|
||||
let err = uvll::uv_err_t { code: 1, sys_errno_: 1 };
|
||||
let err: UvError = UvError(err);
|
||||
fail_unless!(err.to_str() == ~"EOF: end of file");
|
||||
}
|
||||
|
||||
|
||||
/// Given a uv handle, convert a callback status to a UvError
|
||||
// XXX: Follow the pattern below by parameterizing over T: Watcher, not T
|
||||
pub fn status_to_maybe_uv_error<T>(handle: *T, status: c_int) -> Option<UvError> {
|
||||
if status != -1 {
|
||||
None
|
||||
} else {
|
||||
unsafe {
|
||||
rtdebug!("handle: %x", handle as uint);
|
||||
let loop_ = uvll::get_loop_for_uv_handle(handle);
|
||||
rtdebug!("loop: %x", loop_ as uint);
|
||||
let err = uvll::last_error(loop_);
|
||||
Some(UvError(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the uv event loop from a Watcher
|
||||
pub fn loop_from_watcher<H, W: Watcher + NativeHandle<*H>>(
|
||||
watcher: &W) -> Loop {
|
||||
|
||||
let handle = watcher.native_handle();
|
||||
let loop_ = unsafe { uvll::get_loop_for_uv_handle(handle) };
|
||||
NativeHandle::from_native_handle(loop_)
|
||||
}
|
||||
|
||||
/// Set the custom data on a handle to a callback Note: This is only
|
||||
/// suitable for watchers that make just one type of callback. For
|
||||
/// others use WatcherData
|
||||
pub fn set_watcher_callback<H, W: Watcher + NativeHandle<*H>, CB: Callback>(
|
||||
watcher: &mut W, cb: CB) {
|
||||
|
||||
drop_watcher_callback::<H, W, CB>(watcher);
|
||||
// XXX: Boxing the callback so it fits into a
|
||||
// pointer. Unfortunate extra allocation
|
||||
let boxed_cb = ~cb;
|
||||
let data = unsafe { transmute::<~CB, *c_void>(boxed_cb) };
|
||||
unsafe { uvll::set_data_for_uv_handle(watcher.native_handle(), data) };
|
||||
}
|
||||
|
||||
/// Delete a callback from a handle's custom data
|
||||
pub fn drop_watcher_callback<H, W: Watcher + NativeHandle<*H>, CB: Callback>(
|
||||
watcher: &mut W) {
|
||||
|
||||
unsafe {
|
||||
let handle = watcher.native_handle();
|
||||
let handle_data: *c_void = uvll::get_data_for_uv_handle(handle);
|
||||
if handle_data.is_not_null() {
|
||||
// Take ownership of the callback and drop it
|
||||
let _cb = transmute::<*c_void, ~CB>(handle_data);
|
||||
// Make sure the pointer is zeroed
|
||||
uvll::set_data_for_uv_handle(watcher.native_handle(), null::<()>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Take a pointer to the callback installed as custom data
|
||||
pub fn borrow_callback_from_watcher<H, W: Watcher + NativeHandle<*H>,
|
||||
CB: Callback>(watcher: &W) -> &CB {
|
||||
|
||||
unsafe {
|
||||
let handle = watcher.native_handle();
|
||||
let handle_data: *c_void = uvll::get_data_for_uv_handle(handle);
|
||||
fail_unless!(handle_data.is_not_null());
|
||||
let cb = transmute::<&*c_void, &~CB>(&handle_data);
|
||||
return &**cb;
|
||||
}
|
||||
}
|
||||
|
||||
/// Take ownership of the callback installed as custom data
|
||||
pub fn take_callback_from_watcher<H, W: Watcher + NativeHandle<*H>, CB: Callback>(
|
||||
watcher: &mut W) -> CB {
|
||||
|
||||
unsafe {
|
||||
let handle = watcher.native_handle();
|
||||
let handle_data: *c_void = uvll::get_data_for_uv_handle(handle);
|
||||
fail_unless!(handle_data.is_not_null());
|
||||
uvll::set_data_for_uv_handle(handle, null::<()>());
|
||||
let cb: ~CB = transmute::<*c_void, ~CB>(handle_data);
|
||||
let cb = match cb { ~cb => cb };
|
||||
return cb;
|
||||
}
|
||||
}
|
||||
|
||||
/// Callbacks used by StreamWatchers, set as custom data on the foreign handle
|
||||
struct WatcherData {
|
||||
read_cb: Option<ReadCallback>,
|
||||
write_cb: Option<ConnectionCallback>,
|
||||
connect_cb: Option<ConnectionCallback>,
|
||||
close_cb: Option<NullCallback>,
|
||||
alloc_cb: Option<AllocCallback>
|
||||
}
|
||||
|
||||
pub fn install_watcher_data<H, W: Watcher + NativeHandle<*H>>(watcher: &mut W) {
|
||||
unsafe {
|
||||
let data = ~WatcherData {
|
||||
read_cb: None,
|
||||
write_cb: None,
|
||||
connect_cb: None,
|
||||
close_cb: None,
|
||||
alloc_cb: None
|
||||
};
|
||||
let data = transmute::<~WatcherData, *c_void>(data);
|
||||
uvll::set_data_for_uv_handle(watcher.native_handle(), data);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_watcher_data<H, W: Watcher + NativeHandle<*H>>(
|
||||
watcher: &'r mut W) -> &'r mut WatcherData {
|
||||
|
||||
unsafe {
|
||||
let data = uvll::get_data_for_uv_handle(watcher.native_handle());
|
||||
let data = transmute::<&*c_void, &mut ~WatcherData>(&data);
|
||||
return &mut **data;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drop_watcher_data<H, W: Watcher + NativeHandle<*H>>(watcher: &mut W) {
|
||||
unsafe {
|
||||
let data = uvll::get_data_for_uv_handle(watcher.native_handle());
|
||||
let _data = transmute::<*c_void, ~WatcherData>(data);
|
||||
uvll::set_data_for_uv_handle(watcher.native_handle(), null::<()>());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slice_to_uv_buf() {
|
||||
let slice = [0, .. 20];
|
||||
let buf = slice_to_uv_buf(slice);
|
||||
|
||||
fail_unless!(buf.len == 20);
|
||||
|
||||
unsafe {
|
||||
let base = transmute::<*u8, *mut u8>(buf.base);
|
||||
(*base) = 1;
|
||||
(*ptr::mut_offset(base, 1)) = 2;
|
||||
}
|
||||
|
||||
fail_unless!(slice[0] == 1);
|
||||
fail_unless!(slice[1] == 2);
|
||||
}
|
||||
|
||||
/// The uv buffer type
|
||||
pub type Buf = uvll::uv_buf_t;
|
||||
|
||||
/// Borrow a slice to a Buf
|
||||
pub fn slice_to_uv_buf(v: &[u8]) -> Buf {
|
||||
let data = unsafe { vec::raw::to_ptr(v) };
|
||||
unsafe { uvll::buf_init(data, v.len()) }
|
||||
}
|
||||
|
||||
// XXX: Do these conversions without copying
|
||||
|
||||
/// Transmute an owned vector to a Buf
|
||||
pub fn vec_to_uv_buf(v: ~[u8]) -> Buf {
|
||||
let data = unsafe { malloc(v.len() as size_t) } as *u8;
|
||||
fail_unless!(data.is_not_null());
|
||||
do vec::as_imm_buf(v) |b, l| {
|
||||
let data = data as *mut u8;
|
||||
unsafe { ptr::copy_memory(data, b, l) }
|
||||
}
|
||||
let buf = unsafe { uvll::buf_init(data, v.len()) };
|
||||
return buf;
|
||||
}
|
||||
|
||||
/// Transmute a Buf that was once a ~[u8] back to ~[u8]
|
||||
pub fn vec_from_uv_buf(buf: Buf) -> Option<~[u8]> {
|
||||
if !(buf.len == 0 && buf.base.is_null()) {
|
||||
let v = unsafe { vec::from_buf(buf.base, buf.len as uint) };
|
||||
unsafe { free(buf.base as *c_void) };
|
||||
return Some(v);
|
||||
} else {
|
||||
// No buffer
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_smoke_test() {
|
||||
do run_in_bare_thread {
|
||||
let mut loop_ = Loop::new();
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore(reason = "valgrind - loop destroyed before watcher?")]
|
||||
fn idle_new_then_close() {
|
||||
do run_in_bare_thread {
|
||||
let mut loop_ = Loop::new();
|
||||
let mut idle_watcher = { IdleWatcher::new(&mut loop_) };
|
||||
idle_watcher.close();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idle_smoke_test() {
|
||||
do run_in_bare_thread {
|
||||
let mut loop_ = Loop::new();
|
||||
let mut idle_watcher = { IdleWatcher::new(&mut loop_) };
|
||||
let mut count = 10;
|
||||
let count_ptr: *mut int = &mut count;
|
||||
do idle_watcher.start |idle_watcher, status| {
|
||||
let mut idle_watcher = idle_watcher;
|
||||
fail_unless!(status.is_none());
|
||||
if unsafe { *count_ptr == 10 } {
|
||||
idle_watcher.stop();
|
||||
idle_watcher.close();
|
||||
} else {
|
||||
unsafe { *count_ptr = *count_ptr + 1; }
|
||||
}
|
||||
}
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
fail_unless!(count == 10);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idle_start_stop_start() {
|
||||
do run_in_bare_thread {
|
||||
let mut loop_ = Loop::new();
|
||||
let mut idle_watcher = { IdleWatcher::new(&mut loop_) };
|
||||
do idle_watcher.start |idle_watcher, status| {
|
||||
let mut idle_watcher = idle_watcher;
|
||||
fail_unless!(status.is_none());
|
||||
idle_watcher.stop();
|
||||
do idle_watcher.start |idle_watcher, status| {
|
||||
fail_unless!(status.is_none());
|
||||
let mut idle_watcher = idle_watcher;
|
||||
idle_watcher.stop();
|
||||
idle_watcher.close();
|
||||
}
|
||||
}
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
}
|
||||
}
|
483
src/libcore/rt/uv/net.rs
Normal file
483
src/libcore/rt/uv/net.rs
Normal file
@ -0,0 +1,483 @@
|
||||
// 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 <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.
|
||||
|
||||
use prelude::*;
|
||||
use libc::{size_t, ssize_t, c_int, c_void};
|
||||
use cast::{transmute, transmute_mut_region};
|
||||
use super::super::uvll;
|
||||
use super::super::uvll::*;
|
||||
use super::{Loop, Watcher, Request, UvError, Buf, Callback, NativeHandle, NullCallback,
|
||||
loop_from_watcher, status_to_maybe_uv_error,
|
||||
install_watcher_data, get_watcher_data, drop_watcher_data,
|
||||
vec_to_uv_buf, vec_from_uv_buf};
|
||||
use super::super::rtio::{IpAddr, Ipv4, Ipv6};
|
||||
|
||||
#[cfg(test)]
|
||||
use unstable::run_in_bare_thread;
|
||||
#[cfg(test)]
|
||||
use super::super::thread::Thread;
|
||||
#[cfg(test)]
|
||||
use cell::Cell;
|
||||
|
||||
fn ip4_as_uv_ip4(addr: IpAddr, f: &fn(*sockaddr_in)) {
|
||||
match addr {
|
||||
Ipv4(a, b, c, d, p) => {
|
||||
unsafe {
|
||||
let addr = malloc_ip4_addr(fmt!("%u.%u.%u.%u",
|
||||
a as uint,
|
||||
b as uint,
|
||||
c as uint,
|
||||
d as uint), p as int);
|
||||
do (|| {
|
||||
f(addr);
|
||||
}).finally {
|
||||
free_ip4_addr(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ipv6 => fail!()
|
||||
}
|
||||
}
|
||||
|
||||
// uv_stream t is the parent class of uv_tcp_t, uv_pipe_t, uv_tty_t
|
||||
// and uv_file_t
|
||||
pub struct StreamWatcher(*uvll::uv_stream_t);
|
||||
|
||||
impl Watcher for StreamWatcher {
|
||||
fn event_loop(&self) -> Loop {
|
||||
loop_from_watcher(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub type ReadCallback = ~fn(StreamWatcher, int, Buf, Option<UvError>);
|
||||
impl Callback for ReadCallback { }
|
||||
|
||||
// XXX: The uv alloc callback also has a *uv_handle_t arg
|
||||
pub type AllocCallback = ~fn(uint) -> Buf;
|
||||
impl Callback for AllocCallback { }
|
||||
|
||||
pub impl StreamWatcher {
|
||||
|
||||
fn read_start(&mut self, alloc: AllocCallback, cb: ReadCallback) {
|
||||
// XXX: Borrowchk problems
|
||||
let data = get_watcher_data(unsafe { transmute_mut_region(self) });
|
||||
data.alloc_cb = Some(alloc);
|
||||
data.read_cb = Some(cb);
|
||||
|
||||
let handle = self.native_handle();
|
||||
unsafe { uvll::read_start(handle, alloc_cb, read_cb); }
|
||||
|
||||
extern fn alloc_cb(stream: *uvll::uv_stream_t, suggested_size: size_t) -> Buf {
|
||||
let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(stream);
|
||||
let data = get_watcher_data(&mut stream_watcher);
|
||||
let alloc_cb = data.alloc_cb.get_ref();
|
||||
return (*alloc_cb)(suggested_size as uint);
|
||||
}
|
||||
|
||||
extern fn read_cb(stream: *uvll::uv_stream_t, nread: ssize_t, ++buf: Buf) {
|
||||
rtdebug!("buf addr: %x", buf.base as uint);
|
||||
rtdebug!("buf len: %d", buf.len as int);
|
||||
let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(stream);
|
||||
let data = get_watcher_data(&mut stream_watcher);
|
||||
let cb = data.read_cb.get_ref();
|
||||
let status = status_to_maybe_uv_error(stream, nread as c_int);
|
||||
(*cb)(stream_watcher, nread as int, buf, status);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_stop(&mut self) {
|
||||
// It would be nice to drop the alloc and read callbacks here,
|
||||
// but read_stop may be called from inside one of them and we
|
||||
// would end up freeing the in-use environment
|
||||
let handle = self.native_handle();
|
||||
unsafe { uvll::read_stop(handle); }
|
||||
}
|
||||
|
||||
// XXX: Needs to take &[u8], not ~[u8]
|
||||
fn write(&mut self, msg: ~[u8], cb: ConnectionCallback) {
|
||||
// XXX: Borrowck
|
||||
let data = get_watcher_data(unsafe { transmute_mut_region(self) });
|
||||
fail_unless!(data.write_cb.is_none());
|
||||
data.write_cb = Some(cb);
|
||||
|
||||
let req = WriteRequest::new();
|
||||
let buf = vec_to_uv_buf(msg);
|
||||
// XXX: Allocation
|
||||
let bufs = ~[buf];
|
||||
unsafe {
|
||||
fail_unless!(0 == uvll::write(req.native_handle(),
|
||||
self.native_handle(),
|
||||
&bufs, write_cb));
|
||||
}
|
||||
// XXX: Freeing immediately after write. Is this ok?
|
||||
let _v = vec_from_uv_buf(buf);
|
||||
|
||||
extern fn write_cb(req: *uvll::uv_write_t, status: c_int) {
|
||||
let write_request: WriteRequest = NativeHandle::from_native_handle(req);
|
||||
let mut stream_watcher = write_request.stream();
|
||||
write_request.delete();
|
||||
let cb = get_watcher_data(&mut stream_watcher).write_cb.swap_unwrap();
|
||||
let status = status_to_maybe_uv_error(stream_watcher.native_handle(), status);
|
||||
cb(stream_watcher, status);
|
||||
}
|
||||
}
|
||||
|
||||
fn accept(&mut self, stream: StreamWatcher) {
|
||||
let self_handle = self.native_handle() as *c_void;
|
||||
let stream_handle = stream.native_handle() as *c_void;
|
||||
unsafe {
|
||||
fail_unless!(0 == uvll::accept(self_handle, stream_handle));
|
||||
}
|
||||
}
|
||||
|
||||
fn close(self, cb: NullCallback) {
|
||||
{
|
||||
let mut self = self;
|
||||
let data = get_watcher_data(&mut self);
|
||||
fail_unless!(data.close_cb.is_none());
|
||||
data.close_cb = Some(cb);
|
||||
}
|
||||
|
||||
unsafe { uvll::close(self.native_handle(), close_cb); }
|
||||
|
||||
extern fn close_cb(handle: *uvll::uv_stream_t) {
|
||||
let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(handle);
|
||||
{
|
||||
let mut data = get_watcher_data(&mut stream_watcher);
|
||||
data.close_cb.swap_unwrap()();
|
||||
}
|
||||
drop_watcher_data(&mut stream_watcher);
|
||||
unsafe { free_handle(handle as *c_void) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_stream_t> for StreamWatcher {
|
||||
fn from_native_handle(
|
||||
handle: *uvll::uv_stream_t) -> StreamWatcher {
|
||||
StreamWatcher(handle)
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_stream_t {
|
||||
match self { &StreamWatcher(ptr) => ptr }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TcpWatcher(*uvll::uv_tcp_t);
|
||||
|
||||
impl Watcher for TcpWatcher {
|
||||
fn event_loop(&self) -> Loop {
|
||||
loop_from_watcher(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub type ConnectionCallback = ~fn(StreamWatcher, Option<UvError>);
|
||||
impl Callback for ConnectionCallback { }
|
||||
|
||||
pub impl TcpWatcher {
|
||||
fn new(loop_: &mut Loop) -> TcpWatcher {
|
||||
unsafe {
|
||||
let handle = malloc_handle(UV_TCP);
|
||||
fail_unless!(handle.is_not_null());
|
||||
fail_unless!(0 == uvll::tcp_init(loop_.native_handle(), handle));
|
||||
let mut watcher = NativeHandle::from_native_handle(handle);
|
||||
install_watcher_data(&mut watcher);
|
||||
return watcher;
|
||||
}
|
||||
}
|
||||
|
||||
fn bind(&mut self, address: IpAddr) {
|
||||
match address {
|
||||
Ipv4(*) => {
|
||||
do ip4_as_uv_ip4(address) |addr| {
|
||||
let result = unsafe {
|
||||
uvll::tcp_bind(self.native_handle(), addr)
|
||||
};
|
||||
// XXX: bind is likely to fail. need real error handling
|
||||
fail_unless!(result == 0);
|
||||
}
|
||||
}
|
||||
_ => fail!()
|
||||
}
|
||||
}
|
||||
|
||||
fn connect(&mut self, address: IpAddr, cb: ConnectionCallback) {
|
||||
unsafe {
|
||||
fail_unless!(get_watcher_data(self).connect_cb.is_none());
|
||||
get_watcher_data(self).connect_cb = Some(cb);
|
||||
|
||||
let mut connect_watcher = ConnectRequest::new();
|
||||
let connect_handle = connect_watcher.native_handle();
|
||||
match address {
|
||||
Ipv4(*) => {
|
||||
do ip4_as_uv_ip4(address) |addr| {
|
||||
rtdebug!("connect_t: %x", connect_handle as uint);
|
||||
fail_unless!(0 == uvll::tcp_connect(connect_handle,
|
||||
self.native_handle(),
|
||||
addr, connect_cb));
|
||||
}
|
||||
}
|
||||
_ => fail!()
|
||||
}
|
||||
|
||||
extern fn connect_cb(req: *uvll::uv_connect_t, status: c_int) {
|
||||
rtdebug!("connect_t: %x", req as uint);
|
||||
let connect_request: ConnectRequest = NativeHandle::from_native_handle(req);
|
||||
let mut stream_watcher = connect_request.stream();
|
||||
connect_request.delete();
|
||||
let cb: ConnectionCallback = {
|
||||
let data = get_watcher_data(&mut stream_watcher);
|
||||
data.connect_cb.swap_unwrap()
|
||||
};
|
||||
let status = status_to_maybe_uv_error(stream_watcher.native_handle(), status);
|
||||
cb(stream_watcher, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn listen(&mut self, cb: ConnectionCallback) {
|
||||
// XXX: Borrowck
|
||||
let data = get_watcher_data(unsafe { transmute_mut_region(self) });
|
||||
fail_unless!(data.connect_cb.is_none());
|
||||
data.connect_cb = Some(cb);
|
||||
|
||||
unsafe {
|
||||
static BACKLOG: c_int = 128; // XXX should be configurable
|
||||
// XXX: This can probably fail
|
||||
fail_unless!(0 == uvll::listen(self.native_handle(),
|
||||
BACKLOG, connection_cb));
|
||||
}
|
||||
|
||||
extern fn connection_cb(handle: *uvll::uv_stream_t, status: c_int) {
|
||||
rtdebug!("connection_cb");
|
||||
let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(handle);
|
||||
let cb = get_watcher_data(&mut stream_watcher).connect_cb.swap_unwrap();
|
||||
let status = status_to_maybe_uv_error(stream_watcher.native_handle(), status);
|
||||
cb(stream_watcher, status);
|
||||
}
|
||||
}
|
||||
|
||||
fn as_stream(&self) -> StreamWatcher {
|
||||
NativeHandle::from_native_handle(self.native_handle() as *uvll::uv_stream_t)
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_tcp_t> for TcpWatcher {
|
||||
fn from_native_handle(handle: *uvll::uv_tcp_t) -> TcpWatcher {
|
||||
TcpWatcher(handle)
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_tcp_t {
|
||||
match self { &TcpWatcher(ptr) => ptr }
|
||||
}
|
||||
}
|
||||
|
||||
pub type ConnectCallback = ~fn(ConnectRequest, Option<UvError>);
|
||||
impl Callback for ConnectCallback { }
|
||||
|
||||
// uv_connect_t is a subclass of uv_req_t
|
||||
struct ConnectRequest(*uvll::uv_connect_t);
|
||||
|
||||
impl Request for ConnectRequest { }
|
||||
|
||||
impl ConnectRequest {
|
||||
|
||||
fn new() -> ConnectRequest {
|
||||
let connect_handle = unsafe {
|
||||
malloc_req(UV_CONNECT)
|
||||
};
|
||||
fail_unless!(connect_handle.is_not_null());
|
||||
let connect_handle = connect_handle as *uvll::uv_connect_t;
|
||||
ConnectRequest(connect_handle)
|
||||
}
|
||||
|
||||
fn stream(&self) -> StreamWatcher {
|
||||
unsafe {
|
||||
let stream_handle = uvll::get_stream_handle_from_connect_req(self.native_handle());
|
||||
NativeHandle::from_native_handle(stream_handle)
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(self) {
|
||||
unsafe { free_req(self.native_handle() as *c_void) }
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_connect_t> for ConnectRequest {
|
||||
fn from_native_handle(
|
||||
handle: *uvll:: uv_connect_t) -> ConnectRequest {
|
||||
ConnectRequest(handle)
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_connect_t {
|
||||
match self { &ConnectRequest(ptr) => ptr }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WriteRequest(*uvll::uv_write_t);
|
||||
|
||||
impl Request for WriteRequest { }
|
||||
|
||||
pub impl WriteRequest {
|
||||
|
||||
fn new() -> WriteRequest {
|
||||
let write_handle = unsafe {
|
||||
malloc_req(UV_WRITE)
|
||||
};
|
||||
fail_unless!(write_handle.is_not_null());
|
||||
let write_handle = write_handle as *uvll::uv_write_t;
|
||||
WriteRequest(write_handle)
|
||||
}
|
||||
|
||||
fn stream(&self) -> StreamWatcher {
|
||||
unsafe {
|
||||
let stream_handle = uvll::get_stream_handle_from_write_req(self.native_handle());
|
||||
NativeHandle::from_native_handle(stream_handle)
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(self) {
|
||||
unsafe { free_req(self.native_handle() as *c_void) }
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeHandle<*uvll::uv_write_t> for WriteRequest {
|
||||
fn from_native_handle(handle: *uvll:: uv_write_t) -> WriteRequest {
|
||||
WriteRequest(handle)
|
||||
}
|
||||
fn native_handle(&self) -> *uvll::uv_write_t {
|
||||
match self { &WriteRequest(ptr) => ptr }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[ignore(reason = "ffi struct issues")]
|
||||
fn connect_close() {
|
||||
do run_in_bare_thread() {
|
||||
let mut loop_ = Loop::new();
|
||||
let mut tcp_watcher = { TcpWatcher::new(&mut loop_) };
|
||||
// Connect to a port where nobody is listening
|
||||
let addr = Ipv4(127, 0, 0, 1, 2923);
|
||||
do tcp_watcher.connect(addr) |stream_watcher, status| {
|
||||
rtdebug!("tcp_watcher.connect!");
|
||||
fail_unless!(status.is_some());
|
||||
fail_unless!(status.get().name() == ~"ECONNREFUSED");
|
||||
stream_watcher.close(||());
|
||||
}
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore(reason = "need a server to connect to")]
|
||||
fn connect_read() {
|
||||
do run_in_bare_thread() {
|
||||
let mut loop_ = Loop::new();
|
||||
let mut tcp_watcher = { TcpWatcher::new(&mut loop_) };
|
||||
let addr = Ipv4(127, 0, 0, 1, 2924);
|
||||
do tcp_watcher.connect(addr) |stream_watcher, status| {
|
||||
let mut stream_watcher = stream_watcher;
|
||||
rtdebug!("tcp_watcher.connect!");
|
||||
fail_unless!(status.is_none());
|
||||
let alloc: AllocCallback = |size| {
|
||||
vec_to_uv_buf(vec::from_elem(size, 0))
|
||||
};
|
||||
do stream_watcher.read_start(alloc)
|
||||
|stream_watcher, nread, buf, status| {
|
||||
|
||||
let buf = vec_from_uv_buf(buf);
|
||||
rtdebug!("read cb!");
|
||||
if status.is_none() {
|
||||
let bytes = buf.unwrap();
|
||||
rtdebug!("%s", bytes.slice(0, nread as uint).to_str());
|
||||
} else {
|
||||
rtdebug!("status after read: %s", status.get().to_str());
|
||||
rtdebug!("closing");
|
||||
stream_watcher.close(||());
|
||||
}
|
||||
}
|
||||
}
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore(reason = "ffi struct issues")]
|
||||
fn listen() {
|
||||
do run_in_bare_thread() {
|
||||
static MAX: int = 10;
|
||||
let mut loop_ = Loop::new();
|
||||
let mut server_tcp_watcher = { TcpWatcher::new(&mut loop_) };
|
||||
let addr = Ipv4(127, 0, 0, 1, 2925);
|
||||
server_tcp_watcher.bind(addr);
|
||||
let loop_ = loop_;
|
||||
rtdebug!("listening");
|
||||
do server_tcp_watcher.listen |server_stream_watcher, status| {
|
||||
rtdebug!("listened!");
|
||||
fail_unless!(status.is_none());
|
||||
let mut server_stream_watcher = server_stream_watcher;
|
||||
let mut loop_ = loop_;
|
||||
let mut client_tcp_watcher = TcpWatcher::new(&mut loop_);
|
||||
let mut client_tcp_watcher = client_tcp_watcher.as_stream();
|
||||
server_stream_watcher.accept(client_tcp_watcher);
|
||||
let count_cell = Cell(0);
|
||||
let server_stream_watcher = server_stream_watcher;
|
||||
rtdebug!("starting read");
|
||||
let alloc: AllocCallback = |size| {
|
||||
vec_to_uv_buf(vec::from_elem(size, 0))
|
||||
};
|
||||
do client_tcp_watcher.read_start(alloc)
|
||||
|stream_watcher, nread, buf, status| {
|
||||
|
||||
rtdebug!("i'm reading!");
|
||||
let buf = vec_from_uv_buf(buf);
|
||||
let mut count = count_cell.take();
|
||||
if status.is_none() {
|
||||
rtdebug!("got %d bytes", nread);
|
||||
let buf = buf.unwrap();
|
||||
for buf.slice(0, nread as uint).each |byte| {
|
||||
fail_unless!(*byte == count as u8);
|
||||
rtdebug!("%u", *byte as uint);
|
||||
count += 1;
|
||||
}
|
||||
} else {
|
||||
fail_unless!(count == MAX);
|
||||
do stream_watcher.close {
|
||||
server_stream_watcher.close(||());
|
||||
}
|
||||
}
|
||||
count_cell.put_back(count);
|
||||
}
|
||||
}
|
||||
|
||||
let _client_thread = do Thread::start {
|
||||
rtdebug!("starting client thread");
|
||||
let mut loop_ = Loop::new();
|
||||
let mut tcp_watcher = { TcpWatcher::new(&mut loop_) };
|
||||
do tcp_watcher.connect(addr) |stream_watcher, status| {
|
||||
rtdebug!("connecting");
|
||||
fail_unless!(status.is_none());
|
||||
let mut stream_watcher = stream_watcher;
|
||||
let msg = ~[0, 1, 2, 3, 4, 5, 6 ,7 ,8, 9];
|
||||
do stream_watcher.write(msg) |stream_watcher, status| {
|
||||
rtdebug!("writing");
|
||||
fail_unless!(status.is_none());
|
||||
stream_watcher.close(||());
|
||||
}
|
||||
}
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
};
|
||||
|
||||
let mut loop_ = loop_;
|
||||
loop_.run();
|
||||
loop_.close();
|
||||
}
|
||||
}
|
@ -12,13 +12,11 @@ use option::*;
|
||||
use result::*;
|
||||
|
||||
use super::uv::*;
|
||||
use super::io::*;
|
||||
use super::rtio::*;
|
||||
use ops::Drop;
|
||||
use cell::{Cell, empty_cell};
|
||||
use cast::transmute;
|
||||
use super::StreamObject;
|
||||
use super::sched::Scheduler;
|
||||
use super::IoFactoryObject;
|
||||
|
||||
#[cfg(test)] use super::sched::Task;
|
||||
#[cfg(test)] use unstable::run_in_bare_thread;
|
||||
@ -189,12 +187,9 @@ impl TcpListener for UvTcpListener {
|
||||
do server_tcp_watcher.listen |server_stream_watcher, status| {
|
||||
let maybe_stream = if status.is_none() {
|
||||
let mut server_stream_watcher = server_stream_watcher;
|
||||
let mut loop_ =
|
||||
loop_from_watcher(&server_stream_watcher);
|
||||
let mut client_tcp_watcher =
|
||||
TcpWatcher::new(&mut loop_);
|
||||
let mut client_tcp_watcher =
|
||||
client_tcp_watcher.as_stream();
|
||||
let mut loop_ = loop_from_watcher(&server_stream_watcher);
|
||||
let mut client_tcp_watcher = TcpWatcher::new(&mut loop_);
|
||||
let mut client_tcp_watcher = client_tcp_watcher.as_stream();
|
||||
// XXX: Need's to be surfaced in interface
|
||||
server_stream_watcher.accept(client_tcp_watcher);
|
||||
Some(~UvStream::new(client_tcp_watcher))
|
||||
@ -425,8 +420,7 @@ fn test_read_and_block() {
|
||||
// Yield to the other task in hopes that it
|
||||
// will trigger a read callback while we are
|
||||
// not ready for it
|
||||
do scheduler.block_running_task_and_then
|
||||
|scheduler, task| {
|
||||
do scheduler.block_running_task_and_then |scheduler, task| {
|
||||
scheduler.task_queue.push_back(task);
|
||||
}
|
||||
}
|
||||
|
442
src/libcore/rt/uvll.rs
Normal file
442
src/libcore/rt/uvll.rs
Normal file
@ -0,0 +1,442 @@
|
||||
// Copyright 2012 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 <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.
|
||||
|
||||
/*!
|
||||
* Low-level bindings to the libuv library.
|
||||
*
|
||||
* This module contains a set of direct, 'bare-metal' wrappers around
|
||||
* the libuv C-API.
|
||||
*
|
||||
* We're not bothering yet to redefine uv's structs as Rust structs
|
||||
* because they are quite large and change often between versions.
|
||||
* The maintenance burden is just too high. Instead we use the uv's
|
||||
* `uv_handle_size` and `uv_req_size` to find the correct size of the
|
||||
* structs and allocate them on the heap. This can be revisited later.
|
||||
*
|
||||
* There are also a collection of helper functions to ease interacting
|
||||
* with the low-level API.
|
||||
*
|
||||
* As new functionality, existant in uv.h, is added to the rust stdlib,
|
||||
* the mappings should be added in this module.
|
||||
*/
|
||||
|
||||
#[allow(non_camel_case_types)]; // C types
|
||||
|
||||
use libc::{size_t, c_int, c_uint, c_void, c_char, uintptr_t};
|
||||
use libc::{malloc, free};
|
||||
use prelude::*;
|
||||
use ptr::to_unsafe_ptr;
|
||||
|
||||
pub struct uv_err_t {
|
||||
code: c_int,
|
||||
sys_errno_: c_int
|
||||
}
|
||||
|
||||
pub struct uv_buf_t {
|
||||
base: *u8,
|
||||
len: libc::size_t,
|
||||
}
|
||||
|
||||
pub type uv_handle_t = c_void;
|
||||
pub type uv_loop_t = c_void;
|
||||
pub type uv_idle_t = c_void;
|
||||
pub type uv_tcp_t = c_void;
|
||||
pub type uv_connect_t = c_void;
|
||||
pub type uv_write_t = c_void;
|
||||
pub type uv_async_t = c_void;
|
||||
pub type uv_timer_t = c_void;
|
||||
pub type uv_stream_t = c_void;
|
||||
pub type uv_fs_t = c_void;
|
||||
|
||||
pub type uv_idle_cb = *u8;
|
||||
|
||||
pub type sockaddr_in = c_void;
|
||||
pub type sockaddr_in6 = c_void;
|
||||
|
||||
#[deriving(Eq)]
|
||||
pub enum uv_handle_type {
|
||||
UV_UNKNOWN_HANDLE,
|
||||
UV_ASYNC,
|
||||
UV_CHECK,
|
||||
UV_FS_EVENT,
|
||||
UV_FS_POLL,
|
||||
UV_HANDLE,
|
||||
UV_IDLE,
|
||||
UV_NAMED_PIPE,
|
||||
UV_POLL,
|
||||
UV_PREPARE,
|
||||
UV_PROCESS,
|
||||
UV_STREAM,
|
||||
UV_TCP,
|
||||
UV_TIMER,
|
||||
UV_TTY,
|
||||
UV_UDP,
|
||||
UV_SIGNAL,
|
||||
UV_FILE,
|
||||
UV_HANDLE_TYPE_MAX
|
||||
}
|
||||
|
||||
#[deriving(Eq)]
|
||||
pub enum uv_req_type {
|
||||
UV_UNKNOWN_REQ,
|
||||
UV_REQ,
|
||||
UV_CONNECT,
|
||||
UV_WRITE,
|
||||
UV_SHUTDOWN,
|
||||
UV_UDP_SEND,
|
||||
UV_FS,
|
||||
UV_WORK,
|
||||
UV_GETADDRINFO,
|
||||
UV_REQ_TYPE_MAX
|
||||
}
|
||||
|
||||
pub unsafe fn malloc_handle(handle: uv_handle_type) -> *c_void {
|
||||
fail_unless!(handle != UV_UNKNOWN_HANDLE && handle != UV_HANDLE_TYPE_MAX);
|
||||
let size = unsafe { rust_uv_handle_size(handle as uint) };
|
||||
let p = malloc(size);
|
||||
fail_unless!(p.is_not_null());
|
||||
return p;
|
||||
}
|
||||
|
||||
pub unsafe fn free_handle(v: *c_void) {
|
||||
free(v)
|
||||
}
|
||||
|
||||
pub unsafe fn malloc_req(req: uv_req_type) -> *c_void {
|
||||
fail_unless!(req != UV_UNKNOWN_REQ && req != UV_REQ_TYPE_MAX);
|
||||
let size = unsafe { rust_uv_req_size(req as uint) };
|
||||
let p = malloc(size);
|
||||
fail_unless!(p.is_not_null());
|
||||
return p;
|
||||
}
|
||||
|
||||
pub unsafe fn free_req(v: *c_void) {
|
||||
free(v)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handle_sanity_check() {
|
||||
unsafe {
|
||||
fail_unless!(UV_HANDLE_TYPE_MAX as uint == rust_uv_handle_type_max());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_sanity_check() {
|
||||
unsafe {
|
||||
fail_unless!(UV_REQ_TYPE_MAX as uint == rust_uv_req_type_max());
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn loop_new() -> *c_void {
|
||||
return rust_uv_loop_new();
|
||||
}
|
||||
|
||||
pub unsafe fn loop_delete(loop_handle: *c_void) {
|
||||
rust_uv_loop_delete(loop_handle);
|
||||
}
|
||||
|
||||
pub unsafe fn run(loop_handle: *c_void) {
|
||||
rust_uv_run(loop_handle);
|
||||
}
|
||||
|
||||
pub unsafe fn close<T>(handle: *T, cb: *u8) {
|
||||
rust_uv_close(handle as *c_void, cb);
|
||||
}
|
||||
|
||||
pub unsafe fn walk(loop_handle: *c_void, cb: *u8, arg: *c_void) {
|
||||
rust_uv_walk(loop_handle, cb, arg);
|
||||
}
|
||||
|
||||
pub unsafe fn idle_new() -> *uv_idle_t {
|
||||
rust_uv_idle_new()
|
||||
}
|
||||
|
||||
pub unsafe fn idle_delete(handle: *uv_idle_t) {
|
||||
rust_uv_idle_delete(handle)
|
||||
}
|
||||
|
||||
pub unsafe fn idle_init(loop_handle: *uv_loop_t, handle: *uv_idle_t) -> c_int {
|
||||
rust_uv_idle_init(loop_handle, handle)
|
||||
}
|
||||
|
||||
pub unsafe fn idle_start(handle: *uv_idle_t, cb: uv_idle_cb) -> c_int {
|
||||
rust_uv_idle_start(handle, cb)
|
||||
}
|
||||
|
||||
pub unsafe fn idle_stop(handle: *uv_idle_t) -> c_int {
|
||||
rust_uv_idle_stop(handle)
|
||||
}
|
||||
|
||||
pub unsafe fn tcp_init(loop_handle: *c_void, handle: *uv_tcp_t) -> c_int {
|
||||
return rust_uv_tcp_init(loop_handle, handle);
|
||||
}
|
||||
|
||||
// FIXME ref #2064
|
||||
pub unsafe fn tcp_connect(connect_ptr: *uv_connect_t,
|
||||
tcp_handle_ptr: *uv_tcp_t,
|
||||
addr_ptr: *sockaddr_in,
|
||||
after_connect_cb: *u8) -> c_int {
|
||||
return rust_uv_tcp_connect(connect_ptr, tcp_handle_ptr,
|
||||
after_connect_cb, addr_ptr);
|
||||
}
|
||||
// FIXME ref #2064
|
||||
pub unsafe fn tcp_connect6(connect_ptr: *uv_connect_t,
|
||||
tcp_handle_ptr: *uv_tcp_t,
|
||||
addr_ptr: *sockaddr_in6,
|
||||
after_connect_cb: *u8) -> c_int {
|
||||
return rust_uv_tcp_connect6(connect_ptr, tcp_handle_ptr,
|
||||
after_connect_cb, addr_ptr);
|
||||
}
|
||||
// FIXME ref #2064
|
||||
pub unsafe fn tcp_bind(tcp_server_ptr: *uv_tcp_t, addr_ptr: *sockaddr_in) -> c_int {
|
||||
return rust_uv_tcp_bind(tcp_server_ptr, addr_ptr);
|
||||
}
|
||||
// FIXME ref #2064
|
||||
pub unsafe fn tcp_bind6(tcp_server_ptr: *uv_tcp_t, addr_ptr: *sockaddr_in6) -> c_int {
|
||||
return rust_uv_tcp_bind6(tcp_server_ptr, addr_ptr);
|
||||
}
|
||||
|
||||
pub unsafe fn tcp_getpeername(tcp_handle_ptr: *uv_tcp_t, name: *sockaddr_in) -> c_int {
|
||||
return rust_uv_tcp_getpeername(tcp_handle_ptr, name);
|
||||
}
|
||||
|
||||
pub unsafe fn tcp_getpeername6(tcp_handle_ptr: *uv_tcp_t, name: *sockaddr_in6) ->c_int {
|
||||
return rust_uv_tcp_getpeername6(tcp_handle_ptr, name);
|
||||
}
|
||||
|
||||
pub unsafe fn listen<T>(stream: *T, backlog: c_int, cb: *u8) -> c_int {
|
||||
return rust_uv_listen(stream as *c_void, backlog, cb);
|
||||
}
|
||||
|
||||
pub unsafe fn accept(server: *c_void, client: *c_void) -> c_int {
|
||||
return rust_uv_accept(server as *c_void, client as *c_void);
|
||||
}
|
||||
|
||||
pub unsafe fn write<T>(req: *uv_write_t, stream: *T, buf_in: *~[uv_buf_t], cb: *u8) -> c_int {
|
||||
let buf_ptr = vec::raw::to_ptr(*buf_in);
|
||||
let buf_cnt = vec::len(*buf_in) as i32;
|
||||
return rust_uv_write(req as *c_void, stream as *c_void, buf_ptr, buf_cnt, cb);
|
||||
}
|
||||
pub unsafe fn read_start(stream: *uv_stream_t, on_alloc: *u8, on_read: *u8) -> c_int {
|
||||
return rust_uv_read_start(stream as *c_void, on_alloc, on_read);
|
||||
}
|
||||
|
||||
pub unsafe fn read_stop(stream: *uv_stream_t) -> c_int {
|
||||
return rust_uv_read_stop(stream as *c_void);
|
||||
}
|
||||
|
||||
pub unsafe fn last_error(loop_handle: *c_void) -> uv_err_t {
|
||||
return rust_uv_last_error(loop_handle);
|
||||
}
|
||||
|
||||
pub unsafe fn strerror(err: *uv_err_t) -> *c_char {
|
||||
return rust_uv_strerror(err);
|
||||
}
|
||||
pub unsafe fn err_name(err: *uv_err_t) -> *c_char {
|
||||
return rust_uv_err_name(err);
|
||||
}
|
||||
|
||||
pub unsafe fn async_init(loop_handle: *c_void, async_handle: *uv_async_t, cb: *u8) -> c_int {
|
||||
return rust_uv_async_init(loop_handle, async_handle, cb);
|
||||
}
|
||||
|
||||
pub unsafe fn async_send(async_handle: *uv_async_t) {
|
||||
return rust_uv_async_send(async_handle);
|
||||
}
|
||||
pub unsafe fn buf_init(input: *u8, len: uint) -> uv_buf_t {
|
||||
let out_buf = uv_buf_t { base: ptr::null(), len: 0 as size_t };
|
||||
let out_buf_ptr = ptr::addr_of(&out_buf);
|
||||
rust_uv_buf_init(out_buf_ptr, input, len as size_t);
|
||||
return out_buf;
|
||||
}
|
||||
|
||||
pub unsafe fn timer_init(loop_ptr: *c_void, timer_ptr: *uv_timer_t) -> c_int {
|
||||
return rust_uv_timer_init(loop_ptr, timer_ptr);
|
||||
}
|
||||
pub unsafe fn timer_start(timer_ptr: *uv_timer_t, cb: *u8, timeout: uint,
|
||||
repeat: uint) -> c_int {
|
||||
return rust_uv_timer_start(timer_ptr, cb, timeout as c_uint, repeat as c_uint);
|
||||
}
|
||||
pub unsafe fn timer_stop(timer_ptr: *uv_timer_t) -> c_int {
|
||||
return rust_uv_timer_stop(timer_ptr);
|
||||
}
|
||||
|
||||
pub unsafe fn malloc_ip4_addr(ip: &str, port: int) -> *sockaddr_in {
|
||||
do str::as_c_str(ip) |ip_buf| {
|
||||
rust_uv_ip4_addrp(ip_buf as *u8, port as libc::c_int)
|
||||
}
|
||||
}
|
||||
pub unsafe fn malloc_ip6_addr(ip: &str, port: int) -> *sockaddr_in6 {
|
||||
do str::as_c_str(ip) |ip_buf| {
|
||||
rust_uv_ip6_addrp(ip_buf as *u8, port as libc::c_int)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn free_ip4_addr(addr: *sockaddr_in) {
|
||||
rust_uv_free_ip4_addr(addr);
|
||||
}
|
||||
|
||||
pub unsafe fn free_ip6_addr(addr: *sockaddr_in6) {
|
||||
rust_uv_free_ip6_addr(addr);
|
||||
}
|
||||
|
||||
// data access helpers
|
||||
pub unsafe fn get_loop_for_uv_handle<T>(handle: *T) -> *c_void {
|
||||
return rust_uv_get_loop_for_uv_handle(handle as *c_void);
|
||||
}
|
||||
pub unsafe fn get_stream_handle_from_connect_req(connect: *uv_connect_t) -> *uv_stream_t {
|
||||
return rust_uv_get_stream_handle_from_connect_req(connect);
|
||||
}
|
||||
pub unsafe fn get_stream_handle_from_write_req(write_req: *uv_write_t) -> *uv_stream_t {
|
||||
return rust_uv_get_stream_handle_from_write_req(write_req);
|
||||
}
|
||||
pub unsafe fn get_data_for_uv_loop(loop_ptr: *c_void) -> *c_void {
|
||||
rust_uv_get_data_for_uv_loop(loop_ptr)
|
||||
}
|
||||
pub unsafe fn set_data_for_uv_loop(loop_ptr: *c_void, data: *c_void) {
|
||||
rust_uv_set_data_for_uv_loop(loop_ptr, data);
|
||||
}
|
||||
pub unsafe fn get_data_for_uv_handle<T>(handle: *T) -> *c_void {
|
||||
return rust_uv_get_data_for_uv_handle(handle as *c_void);
|
||||
}
|
||||
pub unsafe fn set_data_for_uv_handle<T, U>(handle: *T, data: *U) {
|
||||
rust_uv_set_data_for_uv_handle(handle as *c_void, data as *c_void);
|
||||
}
|
||||
pub unsafe fn get_data_for_req<T>(req: *T) -> *c_void {
|
||||
return rust_uv_get_data_for_req(req as *c_void);
|
||||
}
|
||||
pub unsafe fn set_data_for_req<T, U>(req: *T, data: *U) {
|
||||
rust_uv_set_data_for_req(req as *c_void, data as *c_void);
|
||||
}
|
||||
pub unsafe fn get_base_from_buf(buf: uv_buf_t) -> *u8 {
|
||||
return rust_uv_get_base_from_buf(buf);
|
||||
}
|
||||
pub unsafe fn get_len_from_buf(buf: uv_buf_t) -> size_t {
|
||||
return rust_uv_get_len_from_buf(buf);
|
||||
}
|
||||
pub unsafe fn malloc_buf_base_of(suggested_size: size_t) -> *u8 {
|
||||
return rust_uv_malloc_buf_base_of(suggested_size);
|
||||
}
|
||||
pub unsafe fn free_base_of_buf(buf: uv_buf_t) {
|
||||
rust_uv_free_base_of_buf(buf);
|
||||
}
|
||||
|
||||
pub unsafe fn get_last_err_info(uv_loop: *c_void) -> ~str {
|
||||
let err = last_error(uv_loop);
|
||||
let err_ptr = ptr::addr_of(&err);
|
||||
let err_name = str::raw::from_c_str(err_name(err_ptr));
|
||||
let err_msg = str::raw::from_c_str(strerror(err_ptr));
|
||||
return fmt!("LIBUV ERROR: name: %s msg: %s",
|
||||
err_name, err_msg);
|
||||
}
|
||||
|
||||
pub unsafe fn get_last_err_data(uv_loop: *c_void) -> uv_err_data {
|
||||
let err = last_error(uv_loop);
|
||||
let err_ptr = ptr::addr_of(&err);
|
||||
let err_name = str::raw::from_c_str(err_name(err_ptr));
|
||||
let err_msg = str::raw::from_c_str(strerror(err_ptr));
|
||||
uv_err_data { err_name: err_name, err_msg: err_msg }
|
||||
}
|
||||
|
||||
pub struct uv_err_data {
|
||||
err_name: ~str,
|
||||
err_msg: ~str,
|
||||
}
|
||||
|
||||
extern {
|
||||
|
||||
fn rust_uv_handle_size(type_: uintptr_t) -> size_t;
|
||||
fn rust_uv_req_size(type_: uintptr_t) -> size_t;
|
||||
fn rust_uv_handle_type_max() -> uintptr_t;
|
||||
fn rust_uv_req_type_max() -> uintptr_t;
|
||||
|
||||
// libuv public API
|
||||
fn rust_uv_loop_new() -> *c_void;
|
||||
fn rust_uv_loop_delete(lp: *c_void);
|
||||
fn rust_uv_run(loop_handle: *c_void);
|
||||
fn rust_uv_close(handle: *c_void, cb: *u8);
|
||||
fn rust_uv_walk(loop_handle: *c_void, cb: *u8, arg: *c_void);
|
||||
|
||||
fn rust_uv_idle_new() -> *uv_idle_t;
|
||||
fn rust_uv_idle_delete(handle: *uv_idle_t);
|
||||
fn rust_uv_idle_init(loop_handle: *uv_loop_t, handle: *uv_idle_t) -> c_int;
|
||||
fn rust_uv_idle_start(handle: *uv_idle_t, cb: uv_idle_cb) -> c_int;
|
||||
fn rust_uv_idle_stop(handle: *uv_idle_t) -> c_int;
|
||||
|
||||
fn rust_uv_async_send(handle: *uv_async_t);
|
||||
fn rust_uv_async_init(loop_handle: *c_void,
|
||||
async_handle: *uv_async_t,
|
||||
cb: *u8) -> c_int;
|
||||
fn rust_uv_tcp_init(loop_handle: *c_void, handle_ptr: *uv_tcp_t) -> c_int;
|
||||
// FIXME ref #2604 .. ?
|
||||
fn rust_uv_buf_init(out_buf: *uv_buf_t, base: *u8, len: size_t);
|
||||
fn rust_uv_last_error(loop_handle: *c_void) -> uv_err_t;
|
||||
// FIXME ref #2064
|
||||
fn rust_uv_strerror(err: *uv_err_t) -> *c_char;
|
||||
// FIXME ref #2064
|
||||
fn rust_uv_err_name(err: *uv_err_t) -> *c_char;
|
||||
fn rust_uv_ip4_addrp(ip: *u8, port: c_int) -> *sockaddr_in;
|
||||
fn rust_uv_ip6_addrp(ip: *u8, port: c_int) -> *sockaddr_in6;
|
||||
fn rust_uv_free_ip4_addr(addr: *sockaddr_in);
|
||||
fn rust_uv_free_ip6_addr(addr: *sockaddr_in6);
|
||||
fn rust_uv_ip4_name(src: *sockaddr_in, dst: *u8, size: size_t) -> c_int;
|
||||
fn rust_uv_ip6_name(src: *sockaddr_in6, dst: *u8, size: size_t) -> c_int;
|
||||
fn rust_uv_ip4_port(src: *sockaddr_in) -> c_uint;
|
||||
fn rust_uv_ip6_port(src: *sockaddr_in6) -> c_uint;
|
||||
// FIXME ref #2064
|
||||
fn rust_uv_tcp_connect(connect_ptr: *uv_connect_t,
|
||||
tcp_handle_ptr: *uv_tcp_t,
|
||||
++after_cb: *u8,
|
||||
++addr: *sockaddr_in) -> c_int;
|
||||
// FIXME ref #2064
|
||||
fn rust_uv_tcp_bind(tcp_server: *uv_tcp_t, ++addr: *sockaddr_in) -> c_int;
|
||||
// FIXME ref #2064
|
||||
fn rust_uv_tcp_connect6(connect_ptr: *uv_connect_t,
|
||||
tcp_handle_ptr: *uv_tcp_t,
|
||||
++after_cb: *u8,
|
||||
++addr: *sockaddr_in6) -> c_int;
|
||||
// FIXME ref #2064
|
||||
fn rust_uv_tcp_bind6(tcp_server: *uv_tcp_t, ++addr: *sockaddr_in6) -> c_int;
|
||||
fn rust_uv_tcp_getpeername(tcp_handle_ptr: *uv_tcp_t, ++name: *sockaddr_in) -> c_int;
|
||||
fn rust_uv_tcp_getpeername6(tcp_handle_ptr: *uv_tcp_t, ++name: *sockaddr_in6) ->c_int;
|
||||
fn rust_uv_listen(stream: *c_void, backlog: c_int, cb: *u8) -> c_int;
|
||||
fn rust_uv_accept(server: *c_void, client: *c_void) -> c_int;
|
||||
fn rust_uv_write(req: *c_void,
|
||||
stream: *c_void,
|
||||
++buf_in: *uv_buf_t,
|
||||
buf_cnt: c_int,
|
||||
cb: *u8) -> c_int;
|
||||
fn rust_uv_read_start(stream: *c_void,
|
||||
on_alloc: *u8,
|
||||
on_read: *u8) -> c_int;
|
||||
fn rust_uv_read_stop(stream: *c_void) -> c_int;
|
||||
fn rust_uv_timer_init(loop_handle: *c_void,
|
||||
timer_handle: *uv_timer_t) -> c_int;
|
||||
fn rust_uv_timer_start(timer_handle: *uv_timer_t,
|
||||
cb: *u8,
|
||||
timeout: c_uint,
|
||||
repeat: c_uint) -> c_int;
|
||||
fn rust_uv_timer_stop(handle: *uv_timer_t) -> c_int;
|
||||
|
||||
fn rust_uv_malloc_buf_base_of(sug_size: size_t) -> *u8;
|
||||
fn rust_uv_free_base_of_buf(++buf: uv_buf_t);
|
||||
fn rust_uv_get_stream_handle_from_connect_req(connect_req: *uv_connect_t) -> *uv_stream_t;
|
||||
fn rust_uv_get_stream_handle_from_write_req(write_req: *uv_write_t) -> *uv_stream_t;
|
||||
fn rust_uv_get_loop_for_uv_handle(handle: *c_void) -> *c_void;
|
||||
fn rust_uv_get_data_for_uv_loop(loop_ptr: *c_void) -> *c_void;
|
||||
fn rust_uv_set_data_for_uv_loop(loop_ptr: *c_void, data: *c_void);
|
||||
fn rust_uv_get_data_for_uv_handle(handle: *c_void) -> *c_void;
|
||||
fn rust_uv_set_data_for_uv_handle(handle: *c_void, data: *c_void);
|
||||
fn rust_uv_get_data_for_req(req: *c_void) -> *c_void;
|
||||
fn rust_uv_set_data_for_req(req: *c_void, data: *c_void);
|
||||
fn rust_uv_get_base_from_buf(++buf: uv_buf_t) -> *u8;
|
||||
fn rust_uv_get_len_from_buf(++buf: uv_buf_t) -> size_t;
|
||||
}
|
@ -35,8 +35,6 @@ pub mod extfmt;
|
||||
#[path = "unstable/lang.rs"]
|
||||
#[cfg(notest)]
|
||||
pub mod lang;
|
||||
#[path = "unstable/uvll.rs"]
|
||||
pub mod uvll;
|
||||
|
||||
mod rustrt {
|
||||
use unstable::{raw_thread, rust_little_lock};
|
||||
|
@ -120,16 +120,25 @@ pub unsafe fn strdup_uniq(ptr: *c_uchar, len: uint) -> ~str {
|
||||
#[lang="start"]
|
||||
pub fn start(main: *u8, argc: int, argv: *c_char,
|
||||
crate_map: *u8) -> int {
|
||||
use libc::getenv;
|
||||
use rt::start;
|
||||
|
||||
unsafe {
|
||||
let use_new_rt = do str::as_c_str("RUST_NEWRT") |s| {
|
||||
getenv(s).is_null()
|
||||
};
|
||||
if use_new_rt {
|
||||
return rust_start(main as *c_void, argc as c_int, argv,
|
||||
crate_map as *c_void) as int;
|
||||
} else {
|
||||
return start(main, argc, argv, crate_map);
|
||||
}
|
||||
}
|
||||
|
||||
extern {
|
||||
fn rust_start(main: *c_void, argc: c_int, argv: *c_char,
|
||||
crate_map: *c_void) -> c_int;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
return rust_start(main as *c_void, argc as c_int, argv,
|
||||
crate_map as *c_void) as int;
|
||||
}
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
|
@ -21,20 +21,20 @@ use core::vec;
|
||||
use iotask = uv::iotask::IoTask;
|
||||
use interact = uv::iotask::interact;
|
||||
|
||||
use sockaddr_in = core::unstable::uvll::sockaddr_in;
|
||||
use sockaddr_in6 = core::unstable::uvll::sockaddr_in6;
|
||||
use addrinfo = core::unstable::uvll::addrinfo;
|
||||
use uv_getaddrinfo_t = core::unstable::uvll::uv_getaddrinfo_t;
|
||||
use uv_ip4_name = core::unstable::uvll::ip4_name;
|
||||
use uv_ip4_port = core::unstable::uvll::ip4_port;
|
||||
use uv_ip6_name = core::unstable::uvll::ip6_name;
|
||||
use uv_ip6_port = core::unstable::uvll::ip6_port;
|
||||
use uv_getaddrinfo = core::unstable::uvll::getaddrinfo;
|
||||
use uv_freeaddrinfo = core::unstable::uvll::freeaddrinfo;
|
||||
use create_uv_getaddrinfo_t = core::unstable::uvll::getaddrinfo_t;
|
||||
use set_data_for_req = core::unstable::uvll::set_data_for_req;
|
||||
use get_data_for_req = core::unstable::uvll::get_data_for_req;
|
||||
use ll = core::unstable::uvll;
|
||||
use sockaddr_in = super::uv_ll::sockaddr_in;
|
||||
use sockaddr_in6 = super::uv_ll::sockaddr_in6;
|
||||
use addrinfo = super::uv_ll::addrinfo;
|
||||
use uv_getaddrinfo_t = super::uv_ll::uv_getaddrinfo_t;
|
||||
use uv_ip4_name = super::uv_ll::ip4_name;
|
||||
use uv_ip4_port = super::uv_ll::ip4_port;
|
||||
use uv_ip6_name = super::uv_ll::ip6_name;
|
||||
use uv_ip6_port = super::uv_ll::ip6_port;
|
||||
use uv_getaddrinfo = super::uv_ll::getaddrinfo;
|
||||
use uv_freeaddrinfo = super::uv_ll::freeaddrinfo;
|
||||
use create_uv_getaddrinfo_t = super::uv_ll::getaddrinfo_t;
|
||||
use set_data_for_req = super::uv_ll::set_data_for_req;
|
||||
use get_data_for_req = super::uv_ll::get_data_for_req;
|
||||
use ll = super::uv_ll;
|
||||
|
||||
/// An IP address
|
||||
pub enum IpAddr {
|
||||
|
@ -36,7 +36,7 @@ not required in or otherwise suitable for the core library.
|
||||
extern mod core(vers = "0.6");
|
||||
use core::*;
|
||||
|
||||
pub use uv_ll = core::unstable::uvll;
|
||||
pub mod uv_ll;
|
||||
|
||||
// General io and system-services modules
|
||||
|
||||
|
@ -33,6 +33,6 @@
|
||||
* facilities.
|
||||
*/
|
||||
|
||||
pub use ll = core::unstable::uvll;
|
||||
pub use ll = super::uv_ll;
|
||||
pub use iotask = uv_iotask;
|
||||
pub use global_loop = uv_global_loop;
|
||||
|
@ -32,10 +32,10 @@
|
||||
|
||||
#[allow(non_camel_case_types)]; // C types
|
||||
|
||||
use libc::size_t;
|
||||
use libc::c_void;
|
||||
use prelude::*;
|
||||
use ptr::to_unsafe_ptr;
|
||||
use core::libc::size_t;
|
||||
use core::libc::c_void;
|
||||
use core::prelude::*;
|
||||
use core::ptr::to_unsafe_ptr;
|
||||
|
||||
pub type uv_handle_t = c_void;
|
||||
pub type uv_loop_t = c_void;
|
||||
@ -357,7 +357,7 @@ pub struct uv_getaddrinfo_t {
|
||||
|
||||
pub mod uv_ll_struct_stubgen {
|
||||
|
||||
use ptr;
|
||||
use core::ptr;
|
||||
|
||||
use super::{
|
||||
uv_async_t,
|
||||
@ -930,8 +930,6 @@ pub unsafe fn tcp_connect(connect_ptr: *uv_connect_t,
|
||||
addr_ptr: *sockaddr_in,
|
||||
after_connect_cb: *u8)
|
||||
-> libc::c_int {
|
||||
debug!("b4 foreign tcp_connect--addr port: %u cb: %u",
|
||||
(*addr_ptr).sin_port as uint, after_connect_cb as uint);
|
||||
return rustrt::rust_uv_tcp_connect(connect_ptr, tcp_handle_ptr,
|
||||
after_connect_cb, addr_ptr);
|
||||
}
|
||||
@ -1021,22 +1019,8 @@ pub unsafe fn async_send(async_handle: *uv_async_t) {
|
||||
pub unsafe fn buf_init(input: *u8, len: uint) -> uv_buf_t {
|
||||
let out_buf = uv_buf_t { base: ptr::null(), len: 0 as libc::size_t };
|
||||
let out_buf_ptr = ptr::addr_of(&out_buf);
|
||||
debug!("buf_init - input %u len %u out_buf: %u",
|
||||
input as uint,
|
||||
len as uint,
|
||||
out_buf_ptr as uint);
|
||||
// yuck :/
|
||||
rustrt::rust_uv_buf_init(out_buf_ptr, input, len as size_t);
|
||||
//let result = rustrt::rust_uv_buf_init_2(input, len as size_t);
|
||||
debug!("after rust_uv_buf_init");
|
||||
let res_base = get_base_from_buf(out_buf);
|
||||
let res_len = get_len_from_buf(out_buf);
|
||||
//let res_base = get_base_from_buf(result);
|
||||
debug!("buf_init - result %u len %u",
|
||||
res_base as uint,
|
||||
res_len as uint);
|
||||
return out_buf;
|
||||
//return result;
|
||||
}
|
||||
pub unsafe fn ip4_addr(ip: &str, port: int)
|
||||
-> sockaddr_in {
|
||||
@ -1078,8 +1062,6 @@ pub unsafe fn ip6_name(src: &sockaddr_in6) -> ~str {
|
||||
0u8,0u8,0u8,0u8,0u8,0u8];
|
||||
do vec::as_imm_buf(dst) |dst_buf, size| {
|
||||
let src_unsafe_ptr = to_unsafe_ptr(src);
|
||||
debug!("val of src *sockaddr_in6: %? sockaddr_in6: %?",
|
||||
src_unsafe_ptr, src);
|
||||
let result = rustrt::rust_uv_ip6_name(src_unsafe_ptr,
|
||||
dst_buf, size as libc::size_t);
|
||||
match result {
|
||||
@ -1240,9 +1222,9 @@ pub unsafe fn addrinfo_as_sockaddr_in6(input: *addrinfo) -> *sockaddr_in6 {
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use prelude::*;
|
||||
use core::prelude::*;
|
||||
use core::comm::{SharedChan, stream, GenericChan, GenericPort};
|
||||
use super::*;
|
||||
use comm::{SharedChan, stream, GenericChan, GenericPort};
|
||||
|
||||
enum tcp_read_data {
|
||||
tcp_read_eof,
|
||||
@ -1265,7 +1247,7 @@ pub mod test {
|
||||
suggested_size: libc::size_t)
|
||||
-> uv_buf_t {
|
||||
unsafe {
|
||||
debug!("on_alloc_cb!");
|
||||
debug!(~"on_alloc_cb!");
|
||||
let char_ptr = malloc_buf_base_of(suggested_size);
|
||||
debug!("on_alloc_cb h: %? char_ptr: %u sugsize: %u",
|
||||
handle,
|
||||
@ -1298,15 +1280,15 @@ pub mod test {
|
||||
}
|
||||
else if (nread == -1) {
|
||||
// err .. possibly EOF
|
||||
debug!("read: eof!");
|
||||
debug!(~"read: eof!");
|
||||
}
|
||||
else {
|
||||
// nread == 0 .. do nothing, just free buf as below
|
||||
debug!("read: do nothing!");
|
||||
debug!(~"read: do nothing!");
|
||||
}
|
||||
// when we're done
|
||||
free_base_of_buf(buf);
|
||||
debug!("CLIENT exiting on_read_cb");
|
||||
debug!(~"CLIENT exiting on_read_cb");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1321,8 +1303,7 @@ pub mod test {
|
||||
"CLIENT on_write_complete_cb: tcp:%d write_handle:%d",
|
||||
stream as int, write_req as int);
|
||||
let result = read_start(stream, on_alloc_cb, on_read_cb);
|
||||
debug!(
|
||||
"CLIENT ending on_write_complete_cb .. status: %d",
|
||||
debug!("CLIENT ending on_write_complete_cb .. status: %d",
|
||||
result as int);
|
||||
}
|
||||
}
|
||||
@ -1335,7 +1316,7 @@ pub mod test {
|
||||
let stream =
|
||||
get_stream_handle_from_connect_req(connect_req_ptr);
|
||||
if (status == 0i32) {
|
||||
debug!("on_connect_cb: in status=0 if..");
|
||||
debug!(~"on_connect_cb: in status=0 if..");
|
||||
let client_data = get_data_for_req(
|
||||
connect_req_ptr as *libc::c_void)
|
||||
as *request_wrapper;
|
||||
@ -1353,10 +1334,10 @@ pub mod test {
|
||||
let test_loop = get_loop_for_uv_handle(
|
||||
stream as *libc::c_void);
|
||||
let err_msg = get_last_err_info(test_loop);
|
||||
debug!("%?", err_msg);
|
||||
debug!(err_msg);
|
||||
fail_unless!(false);
|
||||
}
|
||||
debug!("finishing on_connect_cb");
|
||||
debug!(~"finishing on_connect_cb");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1396,9 +1377,9 @@ pub mod test {
|
||||
let tcp_init_result = tcp_init(
|
||||
test_loop as *libc::c_void, tcp_handle_ptr);
|
||||
if (tcp_init_result == 0i32) {
|
||||
debug!("sucessful tcp_init_result");
|
||||
debug!(~"sucessful tcp_init_result");
|
||||
|
||||
debug!("building addr...");
|
||||
debug!(~"building addr...");
|
||||
let addr = ip4_addr(ip, port);
|
||||
// FIXME ref #2064
|
||||
let addr_ptr = ptr::addr_of(&addr);
|
||||
@ -1420,17 +1401,17 @@ pub mod test {
|
||||
set_data_for_uv_handle(
|
||||
tcp_handle_ptr as *libc::c_void,
|
||||
ptr::addr_of(&client_data) as *libc::c_void);
|
||||
debug!("before run tcp req loop");
|
||||
debug!(~"before run tcp req loop");
|
||||
run(test_loop);
|
||||
debug!("after run tcp req loop");
|
||||
debug!(~"after run tcp req loop");
|
||||
}
|
||||
else {
|
||||
debug!("tcp_connect() failure");
|
||||
debug!(~"tcp_connect() failure");
|
||||
fail_unless!(false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug!("tcp_init() failure");
|
||||
debug!(~"tcp_init() failure");
|
||||
fail_unless!(false);
|
||||
}
|
||||
loop_delete(test_loop);
|
||||
@ -1446,8 +1427,7 @@ pub mod test {
|
||||
|
||||
extern fn client_stream_after_close_cb(handle: *libc::c_void) {
|
||||
unsafe {
|
||||
debug!(
|
||||
"SERVER: closed client stream, now closing server stream");
|
||||
debug!(~"SERVER: closed client stream, now closing server stream");
|
||||
let client_data = get_data_for_uv_handle(
|
||||
handle) as
|
||||
*tcp_server_data;
|
||||
@ -1460,7 +1440,7 @@ pub mod test {
|
||||
unsafe {
|
||||
let client_stream_ptr =
|
||||
get_stream_handle_from_write_req(req);
|
||||
debug!("SERVER: resp sent... closing client stream");
|
||||
debug!(~"SERVER: resp sent... closing client stream");
|
||||
close(client_stream_ptr as *libc::c_void,
|
||||
client_stream_after_close_cb)
|
||||
}
|
||||
@ -1491,8 +1471,8 @@ pub mod test {
|
||||
let server_kill_msg = copy (*client_data).server_kill_msg;
|
||||
let write_req = (*client_data).server_write_req;
|
||||
if str::contains(request_str, server_kill_msg) {
|
||||
debug!("SERVER: client req contains kill_msg!");
|
||||
debug!("SERVER: sending response to client");
|
||||
debug!(~"SERVER: client req contains kill_msg!");
|
||||
debug!(~"SERVER: sending response to client");
|
||||
read_stop(client_stream_ptr);
|
||||
let server_chan = (*client_data).server_chan.clone();
|
||||
server_chan.send(request_str);
|
||||
@ -1504,28 +1484,28 @@ pub mod test {
|
||||
debug!("SERVER: resp write result: %d",
|
||||
write_result as int);
|
||||
if (write_result != 0i32) {
|
||||
debug!("bad result for server resp write()");
|
||||
debug!("%s", get_last_err_info(
|
||||
debug!(~"bad result for server resp write()");
|
||||
debug!(get_last_err_info(
|
||||
get_loop_for_uv_handle(client_stream_ptr
|
||||
as *libc::c_void)));
|
||||
fail_unless!(false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug!("SERVER: client req !contain kill_msg!");
|
||||
debug!(~"SERVER: client req !contain kill_msg!");
|
||||
}
|
||||
}
|
||||
else if (nread == -1) {
|
||||
// err .. possibly EOF
|
||||
debug!("read: eof!");
|
||||
debug!(~"read: eof!");
|
||||
}
|
||||
else {
|
||||
// nread == 0 .. do nothing, just free buf as below
|
||||
debug!("read: do nothing!");
|
||||
debug!(~"read: do nothing!");
|
||||
}
|
||||
// when we're done
|
||||
free_base_of_buf(buf);
|
||||
debug!("SERVER exiting on_read_cb");
|
||||
debug!(~"SERVER exiting on_read_cb");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1533,7 +1513,7 @@ pub mod test {
|
||||
*uv_stream_t,
|
||||
status: libc::c_int) {
|
||||
unsafe {
|
||||
debug!("client connecting!");
|
||||
debug!(~"client connecting!");
|
||||
let test_loop = get_loop_for_uv_handle(
|
||||
server_stream_ptr as *libc::c_void);
|
||||
if status != 0i32 {
|
||||
@ -1551,7 +1531,7 @@ pub mod test {
|
||||
client_stream_ptr as *libc::c_void,
|
||||
server_data as *libc::c_void);
|
||||
if (client_init_result == 0i32) {
|
||||
debug!("successfully initialized client stream");
|
||||
debug!(~"successfully initialized client stream");
|
||||
let accept_result = accept(server_stream_ptr as
|
||||
*libc::c_void,
|
||||
client_stream_ptr as
|
||||
@ -1563,7 +1543,7 @@ pub mod test {
|
||||
on_alloc_cb,
|
||||
on_server_read_cb);
|
||||
if (read_result == 0i32) {
|
||||
debug!("successful server read start");
|
||||
debug!(~"successful server read start");
|
||||
}
|
||||
else {
|
||||
debug!("server_connection_cb: bad read:%d",
|
||||
@ -1674,7 +1654,7 @@ pub mod test {
|
||||
let bind_result = tcp_bind(tcp_server_ptr,
|
||||
server_addr_ptr);
|
||||
if (bind_result == 0i32) {
|
||||
debug!("successful uv_tcp_bind, listening");
|
||||
debug!(~"successful uv_tcp_bind, listening");
|
||||
|
||||
// uv_listen()
|
||||
let listen_result = listen(tcp_server_ptr as
|
||||
@ -1694,7 +1674,7 @@ pub mod test {
|
||||
async_send(continue_async_handle_ptr);
|
||||
// uv_run()
|
||||
run(test_loop);
|
||||
debug!("server uv::run() has returned");
|
||||
debug!(~"server uv::run() has returned");
|
||||
}
|
||||
else {
|
||||
debug!("uv_async_init failure: %d",
|
||||
@ -1751,9 +1731,9 @@ pub mod test {
|
||||
};
|
||||
|
||||
// block until the server up is.. possibly a race?
|
||||
debug!("before receiving on server continue_port");
|
||||
debug!(~"before receiving on server continue_port");
|
||||
continue_port.recv();
|
||||
debug!("received on continue port, set up tcp client");
|
||||
debug!(~"received on continue port, set up tcp client");
|
||||
|
||||
let kill_server_msg_copy = copy kill_server_msg;
|
||||
do task::spawn_sched(task::ManualThreads(1u)) {
|
||||
@ -1808,7 +1788,7 @@ pub mod test {
|
||||
let output = fmt!(
|
||||
"STRUCT_SIZE FAILURE: %s -- actual: %u expected: %u",
|
||||
t_name, rust_size, foreign_size as uint);
|
||||
debug!("%s", output);
|
||||
debug!(output);
|
||||
}
|
||||
fail_unless!(sizes_match);
|
||||
}
|
@ -21,17 +21,6 @@
|
||||
|
||||
void* global_crate_map = NULL;
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_key_t sched_key;
|
||||
#else
|
||||
DWORD sched_key;
|
||||
#endif
|
||||
|
||||
extern "C" void*
|
||||
rust_get_sched_tls_key() {
|
||||
return &sched_key;
|
||||
}
|
||||
|
||||
/**
|
||||
The runtime entrypoint. The (C ABI) main function generated by rustc calls
|
||||
`rust_start`, providing the address of the Rust ABI main function, the
|
||||
@ -41,10 +30,6 @@ rust_get_sched_tls_key() {
|
||||
extern "C" CDECL int
|
||||
rust_start(uintptr_t main_fn, int argc, char **argv, void* crate_map) {
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_key_create(&sched_key, NULL);
|
||||
#endif
|
||||
|
||||
// Load runtime configuration options from the environment.
|
||||
// FIXME #1497: Should provide a way to get these from the command
|
||||
// line as well.
|
||||
|
@ -882,6 +882,46 @@ rust_get_rt_env() {
|
||||
return task->kernel->env;
|
||||
}
|
||||
|
||||
typedef void *(*nullary_fn)();
|
||||
|
||||
extern "C" CDECL void
|
||||
rust_call_nullary_fn(nullary_fn f) {
|
||||
f();
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_key_t sched_key;
|
||||
#else
|
||||
DWORD sched_key;
|
||||
#endif
|
||||
|
||||
extern "C" void*
|
||||
rust_get_sched_tls_key() {
|
||||
return &sched_key;
|
||||
}
|
||||
|
||||
// Initialize the global state required by the new scheduler
|
||||
extern "C" CDECL void
|
||||
rust_initialize_global_state() {
|
||||
|
||||
static lock_and_signal init_lock;
|
||||
static bool initialized = false;
|
||||
|
||||
scoped_lock with(init_lock);
|
||||
|
||||
if (!initialized) {
|
||||
|
||||
#ifndef _WIN32
|
||||
assert(!pthread_key_create(&sched_key, NULL));
|
||||
#else
|
||||
sched_key = TlsAlloc();
|
||||
assert(sched_key != TLS_OUT_OF_INDEXES);
|
||||
#endif
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
|
@ -479,6 +479,34 @@ extern "C" struct sockaddr_in6
|
||||
rust_uv_ip6_addr(const char* ip, int port) {
|
||||
return uv_ip6_addr(ip, port);
|
||||
}
|
||||
|
||||
extern "C" struct sockaddr_in*
|
||||
rust_uv_ip4_addrp(const char* ip, int port) {
|
||||
struct sockaddr_in addr = uv_ip4_addr(ip, port);
|
||||
struct sockaddr_in *addrp = (sockaddr_in*)malloc(sizeof(struct sockaddr_in));
|
||||
assert(addrp);
|
||||
memcpy(addrp, &addr, sizeof(struct sockaddr_in));
|
||||
return addrp;
|
||||
}
|
||||
extern "C" struct sockaddr_in6*
|
||||
rust_uv_ip6_addrp(const char* ip, int port) {
|
||||
struct sockaddr_in6 addr = uv_ip6_addr(ip, port);
|
||||
struct sockaddr_in6 *addrp = (sockaddr_in6*)malloc(sizeof(struct sockaddr_in6));
|
||||
assert(addrp);
|
||||
memcpy(addrp, &addr, sizeof(struct sockaddr_in6));
|
||||
return addrp;
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rust_uv_free_ip4_addr(sockaddr_in *addrp) {
|
||||
free(addrp);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
rust_uv_free_ip6_addr(sockaddr_in6 *addrp) {
|
||||
free(addrp);
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
rust_uv_ip4_name(struct sockaddr_in* src, char* dst, size_t size) {
|
||||
return uv_ip4_name(src, dst, size);
|
||||
@ -563,3 +591,23 @@ extern "C" int
|
||||
rust_uv_idle_stop(uv_idle_t* idle) {
|
||||
return uv_idle_stop(idle);
|
||||
}
|
||||
|
||||
extern "C" size_t
|
||||
rust_uv_handle_size(uintptr_t type) {
|
||||
return uv_handle_size((uv_handle_type)type);
|
||||
}
|
||||
|
||||
extern "C" size_t
|
||||
rust_uv_req_size(uintptr_t type) {
|
||||
return uv_req_size((uv_req_type)type);
|
||||
}
|
||||
|
||||
extern "C" uintptr_t
|
||||
rust_uv_handle_type_max() {
|
||||
return UV_HANDLE_TYPE_MAX;
|
||||
}
|
||||
|
||||
extern "C" uintptr_t
|
||||
rust_uv_req_type_max() {
|
||||
return UV_REQ_TYPE_MAX;
|
||||
}
|
||||
|
@ -202,3 +202,15 @@ rust_dbg_extern_identity_TwoU64s
|
||||
rust_dbg_extern_identity_double
|
||||
rust_dbg_extern_identity_u8
|
||||
rust_get_rt_env
|
||||
rust_uv_handle_size
|
||||
rust_uv_req_size
|
||||
rust_uv_handle_type_max
|
||||
rust_uv_req_type_max
|
||||
rust_uv_ip4_addrp
|
||||
rust_uv_ip6_addrp
|
||||
rust_uv_free_ip4_addr
|
||||
rust_uv_free_ip6_addr
|
||||
rust_call_nullary_fn
|
||||
rust_initialize_global_state
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user