@@ -669,6 +669,56 @@ function __init__()
669
669
=# project= proj exception= (exc,catch_backtrace ())
670
670
end
671
671
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 ())
672
722
end
673
723
end
674
724
0 commit comments