Skip to content

Commit

Permalink
Refactor initialization of main Fiber/Thread
Browse files Browse the repository at this point in the history
Remove ThreadLocal usage for current thread. Initialize a main thread globally. Let that initialize the main fiber. Initialize main fiber *of each new thread* when thread starts. Clarify intent different of constructors.

Let fiber know if it's currently running in any thread by Fiber#thread.
  • Loading branch information
bcardiff committed Sep 19, 2018
1 parent f5970ec commit aedde16
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 48 deletions.
30 changes: 18 additions & 12 deletions src/fiber.cr
Expand Up @@ -22,9 +22,11 @@ class Fiber
protected property stack_bottom : Void*
protected property next_fiber : Fiber?
protected property prev_fiber : Fiber?
protected property thread : Thread?
property name : String?

def initialize(@name : String? = nil, &@proc : ->)
@thread = nil
@stack = Fiber.allocate_stack
@stack_bottom = @stack + STACK_SIZE
fiber_main = ->(f : Fiber) { f.run }
Expand Down Expand Up @@ -82,15 +84,23 @@ class Fiber
end
end

def initialize
# Used to initialize the crystal object of the
# existing main fiber in the *thread*.
def initialize(@thread : Thread)
@proc = Proc(Void).new { }
@stack = Pointer(Void).null
@stack_top = _fiber_get_stack_top
@stack_bottom = GC.stack_bottom
@name = "main"

@@list_mutex.synchronize do
@@first_fiber = @@last_fiber = self
@prev_fiber = nil
if last_fiber = @@last_fiber
@prev_fiber = last_fiber
last_fiber.next_fiber = @@last_fiber = self
else
@@first_fiber = @@last_fiber = self
end
end
end

Expand Down Expand Up @@ -267,8 +277,12 @@ class Fiber
end

def resume : Nil
# The purpose of this method is to suspend a fiber (current) and give control back
# to another one (self).
current, Thread.current.current_fiber = Thread.current.current_fiber, self
GC.stack_bottom = @stack_bottom
# current will be suspended, therefore it won't be assigned to any execution thread.
current.thread = nil
self.thread = Thread.current
{% if flag?(:aarch64) %}
Fiber.switch_stacks(pointerof(current.@stack_top), @stack_top)
{% else %}
Expand Down Expand Up @@ -316,14 +330,6 @@ class Fiber
GC.push_stack @stack_top, @stack_bottom
end

@@root = new

def self.root : self
@@root
end

Thread.current.current_fiber = root

def self.current : self
Thread.current.current_fiber
end
Expand All @@ -332,7 +338,7 @@ class Fiber
GC.before_collect do
fiber = @@first_fiber
while fiber
fiber.push_gc_roots unless fiber == Thread.current.current_fiber
fiber.push_gc_roots if fiber.thread.nil?
fiber = fiber.next_fiber
end
end
Expand Down
2 changes: 2 additions & 0 deletions src/lib_c/x86_64-macosx-darwin/c/pthread.cr
Expand Up @@ -8,7 +8,9 @@ lib LibC
fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int
fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int
fun pthread_detach(x0 : PthreadT) : Int
fun pthread_equal(thread1 : PthreadT, thread2 : PthreadT) : Int
fun pthread_join(x0 : PthreadT, x1 : Void**) : Int
fun pthread_main_np : Int
fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int
fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int
fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int
Expand Down
76 changes: 40 additions & 36 deletions src/thread.cr
Expand Up @@ -11,11 +11,9 @@ class Thread
@th : LibC::PthreadT?
@exception : Exception?
@detached = false

property current_fiber
property! current_fiber : Fiber?

def initialize(&@func : ->)
@current_fiber = uninitialized Fiber
@@mutex.synchronize do
@@threads << self
@th = th = uninitialized LibC::PthreadT
Expand All @@ -32,13 +30,17 @@ class Thread
end
end

# Used to initialize the crystal object of the
# existing main thread.
# Note *the* thread initialized with this constructor
# will not call `Thread#start`.
def initialize
@current_fiber = uninitialized Fiber
@func = ->{}

@@mutex.synchronize do
@@threads << self
@th = LibC.pthread_self
@current_fiber = Fiber.new(self)
end
end

Expand All @@ -60,42 +62,21 @@ class Thread
# thread local storage (eg: OpenBSD)
@@threads = Set(Thread).new

{% if flag?(:openbsd) %}
@@main = new

def self.current : Thread
if LibC.pthread_main_np == 1
return @@main
end

current_thread_id = LibC.pthread_self

@@mutex.synchronize do
current_thread = @@threads.find do |thread|
LibC.pthread_equal(thread.id, current_thread_id) != 0
end
end
@@main = new

raise "Error: failed to find current thread" unless current_thread
current_thread
end

protected def id
@th.not_nil!
def self.current : Thread
if main?
return @@main
else
find_current_by_id
end
{% else %}
@[ThreadLocal]
@@current = new

def self.current
@@current
end
{% end %}
end

protected def start
{% unless flag?(:openbsd) %}
@@current = self
{% end %}
# Initialize main fiber of thread once the thread has started.
# Before the thread actually starts there is no fiber information
@current_fiber ||= Fiber.new(self)

begin
@func.call
rescue ex
Expand All @@ -104,4 +85,27 @@ class Thread
@@threads.delete(self)
end
end

# Checks if the current thread is the main thread
def self.main?
LibC.pthread_main_np == 1
end

# Find the current thread object with a linear search among all threads
protected def self.find_current_by_id : Thread
@@mutex.synchronize do
current_thread_id = LibC.pthread_self

current_thread = @@threads.find do |thread|
LibC.pthread_equal(thread.id, current_thread_id) != 0
end

raise "Error: failed to find current thread" unless current_thread
current_thread
end
end

protected def id
@th.not_nil!
end
end

0 comments on commit aedde16

Please sign in to comment.