Skip to content

Commit 12b58c7

Browse files
committed
Add Base.invoke_in_world
Add internal, experimental function invoke_in_world() in analogy to applylatest(). This makes Core._apply_in_world easier to call, (especially for keyword args) and gives us a place to hang the documentation.
1 parent eb6f3a3 commit 12b58c7

File tree

2 files changed

+45
-5
lines changed

2 files changed

+45
-5
lines changed

base/essentials.jl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,41 @@ function invokelatest(@nospecialize(f), @nospecialize args...; kwargs...)
714714
Core._apply_latest(inner)
715715
end
716716

717+
"""
718+
invoke_in_world(world, f, args...; kwargs...)
719+
720+
Call `f(args...; kwargs...)` in a fixed world age, `world`.
721+
722+
This is useful for infrastructure running in the user's Julia session which is
723+
not part of the user's program. For example, things related to the REPL, editor
724+
support libraries, etc. In these cases it can be useful to prevent unwanted
725+
method invalidation and recompilation latency, and to prevent the user from
726+
breaking supporting infrastructure by mistake.
727+
728+
The current world age can be queried using [`Base.get_world_counter()`](@ref)
729+
and stored for later use within the lifetime of the current Julia session, or
730+
when serializing and reloading the system image.
731+
732+
Technically, `invoke_in_world` will prevent any function called by `f` from
733+
being extended by the user during their Julia session. That is, generic
734+
function method tables seen by `f` (and any functions it calls) will be frozen
735+
as they existed at the given `world` age. In a sense, this is like the opposite
736+
of [`invokelatest`](@ref).
737+
738+
!!! note
739+
It is not valid to store world ages obtained in precompilation for later use.
740+
This is because precompilation generates a "parallel universe" where the
741+
world age refers to system state unrelated to the main Julia session.
742+
"""
743+
function invoke_in_world(world::Integer, @nospecialize(f), @nospecialize args...; kwargs...)
744+
world = convert(UInt, world)
745+
if isempty(kwargs)
746+
return Core._apply_in_world(world, f, args)
747+
end
748+
inner() = f(args...; kwargs...)
749+
Core._apply_in_world(world, inner)
750+
end
751+
717752
# TODO: possibly make this an intrinsic
718753
inferencebarrier(@nospecialize(x)) = Ref{Any}(x)[]
719754

test/worlds.jl

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,15 @@ notify(c26506_1)
200200
wait(c26506_2)
201201
@test result26506[1] == 3
202202

203-
# _apply_in_world builtin
204-
f_aiw(x) = "world one; x=$x"
203+
# invoke_in_world
204+
f_inworld(x) = "world one; x=$x"
205+
g_inworld(x; y) = "world one; x=$x, y=$y"
205206
wc_aiw1 = get_world_counter()
206-
f_aiw(x) = "world two; x=$x"
207+
f_inworld(x) = "world two; x=$x"
208+
g_inworld(x; y) = "world two; x=$x, y=$y"
207209
wc_aiw2 = get_world_counter()
208-
@test Core._apply_in_world(wc_aiw1, f_aiw, (2,)) == "world one; x=2"
209-
@test Core._apply_in_world(wc_aiw2, f_aiw, (2,)) == "world two; x=2"
210+
@test Base.invoke_in_world(wc_aiw1, f_inworld, 2) == "world one; x=2"
211+
@test Base.invoke_in_world(wc_aiw2, f_inworld, 2) == "world two; x=2"
212+
@test Base.invoke_in_world(wc_aiw1, g_inworld, 2, y=3) == "world one; x=2, y=3"
213+
@test Base.invoke_in_world(wc_aiw2, g_inworld, 2, y=3) == "world two; x=2, y=3"
214+

0 commit comments

Comments
 (0)