Skip to content

Instantly share code, notes, and snippets.

@wycats
Created January 30, 2014 05:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wycats/8703114 to your computer and use it in GitHub Desktop.
Save wycats/8703114 to your computer and use it in GitHub Desktop.
#[macro_escape];
use std::rt::task::Task;
use std::rt::local::Local;
mod libunwind;
macro_rules! run(
($expr:expr) => (
{
let mut success = false;
let task = ~std::rt::task::Task::new();
task.run(|| { $expr; success = true });
success
}
);
($out:expr, $expr:expr) => (
{
let mut success = false;
let task = ~std::rt::task::Task::new();
task.run(|| { $expr.to_out_ptr(out); success = true });
success
}
);
)
macro_rules! ffi_run(
($code:expr, $args:expr) => {
{
use std::cast::transmute;
use std::rt::local::Local;
use std::rt::task::Task;
if !Local::exists(None::<Task>) {
let task = ~Task::new();
Local::put(task);
}
extern {
fn rust_try(f: extern "C" fn(*c_void, *c_void), code: *c_void, data: *c_void) -> *libunwind::_Unwind_Exception;
}
unsafe {
let ep = rust_try($code, transmute($args), 0 as *c_void);
if !ep.is_null() {
libunwind::_Unwind_DeleteException(ep);
close_task();
let mut task: ~std::rt::task::Task = std::rt::local::Local::take();
task.destroyed = true;
false
} else {
true
}
}
}
};
)
macro_rules! ffi_fn(
($name:ident () -> $out:ty $expr:expr) => {
#[no_mangle]
pub extern "C" fn $name(out: *mut $out) -> bool {
use std::libc::c_void;
use std::cast::transmute;
struct Args { out: *mut $out };
extern "C" fn code(args: *c_void, _: *c_void) {
unsafe {
let Args{ out }: Args = transmute(args);
$expr.to_out_ptr(out);
}
}
let args = Args{ out: out };
ffi_run!(code, args)
}
};
($name:ident ( $($param:ident : $ty:ty),+ ) -> $out:ty $expr:expr) => {
#[no_mangle]
#[allow(unused_variable)]
pub extern "C" fn $name<'a>( $($param : $ty ),+, out: *mut $out) -> bool {
use std::libc::c_void;
use std::cast::transmute;
struct Args<'a> { $($param : $ty),+, out: *mut $out };
extern "C" fn code(args: *c_void, _: *c_void) {
unsafe {
let ~Args{ $($param),+, out }: ~Args = transmute(args);
$expr.to_out_ptr(out);
}
}
let args = ~Args{ $($param : $param),+, out: out };
ffi_run!(code, args)
}
};
($name:ident () $expr:expr) => {
#[no_mangle]
pub extern "C" fn $name() -> bool {
extern "C" fn code(_: *c_void, _: *c_void) {
$expr.to_out_ptr(out)
}
ffi_run!(code, 0 as *c_void);
}
};
($name:ident ( $($param:ident : $ty:ty),+ ) $expr:expr) => {
#[no_mangle]
#[allow(unused_variable)]
pub extern "C" fn $name<'a>( $($param : $ty ),+) -> bool {
use std::libc::c_void;
use std::cast::transmute;
struct Args<'a> { $($param : $ty),+ };
extern "C" fn code(args: *c_void, _: *c_void) {
unsafe {
let ~Args{ $($param),+ }: ~Args = transmute(args);
$expr;
}
}
let args = ~Args{ $($param : $param),+ };
ffi_run!(code, args)
}
};
)
pub fn close_task() {
use std::rt::task::LocalStorage;
fn close_outputs() {
let mut task = Local::borrow(None::<Task>);
let logger = task.get().logger.take();
let stderr = task.get().stderr.take();
let stdout = task.get().stdout.take();
drop(task);
drop(logger); // loggers are responsible for flushing
match stdout { Some(mut w) => w.flush(), None => {} }
match stderr { Some(mut w) => w.flush(), None => {} }
}
close_outputs();
let mut task = Local::borrow(None::<Task>);
{
let task = task.get();
let LocalStorage(ref mut optmap) = task.storage;
if optmap.is_some() { drop(optmap.take()) }
}
drop(task);
close_outputs();
}
@pnkfelix
Copy link

On line 22, shouldn't that be $expr.to_out_ptr($out); ? (I think you are "getting lucky" in your current code, in that our broken hygiene system is letting you get away with passing in a variable named out as the expression for $out, and so this bug goes unnoticed.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment