Skip to content

Commit

Permalink
Implement Fiber and Scheduler for win32
Browse files Browse the repository at this point in the history
The actual stack context switch is still missing.
  • Loading branch information
straight-shoota committed Jul 24, 2019
1 parent 6d14f78 commit 07ab99b
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 15 deletions.
2 changes: 2 additions & 0 deletions src/concurrent.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ require "channel"
require "crystal/scheduler"
require "./concurrent/*"

{% unless flag?(:win32) %}
# Blocks the current fiber for the specified number of seconds.
#
# While this fiber is waiting this time, other ready-to-execute
Expand All @@ -22,6 +23,7 @@ end
def sleep(time : Time::Span)
Crystal::Scheduler.sleep(time)
end
{% end %}

# Blocks the current fiber forever.
#
Expand Down
42 changes: 37 additions & 5 deletions src/crystal/scheduler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,34 @@ class Crystal::Scheduler
end

private def fatal_resume_error(fiber, message)
LibC.dprintf 2, "\nFATAL: #{message}: #{fiber}\n"
caller.each { |line| LibC.dprintf(2, " from #{line}\n") }
print_error "\nFATAL: #{message}: #{fiber}\n"
{% unless flag?(:win32) %}
caller.each { |line| print_error " from #{line}\n" }
{% end %}

exit 1
end

private def print_error(message)
{% if flag?(:unix) %}
LibC.dprintf 2, message
{% elsif flag?(:win32) %}
LibC._write 2, message, message.bytesize
{% end %}
end

protected def reschedule : Nil
if runnable = @runnables.shift?
runnable.resume
else
Crystal::EventLoop.resume
{% if flag?(:unix) %}
Crystal::EventLoop.resume
{% elsif flag?(:win32) %}
print_error "Warning: No runnables in scheduler. Exiting program."
::exit
{% else %}
{% raise "unsupported platform" %}
{% end %}
end
end

Expand All @@ -118,11 +136,25 @@ class Crystal::Scheduler
end

protected def yield : Nil
sleep(0.seconds)
{% if flag?(:unix) %}
sleep(0.seconds)
{% elsif flag?(:win32) %}
@runnables << @current
reschedule
{% else %}
{% raise "unsupported platform" %}
{% end %}
end

protected def yield(fiber : Fiber) : Nil
@current.resume_event.add(0.seconds)
{% if flag?(:unix) %}
@current.resume_event.add(0.seconds)
{% elsif flag?(:win32) %}
@runnables << fiber
{% else %}
{% raise "unsupported platform" %}
{% end %}

resume(fiber)
end
end
2 changes: 2 additions & 0 deletions src/crystal/system/fiber.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ end

{% if flag?(:unix) %}
require "./unix/fiber"
{% elsif flag?(:win32) %}
require "./win32/fiber"
{% else %}
{% raise "fiber not supported" %}
{% end %}
21 changes: 21 additions & 0 deletions src/crystal/system/win32/fiber.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require "c/winbase"
require "c/winnt"
require "c/processthreadsapi"

module Crystal::System::Fiber
def self.allocate_stack(stack_size) : Void*
memory_pointer = LibC.VirtualAlloc(nil, stack_size, LibC::MEM_COMMIT | LibC::MEM_RESERVE, LibC::PAGE_READWRITE)

if memory_pointer.null?
raise WinError.new("VirtualAlloc")
end

memory_pointer
end

def self.free_stack(stack : Void*, stack_size) : Nil
if LibC.VirtualFree(stack, stack_size, LibC::MEM_RELEASE) == 0
raise WinError.new("VirtualFree")
end
end
end
10 changes: 9 additions & 1 deletion src/fiber.cr
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ class Fiber

@context : Context
@stack : Void*
@resume_event : Crystal::Event?

{% unless flag?(:win32) %}
@resume_event : Crystal::Event?
{% end %}

protected property stack_bottom : Void*
property name : String?
@alive = true
Expand Down Expand Up @@ -85,8 +89,10 @@ class Fiber
# Remove the current fiber from the linked list
@@fibers.delete(self)

{% unless flag?(:win32) %}
# Delete the resume event if it was used by `yield` or `sleep`
@resume_event.try &.free
{% end %}

@alive = false
Crystal::Scheduler.reschedule
Expand Down Expand Up @@ -118,10 +124,12 @@ class Fiber
Crystal::Scheduler.resume(self)
end

{% unless flag?(:win32) %}
# :nodoc:
def resume_event
@resume_event ||= Crystal::EventLoop.create_resume_event(self)
end
{% end %}

def self.yield
Crystal::Scheduler.yield
Expand Down
14 changes: 7 additions & 7 deletions src/kernel.cr
Original file line number Diff line number Diff line change
Expand Up @@ -537,15 +537,15 @@ class Process
end
end

{% unless flag?(:win32) %}
# Background loop to cleanup unused fiber stacks.
spawn do
loop do
sleep 5
Fiber.stack_pool.collect
end
# Background loop to cleanup unused fiber stacks.
spawn do
loop do
sleep 5
Fiber.stack_pool.collect
end
end

{% unless flag?(:win32) %}
Signal.setup_default_handlers
LibExt.setup_sigfault_handler
{% end %}
5 changes: 5 additions & 0 deletions src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require "./basetsd"

lib LibC
fun GetCurrentThreadStackLimits(lowLimit : ULONG_PTR*, highLimit : ULONG_PTR*) : Void
end
12 changes: 12 additions & 0 deletions src/lib_c/x86_64-windows-msvc/c/winbase.cr
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,16 @@ lib LibC
fun GetEnvironmentStringsW : LPWCH
fun FreeEnvironmentStringsW(lpszEnvironmentBlock : LPWCH) : BOOL
fun SetEnvironmentVariableW(lpName : LPWSTR, lpValue : LPWSTR) : BOOL

MEM_COMMIT = 0x00001000
MEM_RESERVE = 0x00002000
MEM_RESET = 0x00080000
MEM_RESET_UNDO = 0x01000000

fun VirtualAlloc(lpAddress : Void*, dwSize : SizeT, flAllocationType : DWORD, flProtect : DWORD) : Void*

MEM_DECOMMIT = 0x4000
MEM_RELEASE = 0x8000

fun VirtualFree(lpAddress : Void*, dwSize : SizeT, dwFreeType : DWORD) : BOOL
end
3 changes: 3 additions & 0 deletions src/lib_c/x86_64-windows-msvc/c/winnt.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ lib LibC
FILE_ATTRIBUTE_REPARSE_POINT = 0x400

FILE_READ_ATTRIBUTES = 0x80

# Memory protection constants
PAGE_READWRITE = 0x04
end
2 changes: 1 addition & 1 deletion src/prelude.cr
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ require "box"
require "char"
require "char/reader"
require "class"
no_win require "concurrent"
require "concurrent"
require "crystal/main"
require "deque"
require "dir"
Expand Down
9 changes: 8 additions & 1 deletion src/thread/thread_win32.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ class Thread
class_property! current : Thread?

def initialize
@main_fiber = Fiber.new
@main_fiber = Fiber.new(stack_address, self)
@@threads.push(self)
end

# Returns the Thread object associated to the running system thread.
Expand All @@ -14,4 +15,10 @@ class Thread
# Associates the Thread object to the running system thread.
protected def self.current=(@@current : Thread) : Thread
end

private def stack_address : Void*
LibC.GetCurrentThreadStackLimits(out low_limit, out high_limit)

Pointer(Void).new(low_limit)
end
end

0 comments on commit 07ab99b

Please sign in to comment.