@@ -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
673723end
674724
0 commit comments