diff --git a/base/task.jl b/base/task.jl index bfb105accef14..e84c344c28d17 100644 --- a/base/task.jl +++ b/base/task.jl @@ -721,6 +721,7 @@ A fast, unfair-scheduling version of `schedule(t, arg); yield()` which immediately yields to `t` before calling the scheduler. """ function yield(t::Task, @nospecialize(x=nothing)) + (t._state === task_state_runnable && t.queue === nothing) || error("yield: Task not runnable") t.result = x enq_work(current_task()) set_next_task(t) @@ -736,6 +737,13 @@ call to `yieldto`. This is a low-level call that only switches tasks, not consid or scheduling in any way. Its use is discouraged. """ function yieldto(t::Task, @nospecialize(x=nothing)) + # TODO: these are legacy behaviors; these should perhaps be a scheduler + # state error instead. + if t._state === task_state_done + return x + elseif t._state === task_state_failed + throw(t.result) + end t.result = x set_next_task(t) return try_yieldto(identity) diff --git a/src/task.c b/src/task.c index 24081dfc6c4b3..43ab8db89cfee 100644 --- a/src/task.c +++ b/src/task.c @@ -475,12 +475,8 @@ JL_DLLEXPORT void jl_switch(void) if (t == ct) { return; } - if (t->_state != JL_TASK_STATE_RUNNABLE || (t->started && t->stkbuf == NULL)) { - ct->_isexception = t->_isexception; - ct->result = t->result; - jl_gc_wb(ct, ct->result); - return; - } + if (t->started && t->stkbuf == NULL) + jl_error("attempt to switch to exited task"); if (ptls->in_finalizer) jl_error("task switch not allowed from inside gc finalizer"); if (ptls->in_pure_callback) diff --git a/test/misc.jl b/test/misc.jl index 2305b78250ef2..37c5397b4948f 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -182,6 +182,17 @@ end @test_throws ErrorException("deadlock detected: cannot wait on current task") wait(current_task()) +# issue #41347 +let t = @async 1 + wait(t) + @test_throws ErrorException yield(t) +end + +let t = @async error(42) + Base._wait(t) + @test_throws ErrorException("42") yieldto(t) +end + # test that @sync is lexical (PR #27164) const x27164 = Ref(0)