diff --git a/src/compiler/crystal/codegen/once.cr b/src/compiler/crystal/codegen/once.cr index ba08f0e7958b..e84dd6b541e0 100644 --- a/src/compiler/crystal/codegen/once.cr +++ b/src/compiler/crystal/codegen/once.cr @@ -5,20 +5,16 @@ class Crystal::CodeGenVisitor def once_init if once_init_fun = typed_fun?(@main_mod, ONCE_INIT) + # legacy (kept for backward compatibility): the compiler must save the + # state returned by __crystal_once_init once_init_fun = check_main_fun ONCE_INIT, once_init_fun - if once_init_fun.type.return_type.void? - call once_init_fun - else - # legacy (kept for backward compatibility): the compiler must save the - # state returned by __crystal_once_init - once_state_global = @main_mod.globals.add(once_init_fun.type.return_type, ONCE_STATE) - once_state_global.linkage = LLVM::Linkage::Internal if @single_module - once_state_global.initializer = once_init_fun.type.return_type.null - - state = call once_init_fun - store state, once_state_global - end + once_state_global = @main_mod.globals.add(once_init_fun.type.return_type, ONCE_STATE) + once_state_global.linkage = LLVM::Linkage::Internal if @single_module + once_state_global.initializer = once_init_fun.type.return_type.null + + state = call once_init_fun + store state, once_state_global end end diff --git a/src/crystal/main.cr b/src/crystal/main.cr index c26199fcdb4a..704153fe13f6 100644 --- a/src/crystal/main.cr +++ b/src/crystal/main.cr @@ -53,9 +53,10 @@ module Crystal # :nodoc: def self.init_runtime : Nil # `__crystal_once` directly or indirectly depends on `Fiber` and `Thread` - # so we explicitly initialize their class vars + # so we explicitly initialize their class vars, then init crystal/once Thread.init Fiber.init + Crystal.once_init end # :nodoc: diff --git a/src/crystal/once.cr b/src/crystal/once.cr index 7b6c7e6ed0f2..87fa9147d56b 100644 --- a/src/crystal/once.cr +++ b/src/crystal/once.cr @@ -1,13 +1,13 @@ -# This file defines two functions expected by the compiler: +# This file defines the `__crystal_once` functions expected by the compiler. It +# is called each time a constant or class variable has to be initialized and is +# its responsibility to verify the initializer is executed only once and to fail +# on recursion. # -# - `__crystal_once_init`: executed only once at the beginning of the program -# and, for the legacy implementation, the result is passed on each call to -# `__crystal_once`. +# It also defines the `__crystal_once_init` function for backward compatibility +# with older compiler releases. It is executed only once at the beginning of the +# program and, for the legacy implementation, the result is passed on each call +# to `__crystal_once`. # -# - `__crystal_once`: called each time a constant or class variable has to be -# initialized and is its responsibility to verify the initializer is executed -# only once and to fail on recursion. - # In multithread mode a mutex is used to avoid race conditions between threads. # # On Win32, `Crystal::System::FileDescriptor#@@reader_thread` spawns a new @@ -28,12 +28,15 @@ {% if flag?(:preview_mt) || flag?(:win32) %} @@once_mutex = uninitialized Mutex - - # :nodoc: - def self.once_mutex=(@@once_mutex : Mutex) - end {% end %} + # :nodoc: + def self.once_init : Nil + {% if flag?(:preview_mt) || flag?(:win32) %} + @@once_mutex = Mutex.new(:reentrant) + {% end %} + end + # :nodoc: # Using @[NoInline] so LLVM optimizes for the hot path (var already # initialized). @@ -67,13 +70,6 @@ end end - # :nodoc: - fun __crystal_once_init : Nil - {% if flag?(:preview_mt) || flag?(:win32) %} - Crystal.once_mutex = Mutex.new(:reentrant) - {% end %} - end - # :nodoc: # # Using `@[AlwaysInline]` allows LLVM to optimize const accesses. Since this @@ -94,37 +90,43 @@ # This implementation uses a global array to store the initialization flag # pointers for each value to find infinite loops and raise an error. - # :nodoc: - class Crystal::OnceState - @rec = [] of Bool* - - @[NoInline] - def once(flag : Bool*, initializer : Void*) - unless flag.value - if @rec.includes?(flag) - raise "Recursion while initializing class variables and/or constants" - end - @rec << flag - - Proc(Nil).new(initializer, Pointer(Void).null).call - flag.value = true - - @rec.pop - end - end - - {% if flag?(:preview_mt) || flag?(:win32) %} - @mutex = Mutex.new(:reentrant) + module Crystal + # :nodoc: + class OnceState + @rec = [] of Bool* @[NoInline] def once(flag : Bool*, initializer : Void*) unless flag.value - @mutex.synchronize do - previous_def + if @rec.includes?(flag) + raise "Recursion while initializing class variables and/or constants" end + @rec << flag + + Proc(Nil).new(initializer, Pointer(Void).null).call + flag.value = true + + @rec.pop end end - {% end %} + + {% if flag?(:preview_mt) || flag?(:win32) %} + @mutex = Mutex.new(:reentrant) + + @[NoInline] + def once(flag : Bool*, initializer : Void*) + unless flag.value + @mutex.synchronize do + previous_def + end + end + end + {% end %} + end + + # :nodoc: + def self.once_init : Nil + end end # :nodoc: