diff --git a/spec/compiler/crystal/tools/doc/project_info_spec.cr b/spec/compiler/crystal/tools/doc/project_info_spec.cr index 9e220e4e3fc3..61bf20c2da67 100644 --- a/spec/compiler/crystal/tools/doc/project_info_spec.cr +++ b/spec/compiler/crystal/tools/doc/project_info_spec.cr @@ -33,7 +33,7 @@ describe Crystal::Doc::ProjectInfo do File.write("shard.yml", "name: foo\nversion: 1.0") end - pending_win32 "git missing" do + it "git missing" do Crystal::Git.executable = "git-missing-executable" assert_with_defaults(ProjectInfo.new(nil, nil), ProjectInfo.new("foo", "1.0", refname: nil)) diff --git a/spec/std/http/client/client_spec.cr b/spec/std/http/client/client_spec.cr index b5d522088d31..4c9da8db7ad7 100644 --- a/spec/std/http/client/client_spec.cr +++ b/spec/std/http/client/client_spec.cr @@ -200,7 +200,7 @@ module HTTP end end - pending_win32 "will retry a broken socket" do + it "will retry a broken socket" do server = HTTP::Server.new do |context| context.response.output.print "foo" context.response.output.close diff --git a/spec/std/http/spec_helper.cr b/spec/std/http/spec_helper.cr index 9bf88c82763f..410e211e4dbe 100644 --- a/spec/std/http/spec_helper.cr +++ b/spec/std/http/spec_helper.cr @@ -61,29 +61,27 @@ end def run_handler(handler, &) done = Channel(Exception?).new - begin - IO::Stapled.pipe do |server_io, client_io| - processor = HTTP::Server::RequestProcessor.new(handler) - f = spawn do - processor.process(server_io, server_io) - rescue exc - done.send exc - else - done.send nil - end + IO::Stapled.pipe do |server_io, client_io| + processor = HTTP::Server::RequestProcessor.new(handler) + f = spawn do + processor.process(server_io, server_io) + rescue exc + done.send exc + else + done.send nil + end - client = HTTP::Client.new(client_io) + client = HTTP::Client.new(client_io) - begin - wait_until_blocked f + begin + wait_until_blocked f - yield client - ensure - processor.close - server_io.close - if exc = done.receive - raise exc - end + yield client + ensure + processor.close + server_io.close + if exc = done.receive + raise exc end end end diff --git a/spec/std/io/io_spec.cr b/spec/std/io/io_spec.cr index 010625fd6fe5..f8497f5360bc 100644 --- a/spec/std/io/io_spec.cr +++ b/spec/std/io/io_spec.cr @@ -99,7 +99,7 @@ end describe IO do describe "partial read" do - pending_win32 "doesn't block on first read. blocks on 2nd read" do + it "doesn't block on first read. blocks on 2nd read" do IO.pipe do |read, write| write.puts "hello" slice = Bytes.new 1024 @@ -920,8 +920,8 @@ describe IO do end {% end %} - pending_win32 describe: "#close" do - it "aborts 'read' in a different thread" do + describe "#close" do + it "aborts 'read' in a different fiber" do ch = Channel(SpecChannelStatus).new(1) IO.pipe do |read, write| @@ -942,7 +942,7 @@ describe IO do end end - it "aborts 'write' in a different thread" do + it "aborts 'write' in a different fiber" do ch = Channel(SpecChannelStatus).new(1) IO.pipe do |read, write| diff --git a/spec/std/log/io_backend_spec.cr b/spec/std/log/io_backend_spec.cr index 80f66c08c202..3ebcb846f70e 100644 --- a/spec/std/log/io_backend_spec.cr +++ b/spec/std/log/io_backend_spec.cr @@ -14,7 +14,7 @@ private def io_logger(*, stdout : IO, config = nil, source : String = "") end describe Log::IOBackend do - pending_win32 "creates with defaults" do + it "creates with defaults" do backend = Log::IOBackend.new backend.io.should eq(STDOUT) backend.formatter.should eq(Log::ShortFormat) diff --git a/spec/std/oauth2/client_spec.cr b/spec/std/oauth2/client_spec.cr index e3f24a679ae4..3ee66e29ab49 100644 --- a/spec/std/oauth2/client_spec.cr +++ b/spec/std/oauth2/client_spec.cr @@ -40,7 +40,7 @@ describe OAuth2::Client do end end - pending_win32 describe: "get_access_token_using_*" do + describe "get_access_token_using_*" do describe "using HTTP Basic authentication to pass credentials" do it "#get_access_token_using_authorization_code" do handler = HTTP::Handler::HandlerProc.new do |context| diff --git a/spec/std/process_spec.cr b/spec/std/process_spec.cr index e9e6848828eb..2a2e903c5a92 100644 --- a/spec/std/process_spec.cr +++ b/spec/std/process_spec.cr @@ -137,12 +137,12 @@ describe Process do value.should eq("hello#{newline}") end - pending_win32 "sends input in IO" do + it "sends input in IO" do value = Process.run(*stdin_to_stdout_command, input: IO::Memory.new("hello")) do |proc| proc.input?.should be_nil proc.output.gets_to_end end - value.should eq("hello") + value.chomp.should eq("hello") end it "sends output to IO" do @@ -305,6 +305,8 @@ describe Process do {% end %} end + # TODO: this spec gives "WaitForSingleObject: The handle is invalid." + # is this because standard streams on windows aren't async? pending_win32 "can link processes together" do buffer = IO::Memory.new Process.run(*stdin_to_stdout_command) do |cat| @@ -313,7 +315,7 @@ describe Process do cat.close end end - buffer.to_s.lines.size.should eq(1000) + buffer.to_s.chomp.lines.size.should eq(1000) end end diff --git a/src/crystal/system/unix/file_descriptor.cr b/src/crystal/system/unix/file_descriptor.cr index d8084de32834..8a5c01cff44e 100644 --- a/src/crystal/system/unix/file_descriptor.cr +++ b/src/crystal/system/unix/file_descriptor.cr @@ -44,6 +44,10 @@ module Crystal::System::FileDescriptor fcntl(LibC::F_SETFL, new_flags) unless new_flags == current_flags end + private def system_blocking_init(value) + self.system_blocking = false unless value + end + private def system_close_on_exec? flags = fcntl(LibC::F_GETFD) flags.bits_set? LibC::FD_CLOEXEC diff --git a/src/crystal/system/wasi/file_descriptor.cr b/src/crystal/system/wasi/file_descriptor.cr index df25c9557ffe..9a2bc1a0bd96 100644 --- a/src/crystal/system/wasi/file_descriptor.cr +++ b/src/crystal/system/wasi/file_descriptor.cr @@ -11,6 +11,9 @@ module Crystal::System::FileDescriptor raise NotImplementedError.new "Crystal::System::FileDescriptor.pipe" end + private def system_blocking_init(value) + end + private def system_reopen(other : IO::FileDescriptor) raise NotImplementedError.new "Crystal::System::FileDescriptor#system_reopen" end diff --git a/src/crystal/system/win32/file_descriptor.cr b/src/crystal/system/win32/file_descriptor.cr index f9c0d525bc72..bae125f3638e 100644 --- a/src/crystal/system/win32/file_descriptor.cr +++ b/src/crystal/system/win32/file_descriptor.cr @@ -2,30 +2,50 @@ require "c/io" require "c/consoleapi" require "c/consoleapi2" require "c/winnls" +require "io/overlapped" module Crystal::System::FileDescriptor + include IO::Overlapped + @volatile_fd : Atomic(LibC::Int) + @system_blocking = true private def unbuffered_read(slice : Bytes) - bytes_read = LibC._read(fd, slice, slice.size) - if bytes_read == -1 - if Errno.value == Errno::EBADF - raise IO::Error.new "File not open for reading" - else - raise IO::Error.from_errno("Error reading file") + if system_blocking? + bytes_read = LibC._read(fd, slice, slice.size) + if bytes_read == -1 + if Errno.value == Errno::EBADF + raise IO::Error.new "File not open for reading" + else + raise IO::Error.from_errno("Error reading file") + end + end + bytes_read + else + handle = windows_handle + overlapped_operation(handle, "ReadFile", read_timeout) do |overlapped| + ret = LibC.ReadFile(handle, slice, slice.size, out byte_count, overlapped) + {ret, byte_count} end end - bytes_read end private def unbuffered_write(slice : Bytes) until slice.empty? - bytes_written = LibC._write(fd, slice, slice.size) - if bytes_written == -1 - if Errno.value == Errno::EBADF - raise IO::Error.new "File not open for writing" - else - raise IO::Error.from_errno("Error writing file") + if system_blocking? + bytes_written = LibC._write(fd, slice, slice.size) + if bytes_written == -1 + if Errno.value == Errno::EBADF + raise IO::Error.new "File not open for writing" + else + raise IO::Error.from_errno("Error writing file") + end + end + else + handle = windows_handle + bytes_written = overlapped_operation(handle, "WriteFile", write_timeout, writing: true) do |overlapped| + ret = LibC.WriteFile(handle, slice, slice.size, out byte_count, overlapped) + {ret, byte_count} end end @@ -34,11 +54,17 @@ module Crystal::System::FileDescriptor end private def system_blocking? - true + @system_blocking end private def system_blocking=(blocking) - raise NotImplementedError.new("Crystal::System::FileDescriptor#system_blocking=") unless blocking + unless blocking == @system_blocking + raise IO::Error.new("Cannot reconfigure `IO::FileDescriptor#blocking` after creation") + end + end + + private def system_blocking_init(value) + @system_blocking = value end private def system_close_on_exec? @@ -125,11 +151,12 @@ module Crystal::System::FileDescriptor end private def system_close + LibC.CancelIoEx(windows_handle, nil) unless system_blocking? + file_descriptor_close end def file_descriptor_close - err = nil if LibC._close(fd) != 0 case Errno.value when Errno::EINTR @@ -140,14 +167,26 @@ module Crystal::System::FileDescriptor end end - def self.pipe(read_blocking, write_blocking) - pipe_fds = uninitialized StaticArray(LibC::Int, 2) - if LibC._pipe(pipe_fds, 8192, LibC::O_BINARY | LibC::O_NOINHERIT) != 0 - raise IO::Error.from_errno("Could not create pipe") - end + private PIPE_BUFFER_SIZE = 8192 - r = IO::FileDescriptor.new(pipe_fds[0], read_blocking) - w = IO::FileDescriptor.new(pipe_fds[1], write_blocking) + def self.pipe(read_blocking, write_blocking) + pipe_name = ::Path.windows(::File.tempname("crystal", nil, dir: %q(\\.\pipe))).normalize.to_s + pipe_mode = 0 # PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT + + w_pipe_flags = LibC::PIPE_ACCESS_OUTBOUND | LibC::FILE_FLAG_FIRST_PIPE_INSTANCE + w_pipe_flags |= LibC::FILE_FLAG_OVERLAPPED unless write_blocking + w_pipe = LibC.CreateNamedPipeA(pipe_name, w_pipe_flags, pipe_mode, 1, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, 0, nil) + raise IO::Error.from_winerror("CreateNamedPipeA") if w_pipe == LibC::INVALID_HANDLE_VALUE + Crystal::Scheduler.event_loop.create_completion_port(w_pipe) unless write_blocking + + r_pipe_flags = LibC::FILE_FLAG_NO_BUFFERING + r_pipe_flags |= LibC::FILE_FLAG_OVERLAPPED unless read_blocking + r_pipe = LibC.CreateFileW(System.to_wstr(pipe_name), LibC::GENERIC_READ | LibC::FILE_WRITE_ATTRIBUTES, 0, nil, LibC::OPEN_EXISTING, r_pipe_flags, nil) + raise IO::Error.from_winerror("CreateFileW") if r_pipe == LibC::INVALID_HANDLE_VALUE + Crystal::Scheduler.event_loop.create_completion_port(r_pipe) unless read_blocking + + r = IO::FileDescriptor.new(LibC._open_osfhandle(r_pipe, 0), read_blocking) + w = IO::FileDescriptor.new(LibC._open_osfhandle(w_pipe, 0), write_blocking) w.sync = true {r, w} @@ -187,7 +226,7 @@ module Crystal::System::FileDescriptor end end - io = IO::FileDescriptor.new(fd) + io = IO::FileDescriptor.new(fd, blocking: true) # Set sync or flush_on_newline as described in STDOUT and STDERR docs. # See https://crystal-lang.org/api/toplevel.html#STDERR if console_handle diff --git a/src/crystal/system/win32/socket.cr b/src/crystal/system/win32/socket.cr index dc830a0e5661..eeb0107f697b 100644 --- a/src/crystal/system/win32/socket.cr +++ b/src/crystal/system/win32/socket.cr @@ -131,6 +131,26 @@ module Crystal::System::Socket end end + private def overlapped_connect(socket, method, &) + OverlappedOperation.run(socket) do |operation| + yield operation.start + + schedule_overlapped(read_timeout || 1.seconds) + + operation.wsa_result(socket) do |error| + case error + when .wsa_io_incomplete?, .wsaeconnrefused? + return ::Socket::ConnectError.from_os_error(method, error) + when .error_operation_aborted? + # FIXME: Not sure why this is necessary + return ::Socket::ConnectError.from_os_error(method, error) + end + end + + nil + end + end + private def system_connect_connectionless(addr, timeout, &) ret = LibC.connect(fd, addr, addr.size) if ret == LibC::SOCKET_ERROR @@ -194,6 +214,25 @@ module Crystal::System::Socket true end + private def overlapped_accept(socket, method, &) + OverlappedOperation.run(socket) do |operation| + yield operation.start + + unless schedule_overlapped(read_timeout) + raise IO::TimeoutError.new("accept timed out") + end + + operation.wsa_result(socket) do |error| + case error + when .wsa_io_incomplete?, .wsaenotsock? + return false + end + end + + true + end + end + private def wsa_buffer(bytes) wsabuf = LibC::WSABUF.new wsabuf.len = bytes.size @@ -348,7 +387,7 @@ module Crystal::System::Socket private def unbuffered_read(slice : Bytes) wsabuf = wsa_buffer(slice) - bytes_read = overlapped_operation(fd, "WSARecv", read_timeout, connreset_is_error: false) do |overlapped| + bytes_read = overlapped_read(fd, "WSARecv", connreset_is_error: false) do |overlapped| flags = 0_u32 LibC.WSARecv(fd, pointerof(wsabuf), 1, out bytes_received, pointerof(flags), overlapped, nil) end @@ -365,6 +404,18 @@ module Crystal::System::Socket bytes.to_i32 end + private def overlapped_write(socket, method, &) + wsa_overlapped_operation(socket, method, write_timeout) do |operation| + yield operation + end + end + + private def overlapped_read(socket, method, *, connreset_is_error = true, &) + wsa_overlapped_operation(socket, method, read_timeout, connreset_is_error) do |operation| + yield operation + end + end + def system_close handle = @volatile_fd.swap(LibC::INVALID_SOCKET) diff --git a/src/io/file_descriptor.cr b/src/io/file_descriptor.cr index 2f6cf02ec4a0..2929a62a410a 100644 --- a/src/io/file_descriptor.cr +++ b/src/io/file_descriptor.cr @@ -32,9 +32,7 @@ class IO::FileDescriptor < IO end end - unless blocking || {{ flag?(:win32) || flag?(:wasi) }} - self.blocking = false - end + system_blocking_init(blocking) end # :nodoc: diff --git a/src/io/overlapped.cr b/src/io/overlapped.cr index 011b25b06398..fe7a5445b38c 100644 --- a/src/io/overlapped.cr +++ b/src/io/overlapped.cr @@ -38,18 +38,6 @@ module IO::Overlapped write_timeout end - def overlapped_write(socket, method, &) - overlapped_operation(socket, method, write_timeout) do |operation| - yield operation - end - end - - def overlapped_read(socket, method, &) - overlapped_operation(socket, method, read_timeout) do |operation| - yield operation - end - end - def self.wait_queued_completions(timeout, &) overlapped_entries = uninitialized LibC::OVERLAPPED_ENTRY[1] @@ -87,23 +75,24 @@ module IO::Overlapped CANCELLED end - @overlapped = LibC::WSAOVERLAPPED.new + @overlapped = LibC::OVERLAPPED.new @fiber : Fiber? = nil @state : State = :initialized property next : OverlappedOperation? property previous : OverlappedOperation? @@canceled = Thread::LinkedList(OverlappedOperation).new + property? synchronous = false - def self.run(socket, &) + def self.run(handle, &) operation = OverlappedOperation.new begin yield operation ensure - operation.done(socket) + operation.done(handle) end end - def self.schedule(overlapped : LibC::WSAOVERLAPPED*, &) + def self.schedule(overlapped : LibC::OVERLAPPED*, &) start = overlapped.as(Pointer(UInt8)) - offsetof(OverlappedOperation, @overlapped) operation = Box(OverlappedOperation).unbox(start.as(Pointer(Void))) operation.schedule { |fiber| yield fiber } @@ -116,7 +105,20 @@ module IO::Overlapped pointerof(@overlapped) end - def result(socket, &) + def result(handle, &) + raise Exception.new("Invalid state #{@state}") unless @state.done? || @state.started? + result = LibC.GetOverlappedResult(handle, pointerof(@overlapped), out bytes, 0) + if result.zero? + error = WinError.value + yield error + + raise IO::Error.from_os_error("GetOverlappedResult", error) + end + + bytes + end + + def wsa_result(socket, &) raise Exception.new("Invalid state #{@state}") unless @state.done? || @state.started? flags = 0_u32 result = LibC.WSAGetOverlappedResult(socket, pointerof(@overlapped), out bytes, false, pointerof(flags)) @@ -142,12 +144,17 @@ module IO::Overlapped end end - protected def done(socket) + protected def done(handle) case @state when .started? + handle = LibC::HANDLE.new(handle) if handle.is_a?(LibC::SOCKET) + # Microsoft documentation: - # The application must not free or reuse the OVERLAPPED structure associated with the canceled I/O operations until they have completed - if LibC.CancelIoEx(LibC::HANDLE.new(socket), pointerof(@overlapped)) != 0 + # The application must not free or reuse the OVERLAPPED structure + # associated with the canceled I/O operations until they have completed + # (this applies even to asynchronous operations that completed + # synchronously) + if synchronous? || LibC.CancelIoEx(handle, pointerof(@overlapped)) != 0 @state = :cancelled @@canceled.push(self) # to increase lifetime end @@ -170,67 +177,66 @@ module IO::Overlapped Crystal::Scheduler.event_loop.dequeue(timeout_event) end - def overlapped_operation(socket, method, timeout, connreset_is_error = true, &) - OverlappedOperation.run(socket) do |operation| - result = yield operation.start - - if result == LibC::SOCKET_ERROR - error = WinError.wsa_value - - unless error.wsa_io_pending? + def overlapped_operation(handle, method, timeout, *, writing = false, &) + OverlappedOperation.run(handle) do |operation| + result, value = yield operation.start + + if result == 0 + case error = WinError.value + when .error_handle_eof? + return 0_u32 + when .error_broken_pipe? + return 0_u32 + when .error_io_pending? + # the operation is running asynchronously; do nothing + when .error_access_denied? + raise IO::Error.new "File not open for #{writing ? "writing" : "reading"}" + else raise IO::Error.from_os_error(method, error) end + else + operation.synchronous = true + return value end schedule_overlapped(timeout) - operation.result(socket) do |error| + operation.result(handle) do |error| case error - when .wsa_io_incomplete? - raise TimeoutError.new("#{method} timed out") - when .wsaeconnreset? - return 0_u32 unless connreset_is_error + when .error_io_incomplete? + raise IO::TimeoutError.new("#{method} timed out") + when .error_handle_eof? + return 0_u32 + when .error_broken_pipe? + # TODO: this is needed for `Process.run`, can we do without it? + return 0_u32 end end end end - def overlapped_connect(socket, method, &) + def wsa_overlapped_operation(socket, method, timeout, connreset_is_error = true, &) OverlappedOperation.run(socket) do |operation| - yield operation.start + result = yield operation.start - schedule_overlapped(read_timeout || 1.seconds) + if result == LibC::SOCKET_ERROR + error = WinError.wsa_value - operation.result(socket) do |error| - case error - when .wsa_io_incomplete?, .wsaeconnrefused? - return ::Socket::ConnectError.from_os_error(method, error) - when .error_operation_aborted? - # FIXME: Not sure why this is necessary - return ::Socket::ConnectError.from_os_error(method, error) + unless error.wsa_io_pending? + raise IO::Error.from_os_error(method, error) end end - nil - end - end - - def overlapped_accept(socket, method, &) - OverlappedOperation.run(socket) do |operation| - yield operation.start - - unless schedule_overlapped(read_timeout) - raise IO::TimeoutError.new("accept timed out") - end + schedule_overlapped(timeout) - operation.result(socket) do |error| + operation.wsa_result(socket) do |error| case error - when .wsa_io_incomplete?, .wsaenotsock? - return false + when .wsa_io_incomplete? + raise TimeoutError.new("#{method} timed out") + when .wsaeconnreset? + return 0_u32 unless connreset_is_error end end - - true end end end diff --git a/src/lib_c/x86_64-windows-msvc/c/fileapi.cr b/src/lib_c/x86_64-windows-msvc/c/fileapi.cr index 9b88b5341c09..3ad2e25f663b 100644 --- a/src/lib_c/x86_64-windows-msvc/c/fileapi.cr +++ b/src/lib_c/x86_64-windows-msvc/c/fileapi.cr @@ -50,11 +50,14 @@ lib LibC FILE_ATTRIBUTE_NORMAL = 0x80 FILE_ATTRIBUTE_TEMPORARY = 0x100 - FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 - FILE_FLAG_DELETE_ON_CLOSE = 0x04000000 - FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000 - FILE_FLAG_RANDOM_ACCESS = 0x10000000 - FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000 + FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 + FILE_FLAG_DELETE_ON_CLOSE = 0x04000000 + FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000 + FILE_FLAG_NO_BUFFERING = 0x20000000 + FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000 + FILE_FLAG_OVERLAPPED = 0x40000000 + FILE_FLAG_RANDOM_ACCESS = 0x10000000 + FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000 FILE_SHARE_READ = 0x1 FILE_SHARE_WRITE = 0x2 @@ -70,6 +73,7 @@ lib LibC dwFlagsAndAttributes : DWORD, hTemplateFile : HANDLE) : HANDLE fun ReadFile(hFile : HANDLE, lpBuffer : Void*, nNumberOfBytesToRead : DWORD, lpNumberOfBytesRead : DWORD*, lpOverlapped : OVERLAPPED*) : BOOL + fun WriteFile(hFile : HANDLE, lpBuffer : Void*, nNumberOfBytesToWrite : DWORD, lpNumberOfBytesWritten : DWORD*, lpOverlapped : OVERLAPPED*) : BOOL MAX_PATH = 260 diff --git a/src/lib_c/x86_64-windows-msvc/c/ioapiset.cr b/src/lib_c/x86_64-windows-msvc/c/ioapiset.cr index eaef34deaa3e..1c94b66db4c8 100644 --- a/src/lib_c/x86_64-windows-msvc/c/ioapiset.cr +++ b/src/lib_c/x86_64-windows-msvc/c/ioapiset.cr @@ -1,4 +1,11 @@ lib LibC + fun GetOverlappedResult( + hFile : HANDLE, + lpOverlapped : OVERLAPPED*, + lpNumberOfBytesTransferred : DWORD*, + bWait : BOOL + ) : BOOL + fun CreateIoCompletionPort( fileHandle : HANDLE, existingCompletionPort : HANDLE, diff --git a/src/lib_c/x86_64-windows-msvc/c/minwinbase.cr b/src/lib_c/x86_64-windows-msvc/c/minwinbase.cr index cb56e3269de2..4f22a8242d18 100644 --- a/src/lib_c/x86_64-windows-msvc/c/minwinbase.cr +++ b/src/lib_c/x86_64-windows-msvc/c/minwinbase.cr @@ -19,7 +19,7 @@ lib LibC struct OVERLAPPED_ENTRY lpCompletionKey : ULONG_PTR - lpOverlapped : WSAOVERLAPPED* + lpOverlapped : OVERLAPPED* internal : ULONG_PTR dwNumberOfBytesTransferred : DWORD end diff --git a/src/lib_c/x86_64-windows-msvc/c/winbase.cr b/src/lib_c/x86_64-windows-msvc/c/winbase.cr index 0ac284673695..f9c4e261ab05 100644 --- a/src/lib_c/x86_64-windows-msvc/c/winbase.cr +++ b/src/lib_c/x86_64-windows-msvc/c/winbase.cr @@ -18,8 +18,12 @@ lib LibC SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1 SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 - fun CreateHardLinkW(lpFileName : LPWSTR, lpExistingFileName : LPWSTR, lpSecurityAttributes : Void*) : BOOL + PIPE_ACCESS_OUTBOUND = 0x00000002 + + fun CreateHardLinkW(lpFileName : LPWSTR, lpExistingFileName : LPWSTR, lpSecurityAttributes : SECURITY_ATTRIBUTES*) : BOOL fun CreateSymbolicLinkW(lpSymlinkFileName : LPWSTR, lpTargetFileName : LPWSTR, dwFlags : DWORD) : BOOLEAN + fun CreateNamedPipeA(lpName : LPSTR, dwOpenMode : DWORD, dwPipeMode : DWORD, nMaxInstances : DWORD, + nOutBufferSize : DWORD, nInBufferSize : DWORD, nDefaultTimeOut : DWORD, lpSecurityAttributes : SECURITY_ATTRIBUTES*) : HANDLE fun GetEnvironmentVariableW(lpName : LPWSTR, lpBuffer : LPWSTR, nSize : DWORD) : DWORD fun GetEnvironmentStringsW : LPWCH diff --git a/src/log/io_backend.cr b/src/log/io_backend.cr index 9092495c8bf4..2d520fc9346b 100644 --- a/src/log/io_backend.cr +++ b/src/log/io_backend.cr @@ -3,7 +3,7 @@ class Log::IOBackend < Log::Backend property io : IO property formatter : Formatter - {% if flag?(:win32) || flag?(:wasm32) %} + {% if flag?(:wasm32) %} # TODO: this constructor must go away once channels are fixed in Windows / WebAssembly def initialize(@io = STDOUT, *, @formatter : Formatter = ShortFormat, dispatcher : Dispatcher::Spec = DispatchMode::Sync) super(dispatcher)