Skip to content

Commit 9d9d079

Browse files
committed
Add Fiber::Stack
Holds the stack limits and whether the stack can be reused (i.e. released back into Fiber::StackPool). Also abstracts accessing the first addressable pointer with 16-bytes alignment (as required by most architectures) to pass to `makecontext`.
1 parent a185f4a commit 9d9d079

File tree

7 files changed

+54
-34
lines changed

7 files changed

+54
-34
lines changed

src/crystal/scheduler.cr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class Crystal::Scheduler
124124
{% elsif flag?(:interpreted) %}
125125
# No need to change the stack bottom!
126126
{% else %}
127-
GC.set_stackbottom(fiber.@stack_bottom)
127+
GC.set_stackbottom(fiber.@stack.bottom)
128128
{% end %}
129129

130130
current, @thread.current_fiber = @thread.current_fiber, fiber

src/crystal/system/unix/signal.cr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ module Crystal::System::Signal
183183

184184
is_stack_overflow =
185185
begin
186-
stack_top = Pointer(Void).new(::Fiber.current.@stack.address - 4096)
187-
stack_bottom = ::Fiber.current.@stack_bottom
186+
stack_top = Pointer(Void).new(::Fiber.current.@stack.pointer.address - 4096)
187+
stack_bottom = ::Fiber.current.@stack.bottom
188188
stack_top <= addr < stack_bottom
189189
rescue e
190190
Crystal::System.print_error "Error while trying to determine if a stack overflow has occurred. Probable memory corruption\n"

src/fiber.cr

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require "crystal/system/thread_linked_list"
22
require "crystal/print_buffered"
33
require "./fiber/context"
4+
require "./fiber/stack"
45

56
# :nodoc:
67
@[NoInline]
@@ -56,12 +57,11 @@ class Fiber
5657
end
5758

5859
@context : Context
59-
@stack : Void*
60+
@stack : Stack
6061
@resume_event : Crystal::EventLoop::Event?
6162
@timeout_event : Crystal::EventLoop::Event?
6263
# :nodoc:
6364
property timeout_select_action : Channel::TimeoutAction?
64-
protected property stack_bottom : Void*
6565

6666
# The name of the fiber, used as internal reference.
6767
property name : String?
@@ -98,35 +98,22 @@ class Fiber
9898
#
9999
# *name* is an optional and used only as an internal reference.
100100
def self.new(name : String? = nil, &proc : ->)
101-
stack, stack_bottom =
102-
{% if flag?(:interpreted) %}
103-
{Pointer(Void).null, Pointer(Void).null}
104-
{% else %}
105-
Crystal::Scheduler.stack_pool.checkout
106-
{% end %}
107-
new(name, stack, stack_bottom, &proc)
101+
new(name, Stack.new, &proc)
108102
end
109103

110104
# :nodoc:
111-
def initialize(@name : String?, @stack : Void*, @stack_bottom : Void*, &@proc : ->)
105+
def initialize(@name : String?, @stack : Stack, &@proc : ->)
112106
@context = Context.new
113107

114108
fiber_main = ->(f : Fiber) { f.run }
115-
116-
# point to first addressable pointer on the stack (@stack_bottom points past
117-
# the stack because the stack grows down):
118-
stack_ptr = @stack_bottom - sizeof(Void*)
119-
120-
# align the stack pointer to 16 bytes:
121-
stack_ptr = Pointer(Void*).new(stack_ptr.address & ~0x0f_u64)
122-
109+
stack_ptr = @stack.first_addressable_pointer
123110
makecontext(stack_ptr, fiber_main)
124111

125112
Fiber.fibers.push(self)
126113
end
127114

128115
# :nodoc:
129-
def initialize(@stack : Void*, thread)
116+
def initialize(stack : Void*, thread)
130117
@proc = Proc(Void).new { }
131118

132119
# TODO: should creating a new context for the main fiber also be platform specific?
@@ -138,7 +125,10 @@ class Fiber
138125
{% else %}
139126
Context.new(_fiber_get_stack_top)
140127
{% end %}
141-
thread.gc_thread_handler, @stack_bottom = GC.current_thread_stack_bottom
128+
129+
thread.gc_thread_handler, stack_bottom = GC.current_thread_stack_bottom
130+
@stack = Stack.new(stack, stack_bottom)
131+
142132
@name = "main"
143133
{% if flag?(:preview_mt) %} @current_thread.set(thread) {% end %}
144134
Fiber.fibers.push(self)
@@ -169,7 +159,7 @@ class Fiber
169159

170160
@alive = false
171161
{% unless flag?(:interpreted) %}
172-
Crystal::Scheduler.stack_pool.release(@stack)
162+
@stack.release
173163
{% end %}
174164
Fiber.suspend
175165
end
@@ -322,7 +312,7 @@ class Fiber
322312
# :nodoc:
323313
def push_gc_roots : Nil
324314
# Push the used section of the stack
325-
GC.push_stack @context.stack_top, @stack_bottom
315+
GC.push_stack @context.stack_top, @stack.bottom
326316
end
327317

328318
{% if flag?(:preview_mt) %}

src/fiber/context/aarch64-microsoft.cr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@ class Fiber
1313

1414
# actual stack top, not including guard pages and reserved pages
1515
LibC.GetNativeSystemInfo(out system_info)
16-
stack_top = @stack_bottom - system_info.dwPageSize
16+
stack_top = @stack.bottom - system_info.dwPageSize
1717

1818
stack_ptr[-4] = self.as(Void*) # x0 (r0): puts `self` as first argument for `fiber_main`
1919
stack_ptr[-16] = fiber_main.pointer # x30 (lr): initial `resume` will `ret` to this address
2020

2121
# The following three values are stored in the Thread Information Block (NT_TIB)
2222
# and are used by Windows to track the current stack limits
23-
stack_ptr[-3] = @stack # [x18, #0x1478]: Win32 DeallocationStack
24-
stack_ptr[-2] = stack_top # [x18, #16]: Stack Limit
25-
stack_ptr[-1] = @stack_bottom # [x18, #8]: Stack Base
23+
stack_ptr[-3] = @stack.pointer # [x18, #0x1478]: Win32 DeallocationStack
24+
stack_ptr[-2] = stack_top # [x18, #16]: Stack Limit
25+
stack_ptr[-1] = @stack.bottom # [x18, #8]: Stack Base
2626
end
2727

2828
# :nodoc:

src/fiber/context/x86_64-microsoft.cr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ class Fiber
1212

1313
# actual stack top, not including guard pages and reserved pages
1414
LibC.GetNativeSystemInfo(out system_info)
15-
stack_top = @stack_bottom - system_info.dwPageSize
15+
stack_top = @stack.bottom - system_info.dwPageSize
1616

1717
stack_ptr -= 4 # shadow space (or home space) before return address
1818
stack_ptr[0] = fiber_main.pointer # %rbx: Initial `resume` will `ret` to this address
1919
stack_ptr[-1] = self.as(Void*) # %rcx: puts `self` as first argument for `fiber_main`
2020

2121
# The following three values are stored in the Thread Information Block (NT_TIB)
2222
# and are used by Windows to track the current stack limits
23-
stack_ptr[-2] = @stack # %gs:0x1478: Win32 DeallocationStack
24-
stack_ptr[-3] = stack_top # %gs:0x10: Stack Limit
25-
stack_ptr[-4] = @stack_bottom # %gs:0x08: Stack Base
23+
stack_ptr[-2] = @stack.pointer # %gs:0x1478: Win32 DeallocationStack
24+
stack_ptr[-3] = stack_top # %gs:0x10: Stack Limit
25+
stack_ptr[-4] = @stack.bottom # %gs:0x08: Stack Base
2626
end
2727

2828
# :nodoc:

src/fiber/stack.cr

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
class Fiber
2+
# :nodoc:
3+
struct Stack
4+
getter pointer : Void*
5+
getter bottom : Void*
6+
getter? reusable : Bool
7+
8+
def self.new : self
9+
{% if flag?(:interpreted) %}
10+
new Pointer(Void).null, Pointer(Void).null
11+
{% else %}
12+
stack, stack_bottom = Crystal::Scheduler.stack_pool.checkout
13+
new(stack, stack_bottom, reusable: true)
14+
{% end %}
15+
end
16+
17+
def initialize(@pointer, @bottom, *, @reusable = false)
18+
end
19+
20+
def first_addressable_pointer : Void**
21+
ptr = @bottom # stacks grow down
22+
ptr -= sizeof(Void*) # point to first addressable pointer
23+
Pointer(Void*).new(ptr.address & ~15_u64) # align to 16 bytes
24+
end
25+
26+
def release : Nil
27+
Crystal::Scheduler.stack_pool.release(@pointer) if @reusable
28+
end
29+
end
30+
end

src/gc/boehm.cr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ module GC
223223
{% if flag?(:preview_mt) %}
224224
Thread.unsafe_each do |thread|
225225
if fiber = thread.current_fiber?
226-
GC.set_stackbottom(thread.gc_thread_handler, fiber.@stack_bottom)
226+
GC.set_stackbottom(thread.gc_thread_handler, fiber.@stack.bottom)
227227
end
228228
end
229229
{% end %}

0 commit comments

Comments
 (0)