Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
11 changes: 11 additions & 0 deletions spec/std/process_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ describe Process do
end
end
end

it "doesn't break if process is collected before completion", tags: %w[slow] do
200.times { Process.new(*exit_code_command(0)) }

# run the GC multiple times to unmap as much memory as possible
10.times { GC.collect }

# the processes above have now been queued after completion; if this last
# one finishes at all, nothing was broken by the GC
Process.run(*exit_code_command(0))
end
end

describe "#wait" do
Expand Down
13 changes: 13 additions & 0 deletions src/crystal/system/win32/iocp.cr
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,19 @@ struct Crystal::System::IOCP
property fiber : ::Fiber?
getter tag : Tag

property next : CompletionKey?
property previous : CompletionKey?

# Data structure to extend the lifetime of completion keys, in particular
# those created by `Process.new` without an associated `#wait` call
@@pending = ::Thread::LinkedList(CompletionKey).new

def self.unregister(key : self) : Nil
@@pending.delete(key)
end

def initialize(@tag : Tag, @fiber : ::Fiber? = nil)
@@pending.push(self)
end

def valid?(number_of_bytes_transferred)
Expand Down Expand Up @@ -123,6 +135,7 @@ struct Crystal::System::IOCP
in CompletionKey
Crystal.trace :evloop, "completion", tag: completion_key.tag.to_s, bytes: entry.dwNumberOfBytesTransferred, fiber: completion_key.fiber

CompletionKey.unregister(completion_key)
if completion_key.valid?(entry.dwNumberOfBytesTransferred)
# if `Process` exits before a call to `#wait`, this fiber will be
# reset already
Expand Down