Skip to content

Commit 85b895b

Browse files
authored
give finalizers their own RNG state (#45212)
fixes #42752
1 parent 79bba8a commit 85b895b

File tree

4 files changed

+46
-6
lines changed

4 files changed

+46
-6
lines changed

src/gc.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,15 @@ static void jl_gc_run_finalizers_in_list(jl_task_t *ct, arraylist_t *list)
375375
ct->sticky = sticky;
376376
}
377377

378+
static uint64_t finalizer_rngState[4];
379+
380+
void jl_rng_split(uint64_t to[4], uint64_t from[4]);
381+
382+
JL_DLLEXPORT void jl_gc_init_finalizer_rng_state(void)
383+
{
384+
jl_rng_split(finalizer_rngState, jl_current_task->rngState);
385+
}
386+
378387
static void run_finalizers(jl_task_t *ct)
379388
{
380389
// Racy fast path:
@@ -396,9 +405,16 @@ static void run_finalizers(jl_task_t *ct)
396405
}
397406
jl_atomic_store_relaxed(&jl_gc_have_pending_finalizers, 0);
398407
arraylist_new(&to_finalize, 0);
408+
409+
uint64_t save_rngState[4];
410+
memcpy(&save_rngState[0], &ct->rngState[0], sizeof(save_rngState));
411+
jl_rng_split(ct->rngState, finalizer_rngState);
412+
399413
// This releases the finalizers lock.
400414
jl_gc_run_finalizers_in_list(ct, &copied_list);
401415
arraylist_free(&copied_list);
416+
417+
memcpy(&ct->rngState[0], &save_rngState[0], sizeof(save_rngState));
402418
}
403419

404420
JL_DLLEXPORT void jl_gc_run_pending_finalizers(jl_task_t *ct)

src/task.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,7 @@ uint64_t jl_genrandom(uint64_t rngState[4]) JL_NOTSAFEPOINT
729729
return res;
730730
}
731731

732-
static void rng_split(jl_task_t *from, jl_task_t *to) JL_NOTSAFEPOINT
732+
void jl_rng_split(uint64_t to[4], uint64_t from[4]) JL_NOTSAFEPOINT
733733
{
734734
/* TODO: consider a less ad-hoc construction
735735
Ideally we could just use the output of the random stream to seed the initial
@@ -747,10 +747,10 @@ static void rng_split(jl_task_t *from, jl_task_t *to) JL_NOTSAFEPOINT
747747
0x3688cf5d48899fa7 == hash(UInt(3))|0x01
748748
0x867b4bb4c42e5661 == hash(UInt(4))|0x01
749749
*/
750-
to->rngState[0] = 0x02011ce34bce797f * jl_genrandom(from->rngState);
751-
to->rngState[1] = 0x5a94851fb48a6e05 * jl_genrandom(from->rngState);
752-
to->rngState[2] = 0x3688cf5d48899fa7 * jl_genrandom(from->rngState);
753-
to->rngState[3] = 0x867b4bb4c42e5661 * jl_genrandom(from->rngState);
750+
to[0] = 0x02011ce34bce797f * jl_genrandom(from);
751+
to[1] = 0x5a94851fb48a6e05 * jl_genrandom(from);
752+
to[2] = 0x3688cf5d48899fa7 * jl_genrandom(from);
753+
to[3] = 0x867b4bb4c42e5661 * jl_genrandom(from);
754754
}
755755

756756
JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion_future, size_t ssize)
@@ -790,7 +790,7 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion
790790
// Inherit logger state from parent task
791791
t->logstate = ct->logstate;
792792
// Fork task-local random state from parent
793-
rng_split(ct, t);
793+
jl_rng_split(t->rngState, ct->rngState);
794794
// there is no active exception handler available on this stack yet
795795
t->eh = NULL;
796796
t->sticky = 1;

stdlib/Random/src/RNGs.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ end
388388

389389
function __init__()
390390
seed!(GLOBAL_RNG)
391+
ccall(:jl_gc_init_finalizer_rng_state, Cvoid, ())
391392
end
392393

393394

stdlib/Random/test/runtests.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,3 +994,26 @@ end
994994
@test minimum(m) >= 0.094
995995
@test maximum(m) <= 0.106
996996
end
997+
998+
# issue #42752
999+
# test that running finalizers that launch tasks doesn't change RNG stream
1000+
function f42752(do_gc::Bool, cell = (()->Any[[]])())
1001+
a = rand()
1002+
if do_gc
1003+
finalizer(cell[1]) do _
1004+
@async nothing
1005+
end
1006+
cell[1] = nothing
1007+
GC.gc()
1008+
end
1009+
b = rand()
1010+
(a, b)
1011+
end
1012+
guardseed() do
1013+
for _ in 1:4
1014+
Random.seed!(1)
1015+
val = f42752(false)
1016+
Random.seed!(1)
1017+
@test f42752(true) === val
1018+
end
1019+
end

0 commit comments

Comments
 (0)