Skip to content

Commit b429232

Browse files
committed
Fixed race starting Fiber.
1 parent 5f22ac8 commit b429232

File tree

3 files changed

+112
-67
lines changed

3 files changed

+112
-67
lines changed

machine/builtin/fiber.cpp

Lines changed: 85 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -78,49 +78,53 @@ namespace rubinius {
7878
std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());
7979

8080
if(!vm()->suspended_p()) {
81-
Exception::raise_fiber_error(state, "attempt to restart non-suspended fiber");
82-
}
83-
84-
while(vm()->suspended_p()) {
85-
std::lock_guard<std::mutex> guard(vm()->wait_mutex());
86-
vm()->wait_condition().notify_one();
81+
std::ostringstream msg;
82+
msg << "attempt to restart non-suspended (" << vm()->transition_flag() << ") fiber";
83+
Exception::raise_fiber_error(state, msg.str().c_str());
8784
}
8885

8986
state->vm()->set_suspending();
87+
88+
restart_context(state->vm());
89+
wakeup();
90+
91+
{
92+
UnmanagedPhase unmanaged(state);
93+
94+
while(vm()->suspended_p()) {
95+
std::lock_guard<std::mutex> guard(vm()->wait_mutex());
96+
vm()->wait_condition().notify_one();
97+
}
98+
}
9099
}
91100

92101
while(!vm()->running_p()) {
93102
; // spin wait
94103
}
95-
96-
{
97-
std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());
98-
state->vm()->thread()->current_fiber(state, this);
99-
}
100104
}
101105

102-
void Fiber::suspend(STATE) {
106+
void Fiber::suspend_and_continue(STATE) {
103107
{
104108
std::unique_lock<std::mutex> lk(vm()->wait_mutex());
105-
vm()->set_suspended();
106109

110+
vm()->set_suspended();
107111
{
108112
UnmanagedPhase unmanaged(state);
109-
vm()->wait_condition().wait(lk);
110-
}
111113

114+
while(!wakeup_p()) {
115+
vm()->wait_condition().wait(lk);
116+
}
117+
}
118+
clear_wakeup();
112119
vm()->set_resuming();
113120
}
114121

115-
while(invoke_context()->running_p()) {
116-
; // spin wait
117-
}
118-
119122
{
120123
std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());
124+
121125
vm()->set_running();
122126

123-
while(invoke_context()->suspending_p()) {
127+
while(!restart_context()->suspended_p()) {
124128
; // spin wait
125129
}
126130

@@ -159,7 +163,7 @@ namespace rubinius {
159163

160164
NativeMethod::init_thread(state);
161165

162-
vm->fiber()->suspend(state);
166+
vm->fiber()->suspend_and_continue(state);
163167

164168
Object* value = vm->fiber()->block()->send(state, G(sym_call),
165169
as<Array>(vm->thread()->fiber_value()), vm->fiber()->block());
@@ -179,8 +183,12 @@ namespace rubinius {
179183
vm->fiber()->invoke_context()->fiber()->restart(state);
180184
}
181185

182-
vm->fiber()->status(eDead);
183-
vm->fiber()->vm()->set_finished();
186+
{
187+
std::lock_guard<std::mutex> guard(vm->wait_mutex());
188+
189+
vm->fiber()->status(eDead);
190+
vm->set_suspended();
191+
}
184192

185193
vm->unmanaged_phase();
186194

@@ -295,60 +303,69 @@ namespace rubinius {
295303
}
296304

297305
Object* Fiber::resume(STATE, Arguments& args) {
298-
if(state->vm()->thread() != thread()) {
299-
Exception::raise_fiber_error(state, "attempt to resume fiber across threads");
300-
}
306+
{
307+
std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());
301308

302-
unpack_arguments(state, args);
303-
304-
if(status() == eCreated) {
305-
start(state, args);
306-
} else if(status() == eTransfer) {
307-
Exception::raise_fiber_error(state, "attempt to resume transfered fiber");
308-
} else if(status() == eRunning) {
309-
Exception::raise_fiber_error(state, "attempt to resume running fiber");
310-
} else if(status() == eDead) {
311-
Exception::raise_fiber_error(state, "attempt to resume dead fiber");
312-
} else if(root_p()) {
313-
Exception::raise_fiber_error(state, "attempt to resume root fiber");
314-
}
309+
if(state->vm()->thread() != thread()) {
310+
Exception::raise_fiber_error(state, "attempt to resume fiber across threads");
311+
} else if(status() == eTransfer) {
312+
Exception::raise_fiber_error(state, "attempt to resume transfered fiber");
313+
} else if(status() == eRunning) {
314+
Exception::raise_fiber_error(state, "attempt to resume running fiber");
315+
} else if(status() == eDead) {
316+
Exception::raise_fiber_error(state, "attempt to resume dead fiber");
317+
} else if(root_p()) {
318+
Exception::raise_fiber_error(state, "attempt to resume root fiber");
319+
}
320+
321+
unpack_arguments(state, args);
322+
invoke_context(state->vm());
315323

316-
status(eRunning);
317-
invoke_context(state->vm());
324+
if(status() == eCreated) {
325+
start(state, args);
326+
}
327+
328+
status(eRunning);
329+
}
318330

319331
// Being cooperative...
320332
restart(state);
321333

322334
// Through the worm hole...
323-
state->vm()->fiber()->suspend(state);
335+
state->vm()->fiber()->suspend_and_continue(state);
324336

325337
// We're back...
326338
return return_value(state);
327339
}
328340

329341
Object* Fiber::transfer(STATE, Arguments& args) {
330-
if(state->vm()->thread() != thread()) {
331-
Exception::raise_fiber_error(state, "attempt to transfer fiber across threads");
332-
}
342+
{
343+
std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());
333344

334-
unpack_arguments(state, args);
345+
if(state->vm()->thread() != thread()) {
346+
Exception::raise_fiber_error(state, "attempt to transfer fiber across threads");
347+
} else if(status() == eDead) {
348+
Exception::raise_fiber_error(state, "attempt to transfer to dead fiber");
349+
} else if(state->vm()->fiber() == this) {
350+
// This should arguably be a FiberError
351+
return args.as_array(state);
352+
}
335353

336-
if(status() == eCreated) {
337-
start(state, args);
338-
} else if(state->vm()->fiber() == this) {
339-
return args.as_array(state);
340-
} else if(status() == eDead) {
341-
Exception::raise_fiber_error(state, "attempt to transfer to dead fiber");
342-
}
354+
unpack_arguments(state, args);
355+
invoke_context(state->vm());
356+
357+
if(status() == eCreated) {
358+
start(state, args);
359+
}
343360

344-
status(eTransfer);
345-
invoke_context(state->vm());
361+
status(eTransfer);
362+
}
346363

347364
// Being cooperative...
348365
restart(state);
349366

350367
// Through the worm hole...
351-
state->vm()->fiber()->suspend(state);
368+
state->vm()->fiber()->suspend_and_continue(state);
352369

353370
// We're back...
354371
return return_value(state);
@@ -358,20 +375,24 @@ namespace rubinius {
358375
Fiber* fiber = state->vm()->fiber();
359376
OnStack<1> os(state, fiber);
360377

361-
if(fiber->root_p()) {
362-
Exception::raise_fiber_error(state, "can't yield from root fiber");
363-
} else if(fiber->status() == eTransfer) {
364-
Exception::raise_fiber_error(state, "can't yield from transferred fiber");
365-
}
378+
{
379+
std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());
366380

367-
fiber->unpack_arguments(state, args);
368-
fiber->status(eYielding);
381+
if(fiber->root_p()) {
382+
Exception::raise_fiber_error(state, "can't yield from root fiber");
383+
} else if(fiber->status() == eTransfer) {
384+
Exception::raise_fiber_error(state, "can't yield from transferred fiber");
385+
}
386+
387+
fiber->unpack_arguments(state, args);
388+
fiber->status(eYielding);
389+
}
369390

370391
// Being cooperative...
371392
fiber->invoke_context()->fiber()->restart(state);
372393

373394
// Through the worm hole...
374-
fiber->suspend(state);
395+
fiber->suspend_and_continue(state);
375396

376397
// We're back...
377398
return fiber->return_value(state);

machine/builtin/fiber.hpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@ namespace rubinius {
4848

4949
attr_field(vm, VM*);
5050
attr_field(invoke_context, VM*);
51+
attr_field(restart_context, VM*);
5152

5253
std::atomic<Status> status_;
54+
std::atomic<bool> wakeup_;
5355

5456
public:
5557
static void bootstrap(STATE);
@@ -63,10 +65,12 @@ namespace rubinius {
6365
obj->fiber_id(Fixnum::from(++Fiber::fiber_ids_));
6466
obj->source(nil<String>());
6567
obj->thread(state->vm()->thread());
66-
obj->status(eCreated);
6768
obj->start_time(get_current_time());
6869
obj->vm(NULL);
6970
obj->invoke_context(state->vm());
71+
obj->restart_context(state->vm());
72+
obj->status(eCreated);
73+
obj->clear_wakeup();
7074
}
7175

7276
static void finalize(STATE, Fiber* fib);
@@ -89,6 +93,8 @@ namespace rubinius {
8993
// Rubinius.primitive :fiber_s_main
9094
static Fiber* s_main(STATE);
9195

96+
bool root_p();
97+
9298
Status status() {
9399
return status_;
94100
}
@@ -97,14 +103,24 @@ namespace rubinius {
97103
status_ = status;
98104
}
99105

100-
bool root_p();
106+
void wakeup() {
107+
wakeup_ = true;
108+
}
109+
110+
void clear_wakeup() {
111+
wakeup_ = false;
112+
}
113+
114+
bool wakeup_p() {
115+
return wakeup_;
116+
}
101117

102118
void unpack_arguments(STATE, Arguments& args);
103119
Object* return_value(STATE);
104120

105121
void start(STATE, Arguments& args);
106122
void restart(STATE);
107-
void suspend(STATE);
123+
void suspend_and_continue(STATE);
108124

109125
// Rubinius.primitive :fiber_status
110126
String* status(STATE);

machine/vm.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ namespace rubinius {
191191
return wait_condition_;
192192
}
193193

194+
FiberTransition transition_flag() {
195+
return transition_flag_;
196+
}
197+
194198
bool suspending_p() const {
195199
return transition_flag_ == eSuspending;
196200
}
@@ -207,6 +211,10 @@ namespace rubinius {
207211
return transition_flag_ == eRunning;
208212
}
209213

214+
bool finished_p() const {
215+
return transition_flag_ == eFinished;
216+
}
217+
210218
void set_suspending() {
211219
transition_flag_ = eSuspending;
212220
}

0 commit comments

Comments
 (0)