Skip to content

Commit 6148b1f

Browse files
committed
Implement a task_local! macro
Intended to be very similar to the thread_local!() macro and is useful for storing data for the lifetime of a future.
1 parent 9226739 commit 6148b1f

File tree

5 files changed

+145
-80
lines changed

5 files changed

+145
-80
lines changed

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ composability, and iterator-like interfaces.
1515

1616
[dependencies]
1717
log = { version = "0.3", default-features = false }
18-
typemap = { version = "0.3.3", optional = true }
1918

2019
[features]
21-
use_std = ["typemap"]
20+
use_std = []
2221
default = ["use_std"]

src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,6 @@ pub use select::{Select, SelectNext};
203203
pub use then::Then;
204204

205205
if_std! {
206-
extern crate typemap;
207-
208206
mod lock;
209207
mod slot;
210208
pub mod task;

src/task/data.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
use std::prelude::v1::*;
2+
3+
use std::any::TypeId;
4+
use std::cell::RefCell;
5+
use std::hash::{BuildHasherDefault, Hasher};
6+
use std::collections::HashMap;
7+
8+
/// A macro to create a `static` of type `LocalKey`
9+
///
10+
/// This macro is intentionally similar to the `thread_local!`, and creates a
11+
/// `static` which has a `with` method to access the data on a task.
12+
///
13+
/// The data associated with each task local is per-task, so different tasks
14+
/// will contain different values.
15+
#[macro_export]
16+
macro_rules! task_local {
17+
(static $NAME:ident: $t:ty = $e:expr) => (
18+
static $NAME: $crate::task::LocalKey<$t> = {
19+
fn __init() -> $t { $e }
20+
fn __key() -> ::std::any::TypeId {
21+
struct __A;
22+
::std::any::TypeId::of::<__A>()
23+
}
24+
$crate::task::LocalKey {
25+
__init: __init,
26+
__key: __key,
27+
}
28+
};
29+
)
30+
}
31+
32+
pub type LocalMap = RefCell<HashMap<TypeId,
33+
Box<Opaque>,
34+
BuildHasherDefault<IdHasher>>>;
35+
36+
pub fn local_map() -> LocalMap {
37+
RefCell::new(HashMap::default())
38+
}
39+
40+
pub trait Opaque: Send {}
41+
impl<T: Send> Opaque for T {}
42+
43+
/// A key for task-local data stored in a future's task.
44+
///
45+
/// This type is generated by the `task_local!` macro and performs very
46+
/// similarly to the `thread_local!` macro and `std::thread::LocalKey` types.
47+
/// Data associated with a `LocalKey<T>` is stored inside of a future's task,
48+
/// and the data is destroyed when the future is completed and the task is
49+
/// destroyed.
50+
///
51+
/// Task-local data can migrate between threads and hence requires a `Send`
52+
/// bound. Additionally, task-local data also requires the `'static` bound to
53+
/// ensure it lives long enough. When a key is accessed for the first time the
54+
/// task's data is initialized with the provided initialization expression to
55+
/// the macro.
56+
pub struct LocalKey<T> {
57+
// "private" fields which have to be public to get around macro hygiene, not
58+
// included in the stability story for this type. Can change at any time.
59+
#[doc(hidden)]
60+
pub __key: fn() -> TypeId,
61+
#[doc(hidden)]
62+
pub __init: fn() -> T,
63+
}
64+
65+
pub struct IdHasher {
66+
id: u64,
67+
}
68+
69+
impl Default for IdHasher {
70+
fn default() -> IdHasher {
71+
IdHasher { id: 0 }
72+
}
73+
}
74+
75+
impl Hasher for IdHasher {
76+
fn write(&mut self, _bytes: &[u8]) {
77+
// TODO: need to do something sensible
78+
panic!("can only hash u64");
79+
}
80+
81+
fn write_u64(&mut self, u: u64) {
82+
self.id = u;
83+
}
84+
85+
fn finish(&self) -> u64 {
86+
self.id
87+
}
88+
}
89+
90+
impl<T: Send + 'static> LocalKey<T> {
91+
/// Access this task-local key, running the provided closure with a
92+
/// reference to the value.
93+
///
94+
/// This function will access this task-local key to retrieve the data
95+
/// associated with the current task and this key. If this is the first time
96+
/// this key has been accessed on this task, then the key will be
97+
/// initialized with the initialization expression provided at the time the
98+
/// `task_local!` macro was called.
99+
///
100+
/// The provided closure will be provided a shared reference to the
101+
/// underlying data associated with this task-local-key. The data itself is
102+
/// stored inside of the current task.
103+
///
104+
/// # Panics
105+
///
106+
/// This function can possibly panic for a number of reasons:
107+
///
108+
/// * If there is not a current task.
109+
/// * If the initialization expression is run and it panics
110+
/// * If the closure provided panics
111+
pub fn with<F, R>(&'static self, f: F) -> R
112+
where F: FnOnce(&T) -> R
113+
{
114+
let key = (self.__key)();
115+
super::with(|_, data| {
116+
let raw_pointer = {
117+
let mut data = data.borrow_mut();
118+
let entry = data.entry(key).or_insert_with(|| {
119+
Box::new((self.__init)())
120+
});
121+
&**entry as *const Opaque as *const T
122+
};
123+
unsafe {
124+
f(&*raw_pointer)
125+
}
126+
})
127+
}
128+
}

src/task/mod.rs

Lines changed: 16 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,24 @@
2626
//! function is similar to the standard library's `thread::park` method where it
2727
//! returns a handle to wake up a task at a later date (via an `unpark` method).
2828
29-
mod unpark_mutex;
30-
mod task_rc;
31-
pub use self::task_rc::TaskRc;
32-
3329
use {BoxFuture, Poll};
3430

3531
use std::prelude::v1::*;
3632

37-
use std::thread;
38-
use std::cell::{Cell, RefCell};
33+
use std::cell::Cell;
3934
use std::sync::Arc;
4035
use std::sync::atomic::{Ordering, AtomicUsize, ATOMIC_USIZE_INIT};
36+
use std::thread;
4137

4238
use self::unpark_mutex::UnparkMutex;
4339

44-
use typemap::{SendMap, TypeMap};
40+
mod unpark_mutex;
41+
mod task_rc;
42+
mod data;
43+
pub use self::task_rc::TaskRc;
44+
pub use self::data::LocalKey;
4545

46-
thread_local!(static CURRENT_TASK: Cell<(*const Task, *const TaskData)> = {
46+
thread_local!(static CURRENT_TASK: Cell<(*const Task, *const data::LocalMap)> = {
4747
Cell::new((0 as *const _, 0 as *const _))
4848
});
4949

@@ -57,10 +57,10 @@ fn fresh_task_id() -> usize {
5757
return id
5858
}
5959

60-
fn set<F, R>(task: &Task, data: &TaskData, f: F) -> R
60+
fn set<F, R>(task: &Task, data: &data::LocalMap, f: F) -> R
6161
where F: FnOnce() -> R
6262
{
63-
struct Reset((*const Task, *const TaskData));
63+
struct Reset((*const Task, *const data::LocalMap));
6464
impl Drop for Reset {
6565
fn drop(&mut self) {
6666
CURRENT_TASK.with(|c| c.set(self.0));
@@ -74,7 +74,7 @@ fn set<F, R>(task: &Task, data: &TaskData, f: F) -> R
7474
})
7575
}
7676

77-
fn with<F: FnOnce(&Task, &TaskData) -> R, R>(f: F) -> R {
77+
fn with<F: FnOnce(&Task, &data::LocalMap) -> R, R>(f: F) -> R {
7878
let (task, data) = CURRENT_TASK.with(|c| c.get());
7979
assert!(!task.is_null(), "no Task is currently running");
8080
assert!(!data.is_null());
@@ -148,49 +148,6 @@ pub fn with_unpark_event<F, R>(event: UnparkEvent, f: F) -> R
148148
})
149149
}
150150

151-
/// Access the current task's local data.
152-
///
153-
/// Each task has its own set of local data, which is required to be `Send` but
154-
/// not `Sync`. Futures within a task can access this data at any point.
155-
///
156-
/// # Panics
157-
///
158-
/// This function will panic if a task is not currently being executed. That
159-
/// is, this method can be dangerous to call outside of an implementation of
160-
/// `poll`.
161-
///
162-
/// It will also panic if called in the context of another `with_local_data` or
163-
/// `with_local_data_mut`.
164-
pub fn with_local_data<F, R>(f: F) -> R
165-
where F: FnOnce(&SendMap) -> R
166-
{
167-
with(|_, data| {
168-
f(&*data.borrow())
169-
})
170-
}
171-
172-
/// Access the current task's local data, mutably.
173-
///
174-
/// Each task has its own set of local data, which is required to be `Send` but
175-
/// not `Sync`. Futures within a task can access this data at any point.
176-
///
177-
/// # Panics
178-
///
179-
/// This function will panic if a task is not currently being executed. That
180-
/// is, this method can be dangerous to call outside of an implementation of
181-
/// `poll`.
182-
///
183-
/// It will also panic if called in the context of another `with_local_data` or
184-
/// `with_local_data_mut`.
185-
pub fn with_local_data_mut<F, R>(f: F) -> R
186-
where F: FnOnce(&mut SendMap) -> R
187-
{
188-
with(|_, data| {
189-
f(&mut *data.borrow_mut())
190-
})
191-
}
192-
193-
194151
/// A handle to a "task", which represents a single lightweight "thread" of
195152
/// execution driving a future to completion.
196153
///
@@ -222,11 +179,9 @@ enum TaskKind {
222179
Local(thread::Thread),
223180
}
224181

225-
type TaskData = RefCell<SendMap>;
226-
227182
struct MutexInner {
228183
future: BoxFuture<(), ()>,
229-
task_data: TaskData,
184+
task_data: data::LocalMap,
230185
}
231186

232187
/// A task intended to be run directly on a local thread.
@@ -239,7 +194,7 @@ struct MutexInner {
239194
/// `std::thread::Thread::unpark` for the thread that entered the task.
240195
pub struct ThreadTask {
241196
id: usize,
242-
task_data: TaskData,
197+
task_data: data::LocalMap,
243198
}
244199

245200
/// A handle for running an executor-bound task.
@@ -254,7 +209,7 @@ impl ThreadTask {
254209
pub fn new() -> ThreadTask {
255210
ThreadTask {
256211
id: fresh_task_id(),
257-
task_data: RefCell::new(TypeMap::custom()),
212+
task_data: data::local_map(),
258213
}
259214
}
260215

@@ -364,13 +319,13 @@ impl Task {
364319
///
365320
/// Does not actually begin task execution; use the `unpark` method to do
366321
/// so.
367-
pub fn new() -> Task {
322+
pub fn new(exec: Arc<Executor>, future: BoxFuture<(), ()>) -> Task {
368323
Task {
369324
id: fresh_task_id(),
370325
kind: TaskKind::Executor {
371326
mutex: Arc::new(UnparkMutex::new(MutexInner {
372327
future: future,
373-
task_data: RefCell::new(TypeMap::custom()),
328+
task_data: data::local_map(),
374329
})),
375330
exec: exec
376331
},
@@ -408,15 +363,6 @@ impl Task {
408363
TaskKind::Local(ref thread) => thread.unpark()
409364
}
410365
}
411-
412-
/// Determines whether the underlying future has ever polled with a final
413-
/// result (or panicked), and thus terminated.
414-
pub fn is_done(&self) -> bool {
415-
match self.kind {
416-
TaskKind::Executor { ref mutex, .. } => mutex.is_complete(),
417-
_ => false,
418-
}
419-
}
420366
}
421367

422368
impl Events {

src/task/unpark_mutex.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,4 @@ impl<D> UnparkMutex<D> {
141141
pub unsafe fn complete(&self) {
142142
self.status.store(COMPLETE, SeqCst);
143143
}
144-
145-
146-
/// Determine whether task execution has completed.
147-
pub fn is_complete(&self) -> bool {
148-
self.status.load(SeqCst) == COMPLETE
149-
}
150144
}

0 commit comments

Comments
 (0)