Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicit init of Thread and Fiber class variables #15369

Merged
Show file tree
Hide file tree
Changes from all commits
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
17 changes: 15 additions & 2 deletions src/crystal/main.cr
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ end
module Crystal
# Defines the main routine run by normal Crystal programs:
#
# - Initializes the GC
# - Initializes runtime requirements (GC, ...)
# - Invokes the given *block*
# - Handles unhandled exceptions
# - Invokes `at_exit` handlers
Expand Down Expand Up @@ -37,6 +37,8 @@ module Crystal
{% if flag?(:tracing) %} Crystal::Tracing.init {% end %}
GC.init

init_runtime

status =
begin
yield
Expand All @@ -48,6 +50,14 @@ module Crystal
exit(status, ex)
end

# :nodoc:
def self.init_runtime : Nil
# `__crystal_once` directly or indirectly depends on `Fiber` and `Thread`
# so we explicitly initialize their class vars
Thread.init
Fiber.init
end

# :nodoc:
def self.exit(status : Int32, exception : Exception?) : Int32
status = Crystal::AtExitHandlers.run status, exception
Expand Down Expand Up @@ -130,7 +140,10 @@ fun main(argc : Int32, argv : UInt8**) : Int32
Crystal.main(argc, argv)
end

{% if flag?(:win32) %}
{% if flag?(:interpreted) %}
# the interpreter doesn't call Crystal.main(&)
Crystal.init_runtime
{% elsif flag?(:win32) %}
require "./system/win32/wmain"
{% elsif flag?(:wasi) %}
require "./system/wasi/main"
Expand Down
13 changes: 12 additions & 1 deletion src/crystal/system/thread.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
module Crystal::System::Thread
# alias Handle

# def self.init : Nil

# def self.new_handle(thread_obj : ::Thread) : Handle

# def self.current_handle : Handle
Expand Down Expand Up @@ -48,7 +50,16 @@ class Thread
include Crystal::System::Thread

# all thread objects, so the GC can see them (it doesn't scan thread locals)
protected class_getter(threads) { Thread::LinkedList(Thread).new }
@@threads = uninitialized Thread::LinkedList(Thread)

protected def self.threads : Thread::LinkedList(Thread)
@@threads
end

def self.init : Nil
@@threads = Thread::LinkedList(Thread).new
Crystal::System::Thread.init
end

@system_handle : Crystal::System::Thread::Handle
@exception : Exception?
Expand Down
29 changes: 20 additions & 9 deletions src/crystal/system/unix/pthread.cr
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ module Crystal::System::Thread
raise RuntimeError.from_os_error("pthread_create", Errno.new(ret)) unless ret == 0
end

def self.init : Nil
{% if flag?(:musl) %}
@@main_handle = current_handle
{% elsif flag?(:openbsd) || flag?(:android) %}
ret = LibC.pthread_key_create(out current_key, nil)
raise RuntimeError.from_os_error("pthread_key_create", Errno.new(ret)) unless ret == 0
@@current_key = current_key
{% end %}
end

def self.thread_proc(data : Void*) : Void*
th = data.as(::Thread)

Expand Down Expand Up @@ -53,13 +63,7 @@ module Crystal::System::Thread
# Android appears to support TLS to some degree, but executables fail with
# an underaligned TLS segment, see https://github.com/crystal-lang/crystal/issues/13951
{% if flag?(:openbsd) || flag?(:android) %}
@@current_key : LibC::PthreadKeyT

@@current_key = begin
ret = LibC.pthread_key_create(out current_key, nil)
raise RuntimeError.from_os_error("pthread_key_create", Errno.new(ret)) unless ret == 0
current_key
end
@@current_key = uninitialized LibC::PthreadKeyT

def self.current_thread : ::Thread
if ptr = LibC.pthread_getspecific(@@current_key)
Expand All @@ -84,11 +88,18 @@ module Crystal::System::Thread
end
{% else %}
@[ThreadLocal]
class_property current_thread : ::Thread { ::Thread.new }
@@current_thread : ::Thread?

def self.current_thread : ::Thread
@@current_thread ||= ::Thread.new
end

def self.current_thread? : ::Thread?
@@current_thread
end

def self.current_thread=(@@current_thread : ::Thread)
end
{% end %}

def self.sleep(time : ::Time::Span) : Nil
Expand Down Expand Up @@ -169,7 +180,7 @@ module Crystal::System::Thread
end

{% if flag?(:musl) %}
@@main_handle : Handle = current_handle
@@main_handle = uninitialized Handle

def self.current_is_main?
current_handle == @@main_handle
Expand Down
14 changes: 13 additions & 1 deletion src/crystal/system/wasi/thread.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
module Crystal::System::Thread
alias Handle = Nil

def self.init : Nil
end

def self.new_handle(thread_obj : ::Thread) : Handle
raise NotImplementedError.new("Crystal::System::Thread.new_handle")
end
Expand All @@ -13,7 +16,16 @@ module Crystal::System::Thread
raise NotImplementedError.new("Crystal::System::Thread.yield_current")
end

class_property current_thread : ::Thread { ::Thread.new }
def self.current_thread : ::Thread
@@current_thread ||= ::Thread.new
end

def self.current_thread? : ::Thread?
@@current_thread
end

def self.current_thread=(@@current_thread : ::Thread)
end

def self.sleep(time : ::Time::Span) : Nil
req = uninitialized LibC::Timespec
Expand Down
27 changes: 19 additions & 8 deletions src/crystal/system/win32/thread.cr
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ module Crystal::System::Thread
)
end

def self.init : Nil
{% if flag?(:gnu) %}
current_key = LibC.TlsAlloc
if current_key == LibC::TLS_OUT_OF_INDEXES
Crystal::System.panic("TlsAlloc()", WinError.value)
end
@@current_key = current_key
{% end %}
end

def self.thread_proc(data : Void*) : LibC::UInt
# ensure that even in the case of stack overflow there is enough reserved
# stack space for recovery (for the main thread this is done in
Expand Down Expand Up @@ -47,13 +57,7 @@ module Crystal::System::Thread

# MinGW does not support TLS correctly
{% if flag?(:gnu) %}
@@current_key : LibC::DWORD = begin
current_key = LibC.TlsAlloc
if current_key == LibC::TLS_OUT_OF_INDEXES
Crystal::System.panic("TlsAlloc()", WinError.value)
end
current_key
end
@@current_key = uninitialized LibC::DWORD

def self.current_thread : ::Thread
th = current_thread?
Expand Down Expand Up @@ -82,11 +86,18 @@ module Crystal::System::Thread
end
{% else %}
@[ThreadLocal]
class_property current_thread : ::Thread { ::Thread.new }
@@current_thread : ::Thread?

def self.current_thread : ::Thread
@@current_thread ||= ::Thread.new
end

def self.current_thread? : ::Thread?
@@current_thread
end

def self.current_thread=(@@current_thread : ::Thread)
end
{% end %}

def self.sleep(time : ::Time::Span) : Nil
Expand Down
10 changes: 9 additions & 1 deletion src/fiber.cr
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,16 @@ end
# notifications that IO is ready or a timeout reached. When a fiber can be woken,
# the event loop enqueues it in the scheduler
class Fiber
@@fibers = uninitialized Thread::LinkedList(Fiber)

protected def self.fibers : Thread::LinkedList(Fiber)
@@fibers
end

# :nodoc:
protected class_getter(fibers) { Thread::LinkedList(Fiber).new }
def self.init : Nil
@@fibers = Thread::LinkedList(Fiber).new
end

@context : Context
@stack : Void*
Expand Down
Loading