Skip to content

Commit f4d43ab

Browse files
authored
Merge pull request #62 from JuliaParallel/precompile
Scoped contexts and a proper precompilation workload
2 parents 5a55990 + 311f2ed commit f4d43ab

15 files changed

Lines changed: 321 additions & 263 deletions

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ uuid = "fab6aee4-877b-4bac-a744-3eca44acbb6f"
33
version = "1.2.0"
44

55
[deps]
6+
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
67
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
78
ScopedValues = "7e506255-f358-4e82-b7e4-beb19740aa63"
89
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
@@ -19,6 +20,7 @@ Aqua = "0.8"
1920
Distributed = "1"
2021
LibSSH = "0.7"
2122
LinearAlgebra = "1"
23+
PrecompileTools = "1"
2224
Random = "1"
2325
Revise = "3.7.0"
2426
ScopedValues = "1.6.0"

docs/src/_changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This documents notable changes in DistributedNext.jl. The format is based on
1414
single struct ([#61]). This should not be a user-visible change, but of course
1515
it's possible that some things slipped through the cracks so please open an
1616
issue if you encounter any bugs.
17+
- A precompilation workload was added to improve TTFX ([#62]).
1718

1819
## [v1.2.0] - 2026-03-21
1920

ext/ReviseExt.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ module ReviseExt
33
import DistributedNext
44
import DistributedNext: myid, workers, remotecall
55

6-
import Revise
6+
using PrecompileTools: @recompile_invalidations
77

8+
# Sadly Revise causes quite a few invalidations. TODO: make DistributedNext more
9+
# resistant to invalidations.
10+
@recompile_invalidations import Revise
811

912
struct DistributedNextWorker <: Revise.AbstractWorker
1013
id::Int

src/DistributedNext.jl

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ function _check_distributed_active()
8888
return false
8989
end
9090

91-
if isdefined(Base.loaded_modules[distributed_pkgid].LPROC, :cookie) && CTX.inited[]
91+
if isdefined(Base.loaded_modules[distributed_pkgid].LPROC, :cookie) && CTX[].inited[]
9292
@warn "DistributedNext has detected that the Distributed stdlib may be in use. Be aware that these libraries are not compatible, you should use either one or the other."
9393
return true
9494
else
@@ -123,7 +123,7 @@ Base.lock(l::Lockable) = lock(l.lock)
123123
Base.trylock(l::Lockable) = trylock(l.lock)
124124
Base.unlock(l::Lockable) = unlock(l.lock)
125125

126-
next_ref_id() = Threads.atomic_add!(CTX.ref_id, 1)
126+
next_ref_id() = Threads.atomic_add!(CTX[].ref_id, 1)
127127

128128
struct RRID
129129
whence::Int
@@ -146,12 +146,11 @@ include("macros.jl") # @spawn and friends
146146
include("workerpool.jl")
147147
include("pmap.jl")
148148
include("managers.jl") # LocalManager and SSHManager
149-
include("precompile.jl")
150149

151150
# Bundles all mutable global state for a distributed cluster into a single
152-
# object. Currently a single global instance (`CTX`) is used, but multiple
153-
# independent clusters could be supported in the future.
154-
@kwdef struct ClusterContext
151+
# object. The active context is accessed via the `CTX` ScopedValue, allowing
152+
# multiple independent clusters to coexist in different task scopes.
153+
@kwdef mutable struct ClusterContext
155154
# Process identity
156155
lproc::LocalProcess = LocalProcess()
157156
role::Ref{Symbol} = Ref{Symbol}(:master)
@@ -204,11 +203,42 @@ include("precompile.jl")
204203
# Scoped value for exited callback pid
205204
exited_callback_pid::ScopedValue{Int} = ScopedValue(-1)
206205

206+
# GC messages task
207+
shutting_down::Threads.Atomic{Bool} = Threads.Atomic{Bool}(false)
208+
gc_msgs_task::Union{Task, Nothing} = nothing
209+
207210
# Stdlib watcher
208-
stdlib_watcher_timer::Ref{Union{Timer, Nothing}} = Ref{Union{Timer, Nothing}}(nothing)
211+
stdlib_watcher_timer::Union{Timer, Nothing} = nothing
212+
end
213+
214+
function ClusterContext(f::Base.Callable; kwargs...)
215+
ctx = ClusterContext(; kwargs...)
216+
ret = @with CTX => ctx f()
217+
close(ctx)
218+
219+
return ret
209220
end
210221

211-
const CTX = ClusterContext()
222+
function Base.close(ctx::ClusterContext)
223+
ctx.shutting_down[] = true
224+
if !isnothing(ctx.gc_msgs_task)
225+
@lock ctx.any_gc_flag notify(ctx.any_gc_flag)
226+
wait(ctx.gc_msgs_task::Task)
227+
end
228+
229+
if !isnothing(ctx.stdlib_watcher_timer)
230+
close(ctx.stdlib_watcher_timer::Timer)
231+
end
232+
233+
# Close all tracked sockets
234+
@lock ctx.map_sock_wrkr for sock in keys(ctx.map_sock_wrkr[])
235+
close(sock)
236+
end
237+
end
238+
239+
const CTX = ScopedValue(ClusterContext())
240+
241+
include("precompile.jl")
212242

213243
function __init__()
214244
init_parallel()
@@ -219,13 +249,14 @@ function __init__()
219249
# cluster cookie has been set, which is most likely to have been done
220250
# through Distributed.init_multi() being called by Distributed.addprocs() or
221251
# something.
222-
CTX.stdlib_watcher_timer[] = Timer(0; interval=1) do timer
252+
CTX[].stdlib_watcher_timer = Timer(0; interval=1) do timer
223253
if _check_distributed_active()
224254
close(timer)
225255
end
226256
end
227-
atexit(() -> close(CTX.stdlib_watcher_timer[]))
228257
end
258+
259+
atexit(() -> close(CTX[]))
229260
end
230261

231262
end

0 commit comments

Comments
 (0)