12
12
13
13
use cast;
14
14
use cell:: Cell ;
15
+ use either:: { Either , Left , Right } ;
15
16
use option:: { Option , Some , None } ;
16
17
use prelude:: * ;
17
18
use rt:: task:: Task ;
@@ -26,6 +27,16 @@ static KILL_RUNNING: uint = 0;
26
27
static KILL_KILLED : uint = 1 ;
27
28
static KILL_UNKILLABLE : uint = 2 ;
28
29
30
+ struct KillFlag ( AtomicUint ) ;
31
+ type KillFlagHandle = UnsafeAtomicRcBox < KillFlag > ;
32
+
33
+ /// A handle to a blocked task. Usually this means having the ~Task pointer by
34
+ /// ownership, but if the task is killable, a killer can steal it at any time.
35
+ pub enum BlockedTask {
36
+ Unkillable ( ~Task ) ,
37
+ Killable ( KillFlagHandle ) ,
38
+ }
39
+
29
40
// FIXME(#7544)(bblum): think about the cache efficiency of this
30
41
struct KillHandleInner {
31
42
// Is the task running, blocked, or killed? Possible values:
@@ -35,7 +46,7 @@ struct KillHandleInner {
35
46
// This flag is refcounted because it may also be referenced by a blocking
36
47
// concurrency primitive, used to wake the task normally, whose reference
37
48
// may outlive the handle's if the task is killed.
38
- killed : UnsafeAtomicRcBox < AtomicUint > ,
49
+ killed : KillFlagHandle ,
39
50
// Has the task deferred kill signals? This flag guards the above one.
40
51
// Possible values:
41
52
// * KILL_RUNNING - Not unkillable, no kill pending.
@@ -76,11 +87,93 @@ pub struct Death {
76
87
wont_sleep : int ,
77
88
}
78
89
90
+ impl Drop for KillFlag {
91
+ // Letting a KillFlag with a task inside get dropped would leak the task.
92
+ // We could free it here, but the task should get awoken by hand somehow.
93
+ fn drop ( & self ) {
94
+ match self . load ( Acquire ) {
95
+ KILL_RUNNING | KILL_KILLED => { } ,
96
+ _ => rtabort ! ( "can't drop kill flag with a blocked task inside!" ) ,
97
+ }
98
+ }
99
+ }
100
+
101
+ impl BlockedTask {
102
+ /// Returns Some if the task was successfully woken; None if already killed.
103
+ pub fn wake ( self ) -> Option < ~Task > {
104
+ let mut this = self ;
105
+ match this {
106
+ Unkillable ( task) => Some ( task) ,
107
+ Killable ( ref mut flag_arc) => {
108
+ let flag = unsafe { & mut * * flag_arc. get ( ) } ;
109
+ match flag. swap ( KILL_RUNNING , SeqCst ) {
110
+ KILL_RUNNING => rtabort ! ( "tried to wake an already-running task" ) ,
111
+ KILL_KILLED => None , // a killer stole it already
112
+ task_ptr => Some ( unsafe { cast:: transmute ( task_ptr) } ) ,
113
+ }
114
+ }
115
+ }
116
+ }
117
+
118
+ /// Create a blocked task, unless the task was already killed.
119
+ pub fn try_block ( task : ~Task ) -> Either < ~Task , BlockedTask > {
120
+ if task. death . unkillable > 0 { // FIXME(#7544): || self.indestructible
121
+ Right ( Unkillable ( task) )
122
+ } else {
123
+ rtassert ! ( task. death. kill_handle. is_some( ) ) ;
124
+ unsafe {
125
+ // FIXME(#7544) optimz
126
+ let flag_arc = ( * task. death . kill_handle . get_ref ( ) . get ( ) ) . killed . clone ( ) ;
127
+ let flag = & mut * * flag_arc. get ( ) ;
128
+ let task_ptr = cast:: transmute ( task) ;
129
+ // Expect flag to contain RUNNING. If KILLED, it should stay KILLED.
130
+ match flag. compare_and_swap ( KILL_RUNNING , task_ptr, SeqCst ) {
131
+ KILL_RUNNING => Right ( Killable ( flag_arc) ) ,
132
+ KILL_KILLED => Left ( cast:: transmute ( task_ptr) ) ,
133
+ x => rtabort ! ( "can't block task! kill flag = %?" , x) ,
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ /// Convert to an unsafe uint value. Useful for storing in a pipe's state flag.
140
+ #[ inline]
141
+ pub unsafe fn cast_to_uint ( self ) -> uint {
142
+ // Use the low bit to distinguish the enum variants, to save a second
143
+ // allocation in the indestructible case.
144
+ match self {
145
+ Unkillable ( task) => {
146
+ let blocked_task_ptr: uint = cast:: transmute ( task) ;
147
+ rtassert ! ( blocked_task_ptr & 0x1 == 0 ) ;
148
+ blocked_task_ptr
149
+ } ,
150
+ Killable ( flag_arc) => {
151
+ let blocked_task_ptr: uint = cast:: transmute ( ~flag_arc) ;
152
+ rtassert ! ( blocked_task_ptr & 0x1 == 0 ) ;
153
+ blocked_task_ptr | 0x1
154
+ }
155
+ }
156
+ }
157
+
158
+ /// Convert from an unsafe uint value. Useful for retrieving a pipe's state flag.
159
+ #[ inline]
160
+ pub unsafe fn cast_from_uint ( blocked_task_ptr : uint ) -> BlockedTask {
161
+ if blocked_task_ptr & 0x1 == 0 {
162
+ Unkillable ( cast:: transmute ( blocked_task_ptr) )
163
+ } else {
164
+ let ptr: ~KillFlagHandle = cast:: transmute ( blocked_task_ptr & !0x1 ) ;
165
+ match ptr {
166
+ ~flag_arc => Killable ( flag_arc)
167
+ }
168
+ }
169
+ }
170
+ }
171
+
79
172
impl KillHandle {
80
173
pub fn new ( ) -> KillHandle {
81
174
KillHandle ( UnsafeAtomicRcBox :: new ( KillHandleInner {
82
175
// Linked failure fields
83
- killed : UnsafeAtomicRcBox :: new ( AtomicUint :: new ( KILL_RUNNING ) ) ,
176
+ killed : UnsafeAtomicRcBox :: new ( KillFlag ( AtomicUint :: new ( KILL_RUNNING ) ) ) ,
84
177
unkillable : AtomicUint :: new ( KILL_RUNNING ) ,
85
178
// Exit code propagation fields
86
179
any_child_failed : false ,
0 commit comments