Skip to content

Commit 8667b80

Browse files
committed
Unstick parent if all child tasks are done
1 parent 2a4eea8 commit 8667b80

File tree

7 files changed

+80
-8
lines changed

7 files changed

+80
-8
lines changed

base/boot.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ primitive type Char <: AbstractChar 32 end
222222
primitive type Int8 <: Signed 8 end
223223
#primitive type UInt8 <: Unsigned 8 end
224224
primitive type Int16 <: Signed 16 end
225-
primitive type UInt16 <: Unsigned 16 end
225+
#primitive type UInt16 <: Unsigned 16 end
226226
#primitive type Int32 <: Signed 32 end
227227
#primitive type UInt32 <: Unsigned 32 end
228228
#primitive type Int64 <: Signed 64 end

base/task.jl

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,21 @@ end
166166
elseif field === :exception
167167
# TODO: this field name should be deprecated in 2.0
168168
return t._isexception ? t.result : nothing
169+
elseif field === :sticky
170+
return getfield(t, :sticky_count) != 0
169171
else
170172
return getfield(t, field)
171173
end
172174
end
173175

176+
function setproperty!(t::Task, field::Symbol, x)
177+
if field === :sticky
178+
t.sticky_count = convert(Bool, x)
179+
else
180+
setfield!(t, field, convert(fieldtype(Task, field), x))
181+
end
182+
end
183+
174184
"""
175185
istaskdone(t::Task) -> Bool
176186
@@ -611,6 +621,9 @@ function __preinit_threads__()
611621
nothing
612622
end
613623

624+
# Factored out so that the behavior after saturation can be tested:
625+
is_sticky_count_saturated(t::Task) = t.sticky_count === typemax(t.sticky_count)
626+
614627
function enq_work(t::Task)
615628
(t._state === task_state_runnable && t.queue === nothing) || error("schedule: Task not runnable")
616629
tid = Threads.threadid(t)
@@ -625,8 +638,30 @@ function enq_work(t::Task)
625638
# t.sticky && tid == 0 is a task that needs to be co-scheduled with
626639
# the parent task. If the parent (current_task) is not sticky we must
627640
# set it to be sticky.
628-
# XXX: Ideally we would be able to unset this
629-
current_task().sticky = true
641+
parent_task = current_task()
642+
if t.sticky && !is_sticky_count_saturated(parent_task)
643+
parent_task.sticky_count += 1
644+
original_code = t.code
645+
t.code = function wrapper_code()
646+
try
647+
original_code()
648+
finally
649+
if !is_sticky_count_saturated(parent_task)
650+
# Once `parent_task.sticky_count` hits the typemax (which
651+
# practically never happens), we stop un-sticking the parent
652+
# task. This only affects the performance in rare cases (which
653+
# already torturing the scheulder anyway) and does not
654+
# sacrifice the correctness. Checking saturation should be done
655+
# for all tasks includding those started with
656+
#`parent_task.sticky_count < typemax -1` since there may be
657+
# sticky tasks started realying on that the coutner is
658+
# saturated.
659+
parent_task.sticky_count -= 1
660+
end
661+
end
662+
end
663+
end
664+
630665
tid = Threads.threadid()
631666
ccall(:jl_set_task_tid, Cvoid, (Any, Cint), t, tid-1)
632667
end

src/builtins.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1868,6 +1868,7 @@ void jl_init_primitives(void) JL_GC_DISABLED
18681868
add_builtin("UInt8", (jl_value_t*)jl_uint8_type);
18691869
add_builtin("Int32", (jl_value_t*)jl_int32_type);
18701870
add_builtin("Int64", (jl_value_t*)jl_int64_type);
1871+
add_builtin("UInt16", (jl_value_t*)jl_uint16_type);
18711872
add_builtin("UInt32", (jl_value_t*)jl_uint32_type);
18721873
add_builtin("UInt64", (jl_value_t*)jl_uint64_type);
18731874
#ifdef _P64

src/init.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,6 @@ static void post_boot_hooks(void)
805805
jl_char_type = (jl_datatype_t*)core("Char");
806806
jl_int8_type = (jl_datatype_t*)core("Int8");
807807
jl_int16_type = (jl_datatype_t*)core("Int16");
808-
jl_uint16_type = (jl_datatype_t*)core("UInt16");
809808
jl_float16_type = (jl_datatype_t*)core("Float16");
810809
jl_float32_type = (jl_datatype_t*)core("Float32");
811810
jl_float64_type = (jl_datatype_t*)core("Float64");
@@ -819,6 +818,7 @@ static void post_boot_hooks(void)
819818
jl_uint8_type->super = jl_unsigned_type;
820819
jl_int32_type->super = jl_signed_type;
821820
jl_int64_type->super = jl_signed_type;
821+
jl_uint16_type->super = jl_unsigned_type;
822822
jl_uint32_type->super = jl_unsigned_type;
823823
jl_uint64_type->super = jl_unsigned_type;
824824

src/jltypes.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2092,6 +2092,8 @@ void jl_init_types(void) JL_GC_DISABLED
20922092
jl_any_type, jl_emptysvec, 32);
20932093
jl_int64_type = jl_new_primitivetype((jl_value_t*)jl_symbol("Int64"), core,
20942094
jl_any_type, jl_emptysvec, 64);
2095+
jl_uint16_type = jl_new_primitivetype((jl_value_t*)jl_symbol("UInt16"), core,
2096+
jl_any_type, jl_emptysvec, 16);
20952097
jl_uint32_type = jl_new_primitivetype((jl_value_t*)jl_symbol("UInt32"), core,
20962098
jl_any_type, jl_emptysvec, 32);
20972099
jl_uint64_type = jl_new_primitivetype((jl_value_t*)jl_symbol("UInt64"), core,
@@ -2539,8 +2541,8 @@ void jl_init_types(void) JL_GC_DISABLED
25392541
"result",
25402542
"logstate",
25412543
"code",
2544+
"sticky_count",
25422545
"_state",
2543-
"sticky",
25442546
"_isexception",
25452547
"rngState0",
25462548
"rngState1",
@@ -2554,9 +2556,9 @@ void jl_init_types(void) JL_GC_DISABLED
25542556
jl_any_type,
25552557
jl_any_type,
25562558
jl_any_type,
2559+
jl_uint16_type,
25572560
jl_uint8_type,
25582561
jl_bool_type,
2559-
jl_bool_type,
25602562
jl_uint64_type,
25612563
jl_uint64_type,
25622564
jl_uint64_type,

src/julia.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1829,8 +1829,8 @@ typedef struct _jl_task_t {
18291829
jl_value_t *result;
18301830
jl_value_t *logstate;
18311831
jl_function_t *start;
1832+
uint16_t sticky; // 0 means this Task can be migrated to a new thread
18321833
uint8_t _state;
1833-
uint8_t sticky; // record whether this Task can be migrated to a new thread
18341834
uint8_t _isexception; // set if `result` is an exception to throw or that we exited with
18351835
uint64_t rngState0; // really rngState[4], but more convenient to split
18361836
uint64_t rngState1;

test/threads_exec.jl

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,7 @@ fib34666(x) =
849849
wait(child)
850850
end
851851
wait(parent)
852-
@test parent.sticky == true
852+
@test parent.sticky == false
853853
end
854854

855855
function jitter_channel(f, k, delay, ntasks, schedule)
@@ -911,3 +911,37 @@ end
911911
@test reproducible_rand(r, 10) == val
912912
end
913913
end
914+
915+
# [ADD TESTS ABOVE THIS COMMENT]
916+
#
917+
# The following tests must be done at the end, since they need to monkey-patch runtime.
918+
const MAX_STICKY_COUNT = 3
919+
@assert MAX_STICKY_COUNT <= typemax(fieldtype(Task, :sticky_count))
920+
Base.is_sticky_count_saturated(t::Task) = t.sticky_count == MAX_STICKY_COUNT
921+
922+
@testset "Saturated stick_count" begin
923+
@testset for nchild in MAX_STICKY_COUNT-1:MAX_STICKY_COUNT+1
924+
local is_sticky_pre, is_sticky_post, sticky_count_pre, sticky_count_post
925+
@sync Threads.@spawn begin
926+
is_sticky_pre = current_task().sticky
927+
sticky_count_pre = current_task().sticky_count
928+
@sync for _ in 1:nchild
929+
@async nothing
930+
end
931+
is_sticky_post = current_task().sticky
932+
sticky_count_post = current_task().sticky_count
933+
end
934+
@test !is_sticky_pre
935+
@test sticky_count_pre == 0
936+
if nchild < MAX_STICKY_COUNT
937+
@test !is_sticky_post
938+
@test sticky_count_post == 0
939+
else
940+
@test is_sticky_post
941+
@test sticky_count_post == MAX_STICKY_COUNT
942+
end
943+
end
944+
end
945+
946+
# Please do not add tests at the end of this file. Pleaes add tests above the above
947+
# comment [ADD TESTS ABOVE THIS COMMENT].

0 commit comments

Comments
 (0)