From 944bee24a2ad34160b3f9a4eadaf673c53d4e91f Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Mon, 15 Apr 2024 17:37:29 +0800 Subject: [PATCH] Allow `#fsync` and `#flock_*` on `IO::FileDescriptor` (#14432) --- src/crystal/system/unix/file.cr | 54 ------------------- src/crystal/system/unix/file_descriptor.cr | 54 +++++++++++++++++++ src/crystal/system/wasi/file.cr | 16 ------ src/crystal/system/wasi/file_descriptor.cr | 16 ++++++ src/crystal/system/win32/file.cr | 57 --------------------- src/crystal/system/win32/file_descriptor.cr | 57 +++++++++++++++++++++ 6 files changed, 127 insertions(+), 127 deletions(-) diff --git a/src/crystal/system/unix/file.cr b/src/crystal/system/unix/file.cr index ca04693d0b1a..a353cf29cd3c 100644 --- a/src/crystal/system/unix/file.cr +++ b/src/crystal/system/unix/file.cr @@ -218,58 +218,4 @@ module Crystal::System::File raise ::File::Error.from_errno("Error truncating file", file: path) end end - - private def system_flock_shared(blocking) - flock LibC::FlockOp::SH, blocking - end - - private def system_flock_exclusive(blocking) - flock LibC::FlockOp::EX, blocking - end - - private def system_flock_unlock - flock LibC::FlockOp::UN - end - - private def flock(op : LibC::FlockOp, retry : Bool) : Nil - op |= LibC::FlockOp::NB - - if retry - until flock(op) - sleep 0.1 - end - else - flock(op) || raise IO::Error.from_errno("Error applying file lock: file is already locked", target: self) - end - end - - private def flock(op) : Bool - if 0 == LibC.flock(fd, op) - true - else - errno = Errno.value - if errno.in?(Errno::EAGAIN, Errno::EWOULDBLOCK) - false - else - raise IO::Error.from_os_error("Error applying or removing file lock", errno, target: self) - end - end - end - - private def system_fsync(flush_metadata = true) : Nil - ret = - if flush_metadata - LibC.fsync(fd) - else - {% if flag?(:dragonfly) %} - LibC.fsync(fd) - {% else %} - LibC.fdatasync(fd) - {% end %} - end - - if ret != 0 - raise IO::Error.from_errno("Error syncing file", target: self) - end - end end diff --git a/src/crystal/system/unix/file_descriptor.cr b/src/crystal/system/unix/file_descriptor.cr index c5810b5ae15b..6e82f4b7ae48 100644 --- a/src/crystal/system/unix/file_descriptor.cr +++ b/src/crystal/system/unix/file_descriptor.cr @@ -154,6 +154,60 @@ module Crystal::System::FileDescriptor end end + private def system_flock_shared(blocking) + flock LibC::FlockOp::SH, blocking + end + + private def system_flock_exclusive(blocking) + flock LibC::FlockOp::EX, blocking + end + + private def system_flock_unlock + flock LibC::FlockOp::UN + end + + private def flock(op : LibC::FlockOp, retry : Bool) : Nil + op |= LibC::FlockOp::NB + + if retry + until flock(op) + sleep 0.1 + end + else + flock(op) || raise IO::Error.from_errno("Error applying file lock: file is already locked", target: self) + end + end + + private def flock(op) : Bool + if 0 == LibC.flock(fd, op) + true + else + errno = Errno.value + if errno.in?(Errno::EAGAIN, Errno::EWOULDBLOCK) + false + else + raise IO::Error.from_os_error("Error applying or removing file lock", errno, target: self) + end + end + end + + private def system_fsync(flush_metadata = true) : Nil + ret = + if flush_metadata + LibC.fsync(fd) + else + {% if flag?(:dragonfly) %} + LibC.fsync(fd) + {% else %} + LibC.fdatasync(fd) + {% end %} + end + + if ret != 0 + raise IO::Error.from_errno("Error syncing file", target: self) + end + end + def self.pipe(read_blocking, write_blocking) pipe_fds = uninitialized StaticArray(LibC::Int, 2) if LibC.pipe(pipe_fds) != 0 diff --git a/src/crystal/system/wasi/file.cr b/src/crystal/system/wasi/file.cr index ecf4daf48159..0d197550e3db 100644 --- a/src/crystal/system/wasi/file.cr +++ b/src/crystal/system/wasi/file.cr @@ -18,22 +18,6 @@ module Crystal::System::File raise NotImplementedError.new "Crystal::System::File.utime" end - private def system_flock_shared(blocking) - raise NotImplementedError.new "Crystal::System::File#system_flock_shared" - end - - private def system_flock_exclusive(blocking) - raise NotImplementedError.new "Crystal::System::File#system_flock_exclusive" - end - - private def system_flock_unlock - raise NotImplementedError.new "Crystal::System::File#system_flock_unlock" - end - - private def flock(op : LibC::FlockOp, blocking : Bool = true) - raise NotImplementedError.new "Crystal::System::File#flock" - end - def self.delete(path : String, *, raise_on_missing : Bool) : Bool raise NotImplementedError.new "Crystal::System::File.delete" end diff --git a/src/crystal/system/wasi/file_descriptor.cr b/src/crystal/system/wasi/file_descriptor.cr index 6eae9b8832d2..ffd15817a104 100644 --- a/src/crystal/system/wasi/file_descriptor.cr +++ b/src/crystal/system/wasi/file_descriptor.cr @@ -24,6 +24,22 @@ module Crystal::System::FileDescriptor raise NotImplementedError.new "Crystal::System::FileDescriptor#system_reopen" end + private def system_flock_shared(blocking) + raise NotImplementedError.new "Crystal::System::File#system_flock_shared" + end + + private def system_flock_exclusive(blocking) + raise NotImplementedError.new "Crystal::System::File#system_flock_exclusive" + end + + private def system_flock_unlock + raise NotImplementedError.new "Crystal::System::File#system_flock_unlock" + end + + private def flock(op : LibC::FlockOp, blocking : Bool = true) + raise NotImplementedError.new "Crystal::System::File#flock" + end + private def system_echo(enable : Bool, & : ->) raise NotImplementedError.new "Crystal::System::FileDescriptor#system_echo" end diff --git a/src/crystal/system/win32/file.cr b/src/crystal/system/win32/file.cr index 8203e7920a5f..d95202be5732 100644 --- a/src/crystal/system/win32/file.cr +++ b/src/crystal/system/win32/file.cr @@ -461,61 +461,4 @@ module Crystal::System::File LibC.SetFilePointerEx(handle, old_pos, nil, IO::Seek::Set) end end - - private def system_flock_shared(blocking : Bool) : Nil - flock(false, blocking) - end - - private def system_flock_exclusive(blocking : Bool) : Nil - flock(true, blocking) - end - - private def system_flock_unlock : Nil - unlock_file(windows_handle) - end - - private def flock(exclusive, retry) - flags = LibC::LOCKFILE_FAIL_IMMEDIATELY - flags |= LibC::LOCKFILE_EXCLUSIVE_LOCK if exclusive - - handle = windows_handle - if retry - until lock_file(handle, flags) - sleep 0.1 - end - else - lock_file(handle, flags) || raise IO::Error.from_winerror("Error applying file lock: file is already locked") - end - end - - private def lock_file(handle, flags) - # lpOverlapped must be provided despite the synchronous use of this method. - overlapped = LibC::OVERLAPPED.new - # lock the entire file with offset 0 in overlapped and number of bytes set to max value - if 0 != LibC.LockFileEx(handle, flags, 0, 0xFFFF_FFFF, 0xFFFF_FFFF, pointerof(overlapped)) - true - else - winerror = WinError.value - if winerror == WinError::ERROR_LOCK_VIOLATION - false - else - raise IO::Error.from_os_error("LockFileEx", winerror, target: self) - end - end - end - - private def unlock_file(handle) - # lpOverlapped must be provided despite the synchronous use of this method. - overlapped = LibC::OVERLAPPED.new - # unlock the entire file with offset 0 in overlapped and number of bytes set to max value - if 0 == LibC.UnlockFileEx(handle, 0, 0xFFFF_FFFF, 0xFFFF_FFFF, pointerof(overlapped)) - raise IO::Error.from_winerror("UnLockFileEx") - end - end - - private def system_fsync(flush_metadata = true) : Nil - if LibC.FlushFileBuffers(windows_handle) == 0 - raise IO::Error.from_winerror("Error syncing file", target: self) - end - end end diff --git a/src/crystal/system/win32/file_descriptor.cr b/src/crystal/system/win32/file_descriptor.cr index 141ad2e7033d..aeea22fa2a18 100644 --- a/src/crystal/system/win32/file_descriptor.cr +++ b/src/crystal/system/win32/file_descriptor.cr @@ -178,6 +178,63 @@ module Crystal::System::FileDescriptor end end + private def system_flock_shared(blocking : Bool) : Nil + flock(false, blocking) + end + + private def system_flock_exclusive(blocking : Bool) : Nil + flock(true, blocking) + end + + private def system_flock_unlock : Nil + unlock_file(windows_handle) + end + + private def flock(exclusive, retry) + flags = LibC::LOCKFILE_FAIL_IMMEDIATELY + flags |= LibC::LOCKFILE_EXCLUSIVE_LOCK if exclusive + + handle = windows_handle + if retry + until lock_file(handle, flags) + sleep 0.1 + end + else + lock_file(handle, flags) || raise IO::Error.from_winerror("Error applying file lock: file is already locked") + end + end + + private def lock_file(handle, flags) + # lpOverlapped must be provided despite the synchronous use of this method. + overlapped = LibC::OVERLAPPED.new + # lock the entire file with offset 0 in overlapped and number of bytes set to max value + if 0 != LibC.LockFileEx(handle, flags, 0, 0xFFFF_FFFF, 0xFFFF_FFFF, pointerof(overlapped)) + true + else + winerror = WinError.value + if winerror == WinError::ERROR_LOCK_VIOLATION + false + else + raise IO::Error.from_os_error("LockFileEx", winerror, target: self) + end + end + end + + private def unlock_file(handle) + # lpOverlapped must be provided despite the synchronous use of this method. + overlapped = LibC::OVERLAPPED.new + # unlock the entire file with offset 0 in overlapped and number of bytes set to max value + if 0 == LibC.UnlockFileEx(handle, 0, 0xFFFF_FFFF, 0xFFFF_FFFF, pointerof(overlapped)) + raise IO::Error.from_winerror("UnLockFileEx") + end + end + + private def system_fsync(flush_metadata = true) : Nil + if LibC.FlushFileBuffers(windows_handle) == 0 + raise IO::Error.from_winerror("Error syncing file", target: self) + end + end + private PIPE_BUFFER_SIZE = 8192 def self.pipe(read_blocking, write_blocking)