@@ -173,6 +173,56 @@ function __init__()
173173 =# project= proj exception= (exc,catch_backtrace ())
174174 end
175175 end
176+ # Call any post-__init__() callbacks that were registered before __init__() was called,
177+ # or had chance to finish.
178+ lock (_PROJECT_INIT_LOCK) do
179+ _PROJECT_INITIALIZED[] = true
180+ for f in _PROJECT_INIT_CALLBACKS
181+ _invoke_init_cb (f)
182+ end
183+ # No need to keep the callbacks around, and maybe the GC can free some memory.
184+ empty! (_PROJECT_INIT_CALLBACKS)
185+ end
186+ end
187+ end
188+
189+ # The register_post_init_callback() can be used to add a callback that will get called
190+ # when DataSets.__init__() has run. Note: if f() throws an error, it does not cause a crash.
191+ #
192+ # This is useful for sysimages where the module is already be loaded (in Base.loaded_modules),
193+ # but __init__() has not been called yet. In particular, this means that other packages' __init__
194+ # functions can be sure that when they call initialization code that affects DataSets (in particular,
195+ # DataSets.PROJECT), then that code runs after __init__() has run.
196+ #
197+ # In the non-sysimage case, DataSets.__init__() would normally have already been called when
198+ # once register_post_init_callback() becomes available, and so in those situations, the callback
199+ # gets called immediately. However, in a system image, DataSets may have to queue up (FIFO) the
200+ # callback functions and wait until DataSets.__init__() has finished.
201+ #
202+ # Since the __init__() functions in sysimages can run in parallel, we use a lock just in case,
203+ # to make sure that two parallel calls would succeed.
204+ const _PROJECT_INIT_LOCK = ReentrantLock ()
205+ const _PROJECT_INITIALIZED = Ref {Bool} (false )
206+ const _PROJECT_INIT_CALLBACKS = Base. Callable[]
207+ function register_post_init_callback (f:: Base.Callable )
208+ invoke = lock (_PROJECT_INIT_LOCK) do
209+ if _PROJECT_INITIALIZED[]
210+ return true
211+ end
212+ push! (_PROJECT_INIT_CALLBACKS, f)
213+ return false
214+ end
215+ # We'll invoke outside of the lock, so that a long-running f() call
216+ # wouldn't block other calls to register_post_init_callback()
217+ invoke && _invoke_init_cb (f)
218+ return nothing
219+ end
220+
221+ function _invoke_init_cb (f:: Base.Callable )
222+ try
223+ Base. invokelatest (f)
224+ catch e
225+ @error " Failed to run init callback: $f " exception = (e, catch_backtrace ())
176226 end
177227end
178228
0 commit comments