about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorAaron Turon <aturon@mozilla.com>2014-10-15 15:45:59 -0700
committerAaron Turon <aturon@mozilla.com>2014-11-08 20:40:38 -0800
commit3d195482a45bf3ed0f12dc9d70d14192262ca711 (patch)
treed9cf1a93cc1e862e641cd17fdb41ebe93d22bfea /src/libstd
parentd34b1b0ca9bf5e0d7cd30952f5de0ab09ed57b41 (diff)
downloadrust-3d195482a45bf3ed0f12dc9d70d14192262ca711.tar.gz
rust-3d195482a45bf3ed0f12dc9d70d14192262ca711.zip
Runtime removal: refactor helper threads
This patch continues the runtime removal by moving
libnative::io::helper_thread into sys::helper_signal and
sys_common::helper_thread

Because this eliminates APIs in `libnative` and `librustrt`, it is a:

[breaking-change]

This functionality is likely to be available publicly, in some form,
from `std` in the future.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/sys/common/helper_thread.rs142
-rw-r--r--src/libstd/sys/common/mod.rs1
-rw-r--r--src/libstd/sys/common/net.rs2
-rw-r--r--src/libstd/sys/unix/helper_signal.rs29
-rw-r--r--src/libstd/sys/unix/mod.rs11
-rw-r--r--src/libstd/sys/windows/helper_signal.rs38
-rw-r--r--src/libstd/sys/windows/mod.rs1
7 files changed, 224 insertions, 0 deletions
diff --git a/src/libstd/sys/common/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs
new file mode 100644
index 00000000000..8c8ec4466a7
--- /dev/null
+++ b/src/libstd/sys/common/helper_thread.rs
@@ -0,0 +1,142 @@
+// Copyright 2013-2014 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.
+
+//! Implementation of the helper thread for the timer module
+//!
+//! This module contains the management necessary for the timer worker thread.
+//! This thread is responsible for performing the send()s on channels for timers
+//! that are using channels instead of a blocking call.
+//!
+//! The timer thread is lazily initialized, and it's shut down via the
+//! `shutdown` function provided. It must be maintained as an invariant that
+//! `shutdown` is only called when the entire program is finished. No new timers
+//! can be created in the future and there must be no active timers at that
+//! time.
+
+#![macro_escape]
+
+use mem;
+use rt::bookkeeping;
+use rt::mutex::StaticNativeMutex;
+use rt;
+use cell::UnsafeCell;
+use sys::helper_signal;
+use prelude::*;
+
+use task;
+
+/// A structure for management of a helper thread.
+///
+/// This is generally a static structure which tracks the lifetime of a helper
+/// thread.
+///
+/// The fields of this helper are all public, but they should not be used, this
+/// is for static initialization.
+pub struct Helper<M> {
+    /// Internal lock which protects the remaining fields
+    pub lock: StaticNativeMutex,
+
+    // You'll notice that the remaining fields are UnsafeCell<T>, and this is
+    // because all helper thread operations are done through &self, but we need
+    // these to be mutable (once `lock` is held).
+
+    /// Lazily allocated channel to send messages to the helper thread.
+    pub chan: UnsafeCell<*mut Sender<M>>,
+
+    /// OS handle used to wake up a blocked helper thread
+    pub signal: UnsafeCell<uint>,
+
+    /// Flag if this helper thread has booted and been initialized yet.
+    pub initialized: UnsafeCell<bool>,
+}
+
+macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
+    static $name: Helper<$m> = Helper {
+        lock: ::std::rt::mutex::NATIVE_MUTEX_INIT,
+        chan: ::std::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
+        signal: ::std::cell::UnsafeCell { value: 0 },
+        initialized: ::std::cell::UnsafeCell { value: false },
+    };
+) )
+
+impl<M: Send> Helper<M> {
+    /// Lazily boots a helper thread, becoming a no-op if the helper has already
+    /// been spawned.
+    ///
+    /// This function will check to see if the thread has been initialized, and
+    /// if it has it returns quickly. If initialization has not happened yet,
+    /// the closure `f` will be run (inside of the initialization lock) and
+    /// passed to the helper thread in a separate task.
+    ///
+    /// This function is safe to be called many times.
+    pub fn boot<T: Send>(&'static self,
+                         f: || -> T,
+                         helper: fn(helper_signal::signal, Receiver<M>, T)) {
+        unsafe {
+            let _guard = self.lock.lock();
+            if !*self.initialized.get() {
+                let (tx, rx) = channel();
+                *self.chan.get() = mem::transmute(box tx);
+                let (receive, send) = helper_signal::new();
+                *self.signal.get() = send as uint;
+
+                let t = f();
+                task::spawn(proc() {
+                    bookkeeping::decrement();
+                    helper(receive, rx, t);
+                    self.lock.lock().signal()
+                });
+
+                rt::at_exit(proc() { self.shutdown() });
+                *self.initialized.get() = true;
+            }
+        }
+    }
+
+    /// Sends a message to a spawned worker thread.
+    ///
+    /// This is only valid if the worker thread has previously booted
+    pub fn send(&'static self, msg: M) {
+        unsafe {
+            let _guard = self.lock.lock();
+
+            // Must send and *then* signal to ensure that the child receives the
+            // message. Otherwise it could wake up and go to sleep before we
+            // send the message.
+            assert!(!self.chan.get().is_null());
+            (**self.chan.get()).send(msg);
+            helper_signal::signal(*self.signal.get() as helper_signal::signal);
+        }
+    }
+
+    fn shutdown(&'static self) {
+        unsafe {
+            // Shut down, but make sure this is done inside our lock to ensure
+            // that we'll always receive the exit signal when the thread
+            // returns.
+            let guard = self.lock.lock();
+
+            // Close the channel by destroying it
+            let chan: Box<Sender<M>> = mem::transmute(*self.chan.get());
+            *self.chan.get() = 0 as *mut Sender<M>;
+            drop(chan);
+            helper_signal::signal(*self.signal.get() as helper_signal::signal);
+
+            // Wait for the child to exit
+            guard.wait();
+            drop(guard);
+
+            // Clean up after ourselves
+            self.lock.destroy();
+            helper_signal::close(*self.signal.get() as helper_signal::signal);
+            *self.signal.get() = 0;
+        }
+    }
+}
diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs
index 402c62bb35e..75c2987078d 100644
--- a/src/libstd/sys/common/mod.rs
+++ b/src/libstd/sys/common/mod.rs
@@ -20,6 +20,7 @@ use path::BytesContainer;
 use collections;
 
 pub mod net;
+pub mod helper_thread;
 
 // common error constructors
 
diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs
index 0559005100f..7c44142d93c 100644
--- a/src/libstd/sys/common/net.rs
+++ b/src/libstd/sys/common/net.rs
@@ -24,6 +24,8 @@ use prelude::*;
 use cmp;
 use io;
 
+// FIXME: move uses of Arc and deadline tracking to std::io
+
 #[deriving(Show)]
 pub enum SocketStatus {
     Readable,
diff --git a/src/libstd/sys/unix/helper_signal.rs b/src/libstd/sys/unix/helper_signal.rs
new file mode 100644
index 00000000000..a806bea2568
--- /dev/null
+++ b/src/libstd/sys/unix/helper_signal.rs
@@ -0,0 +1,29 @@
+// Copyright 2014 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 libc;
+use os;
+
+use sys::fs::FileDesc;
+
+pub type signal = libc::c_int;
+
+pub fn new() -> (signal, signal) {
+    let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
+    (reader, writer)
+}
+
+pub fn signal(fd: libc::c_int) {
+    FileDesc::new(fd, false).write([0]).ok().unwrap();
+}
+
+pub fn close(fd: libc::c_int) {
+    let _fd = FileDesc::new(fd, true);
+}
diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs
index 5a43fd08f90..6295864e0e1 100644
--- a/src/libstd/sys/unix/mod.rs
+++ b/src/libstd/sys/unix/mod.rs
@@ -17,12 +17,23 @@ use prelude::*;
 use io::{mod, IoResult, IoError};
 use sys_common::mkerr_libc;
 
+
+macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
+    static $name: Helper<$m> = Helper {
+        lock: ::rt::mutex::NATIVE_MUTEX_INIT,
+        chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
+        signal: ::cell::UnsafeCell { value: 0 },
+        initialized: ::cell::UnsafeCell { value: false },
+    };
+) )
+
 pub mod c;
 pub mod fs;
 pub mod os;
 pub mod tcp;
 pub mod udp;
 pub mod pipe;
+pub mod helper_signal;
 
 pub mod addrinfo {
     pub use sys_common::net::get_host_addresses;
diff --git a/src/libstd/sys/windows/helper_signal.rs b/src/libstd/sys/windows/helper_signal.rs
new file mode 100644
index 00000000000..c547c79e83a
--- /dev/null
+++ b/src/libstd/sys/windows/helper_signal.rs
@@ -0,0 +1,38 @@
+// Copyright 2014 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 libc::{mod, BOOL, LPCSTR, HANDLE, LPSECURITY_ATTRIBUTES, CloseHandle};
+use ptr;
+
+pub type signal = HANDLE;
+
+pub fn new() -> (HANDLE, HANDLE) {
+    unsafe {
+        let handle = CreateEventA(ptr::null_mut(), libc::FALSE, libc::FALSE,
+                                  ptr::null());
+        (handle, handle)
+    }
+}
+
+pub fn signal(handle: HANDLE) {
+    assert!(unsafe { SetEvent(handle) != 0 });
+}
+
+pub fn close(handle: HANDLE) {
+    assert!(unsafe { CloseHandle(handle) != 0 });
+}
+
+extern "system" {
+    fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
+                    bManualReset: BOOL,
+                    bInitialState: BOOL,
+                    lpName: LPCSTR) -> HANDLE;
+    fn SetEvent(hEvent: HANDLE) -> BOOL;
+}
diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs
index 85fbc6b936c..6f6ca3f2e62 100644
--- a/src/libstd/sys/windows/mod.rs
+++ b/src/libstd/sys/windows/mod.rs
@@ -39,6 +39,7 @@ pub mod os;
 pub mod tcp;
 pub mod udp;
 pub mod pipe;
+pub mod helper_signal;
 
 pub mod addrinfo {
     pub use sys_common::net::get_host_addresses;