From 0a9fda0cf63dbd39459bae79c95b56f784819292 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Tue, 29 Jul 2025 16:33:36 +0200 Subject: [PATCH 1/6] Add `IO::FileDescriptor.set_blocking` and deprecate `IO::FileDescriptor#blocking[=]` --- spec/std/io/file_descriptor_spec.cr | 14 ++++++++++++++ src/crystal/system/unix/file_descriptor.cr | 2 +- src/crystal/system/unix/process.cr | 2 +- src/crystal/system/wasi/file_descriptor.cr | 4 ++++ src/crystal/system/win32/file_descriptor.cr | 8 ++++++-- src/io/file_descriptor.cr | 10 ++++++++++ src/process.cr | 2 +- 7 files changed, 37 insertions(+), 5 deletions(-) diff --git a/spec/std/io/file_descriptor_spec.cr b/spec/std/io/file_descriptor_spec.cr index 2e10ea99c030..3f9922e195c1 100644 --- a/spec/std/io/file_descriptor_spec.cr +++ b/spec/std/io/file_descriptor_spec.cr @@ -189,6 +189,20 @@ describe IO::FileDescriptor do end {% end %} + {% if flag?(:unix) %} + it ".set_blocking" do + File.open(datapath("test_file.txt"), "r") do |file| + fd = file.fd + + IO::FileDescriptor.set_blocking(fd, false) + IO::FileDescriptor.fcntl(fd, LibC::F_GETFL).bits_set?(LibC::O_NONBLOCK).should be_true + + IO::FileDescriptor.set_blocking(fd, true) + IO::FileDescriptor.fcntl(fd, LibC::F_GETFL).bits_set?(LibC::O_NONBLOCK).should be_false + end + end + {% end %} + typeof(STDIN.noecho { }) typeof(STDIN.noecho!) typeof(STDIN.echo { }) diff --git a/src/crystal/system/unix/file_descriptor.cr b/src/crystal/system/unix/file_descriptor.cr index f671fd5da842..d03a006c12b0 100644 --- a/src/crystal/system/unix/file_descriptor.cr +++ b/src/crystal/system/unix/file_descriptor.cr @@ -27,7 +27,7 @@ module Crystal::System::FileDescriptor FileDescriptor.set_blocking(fd, value) end - protected def self.set_blocking(fd, value) + protected def self.set_blocking(fd : Handle, value : Bool) current_flags = fcntl(fd, LibC::F_GETFL) new_flags = current_flags if value diff --git a/src/crystal/system/unix/process.cr b/src/crystal/system/unix/process.cr index 59714a71bf50..36d8da14be21 100644 --- a/src/crystal/system/unix/process.cr +++ b/src/crystal/system/unix/process.cr @@ -361,7 +361,7 @@ struct Crystal::System::Process ret = LibC.dup2(src_io.fd, dst_io.fd) raise IO::Error.from_errno("dup2") if ret == -1 - dst_io.blocking = true + FileDescriptor.set_blocking(dst_io.fd, true) dst_io.close_on_exec = false end end diff --git a/src/crystal/system/wasi/file_descriptor.cr b/src/crystal/system/wasi/file_descriptor.cr index 989df18a1058..41e30861a4ab 100644 --- a/src/crystal/system/wasi/file_descriptor.cr +++ b/src/crystal/system/wasi/file_descriptor.cr @@ -13,6 +13,10 @@ module Crystal::System::FileDescriptor r end + def self.set_blocking(fd : Handle, value : Bool) + raise NotImplementedError.new("Crystal::System::FileDescriptor.set_blocking") + end + protected def system_blocking_init(blocking : Bool?) end diff --git a/src/crystal/system/win32/file_descriptor.cr b/src/crystal/system/win32/file_descriptor.cr index d6a9d8fc4ab6..0e3c0e5ccfaa 100644 --- a/src/crystal/system/win32/file_descriptor.cr +++ b/src/crystal/system/win32/file_descriptor.cr @@ -98,8 +98,12 @@ module Crystal::System::FileDescriptor @system_blocking end + def self.set_blocking(fd : Handle, value : Bool) + raise NotImplementedError.new("Cannot change the blocking mode of an `IO::FileDescriptor` after creation") + end + private def system_blocking=(blocking) - unless blocking == self.blocking + unless blocking == system_blocking? raise IO::Error.new("Cannot reconfigure `IO::FileDescriptor#blocking` after creation") end end @@ -366,7 +370,7 @@ module Crystal::System::FileDescriptor # `blocking` must be set to `true` because the underlying handles never # support overlapped I/O; instead, `#emulated_blocking?` should return # `false` for `STDIN` as it uses a separate thread - io = IO::FileDescriptor.new(handle.address, blocking: true) + io = IO::FileDescriptor.new(handle: handle.address, blocking: true) # Set sync or flush_on_newline as described in STDOUT and STDERR docs. # See https://crystal-lang.org/api/toplevel.html#STDERR diff --git a/src/io/file_descriptor.cr b/src/io/file_descriptor.cr index 4a936cf88548..43674775b4fa 100644 --- a/src/io/file_descriptor.cr +++ b/src/io/file_descriptor.cr @@ -79,12 +79,21 @@ class IO::FileDescriptor < IO # This might be different from the internal file descriptor. For example, when # `STDIN` is a terminal on Windows, this returns `false` since the underlying # blocking reads are done on a completely separate thread. + @[Deprecated("There are no replacement.")] def blocking emulated = emulated_blocking? return emulated unless emulated.nil? system_blocking? end + # Changes the blocking mode of *fd* to be blocking (true) or non blocking + # (false). + # + # NOTE: Only implemented on UNIX targets. Raises on Windows. + def self.set_blocking(fd : Handle, value : Bool) + Crystal::System::FileDescriptor.set_blocking(fd, value) + end + # Changes the file descriptor's mode to blocking (true) or non blocking # (false). # @@ -92,6 +101,7 @@ class IO::FileDescriptor < IO # the event loop runtime requirements. Changing the blocking mode can cause # the event loop to misbehave, for example block the entire program when a # fiber tries to read from this file descriptor. + @[Deprecated("Use IO::FileDescriptor.set_blocking instead.")] def blocking=(value) self.system_blocking = value end diff --git a/src/process.cr b/src/process.cr index 63b78bf0f716..661cb42fafff 100644 --- a/src/process.cr +++ b/src/process.cr @@ -296,7 +296,7 @@ class Process # regular files will report an error and those require a separate pipe # (https://github.com/crystal-lang/crystal/pull/13362#issuecomment-1519082712) {% if flag?(:win32) %} - unless stdio.blocking || stdio.info.type.pipe? + unless stdio.system_blocking? || stdio.info.type.pipe? return io_to_fd(stdio, for: dst_io) end {% end %} From 6c6662c43f824fd08aee4562c7e49aa74a68bd8c Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Tue, 29 Jul 2025 16:35:53 +0200 Subject: [PATCH 2/6] Add `Socket.set_blocking` and deprecate `Socket#blocking[=]` --- spec/std/socket/socket_spec.cr | 17 +++++++++++++++++ src/crystal/system/unix/socket.cr | 8 ++++++-- src/crystal/system/wasi/socket.cr | 8 ++++++-- src/crystal/system/win32/socket.cr | 6 +++--- src/socket.cr | 14 +++++++++++--- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/spec/std/socket/socket_spec.cr b/spec/std/socket/socket_spec.cr index 54046081602c..e0447b6478b3 100644 --- a/spec/std/socket/socket_spec.cr +++ b/spec/std/socket/socket_spec.cr @@ -181,6 +181,23 @@ describe Socket, tags: "network" do end {% end %} + it ".set_blocking" do + # we can't check the blocking mode on windows; we thus merely check that the + # methods compile and run without errors + socket = Socket.tcp(Socket::Family::INET) + fd = socket.fd + + Socket.set_blocking(fd, true) + {% unless flag?(:win32) %} + Socket.fcntl(fd, LibC::F_GETFL).bits_set?(LibC::O_NONBLOCK).should be_false + {% end %} + + Socket.set_blocking(fd, false) + {% unless flag?(:win32) %} + IO::FileDescriptor.fcntl(fd, LibC::F_GETFL).bits_set?(LibC::O_NONBLOCK).should be_true + {% end %} + end + describe "#finalize" do it "does not flush" do port = unused_local_tcp_port diff --git a/src/crystal/system/unix/socket.cr b/src/crystal/system/unix/socket.cr index 5f7307c46cc6..9dfaf0ebf771 100644 --- a/src/crystal/system/unix/socket.cr +++ b/src/crystal/system/unix/socket.cr @@ -160,13 +160,17 @@ module Crystal::System::Socket end private def system_blocking=(value) - flags = fcntl(LibC::F_GETFL) + Socket.set_blocking(fd, value) + end + + def self.set_blocking(fd : Handle, value : Bool) + flags = fcntl(fd, LibC::F_GETFL) if value flags &= ~LibC::O_NONBLOCK else flags |= LibC::O_NONBLOCK end - fcntl(LibC::F_SETFL, flags) + fcntl(fd, LibC::F_SETFL, flags) end private def system_close_on_exec? diff --git a/src/crystal/system/wasi/socket.cr b/src/crystal/system/wasi/socket.cr index b5bc4035aec1..ecce311125e7 100644 --- a/src/crystal/system/wasi/socket.cr +++ b/src/crystal/system/wasi/socket.cr @@ -106,13 +106,17 @@ module Crystal::System::Socket end private def system_blocking=(value) - flags = fcntl(LibC::F_GETFL) + System.set_blocking(fd, value) + end + + def self.set_blocking(fd : Handle, value : Bool) + flags = fcntl(fd, LibC::F_GETFL) if value flags &= ~LibC::O_NONBLOCK else flags |= LibC::O_NONBLOCK end - fcntl(LibC::F_SETFL, flags) + fcntl(fd, LibC::F_SETFL, flags) end private def system_close_on_exec? diff --git a/src/crystal/system/win32/socket.cr b/src/crystal/system/win32/socket.cr index 99c2002d5620..12ef42f2846a 100644 --- a/src/crystal/system/win32/socket.cr +++ b/src/crystal/system/win32/socket.cr @@ -331,7 +331,7 @@ module Crystal::System::Socket ret end - @blocking = true + @blocking : Bool = true # WSA does not provide a direct way to query the blocking mode of a file descriptor. # The best option seems to be just keeping track in an instance variable. @@ -347,8 +347,8 @@ module Crystal::System::Socket # Changes the blocking mode as per BSD sockets, has no effect on the # overlapped flag. - def self.set_blocking(fd, blocking) - mode = blocking ? 1_u32 : 0_u32 + def self.set_blocking(fd : Handle, value : Bool) + mode = value ? 1_u32 : 0_u32 ret = LibC.WSAIoctl(fd, LibC::FIONBIO, pointerof(mode), sizeof(UInt32), nil, 0, out _, nil, nil) raise ::Socket::Error.from_wsa_error("WSAIoctl") unless ret.zero? end diff --git a/src/socket.cr b/src/socket.cr index 19e1c90b66dd..dbed3a3e4a49 100644 --- a/src/socket.cr +++ b/src/socket.cr @@ -87,7 +87,7 @@ class Socket < IO def initialize(fd, @family : Family, @type : Type, @protocol : Protocol = Protocol::IP, blocking = nil) initialize(handle: fd, family: family, type: type, protocol: protocol) blocking = Crystal::EventLoop.default_socket_blocking? if blocking.nil? - self.blocking = blocking unless blocking + Crystal::System::Socket.set_blocking(fd, blocking) unless blocking self.sync = true end @@ -228,10 +228,10 @@ class Socket < IO def accept? : Socket? if rs = Crystal::EventLoop.current.accept(self) sock = Socket.new(handle: rs[0], family: family, type: type, protocol: protocol, blocking: rs[1]) - unless (blocking = self.blocking) == rs[1] + unless (blocking = system_blocking?) == rs[1] # FIXME: unlike the overloads in TCPServer and UNIXServer, this version # carries the blocking mode from the server socket to the client socket - sock.blocking = blocking + Crystal::System::Socket.set_blocking(fd, blocking) end sock.sync = sync? sock @@ -421,6 +421,7 @@ class Socket < IO end # Returns whether the socket's mode is blocking (true) or non blocking (false). + @[Deprecated("There are no replacement.")] def blocking system_blocking? end @@ -431,10 +432,17 @@ class Socket < IO # loop runtime requirements. Changing the blocking mode can cause the event # loop to misbehave, for example block the entire program when a fiber tries # to read from this socket. + @[Deprecated("Use Socket.set_blocking(fd, value) instead.")] def blocking=(value) self.system_blocking = value end + # Changes the blocking mode of *fd* to be blocking (true) or non blocking + # (false). + def self.set_blocking(fd : Handle, value : Bool) + Crystal::System::Socket.set_blocking(fd, value) + end + def close_on_exec? system_close_on_exec? end From 368665f4069f2eef808d884cc0c86f0d2335363a Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Tue, 29 Jul 2025 17:09:51 +0200 Subject: [PATCH 3/6] fixup! Add `IO::FileDescriptor.set_blocking` and deprecate `IO::FileDescriptor#blocking[=]` --- src/io/file_descriptor.cr | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/io/file_descriptor.cr b/src/io/file_descriptor.cr index 43674775b4fa..fa4a49379bc3 100644 --- a/src/io/file_descriptor.cr +++ b/src/io/file_descriptor.cr @@ -61,11 +61,15 @@ class IO::FileDescriptor < IO # :nodoc: # - # Internal constructor to wrap a system *handle*. - def initialize(*, handle : Handle, @close_on_finalize = true) + # Internal constructor to wrap a system *handle*. The *blocking* arg is purely + # informational. + def initialize(*, handle : Handle, @close_on_finalize = true, blocking = nil) @volatile_fd = Atomic.new(handle) @closed = true # This is necessary so we can reference `self` in `system_closed?` (in case of an exception) @closed = system_closed? + {% if flag?(:win32) %} + @system_blocking = !!blocking + {% end %} end # :nodoc: From 8c38ada8051ffcf405468a61c1137dc02a2651cb Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 31 Jul 2025 11:38:32 +0200 Subject: [PATCH 4/6] Update src/io/file_descriptor.cr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Johannes Müller --- src/io/file_descriptor.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/file_descriptor.cr b/src/io/file_descriptor.cr index fa4a49379bc3..c7a0e8180247 100644 --- a/src/io/file_descriptor.cr +++ b/src/io/file_descriptor.cr @@ -83,7 +83,7 @@ class IO::FileDescriptor < IO # This might be different from the internal file descriptor. For example, when # `STDIN` is a terminal on Windows, this returns `false` since the underlying # blocking reads are done on a completely separate thread. - @[Deprecated("There are no replacement.")] + @[Deprecated("There is no replacement.")] def blocking emulated = emulated_blocking? return emulated unless emulated.nil? From 62cc2b574dc244fa335ef42a89c21b261b45a80c Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 31 Jul 2025 12:52:19 +0200 Subject: [PATCH 5/6] Add `.get_blocking` to `IO::FileDescriptor` and `Socket` --- spec/std/io/file_descriptor_spec.cr | 22 ++++++++++------- spec/std/socket/socket_spec.cr | 16 +++++++------ src/crystal/system/unix/file_descriptor.cr | 4 ++++ src/crystal/system/unix/socket.cr | 6 ++++- src/crystal/system/wasi/file_descriptor.cr | 4 ++++ src/crystal/system/wasi/socket.cr | 8 +++++-- src/crystal/system/win32/file_descriptor.cr | 4 ++++ src/crystal/system/win32/socket.cr | 4 ++++ src/io/file_descriptor.cr | 26 ++++++++++++++------- src/socket.cr | 13 +++++++++-- 10 files changed, 77 insertions(+), 30 deletions(-) diff --git a/spec/std/io/file_descriptor_spec.cr b/spec/std/io/file_descriptor_spec.cr index 3f9922e195c1..1c3ce5b5b391 100644 --- a/spec/std/io/file_descriptor_spec.cr +++ b/spec/std/io/file_descriptor_spec.cr @@ -189,19 +189,23 @@ describe IO::FileDescriptor do end {% end %} - {% if flag?(:unix) %} - it ".set_blocking" do - File.open(datapath("test_file.txt"), "r") do |file| - fd = file.fd - + it ".set_blocking and .get_blocking" do + File.open(datapath("test_file.txt"), "r") do |file| + fd = file.fd + + {% if flag?(:win32) %} + expect_raises(NotImplementedError) { IO::FileDescriptor.set_blocking(fd, false) } + expect_raises(NotImplementedError) { IO::FileDescriptor.set_blocking(fd, true) } + expect_raises(NotImplementedError) { IO::FileDescriptor.get_blocking(fd) } + {% else %} IO::FileDescriptor.set_blocking(fd, false) - IO::FileDescriptor.fcntl(fd, LibC::F_GETFL).bits_set?(LibC::O_NONBLOCK).should be_true + IO::FileDescriptor.get_blocking(fd).should be_false IO::FileDescriptor.set_blocking(fd, true) - IO::FileDescriptor.fcntl(fd, LibC::F_GETFL).bits_set?(LibC::O_NONBLOCK).should be_false - end + IO::FileDescriptor.get_blocking(fd).should be_true + {% end %} end - {% end %} + end typeof(STDIN.noecho { }) typeof(STDIN.noecho!) diff --git a/spec/std/socket/socket_spec.cr b/spec/std/socket/socket_spec.cr index e0447b6478b3..1dd9921833e4 100644 --- a/spec/std/socket/socket_spec.cr +++ b/spec/std/socket/socket_spec.cr @@ -181,20 +181,22 @@ describe Socket, tags: "network" do end {% end %} - it ".set_blocking" do - # we can't check the blocking mode on windows; we thus merely check that the - # methods compile and run without errors + it ".set_blocking and .get_blocking" do socket = Socket.tcp(Socket::Family::INET) fd = socket.fd Socket.set_blocking(fd, true) - {% unless flag?(:win32) %} - Socket.fcntl(fd, LibC::F_GETFL).bits_set?(LibC::O_NONBLOCK).should be_false + {% if flag?(:win32) %} + expect_raises(NotImplementedError) { IO::FileDescriptor.get_blocking(fd) } + {% else %} + Socket.get_blocking(fd).should be_true {% end %} Socket.set_blocking(fd, false) - {% unless flag?(:win32) %} - IO::FileDescriptor.fcntl(fd, LibC::F_GETFL).bits_set?(LibC::O_NONBLOCK).should be_true + {% if flag?(:win32) %} + expect_raises(NotImplementedError) { IO::FileDescriptor.get_blocking(fd) } + {% else %} + Socket.get_blocking(fd).should be_false {% end %} end diff --git a/src/crystal/system/unix/file_descriptor.cr b/src/crystal/system/unix/file_descriptor.cr index d03a006c12b0..1afe22b08ca7 100644 --- a/src/crystal/system/unix/file_descriptor.cr +++ b/src/crystal/system/unix/file_descriptor.cr @@ -27,6 +27,10 @@ module Crystal::System::FileDescriptor FileDescriptor.set_blocking(fd, value) end + protected def self.get_blocking(fd : Handle) + fcntl(fd, LibC::F_GETFL) & LibC::O_NONBLOCK == 0 + end + protected def self.set_blocking(fd : Handle, value : Bool) current_flags = fcntl(fd, LibC::F_GETFL) new_flags = current_flags diff --git a/src/crystal/system/unix/socket.cr b/src/crystal/system/unix/socket.cr index 9dfaf0ebf771..11226eec7f82 100644 --- a/src/crystal/system/unix/socket.cr +++ b/src/crystal/system/unix/socket.cr @@ -156,13 +156,17 @@ module Crystal::System::Socket end private def system_blocking? - fcntl(LibC::F_GETFL) & LibC::O_NONBLOCK == 0 + Socket.get_blocking(fd) end private def system_blocking=(value) Socket.set_blocking(fd, value) end + def self.get_blocking(fd : Handle) + fcntl(fd, LibC::F_GETFL) & LibC::O_NONBLOCK == 0 + end + def self.set_blocking(fd : Handle, value : Bool) flags = fcntl(fd, LibC::F_GETFL) if value diff --git a/src/crystal/system/wasi/file_descriptor.cr b/src/crystal/system/wasi/file_descriptor.cr index 41e30861a4ab..540efa95b9f3 100644 --- a/src/crystal/system/wasi/file_descriptor.cr +++ b/src/crystal/system/wasi/file_descriptor.cr @@ -13,6 +13,10 @@ module Crystal::System::FileDescriptor r end + def self.get_blocking(fd : Handle) + raise NotImplementedError.new("Crystal::System::FileDescriptor.get_blocking") + end + def self.set_blocking(fd : Handle, value : Bool) raise NotImplementedError.new("Crystal::System::FileDescriptor.set_blocking") end diff --git a/src/crystal/system/wasi/socket.cr b/src/crystal/system/wasi/socket.cr index ecce311125e7..edb811837c6c 100644 --- a/src/crystal/system/wasi/socket.cr +++ b/src/crystal/system/wasi/socket.cr @@ -102,11 +102,15 @@ module Crystal::System::Socket end private def system_blocking? - fcntl(LibC::F_GETFL) & LibC::O_NONBLOCK == 0 + Socket.get_blocking(fd) end private def system_blocking=(value) - System.set_blocking(fd, value) + Socket.set_blocking(fd, value) + end + + def self.get_blocking(fd : Handle) + fcntl(fd, LibC::F_GETFL) & LibC::O_NONBLOCK == 0 end def self.set_blocking(fd : Handle, value : Bool) diff --git a/src/crystal/system/win32/file_descriptor.cr b/src/crystal/system/win32/file_descriptor.cr index 0e3c0e5ccfaa..26ee4d7e9753 100644 --- a/src/crystal/system/win32/file_descriptor.cr +++ b/src/crystal/system/win32/file_descriptor.cr @@ -98,6 +98,10 @@ module Crystal::System::FileDescriptor @system_blocking end + def self.get_blocking(fd : Handle) + raise NotImplementedError.new("Cannot query the blocking mode of an `IO::FileDescriptor`") + end + def self.set_blocking(fd : Handle, value : Bool) raise NotImplementedError.new("Cannot change the blocking mode of an `IO::FileDescriptor` after creation") end diff --git a/src/crystal/system/win32/socket.cr b/src/crystal/system/win32/socket.cr index 12ef42f2846a..8513dd0ca115 100644 --- a/src/crystal/system/win32/socket.cr +++ b/src/crystal/system/win32/socket.cr @@ -345,6 +345,10 @@ module Crystal::System::Socket Socket.set_blocking(fd, blocking) end + def self.get_blocking(fd : Handle) + raise NotImplementedError.new("Cannot query the blocking mode of a `Socket`") + end + # Changes the blocking mode as per BSD sockets, has no effect on the # overlapped flag. def self.set_blocking(fd : Handle, value : Bool) diff --git a/src/io/file_descriptor.cr b/src/io/file_descriptor.cr index c7a0e8180247..b08b8fcd3576 100644 --- a/src/io/file_descriptor.cr +++ b/src/io/file_descriptor.cr @@ -83,21 +83,13 @@ class IO::FileDescriptor < IO # This might be different from the internal file descriptor. For example, when # `STDIN` is a terminal on Windows, this returns `false` since the underlying # blocking reads are done on a completely separate thread. - @[Deprecated("There is no replacement.")] + @[Deprecated("Use Socket.get_blocking instead.")] def blocking emulated = emulated_blocking? return emulated unless emulated.nil? system_blocking? end - # Changes the blocking mode of *fd* to be blocking (true) or non blocking - # (false). - # - # NOTE: Only implemented on UNIX targets. Raises on Windows. - def self.set_blocking(fd : Handle, value : Bool) - Crystal::System::FileDescriptor.set_blocking(fd, value) - end - # Changes the file descriptor's mode to blocking (true) or non blocking # (false). # @@ -110,6 +102,22 @@ class IO::FileDescriptor < IO self.system_blocking = value end + # Returns whether the blocking mode of *fd* is blocking (true) or non blocking + # (false). + # + # NOTE: Only implemented on UNIX targets. Raises on Windows. + def self.get_blocking(fd : Handle) : Bool + Crystal::System::Socket.get_blocking(fd) + end + + # Changes the blocking mode of *fd* to be blocking (true) or non blocking + # (false). + # + # NOTE: Only implemented on UNIX targets. Raises on Windows. + def self.set_blocking(fd : Handle, value : Bool) + Crystal::System::FileDescriptor.set_blocking(fd, value) + end + def close_on_exec? : Bool system_close_on_exec? end diff --git a/src/socket.cr b/src/socket.cr index dbed3a3e4a49..637d06dcda0e 100644 --- a/src/socket.cr +++ b/src/socket.cr @@ -421,7 +421,7 @@ class Socket < IO end # Returns whether the socket's mode is blocking (true) or non blocking (false). - @[Deprecated("There are no replacement.")] + @[Deprecated("Use Socket.get_blocking instead.")] def blocking system_blocking? end @@ -432,11 +432,20 @@ class Socket < IO # loop runtime requirements. Changing the blocking mode can cause the event # loop to misbehave, for example block the entire program when a fiber tries # to read from this socket. - @[Deprecated("Use Socket.set_blocking(fd, value) instead.")] + @[Deprecated("Use Socket.set_blocking instead.")] def blocking=(value) self.system_blocking = value end + + # Returns whether the blocking mode of *fd* is blocking (true) or non blocking + # (false). + # + # NOTE: Only implemented on UNIX targets. Raises on Windows. + def self.get_blocking(fd : Handle) : Bool + Crystal::System::Socket.get_blocking(fd) + end + # Changes the blocking mode of *fd* to be blocking (true) or non blocking # (false). def self.set_blocking(fd : Handle, value : Bool) From 5dc8d79882c7cd19ba0f2b6c0306983522a7059b Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 31 Jul 2025 14:25:40 +0200 Subject: [PATCH 6/6] fixup! Add `.get_blocking` to `IO::FileDescriptor` and `Socket` --- src/socket.cr | 1 - 1 file changed, 1 deletion(-) diff --git a/src/socket.cr b/src/socket.cr index 637d06dcda0e..e8116a4f2bfe 100644 --- a/src/socket.cr +++ b/src/socket.cr @@ -437,7 +437,6 @@ class Socket < IO self.system_blocking = value end - # Returns whether the blocking mode of *fd* is blocking (true) or non blocking # (false). #