Skip to content

Commit

Permalink
Allow #fsync and #flock_* on IO::FileDescriptor (#14432)
Browse files Browse the repository at this point in the history
  • Loading branch information
HertzDevil committed Apr 15, 2024
1 parent 2383ec1 commit 944bee2
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 127 deletions.
54 changes: 0 additions & 54 deletions src/crystal/system/unix/file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
54 changes: 54 additions & 0 deletions src/crystal/system/unix/file_descriptor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 0 additions & 16 deletions src/crystal/system/wasi/file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions src/crystal/system/wasi/file_descriptor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
57 changes: 0 additions & 57 deletions src/crystal/system/win32/file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
57 changes: 57 additions & 0 deletions src/crystal/system/win32/file_descriptor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 944bee2

Please sign in to comment.