Skip to content

Commit 2d706db

Browse files
authored
Merge pull request #65 from JuliaComputing/mp/sysimage
[release-0.2] feat: add way to register post-__init__ callbacks
2 parents 9a6ae59 + bc02342 commit 2d706db

File tree

3 files changed

+59
-2
lines changed

3 files changed

+59
-2
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "DataSets"
22
uuid = "c9661210-8a83-48f0-b833-72e62abce419"
33
authors = ["Chris Foster <[email protected]> and contributors"]
4-
version = "0.2.9"
4+
version = "0.2.10"
55

66
[deps]
77
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"

src/DataSets.jl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,56 @@ function __init__()
669669
=# project=proj exception=(exc,catch_backtrace())
670670
end
671671
end
672+
# Call any post-__init__() callbacks that were registered before __init__() was called,
673+
# or had chance to finish.
674+
lock(_PROJECT_INIT_LOCK) do
675+
_PROJECT_INITIALIZED[] = true
676+
for f in _PROJECT_INIT_CALLBACKS
677+
_invoke_init_cb(f)
678+
end
679+
# No need to keep the callbacks around, and maybe the GC can free some memory.
680+
empty!(_PROJECT_INIT_CALLBACKS)
681+
end
682+
end
683+
end
684+
685+
# The register_post_init_callback() can be used to add a callback that will get called
686+
# when DataSets.__init__() has run. Note: if f() throws an error, it does not cause a crash.
687+
#
688+
# This is useful for sysimages where the module is already be loaded (in Base.loaded_modules),
689+
# but __init__() has not been called yet. In particular, this means that other packages' __init__
690+
# functions can be sure that when they call initialization code that affects DataSets (in particular,
691+
# DataSets.PROJECT), then that code runs after __init__() has run.
692+
#
693+
# In the non-sysimage case, DataSets.__init__() would normally have already been called when
694+
# once register_post_init_callback() becomes available, and so in those situations, the callback
695+
# gets called immediately. However, in a system image, DataSets may have to queue up (FIFO) the
696+
# callback functions and wait until DataSets.__init__() has finished.
697+
#
698+
# Since the __init__() functions in sysimages can run in parallel, we use a lock just in case,
699+
# to make sure that two parallel calls would succeed.
700+
const _PROJECT_INIT_LOCK = ReentrantLock()
701+
const _PROJECT_INITIALIZED = Ref{Bool}(false)
702+
const _PROJECT_INIT_CALLBACKS = Base.Callable[]
703+
function register_post_init_callback(f::Base.Callable)
704+
invoke = lock(_PROJECT_INIT_LOCK) do
705+
if _PROJECT_INITIALIZED[]
706+
return true
707+
end
708+
push!(_PROJECT_INIT_CALLBACKS, f)
709+
return false
710+
end
711+
# We'll invoke outside of the lock, so that a long-running f() call
712+
# wouldn't block other calls to register_post_init_callback()
713+
invoke && _invoke_init_cb(f)
714+
return nothing
715+
end
716+
717+
function _invoke_init_cb(f::Base.Callable)
718+
try
719+
Base.invokelatest(f)
720+
catch e
721+
@error "Failed to run init callback: $f" exception = (e, catch_backtrace())
672722
end
673723
end
674724

test/runtests.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@ using ResourceContexts
77

88
using DataSets: FileSystemRoot
99

10-
#-------------------------------------------------------------------------------
10+
@testset "register_post_init_callback" begin
11+
init_was_called = Ref(false)
12+
DataSets.register_post_init_callback() do
13+
init_was_called[] = true
14+
end
15+
@test init_was_called[]
16+
end
17+
1118
@testset "DataSet config" begin
1219
proj = DataSets.load_project("Data.toml")
1320

0 commit comments

Comments
 (0)