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
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
@@ -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
@@ -37,6 +37,8 @@ module Crystal
{% if flag?(:tracing) %} Crystal::Tracing.init {% end %}
GC.init

init_runtime

status =
begin
yield
@@ -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
@@ -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"
13 changes: 12 additions & 1 deletion src/crystal/system/thread.cr
Original file line number Diff line number Diff line change
@@ -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
@@ -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?
29 changes: 20 additions & 9 deletions src/crystal/system/unix/pthread.cr
Original file line number Diff line number Diff line change
@@ -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)

@@ -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)
@@ -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
@@ -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
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
@@ -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
27 changes: 19 additions & 8 deletions src/crystal/system/win32/thread.cr
Original file line number Diff line number Diff line change
@@ -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
@@ -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?
@@ -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
10 changes: 9 additions & 1 deletion src/fiber.cr
Original file line number Diff line number Diff line change
@@ -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*