Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions src/object.cr
Original file line number Diff line number Diff line change
Expand Up @@ -707,4 +707,55 @@ class Object
ptr.as(Pointer(typeof(crystal_instance_type_id))).value = crystal_instance_type_id
ptr
end

# :nodoc:
#
Comment thread
ysbaddaden marked this conversation as resolved.
# Generates a class getter with a lazy initializer for a thread local
# variable.
#
# Unlike the `ThreadLocal` annotation, the value is guaranteed to always be
# reachable by the GC that won't collect it until it's really unreachable. We
# achieve that by using the `ThreadLocal` annocation (unreachable by GC) to
# speedup direct accesses while still keeping a direct reference on the
# current `Thread` object whose lifetime outlives the actual thread.
#
# For example:
#
# ```
# class Foo
# thread_local(instance : Foo) { Foo.new }
# end
#
# # thread 1:
# Foo.instance # => <Foo:0x76a066968000>
# Foo.instance # => <Foo:0x76a066968000>
#
# # thread 2:
# Foo.instance # => <Foo:0x76a06116ae30>
# ```
macro thread_local(decl, &constructor)
{% raise "The thread_local macro expects a TypeDeclaration" unless decl.is_a?(TypeDeclaration) %}
{% name = decl.var.id %}
{% tls_name = "__tls_#{@type.name.gsub(/:/, "_")}__#{name}".id %}

@[ThreadLocal]
@@{{name}} : {{decl.type}} | Nil
Comment thread
ysbaddaden marked this conversation as resolved.
Outdated

def self.{{name}} : {{decl.type}}
{% if flag?(:android) || flag?(:openbsd) || (flag?(:win32) && flag?(:gnu)) %}
Thread.current.{{tls_name}} ||= {{yield}}
{% else %}
if (value = @@{{name}}).nil?
Thread.current.{{tls_name}} = @@{{name}} = {{yield}}
else
value
end
{% end %}
end

class ::Thread
# :nodoc:
property {{tls_name}} : {{decl.type}} | Nil
end
end
end