LLFIO v2.00
Loading...
Searching...
No Matches
llfio_v2_xxx::algorithm::shared_fs_mutex::byte_ranges Class Reference

Many entity shared/exclusive file system based lock. More...

#include "byte_ranges.hpp"

Inheritance diagram for llfio_v2_xxx::algorithm::shared_fs_mutex::byte_ranges:
llfio_v2_xxx::algorithm::shared_fs_mutex::shared_fs_mutex

Public Types

using entity_type = shared_fs_mutex::entity_type
 The type of an entity id.
 
using entities_type = shared_fs_mutex::entities_type
 The type of a sequence of entities.
 

Public Member Functions

 byte_ranges (const byte_ranges &)=delete
 No copy construction.
 
byte_rangesoperator= (const byte_ranges &)=delete
 No copy assignment.
 
 byte_ranges (byte_ranges &&o) noexcept
 Move constructor.
 
byte_rangesoperator= (byte_ranges &&o) noexcept
 Move assign.
 
const file_handlehandle () const noexcept
 Return the handle to file being used for this lock.
 
virtual void unlock (entities_type entities, unsigned long long) noexcept final
 Unlock a previously locked sequence of entities.
 
entity_type entity_from_buffer (const char *buffer, size_t bytes, bool exclusive=true) noexcept
 Generates an entity id from a sequence of bytes.
 
template<typename T >
entity_type entity_from_string (const std::basic_string< T > &str, bool exclusive=true) noexcept
 Generates an entity id from a string.
 
entity_type random_entity (bool exclusive=true) noexcept
 Generates a cryptographically random entity id.
 
void fill_random_entities (span< entity_type > seq, bool exclusive=true) noexcept
 Fills a sequence of entity ids with cryptographic randomness. Much faster than calling random_entity() individually.
 
result< entities_guardlock (entities_type entities, deadline d=deadline(), bool spin_not_sleep=false) noexcept
 Lock all of a sequence of entities for exclusive or shared access.
 
result< entities_guardlock (entity_type entity, deadline d=deadline(), bool spin_not_sleep=false) noexcept
 Lock a single entity for exclusive or shared access.
 
result< entities_guardtry_lock (entities_type entities) noexcept
 Try to lock all of a sequence of entities for exclusive or shared access.
 
result< entities_guardtry_lock (entity_type entity) noexcept
 Try to lock a single entity for exclusive or shared access.
 

Static Public Member Functions

static result< byte_rangesfs_mutex_byte_ranges (const path_handle &base, path_view lockfile) noexcept
 Initialises a shared filing system mutex using the file at lockfile.
 

Protected Member Functions

virtual result< void > _lock (entities_guard &out, deadline d, bool spin_not_sleep) noexcept final
 

Detailed Description

Many entity shared/exclusive file system based lock.

This is a simple many entity shared mutex. It works by locking in the same file the byte at the offset of the entity id. If it fails to lock a byte, it backs out all preceding locks, randomises the order and tries locking them again until success. Needless to say this algorithm puts a lot of strain on your byte range locking implementation, some NFS implementations have been known to fail to cope.

Note
Most users will want to use safe_byte_ranges instead of this class directly.
  • Compatible with networked file systems, though be cautious with older NFS.
  • Linear complexity to number of concurrent users.
  • Exponential complexity to number of entities being concurrently locked, though some OSs provide linear complexity so long as total concurrent waiting processes is CPU core count or less.
  • Does a reasonable job of trying to sleep the thread if any of the entities are locked.
  • Sudden process exit with lock held is recovered from.
  • Sudden power loss during use is recovered from.
  • Safe for multithreaded usage of the same instance.

Caveats:

  • When entities being locked is more than one, the algorithm places the contending lock at the front of the list during the randomisation after lock failure so we can sleep the thread until it becomes free. However, under heavy churn the thread will generally spin, consuming 100% CPU.
  • Byte range locks need to work properly on your system. Misconfiguring NFS or Samba to cause byte range locks to not work right will produce bad outcomes.
  • If your OS doesn't have sane byte range locks (OS X, BSD, older Linuxes) and multiple objects in your process use the same lock file, misoperation will occur. Use lock_files or share a single instance of this class per lock file in this case.
  • If you are on POSIX and the same process relocks an entity a second time, it will release everything on the first unlock. On Windows, the first unlock releases the exclusive lock and the second unlock will release the shared lock.

Constructor & Destructor Documentation

◆ byte_ranges()

llfio_v2_xxx::algorithm::shared_fs_mutex::byte_ranges::byte_ranges ( byte_ranges &&  o)
inlinenoexcept

Move constructor.

94: _h(std::move(o._h)) {}

Member Function Documentation

◆ _lock()

virtual result< void > llfio_v2_xxx::algorithm::shared_fs_mutex::byte_ranges::_lock ( entities_guard out,
deadline  d,
bool  spin_not_sleep 
)
inlinefinalprotectedvirtualnoexcept

Implements llfio_v2_xxx::algorithm::shared_fs_mutex::shared_fs_mutex.

120 {
121 LLFIO_LOG_FUNCTION_CALL(this);
122 std::chrono::steady_clock::time_point began_steady;
123 std::chrono::system_clock::time_point end_utc;
124 if(d)
125 {
126 if((d).steady)
127 {
128 began_steady = std::chrono::steady_clock::now();
129 }
130 else
131 {
132 end_utc = (d).to_time_point();
133 }
134 }
135 // Fire this if an error occurs
136 auto disableunlock = make_scope_exit([&]() noexcept { out.release(); });
137 size_t n;
138 for(;;)
139 {
140 auto was_contended = static_cast<size_t>(-1);
141 {
142 auto undo = make_scope_exit([&]() noexcept {
143 // 0 to (n-1) need to be closed
144 if(n > 0)
145 {
146 --n;
147 // Now 0 to n needs to be closed
148 for(; n > 0; n--)
149 {
150 _h.unlock_file_range(out.entities[n].value, 1);
151 }
152 _h.unlock_file_range(out.entities[0].value, 1);
153 }
154 });
155 for(n = 0; n < out.entities.size(); n++)
156 {
157 deadline nd;
158 // Only for very first entity will we sleep until its lock becomes available
159 if(n != 0u)
160 {
161 nd = deadline(std::chrono::seconds(0));
162 }
163 else
164 {
165 nd = deadline();
166 if(d)
167 {
168 if((d).steady)
169 {
170 std::chrono::nanoseconds ns = std::chrono::duration_cast<std::chrono::nanoseconds>((began_steady + std::chrono::nanoseconds((d).nsecs)) - std::chrono::steady_clock::now());
171 if(ns.count() < 0)
172 {
173 (nd).nsecs = 0;
174 }
175 else
176 {
177 (nd).nsecs = ns.count();
178 }
179 }
180 else
181 {
182 (nd) = (d);
183 }
184 }
185 }
186 auto outcome = _h.lock_file_range(out.entities[n].value, 1, (out.entities[n].exclusive != 0u) ? lock_kind::exclusive : lock_kind::shared, nd);
187 if(!outcome)
188 {
189 was_contended = n;
190 goto failed;
191 }
192 outcome.value().release();
193 }
194 // Everything is locked, exit
195 undo.release();
196 disableunlock.release();
197 return success();
198 }
199 failed:
200 if(d)
201 {
202 if((d).steady)
203 {
204 if(std::chrono::steady_clock::now() >= (began_steady + std::chrono::nanoseconds((d).nsecs)))
205 {
206 return errc::timed_out;
207 }
208 }
209 else
210 {
211 if(std::chrono::system_clock::now() >= end_utc)
212 {
213 return errc::timed_out;
214 }
215 }
216 }
217 // Move was_contended to front and randomise rest of out.entities
218 std::swap(out.entities[was_contended], out.entities[0]);
219 auto front = out.entities.begin();
220 ++front;
221 QUICKCPPLIB_NAMESPACE::algorithm::small_prng::random_shuffle(front, out.entities.end());
222 if(!spin_not_sleep)
223 {
224 std::this_thread::yield();
225 }
226 }
227 // return success();
228 }
virtual void unlock_file_range(extent_type offset, extent_type bytes) noexcept
EXTENSION: Unlocks a byte range previously locked.
virtual result< extent_guard > lock_file_range(extent_type offset, extent_type bytes, lock_kind kind, deadline d=deadline()) noexcept
EXTENSION: Tries to lock the range of bytes specified for shared or exclusive access....
@ shared
Exclude only those requesting an exclusive lock on the same inode.
@ exclusive
Exclude those requesting any kind of lock on the same inode.

◆ entity_from_buffer()

entity_type llfio_v2_xxx::algorithm::shared_fs_mutex::shared_fs_mutex::entity_from_buffer ( const char *  buffer,
size_t  bytes,
bool  exclusive = true 
)
inlinenoexceptinherited

Generates an entity id from a sequence of bytes.

116 {
117 uint128 hash = QUICKCPPLIB_NAMESPACE::algorithm::hash::fast_hash::hash(buffer, bytes);
118 return {hash.as_longlongs[0] ^ hash.as_longlongs[1], exclusive};
119 }
QUICKCPPLIB_NAMESPACE::integers128::uint128 uint128
Unsigned 128 bit integer.
Definition base.hpp:45

◆ entity_from_string()

template<typename T >
entity_type llfio_v2_xxx::algorithm::shared_fs_mutex::shared_fs_mutex::entity_from_string ( const std::basic_string< T > &  str,
bool  exclusive = true 
)
inlinenoexceptinherited

Generates an entity id from a string.

122 {
123 uint128 hash = QUICKCPPLIB_NAMESPACE::algorithm::hash::fast_hash::hash(str);
124 return {hash.as_longlongs[0] ^ hash.as_longlongs[1], exclusive};
125 }

◆ fill_random_entities()

void llfio_v2_xxx::algorithm::shared_fs_mutex::shared_fs_mutex::fill_random_entities ( span< entity_type seq,
bool  exclusive = true 
)
inlinenoexceptinherited

Fills a sequence of entity ids with cryptographic randomness. Much faster than calling random_entity() individually.

135 {
136 utils::random_fill(reinterpret_cast<char *>(seq.data()), seq.size() * sizeof(entity_type));
137 for(auto &i : seq)
138 {
139 i.exclusive = exclusive; // NOLINT
140 }
141 }
void random_fill(char *buffer, size_t bytes) noexcept
Fills the buffer supplied with cryptographically strong randomness. Uses the OS kernel API.

◆ fs_mutex_byte_ranges()

static result< byte_ranges > llfio_v2_xxx::algorithm::shared_fs_mutex::byte_ranges::fs_mutex_byte_ranges ( const path_handle base,
path_view  lockfile 
)
inlinestaticnoexcept

Initialises a shared filing system mutex using the file at lockfile.

109 {
110 LLFIO_LOG_FUNCTION_CALL(0);
111 OUTCOME_TRY(auto &&ret, file_handle::file(base, lockfile, file_handle::mode::write, file_handle::creation::if_needed, file_handle::caching::temporary));
112 return byte_ranges(std::move(ret));
113 }
static result< file_handle > file(const path_handle &base, path_view_type path, mode _mode=mode::read, creation _creation=creation::open_existing, caching _caching=caching::all, flag flags=flag::none) noexcept

◆ handle()

const file_handle & llfio_v2_xxx::algorithm::shared_fs_mutex::byte_ranges::handle ( ) const
inlinenoexcept

Return the handle to file being used for this lock.

116{ return _h; }

◆ lock() [1/2]

result< entities_guard > llfio_v2_xxx::algorithm::shared_fs_mutex::shared_fs_mutex::lock ( entities_type  entities,
deadline  d = deadline(),
bool  spin_not_sleep = false 
)
inlinenoexceptinherited

Lock all of a sequence of entities for exclusive or shared access.

222 {
223 entities_guard ret(this, entities);
224 OUTCOME_TRYV(_lock(ret, d, spin_not_sleep));
225 return {std::move(ret)};
226 }

◆ lock() [2/2]

result< entities_guard > llfio_v2_xxx::algorithm::shared_fs_mutex::shared_fs_mutex::lock ( entity_type  entity,
deadline  d = deadline(),
bool  spin_not_sleep = false 
)
inlinenoexceptinherited

Lock a single entity for exclusive or shared access.

229 {
230 entities_guard ret(this, entity);
231 OUTCOME_TRYV(_lock(ret, d, spin_not_sleep));
232 return {std::move(ret)};
233 }

◆ operator=()

byte_ranges & llfio_v2_xxx::algorithm::shared_fs_mutex::byte_ranges::operator= ( byte_ranges &&  o)
inlinenoexcept

Move assign.

97 {
98 if(this == &o)
99 {
100 return *this;
101 }
102 _h = std::move(o._h);
103 return *this;
104 }

◆ random_entity()

entity_type llfio_v2_xxx::algorithm::shared_fs_mutex::shared_fs_mutex::random_entity ( bool  exclusive = true)
inlinenoexceptinherited

Generates a cryptographically random entity id.

128 {
130 utils::random_fill(reinterpret_cast<char *>(&v), sizeof(v));
131 return {v, exclusive};
132 }
handle::extent_type value_type
The type backing the value.
Definition base.hpp:63

◆ try_lock() [1/2]

result< entities_guard > llfio_v2_xxx::algorithm::shared_fs_mutex::shared_fs_mutex::try_lock ( entities_type  entities)
inlinenoexceptinherited

Try to lock all of a sequence of entities for exclusive or shared access.

235{ return lock(entities, deadline(std::chrono::seconds(0))); }
result< entities_guard > lock(entities_type entities, deadline d=deadline(), bool spin_not_sleep=false) noexcept
Lock all of a sequence of entities for exclusive or shared access.
Definition base.hpp:221

◆ try_lock() [2/2]

result< entities_guard > llfio_v2_xxx::algorithm::shared_fs_mutex::shared_fs_mutex::try_lock ( entity_type  entity)
inlinenoexceptinherited

Try to lock a single entity for exclusive or shared access.

237{ return lock(entity, deadline(std::chrono::seconds(0))); }

◆ unlock()

virtual void llfio_v2_xxx::algorithm::shared_fs_mutex::byte_ranges::unlock ( entities_type  entities,
unsigned long long  hint 
)
inlinefinalvirtualnoexcept

Unlock a previously locked sequence of entities.

Implements llfio_v2_xxx::algorithm::shared_fs_mutex::shared_fs_mutex.

232 {
233 LLFIO_LOG_FUNCTION_CALL(this);
234 for(const auto &i : entities)
235 {
236 _h.unlock_file_range(i.value, 1);
237 }
238 }

The documentation for this class was generated from the following file: