Skip to content

Commit

Permalink
Fix: interpreter issues
Browse files Browse the repository at this point in the history
1. it fails to translate symbol to the enum value
2. it doesn't call `__crystal_once_init`
  • Loading branch information
ysbaddaden committed Jan 21, 2025
1 parent 5728135 commit cabbc9c
Showing 1 changed file with 37 additions and 20 deletions.
57 changes: 37 additions & 20 deletions src/crystal/once.cr
Original file line number Diff line number Diff line change
Expand Up @@ -32,39 +32,43 @@
end

# :nodoc:
#
# Identical to `__crystal_once` but takes a block with possibly closured
# data. Used by `class_[getter|property](declaration, &block)` for example.
@[AlwaysInline]
def self.once(flag : OnceState*, &block) : Nil
def self.once(flag : OnceState*, &) : Nil
return if flag.value.initialized?
once(flag, block.pointer, block.closure_data)
Intrinsics.unreachable unless flag.value.initialized?
once_exec(flag) { yield }
end

# :nodoc:
#
# Using @[NoInline] so LLVM optimizes for the hot path (var already
# initialized).
@[NoInline]
def self.once(flag : OnceState*, initializer : Void*, closure_data : Void*) : Nil
once_exec(flag) { Proc(Nil).new(initializer, closure_data).call }

# safety check, and allows to safely call `Intrinsics.unreachable` in
# `__crystal_once`
unless flag.value.initialized?
System.print_error "BUG: failed to initialize constant or class variable\n"
LibC._exit(1)
end
end

private def self.once_exec(flag, &)
@@once_mutex.synchronize do
case flag.value
in .initialized?
return
in .uninitialized?
flag.value = :processing
Proc(Nil).new(initializer, closure_data).call
flag.value = :initialized
flag.value = OnceState::Processing
yield
flag.value = OnceState::Initialized
in .processing?
raise "Recursion while initializing class variables and/or constants"
end
end

# safety check, and allows to safely call `Intrinsics.unreachable` in
# `__crystal_once`
unless flag.value.initialized?
System.print_error "BUG: failed to initialize constant or class variable\n"
LibC._exit(1)
end
end
end

Expand Down Expand Up @@ -101,10 +105,17 @@
@mutex = Mutex.new(:reentrant)
@rec = [] of Bool*

def once(flag : Bool*, &)
return if flag.value
once_exec(flag) { yield }
end

@[NoInline]
def once(flag : Bool*, initializer : Void*, closure_data : Void*)
return if flag.value
once_exec(flag) { Proc(Nil).new(initializer, closure_data).call }
end

private def once_exec(flag, &)
@mutex.synchronize do
return if flag.value

Expand All @@ -113,7 +124,7 @@
end
@rec << flag

Proc(Nil).new(initializer, closure_data).call
yield
flag.value = true

@rec.pop
Expand All @@ -128,11 +139,9 @@
end

# :nodoc:
@[AlwaysInline]
def self.once(flag : Bool*, &block) : Nil
def self.once(flag : Bool*, &) : Nil
return if flag.value
@@once_state.once(flag, block.pointer, block.closure_data)
Intrinsics.unreachable unless flag.value
@@once_state.once(flag) { yield }
end
end

Expand All @@ -151,3 +160,11 @@
Intrinsics.unreachable unless flag.value
end
{% end %}

{% if flag?(:interpreted) %}
# make sure to initialize the mutex so we can use Crystal.once in the
# class_[getter|property]? macros; the compiler does the call by itself, but
# the interpreter doesn't (it doesn't use __crystal_once to protect the
# initialization of constants and class vars).
__crystal_once_init
{% end %}

0 comments on commit cabbc9c

Please sign in to comment.