Description
A class_getter
with a block usually runs exactly once:
module Foo
class_getter(x : Int32) { 1 }
# same as:
def self.x : Int32
if (x = @@x).nil?
@@x = 1
else
x
end
end
end
However, if the current fiber is ever suspended, the block might be run multiple times:
module Foo
class_getter x : Int32 do
puts "Foo.x" # this runs 5 times
Fiber.yield
1
end
end
ch = Channel(Int32).new
5.times { spawn { ch.send Foo.x } }
5.times { ch.receive }
And if -Dpreview_mt
is in effect, the block also gets run more than once, even without the Fiber.yield
.
Often, Foo.x
represents some kind of cached object that might be expensive to compute, like #14891 and the Unicode data tables; if we also disregard the block's return value, then this would include the various interrupt handlers guarded by Atomic::Flag
s too. There should be an easier way in the standard library to ensure this block is really run exactly once, regardless of the degree of concurrent access.
Constants are one alternative, but whether they run at program startup or on first access is rather opaque, and also they are slightly broken, such as in #13054.
This could apply to class_property
as well, and less likely to the instance variants getter
and property
.
Metadata
Assignees
Type
Projects
Status
In Progress