diff --git a/src/crystal/main.cr b/src/crystal/main.cr index 9b4384f16a8c..c26199fcdb4a 100644 --- a/src/crystal/main.cr +++ b/src/crystal/main.cr @@ -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" diff --git a/src/crystal/system/thread.cr b/src/crystal/system/thread.cr index 92136d1f3989..878a27e4c578 100644 --- a/src/crystal/system/thread.cr +++ b/src/crystal/system/thread.cr @@ -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? diff --git a/src/crystal/system/unix/pthread.cr b/src/crystal/system/unix/pthread.cr index 98629a70fbb6..e91990689084 100644 --- a/src/crystal/system/unix/pthread.cr +++ b/src/crystal/system/unix/pthread.cr @@ -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 diff --git a/src/crystal/system/wasi/thread.cr b/src/crystal/system/wasi/thread.cr index 1e8f6957d526..d103c7d9fc44 100644 --- a/src/crystal/system/wasi/thread.cr +++ b/src/crystal/system/wasi/thread.cr @@ -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 diff --git a/src/crystal/system/win32/thread.cr b/src/crystal/system/win32/thread.cr index 9cb60f01ced8..2ff7ca438d87 100644 --- a/src/crystal/system/win32/thread.cr +++ b/src/crystal/system/win32/thread.cr @@ -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 diff --git a/src/fiber.cr b/src/fiber.cr index b27c34fd6b36..3d2fe9a89797 100644 --- a/src/fiber.cr +++ b/src/fiber.cr @@ -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*