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

Refactor the IOCP event loop (timers, ...) #15238

Merged
merged 16 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/mingw-w64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ jobs:
cc crystal.obj -o .build/crystal.exe -municode \
$(pkg-config bdw-gc libpcre2-8 iconv zlib libffi --libs) \
$(llvm-config --libs --system-libs --ldflags) \
-lole32 -lWS2_32 -Wl,--stack,0x800000
-lole32 -lWS2_32 -lntdll -Wl,--stack,0x800000

- name: Package Crystal
shell: msys2 {0}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
{% skip_file unless Crystal::EventLoop.has_constant?(:Polling) %}

require "spec"
require "crystal/event_loop/timers"

private struct Timer
include Crystal::PointerPairingHeap::Node

property! wake_at : Time::Span

def initialize(timeout : Time::Span? = nil)
@wake_at = Time.monotonic + timeout if timeout
end

def heap_compare(other : Pointer(self)) : Bool
wake_at < other.value.wake_at
end
end

describe Crystal::EventLoop::Polling::Timers do
describe Crystal::EventLoop::Timers do
it "#empty?" do
timers = Crystal::EventLoop::Polling::Timers.new
timers = Crystal::EventLoop::Timers(Timer).new
timers.empty?.should be_true

event = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 7.seconds)
event = Timer.new(7.seconds)
timers.add(pointerof(event))
timers.empty?.should be_false

Expand All @@ -17,13 +30,13 @@ describe Crystal::EventLoop::Polling::Timers do

it "#next_ready?" do
# empty
timers = Crystal::EventLoop::Polling::Timers.new
timers = Crystal::EventLoop::Timers(Timer).new
timers.next_ready?.should be_nil

# with events
event1s = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 1.second)
event3m = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 3.minutes)
event5m = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 5.minutes)
event1s = Timer.new(1.second)
event3m = Timer.new(3.minutes)
event5m = Timer.new(5.minutes)

timers.add(pointerof(event5m))
timers.next_ready?.should eq(event5m.wake_at?)
Expand All @@ -36,24 +49,24 @@ describe Crystal::EventLoop::Polling::Timers do
end

it "#dequeue_ready" do
timers = Crystal::EventLoop::Polling::Timers.new
timers = Crystal::EventLoop::Timers(Timer).new

event1 = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 0.seconds)
event2 = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 0.seconds)
event3 = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 1.minute)
event1 = Timer.new(0.seconds)
event2 = Timer.new(0.seconds)
event3 = Timer.new(1.minute)

# empty
called = 0
timers.dequeue_ready { called += 1 }
called.should eq(0)

# add events in non chronological order
timers = Crystal::EventLoop::Polling::Timers.new
timers = Crystal::EventLoop::Timers(Timer).new
timers.add(pointerof(event1))
timers.add(pointerof(event3))
timers.add(pointerof(event2))

events = [] of Crystal::EventLoop::Polling::Event*
events = [] of Timer*
timers.dequeue_ready { |event| events << event }

events.should eq([
Expand All @@ -64,12 +77,12 @@ describe Crystal::EventLoop::Polling::Timers do
end

it "#add" do
timers = Crystal::EventLoop::Polling::Timers.new
timers = Crystal::EventLoop::Timers(Timer).new

event0 = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current)
event1 = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 0.seconds)
event2 = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 2.minutes)
event3 = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 1.minute)
event0 = Timer.new
event1 = Timer.new(0.seconds)
event2 = Timer.new(2.minutes)
event3 = Timer.new(1.minute)

# add events in non chronological order
timers.add(pointerof(event1)).should be_true # added to the head (next ready)
Expand All @@ -81,13 +94,13 @@ describe Crystal::EventLoop::Polling::Timers do
end

it "#delete" do
event1 = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 0.seconds)
event2 = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 0.seconds)
event3 = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 1.minute)
event4 = Crystal::EventLoop::Polling::Event.new(:sleep, Fiber.current, timeout: 4.minutes)
event1 = Timer.new(0.seconds)
event2 = Timer.new(0.seconds)
event3 = Timer.new(1.minute)
event4 = Timer.new(4.minutes)

# add events in non chronological order
timers = Crystal::EventLoop::Polling::Timers.new
timers = Crystal::EventLoop::Timers(Timer).new
timers.add(pointerof(event1))
timers.add(pointerof(event3))
timers.add(pointerof(event2))
Expand Down
Loading
Loading