Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/llfio.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'include/llfio/v2.0/file_handle.hpp')
-rw-r--r--include/llfio/v2.0/file_handle.hpp166
1 files changed, 166 insertions, 0 deletions
diff --git a/include/llfio/v2.0/file_handle.hpp b/include/llfio/v2.0/file_handle.hpp
index b3a0ebc6..c70d192b 100644
--- a/include/llfio/v2.0/file_handle.hpp
+++ b/include/llfio/v2.0/file_handle.hpp
@@ -74,6 +74,14 @@ public:
using ino_t = fs_handle::ino_t;
using path_view_type = fs_handle::path_view_type;
+ //! The kinds of concurrent user exclusion which can be performed.
+ enum class lock_kind
+ {
+ unknown,
+ shared, //!< Exclude only those requesting an exclusive lock on the same inode.
+ exclusive //!< Exclude those requesting any kind of lock on the same inode.
+ };
+
protected:
io_service *_service{nullptr};
@@ -254,6 +262,164 @@ public:
return std::move(ret).error();
}
+ /*! \class extent_guard
+ \brief EXTENSION: RAII holder a locked extent of bytes in a file.
+ */
+ class extent_guard
+ {
+ friend class file_handle;
+ file_handle *_h{nullptr};
+ extent_type _offset{0}, _length{0};
+ lock_kind _kind{lock_kind::unknown};
+
+ protected:
+ constexpr extent_guard(file_handle *h, extent_type offset, extent_type length, lock_kind kind)
+ : _h(h)
+ , _offset(offset)
+ , _length(length)
+ , _kind(kind)
+ {
+ }
+
+ public:
+ extent_guard(const extent_guard &) = delete;
+ extent_guard &operator=(const extent_guard &) = delete;
+
+ //! Default constructor
+ constexpr extent_guard() {} // NOLINT
+ //! Move constructor
+ extent_guard(extent_guard &&o) noexcept
+ : _h(o._h)
+ , _offset(o._offset)
+ , _length(o._length)
+ , _kind(o._kind)
+ {
+ o.release();
+ }
+ //! Move assign
+ extent_guard &operator=(extent_guard &&o) noexcept
+ {
+ unlock();
+ _h = o._h;
+ _offset = o._offset;
+ _length = o._length;
+ _kind = o._kind;
+ o.release();
+ return *this;
+ }
+ ~extent_guard()
+ {
+ if(_h != nullptr)
+ {
+ unlock();
+ }
+ }
+ //! True if extent guard is valid
+ explicit operator bool() const noexcept { return _h != nullptr; }
+
+ //! The `file_handle` to be unlocked
+ file_handle *handle() const noexcept { return _h; }
+ //! Sets the `file_handle` to be unlocked
+ void set_handle(file_handle *h) noexcept { _h = h; }
+ //! The extent to be unlocked
+ std::tuple<extent_type, extent_type, lock_kind> extent() const noexcept { return std::make_tuple(_offset, _length, _kind); }
+
+ //! Unlocks the locked extent immediately
+ void unlock() noexcept
+ {
+ if(_h != nullptr)
+ {
+ _h->unlock_range(_offset, _length);
+ release();
+ }
+ }
+
+ //! Detach this RAII unlocker from the locked state
+ void release() noexcept
+ {
+ _h = nullptr;
+ _offset = 0;
+ _length = 0;
+ _kind = lock_kind::unknown;
+ }
+ };
+
+ /*! \brief EXTENSION: Tries to lock the range of bytes specified for shared or exclusive access. Be aware
+ this passes through the same semantics as the underlying OS call, including any POSIX insanity
+ present on your platform:
+
+ - Any fd closed on an inode must release all byte range locks on that inode for all
+ other fds. If your OS isn't new enough to support the non-insane lock API,
+ `flag::byte_lock_insanity` will be set in flags() after the first call to this function.
+ - Threads replace each other's locks, indeed locks replace each other's locks.
+
+ You almost cetainly should use your choice of an `algorithm::shared_fs_mutex::*` instead of this
+ as those are more portable and performant, or use the `SharedMutex` modelling member functions
+ which lock the whole inode for exclusive or shared access.
+
+ \warning This is a low-level API which you should not use directly in portable code. Another
+ issue is that atomic lock upgrade/downgrade, if your platform implements that (you should assume
+ it does not in portable code), means that on POSIX you need to *release* the old `extent_guard`
+ after creating a new one over the same byte range, otherwise the old `extent_guard`'s destructor
+ will simply unlock the range entirely. On Windows however upgrade/downgrade locks overlay, so on
+ that platform you must *not* release the old `extent_guard`. Look into
+ `algorithm::shared_fs_mutex::safe_byte_ranges` for a portable solution.
+
+ \return An extent guard, the destruction of which will call unlock().
+ \param offset The offset to lock. Note that on POSIX the top bit is always cleared before use
+ as POSIX uses signed transport for offsets. If you want an advisory rather than mandatory lock
+ on Windows, one technique is to force top bit set so the region you lock is not the one you will
+ i/o - obviously this reduces maximum file size to (2^63)-1.
+ \param bytes The number of bytes to lock.
+ \param kind Whether the lock is to be shared or exclusive.
+ \param d An optional deadline by which the lock must complete, else it is cancelled.
+ \errors Any of the values POSIX fcntl() can return, `errc::timed_out`, `errc::not_supported` may be
+ returned if deadline i/o is not possible with this particular handle configuration (e.g.
+ non-overlapped HANDLE on Windows).
+ \mallocs The default synchronous implementation in file_handle performs no memory allocation.
+ The asynchronous implementation in async_file_handle performs one calloc and one free.
+ */
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<extent_guard> lock_range(extent_type offset, extent_type bytes, lock_kind kind, deadline d = deadline()) noexcept;
+ //! \overload
+ result<extent_guard> try_lock_range(extent_type offset, extent_type bytes, lock_kind kind) noexcept { return lock_range(offset, bytes, kind, deadline(std::chrono::seconds(0))); }
+ //! \overload EXTENSION: Locks for shared access
+ result<extent_guard> lock_range(io_request<buffers_type> reqs, deadline d = deadline()) noexcept
+ {
+ size_t bytes = 0;
+ for(auto &i : reqs.buffers)
+ {
+ if(bytes + i.size() < bytes)
+ {
+ return errc::value_too_large;
+ }
+ bytes += i.size();
+ }
+ return lock_range(reqs.offset, bytes, lock_kind::shared, d);
+ }
+ //! \overload EXTENSION: Locks for exclusive access
+ result<extent_guard> lock_range(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept
+ {
+ size_t bytes = 0;
+ for(auto &i : reqs.buffers)
+ {
+ if(bytes + i.size() < bytes)
+ {
+ return errc::value_too_large;
+ }
+ bytes += i.size();
+ }
+ return lock_range(reqs.offset, bytes, lock_kind::exclusive, d);
+ }
+
+ /*! \brief EXTENSION: Unlocks a byte range previously locked.
+
+ \param offset The offset to unlock. This should be an offset previously locked.
+ \param bytes The number of bytes to unlock. This should be a byte extent previously locked.
+ \errors Any of the values POSIX fcntl() can return.
+ \mallocs None.
+ */
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC void unlock_range(extent_type offset, extent_type bytes) noexcept;
+
/*! Return the current maximum permitted extent of the file.
\errors Any of the values POSIX fstat() or GetFileInformationByHandleEx() can return.