Skip to content

Commit 6a05297

Browse files
JeffBezansonKristofferC
authored andcommitted
give finalizers their own RNG state (#45212)
fixes #42752 (cherry picked from commit 85b895b)
1 parent 3dc152b commit 6a05297

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
@@ -371,6 +371,15 @@ static void jl_gc_run_finalizers_in_list(jl_task_t *ct, arraylist_t *list)
371371
JL_GC_POP();
372372
}
373373

374+
static uint64_t finalizer_rngState[4];
375+
376+
void jl_rng_split(uint64_t to[4], uint64_t from[4]);
377+
378+
JL_DLLEXPORT void jl_gc_init_finalizer_rng_state(void)
379+
{
380+
jl_rng_split(finalizer_rngState, jl_current_task->rngState);
381+
}
382+
374383
static void run_finalizers(jl_task_t *ct)
375384
{
376385
// Racy fast path:
@@ -392,9 +401,16 @@ static void run_finalizers(jl_task_t *ct)
392401
}
393402
jl_atomic_store_relaxed(&jl_gc_have_pending_finalizers, 0);
394403
arraylist_new(&to_finalize, 0);
404+
405+
uint64_t save_rngState[4];
406+
memcpy(&save_rngState[0], &ct->rngState[0], sizeof(save_rngState));
407+
jl_rng_split(ct->rngState, finalizer_rngState);
408+
395409
// This releases the finalizers lock.
396410
jl_gc_run_finalizers_in_list(ct, &copied_list);
397411
arraylist_free(&copied_list);
412+
413+
memcpy(&ct->rngState[0], &save_rngState[0], sizeof(save_rngState));
398414
}
399415

400416
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
@@ -730,7 +730,7 @@ uint64_t jl_genrandom(uint64_t rngState[4]) JL_NOTSAFEPOINT
730730
return res;
731731
}
732732

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

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

stdlib/Random/src/RNGs.jl

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

376376
function __init__()
377377
seed!(GLOBAL_RNG)
378+
ccall(:jl_gc_init_finalizer_rng_state, Cvoid, ())
378379
end
379380

380381

stdlib/Random/test/runtests.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,3 +975,26 @@ end
975975
@test minimum(m) >= 0.094
976976
@test maximum(m) <= 0.106
977977
end
978+
979+
# issue #42752
980+
# test that running finalizers that launch tasks doesn't change RNG stream
981+
function f42752(do_gc::Bool, cell = (()->Any[[]])())
982+
a = rand()
983+
if do_gc
984+
finalizer(cell[1]) do _
985+
@async nothing
986+
end
987+
cell[1] = nothing
988+
GC.gc()
989+
end
990+
b = rand()
991+
(a, b)
992+
end
993+
guardseed() do
994+
for _ in 1:4
995+
Random.seed!(1)
996+
val = f42752(false)
997+
Random.seed!(1)
998+
@test f42752(true) === val
999+
end
1000+
end

0 commit comments

Comments
 (0)