How to avoid multiple __init__ calls?

I use project wide config used by many modules. And sometimes I observe its __init__ function called many times, I assume during compilation.

How to avoid that is the proper way to use ccall(:jl_generating_output, Cint, ()) == 1 && return nothing?

The config

module Config

now::Date = Date(0)

const clear_cache_cbs = Function[]
clear_cache!() = for cb in clear_cache_cbs; cb() end
clear_cache!(cb::Function) = push!(clear_cache_cbs, cb)

set_now(d::Date) = begin
  Config.now == d && return
  Config.now = d
  clear_cache!()
  @info "config" (; now=Config.now)
end

add_load_path!(p) = begin
  p = realpath(p); p ∉ LOAD_PATH && push!(LOAD_PATH, p)
end

__init__() = begin
  # ccall(:jl_generating_output, Cint, ()) == 1 && return nothing

  set_now(haskey(ENV, "now") ? Date(ENV["now"]) : Date(Dates.now(UTC)))
  haskey(ENV, "now") && @warn "config" (; now=Config.now)

  add_load_path!("$(@__DIR__)/reports")
  add_load_path!("$(@__DIR__)/lib")
  add_load_path!("$(@__DIR__)/SV")
  add_load_path!("$(@__DIR__)/options")
  add_load_path!("$(@__DIR__)/garch")
end

end

I think this needs more context, e.g. on how (by which mechanism) it is used by multiple modules and what you want to achieve with the config. Manipulating LOAD_PATH from an __init__ function looks scary to me. There have to be better alternatives.

The . added to LOAD_PATH in julia global startup file, and Revise.

The main.jl then can look like:

import Config # Always loaded first, it defines project paths and other settings
import SVJ # located in ./SV/SVJ.jl

println("hi")

Any module will be automatically loaded on demand and changes tracked and reloaded.

Config also imported (imported, not included) by many other modules, like SVJ.jl.

Everything loaded and reloaded automatically. No need to explicitly use include, define packages etc.

It works well, but I assume during compilation Julia reloads modules in some strange way, and Config.__init__ called multiple times, I made it idempotent so it already works, but would like to fix it properly.

I would say that the proper fix is to use include, define packages, etc.

But if you absolutely don’t want to do that, you can investigate whether this new Julia 1.12 feature is of any help to you:

OncePerProcess{T}(init::Function)() -> T

Calling a OncePerProcess object returns a value of type T by running the function initializer exactly once per process. All concurrent and future calls in the same process will return exactly the same value. This is useful in code that will be precompiled, as it allows setting up caches or other state which won’t get serialized.