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

[WIP] Windows #3582

Closed
wants to merge 46 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
8fc5762
Make it able to compile targeting Windows
lbguilherme Nov 26, 2016
1a42d97
merge
lbguilherme Nov 26, 2016
773c0a2
Basic support for writing IO (blocking)
lbguilherme Nov 26, 2016
a8c45b6
Don't LibGC.set_handle_fork on Windows
lbguilherme Nov 26, 2016
06672f3
Allow more of the prelude
lbguilherme Nov 26, 2016
1097d34
Make Fibers work (no actual async IO yet)
lbguilherme Nov 26, 2016
61917a2
Fix previous commit
lbguilherme Nov 26, 2016
1c2f38e
Don't break Fiber for other plataforms
lbguilherme Nov 27, 2016
51c2686
Scheduler, block forever without timeout waiting for events
lbguilherme Nov 27, 2016
81e5d78
Implement Thread for Windows
lbguilherme Nov 27, 2016
01ade3d
Implement sleep for Windows
lbguilherme Nov 27, 2016
df79849
Handle Windows error codes
lbguilherme Nov 27, 2016
decfd0d
Delete Timer after it triggered
lbguilherme Nov 27, 2016
f77e00c
Merge crystal v0.20.3
Dec 29, 2016
83cbb5f
stub file for windows due to update to 0.20.3
Dec 29, 2016
21a7275
fix src to be able to build the compiler (in linux)
Dec 30, 2016
ba028e8
Initial work on File
lbguilherme Dec 31, 2016
6b29c91
Merge pull request #1 from bcardiff/win
lbguilherme Dec 31, 2016
73b6e7e
Merge
lbguilherme Jan 1, 2017
22f0d99
Merge
lbguilherme Jan 1, 2017
b5a922e
Move windows-predule to src to be able to do --prelude=windows-prelude
lbguilherme Jan 1, 2017
296d53a
fix build
Jan 2, 2017
a47ee02
run travis, run.
Jan 2, 2017
4a78d2c
allow travis to build any win prefixed branch
Jan 2, 2017
bcafe8e
regex the travis branch filter
Jan 2, 2017
1154bd0
allow printing floats. add LibC.memchr
Jan 2, 2017
1ad47f7
Merge pull request #3 from bcardiff/win-float
lbguilherme Jan 3, 2017
6fdb959
get current time: Time.now, Time.utc_now (#4)
bcardiff Jan 3, 2017
696e6f1
WinError: Must call get_last_error before allocate for accurate message
lbguilherme Jan 3, 2017
5e501f5
Small comment explanation change
lbguilherme Jan 3, 2017
3e22aa7
Move @[CallConvention] from fun level to lib level
lbguilherme Jan 3, 2017
facfdaf
Use a noncrashing fatal version of raise for Windows
lbguilherme Jan 3, 2017
6c5e352
crystal tool format
lbguilherme Jan 3, 2017
c5e89f3
Implement basic creating/opening/reading/writing/closing files
lbguilherme Jan 3, 2017
9b96b65
merge
lbguilherme Jan 3, 2017
c03200d
merge
lbguilherme Jan 3, 2017
8b4319a
ci fix. avoid including file_descriptor.(posix|windows).cr due to io/**
Jan 4, 2017
68cbf2a
merge
lbguilherme Jan 8, 2017
b195615
Implement async File IO!
lbguilherme Jan 8, 2017
0b7a07c
Better error check on Scheduler.attach_to_completion_port
lbguilherme Jan 8, 2017
2bd96fb
Cleanup: no need to zero-out every struct field manually
lbguilherme Jan 8, 2017
ad888c4
async io: to take advantage of synchronous return
lbguilherme Jan 9, 2017
de9af24
merge
lbguilherme Feb 25, 2017
a41a541
merge
lbguilherme May 14, 2017
f2887ca
fix merge conflict
lbguilherme May 14, 2017
ea7cd51
Fix source format
lbguilherme May 14, 2017
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
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ branches:
only:
- master
- /\Arelease\/.+\z/
- /^win\W/
notifications:
irc:
channels:
Expand Down
23 changes: 14 additions & 9 deletions src/callstack.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
require "c/dlfcn"
{% if !flag?(:windows) %}
require "c/dlfcn"
{% end %}
require "c/stdio"
require "c/string"
require "callstack/lib_unwind"
Expand Down Expand Up @@ -389,16 +391,19 @@ struct CallStack
{% end %}

protected def self.decode_frame(ip, original_ip = ip)
if LibC.dladdr(ip, out info) != 0
offset = original_ip - info.dli_saddr
{% if flag?(:windows) %}
{% else %}
if LibC.dladdr(ip, out info) != 0
offset = original_ip - info.dli_saddr

if offset == 0
return decode_frame(ip - 1, original_ip)
end
if offset == 0
return decode_frame(ip - 1, original_ip)
end

unless info.dli_sname.null?
{offset, info.dli_sname}
unless info.dli_sname.null?
{offset, info.dli_sname}
end
end
end
{% end %}
end
end
5 changes: 4 additions & 1 deletion src/concurrent.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
require "fiber"
require "./concurrent/*"
require "./concurrent/channel"
require "./concurrent/error"
require "./concurrent/future"
require "./concurrent/scheduler"

# Blocks the current fiber for the specified number of seconds.
#
Expand Down
94 changes: 5 additions & 89 deletions src/concurrent/scheduler.cr
Original file line number Diff line number Diff line change
@@ -1,89 +1,5 @@
require "event"

# :nodoc:
class Scheduler
@@runnables = Deque(Fiber).new
@@eb = Event::Base.new

def self.reschedule
if runnable = @@runnables.shift?
runnable.resume
else
loop_fiber.resume
end
nil
end

def self.loop_fiber
@@loop_fiber ||= Fiber.new { @@eb.run_loop }
end

def self.after_fork
@@eb.reinit
end

def self.create_resume_event(fiber)
@@eb.new_event(-1, LibEvent2::EventFlags::None, fiber) do |s, flags, data|
data.as(Fiber).resume
end
end

def self.create_fd_write_event(io : IO::FileDescriptor, edge_triggered : Bool = false)
flags = LibEvent2::EventFlags::Write
flags |= LibEvent2::EventFlags::Persist | LibEvent2::EventFlags::ET if edge_triggered
event = @@eb.new_event(io.fd, flags, io) do |s, flags, data|
fd_io = data.as(IO::FileDescriptor)
if flags.includes?(LibEvent2::EventFlags::Write)
fd_io.resume_write
elsif flags.includes?(LibEvent2::EventFlags::Timeout)
fd_io.write_timed_out = true
fd_io.resume_write
end
end
event
end

def self.create_fd_read_event(io : IO::FileDescriptor, edge_triggered : Bool = false)
flags = LibEvent2::EventFlags::Read
flags |= LibEvent2::EventFlags::Persist | LibEvent2::EventFlags::ET if edge_triggered
event = @@eb.new_event(io.fd, flags, io) do |s, flags, data|
fd_io = data.as(IO::FileDescriptor)
if flags.includes?(LibEvent2::EventFlags::Read)
fd_io.resume_read
elsif flags.includes?(LibEvent2::EventFlags::Timeout)
fd_io.read_timed_out = true
fd_io.resume_read
end
end
event
end

def self.create_signal_event(signal : Signal, chan)
flags = LibEvent2::EventFlags::Signal | LibEvent2::EventFlags::Persist
event = @@eb.new_event(Int32.new(signal.to_i), flags, chan) do |s, flags, data|
ch = data.as(Channel::Buffered(Signal))
sig = Signal.new(s)
ch.send sig
end
event.add
event
end

@@dns_base : Event::DnsBase?

private def self.dns_base
@@dns_base ||= @@eb.new_dns_base
end

def self.create_dns_request(nodename, servname, hints, data, &callback : LibEvent2::DnsGetAddrinfoCallback)
dns_base.getaddrinfo(nodename, servname, hints, data, &callback)
end

def self.enqueue(fiber : Fiber)
@@runnables << fiber
end

def self.enqueue(fibers : Enumerable(Fiber))
@@runnables.concat fibers
end
end
{% if flag?(:windows) %}
require "./scheduler.windows.cr"
{% else %}
require "./scheduler.posix.cr"
{% end %}
89 changes: 89 additions & 0 deletions src/concurrent/scheduler.posix.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
require "event"

# :nodoc:
class Scheduler
@@runnables = Deque(Fiber).new
@@eb = Event::Base.new

def self.reschedule
if runnable = @@runnables.shift?
runnable.resume
else
loop_fiber.resume
end
nil
end

def self.loop_fiber
@@loop_fiber ||= Fiber.new { @@eb.run_loop }
end

def self.after_fork
@@eb.reinit
end

def self.create_resume_event(fiber)
@@eb.new_event(-1, LibEvent2::EventFlags::None, fiber) do |s, flags, data|
data.as(Fiber).resume
end
end

def self.create_fd_write_event(io : IO::FileDescriptor, edge_triggered : Bool = false)
flags = LibEvent2::EventFlags::Write
flags |= LibEvent2::EventFlags::Persist | LibEvent2::EventFlags::ET if edge_triggered
event = @@eb.new_event(io.fd, flags, io) do |s, flags, data|
fd_io = data.as(IO::FileDescriptor)
if flags.includes?(LibEvent2::EventFlags::Write)
fd_io.resume_write
elsif flags.includes?(LibEvent2::EventFlags::Timeout)
fd_io.write_timed_out = true
fd_io.resume_write
end
end
event
end

def self.create_fd_read_event(io : IO::FileDescriptor, edge_triggered : Bool = false)
flags = LibEvent2::EventFlags::Read
flags |= LibEvent2::EventFlags::Persist | LibEvent2::EventFlags::ET if edge_triggered
event = @@eb.new_event(io.fd, flags, io) do |s, flags, data|
fd_io = data.as(IO::FileDescriptor)
if flags.includes?(LibEvent2::EventFlags::Read)
fd_io.resume_read
elsif flags.includes?(LibEvent2::EventFlags::Timeout)
fd_io.read_timed_out = true
fd_io.resume_read
end
end
event
end

def self.create_signal_event(signal : Signal, chan)
flags = LibEvent2::EventFlags::Signal | LibEvent2::EventFlags::Persist
event = @@eb.new_event(Int32.new(signal.to_i), flags, chan) do |s, flags, data|
ch = data.as(Channel::Buffered(Signal))
sig = Signal.new(s)
ch.send sig
end
event.add
event
end

@@dns_base : Event::DnsBase?

private def self.dns_base
@@dns_base ||= @@eb.new_dns_base
end

def self.create_dns_request(nodename, servname, hints, data, &callback : LibEvent2::DnsGetAddrinfoCallback)
dns_base.getaddrinfo(nodename, servname, hints, data, &callback)
end

def self.enqueue(fiber : Fiber)
@@runnables << fiber
end

def self.enqueue(fibers : Enumerable(Fiber))
@@runnables.concat fibers
end
end
138 changes: 138 additions & 0 deletions src/concurrent/scheduler.windows.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
require "event"

# :nodoc:
class Scheduler
@@runnables = Deque(Fiber).new
@@timers = Hash(Fiber, LibWindows::Handle).new

def self.reschedule
if runnable = @@runnables.shift?
runnable.resume
else
loop_fiber.resume
end
nil
end

def self.completion_port
completion_port = @@completion_port ||= LibWindows.create_io_completion_port(LibWindows::INVALID_HANDLE_VALUE, nil, nil, 0)
if completion_port.null?
raise WinError.new("CreateIoCompletionPort")
end
completion_port
end

def self.attach_to_completion_port(handle, fd) : Bool
if LibWindows.create_io_completion_port(handle, Scheduler.completion_port, fd.as(Void*), 0).null?
if LibWindows.get_last_error == WinError::ERROR_INVALID_PARAMETER
# It is allowed to fail if the handle doesn't have FILE_FLAG_OVERLAPPED.
# But better check before calling. How to check for if the flag is set on the handle?
return false
end

raise WinError.new("CreateIoCompletionPort")
end
true
end

def self.loop_fiber
@@loop_fiber ||= Fiber.new do
loop do
bytes_transfered = 0u32
data = Pointer(Void).null
entry = uninitialized LibWindows::Overlapped*
if LibWindows.get_queued_completion_status(Scheduler.completion_port, pointerof(bytes_transfered), pointerof(data), pointerof(entry), LibWindows::INFINITY)
if entry.null?
# It is just a fiber wanting to be resumed
fiber = data.as(Fiber)
if timer_handle = @@timers[fiber]?
unless LibWindows.delete_timer_queue_timer(nil, timer_handle, nil)
error = WinError.new "DeleteTimerQueueTimer"
raise error if error.code != WinError::ERROR_IO_PENDING && error.code != WinError::ERROR_SUCCESS
end
@@timers.delete(fiber)
end
fiber.resume
else
fd = data.as(IO::FileDescriptor)
fd.resume_overlapped entry
end
end
end
end
end

def self.create_resume_event(fiber)
unless LibWindows.post_queued_completion_status(Scheduler.completion_port, 0, fiber.as(Void*), nil)
raise WinError.new "PostQueueCompletionStatus"
end
end

def self.create_sleep_event(fiber, seconds)
LibWindows.create_timer_queue_timer(out handle, nil, ->(data, fired) {
# this is run inside a thread from a pool managed by the system
Scheduler.create_resume_event(data.as(Fiber))
}, fiber.as(Void*), (seconds*1000).to_u32, 0, 0)

@@timers[fiber] = handle
end

# def self.create_fd_write_event(io : IO::FileDescriptor, edge_triggered : Bool = false)
# flags = LibEvent2::EventFlags::Write
# flags |= LibEvent2::EventFlags::Persist | LibEvent2::EventFlags::ET if edge_triggered
# event = @@eb.new_event(io.fd, flags, io) do |s, flags, data|
# fd_io = data.as(IO::FileDescriptor)
# if flags.includes?(LibEvent2::EventFlags::Write)
# fd_io.resume_write
# elsif flags.includes?(LibEvent2::EventFlags::Timeout)
# fd_io.write_timed_out = true
# fd_io.resume_write
# end
# end
# event
# end

# def self.create_fd_read_event(io : IO::FileDescriptor, edge_triggered : Bool = false)
# flags = LibEvent2::EventFlags::Read
# flags |= LibEvent2::EventFlags::Persist | LibEvent2::EventFlags::ET if edge_triggered
# event = @@eb.new_event(io.fd, flags, io) do |s, flags, data|
# fd_io = data.as(IO::FileDescriptor)
# if flags.includes?(LibEvent2::EventFlags::Read)
# fd_io.resume_read
# elsif flags.includes?(LibEvent2::EventFlags::Timeout)
# fd_io.read_timed_out = true
# fd_io.resume_read
# end
# end
# event
# end

# def self.create_signal_event(signal : Signal, chan)
# flags = LibEvent2::EventFlags::Signal | LibEvent2::EventFlags::Persist
# event = @@eb.new_event(Int32.new(signal.to_i), flags, chan) do |s, flags, data|
# ch = data.as(Channel::Buffered(Signal))
# sig = Signal.new(s)
# ch.send sig
# end
# event.add
# event
# end

# @@dns_base : Event::DnsBase?

# private def self.dns_base
# @@dns_base ||= @@eb.new_dns_base
# end

# def self.create_dns_request(nodename, servname, hints, data, &callback : LibEvent2::DnsGetAddrinfoCallback)
# dns_base.getaddrinfo(nodename, servname, hints, data, &callback)
# end

def self.enqueue(fiber : Fiber)
@@runnables << fiber
end

def self.enqueue(fibers : Enumerable(Fiber))
@@runnables.concat fibers
end
end
2 changes: 1 addition & 1 deletion src/docs_main.cr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require "./fiber"
require "./gc/**"
require "./html"
require "./http/**"
require "./io/**"
require "./io"
require "./json"
require "./llvm"
require "./logger"
Expand Down
Loading