1pub use crate::read_write_mutex::*;
42
43use crate::file_descriptor::FileDescriptor;
44use crate::file_descriptor::FileDescriptorBased;
45use crate::process::{Process, ProcessId};
46use core::fmt::Debug;
47use core::{ops::Deref, ops::DerefMut};
48use iceoryx2_bb_concurrency::atomic::AtomicI64;
49use iceoryx2_bb_concurrency::atomic::Ordering;
50use iceoryx2_bb_elementary::enum_gen;
51use iceoryx2_log::fail;
52use iceoryx2_pal_posix::posix::errno::Errno;
53use iceoryx2_pal_posix::posix::MemZeroedStruct;
54use iceoryx2_pal_posix::*;
55
56use crate::clock::NanosleepError;
57
58enum_gen! { FileWriterGetLockError
59 mapping:
60 FileTryLockError,
61 NanosleepError,
62 ReadWriteMutexWriteLockError
63}
64
65enum_gen! { FileReaderGetLockError
66 mapping:
67 FileTryLockError,
68 NanosleepError,
69 ReadWriteMutexReadLockError
70}
71
72enum_gen! { FileTryLockError
73 entry:
74 Interrupt,
75 ExceedsMaximumNumberOfLockedRegionsInSystem,
76 InvalidFileDescriptorOrWrongOpenMode,
77 DeadlockConditionDetected,
78 UnknownError(i32)
79}
80
81enum_gen! { FileWriterTryLockError
82 mapping:
83 FileTryLockError,
84 ReadWriteMutexWriteLockError
85}
86
87enum_gen! { FileReaderTryLockError
88 mapping:
89 FileTryLockError,
90 ReadWriteMutexReadLockError
91}
92
93enum_gen! { FileUnlockError
94 entry:
95 Interrupt,
96 InvalidFileDescriptorOrWrongOpenMode,
97 IsNotLocked,
98 UnknownError(i32)
99}
100
101enum_gen! { FileLockStateError
102 entry:
103 InvalidFileDescriptor,
104 Interrupt,
105 UnknownError(i32)
106
107 mapping:
108 ReadWriteMutexReadLockError
109}
110
111enum_gen! {
112 FileLockError
117 generalization:
118 UnableToAcquireLock <= FileWriterGetLockError; FileReaderGetLockError; FileTryLockError; FileWriterTryLockError; FileReaderTryLockError; FileUnlockError; FileLockStateError
119}
120
121#[derive(Debug)]
126pub struct FileLockWriteGuard<'handle, 'b, T: FileDescriptorBased + Debug> {
127 file_lock: &'handle FileLock<'b, T>,
128 guard: MutexWriteGuard<'handle, T>,
129}
130
131unsafe impl<T: Send + FileDescriptorBased + Debug> Send for FileLockWriteGuard<'_, '_, T> {}
132unsafe impl<T: Send + Sync + FileDescriptorBased + Debug> Sync for FileLockWriteGuard<'_, '_, T> {}
133
134impl<T: FileDescriptorBased + Debug> Deref for FileLockWriteGuard<'_, '_, T> {
135 type Target = T;
136
137 fn deref(&self) -> &Self::Target {
138 &self.guard
139 }
140}
141
142impl<T: FileDescriptorBased + Debug> DerefMut for FileLockWriteGuard<'_, '_, T> {
143 fn deref_mut(&mut self) -> &mut Self::Target {
144 &mut self.guard
145 }
146}
147
148impl<T: FileDescriptorBased + Debug> Drop for FileLockWriteGuard<'_, '_, T> {
149 fn drop(&mut self) {
150 self.file_lock.release(self.guard.file_descriptor()).ok();
151 }
152}
153
154#[derive(Debug)]
159pub struct FileLockReadGuard<'handle, 'b, T: FileDescriptorBased + Debug> {
160 file_lock: &'handle FileLock<'b, T>,
161 guard: MutexReadGuard<'handle, T>,
162}
163
164unsafe impl<T: Send + FileDescriptorBased + Debug> Send for FileLockReadGuard<'_, '_, T> {}
165unsafe impl<T: Send + Sync + FileDescriptorBased + Debug> Sync for FileLockReadGuard<'_, '_, T> {}
166
167impl<T: FileDescriptorBased + Debug> Deref for FileLockReadGuard<'_, '_, T> {
168 type Target = T;
169
170 fn deref(&self) -> &Self::Target {
171 &self.guard
172 }
173}
174
175impl<T: FileDescriptorBased + Debug> Drop for FileLockReadGuard<'_, '_, T> {
176 fn drop(&mut self) {
177 self.file_lock.release(self.guard.file_descriptor()).ok();
178 }
179}
180
181#[derive(Debug, Default)]
186pub struct FileLockBuilder {}
187
188impl FileLockBuilder {
189 pub fn new() -> Self {
190 Self::default()
191 }
192
193 pub fn create<T: FileDescriptorBased + Debug>(
194 self,
195 value: T,
196 handle: &ReadWriteMutexHandle<T>,
197 ) -> Result<FileLock<'_, T>, ReadWriteMutexCreationError> {
198 FileLock::new(value, self, handle)
199 }
200}
201
202#[derive(Debug)]
214pub struct FileLock<'a, T: FileDescriptorBased + Debug> {
215 file: ReadWriteMutex<'a, 'a, T>,
216 lock_state: AtomicI64,
217}
218
219unsafe impl<T: Send + FileDescriptorBased + Debug> Send for FileLock<'_, T> {}
220unsafe impl<T: Send + Sync + FileDescriptorBased + Debug> Sync for FileLock<'_, T> {}
221
222#[derive(Debug, Clone, Copy, PartialEq, Eq)]
223#[repr(i16)]
224pub enum LockType {
225 Read = posix::F_RDLCK as i16,
226 Write = posix::F_WRLCK as i16,
227 Unlock = posix::F_UNLCK as i16,
228}
229
230#[derive(Debug)]
234pub struct LockState {
235 lock_type: LockType,
236 pid: ProcessId,
237}
238
239impl LockState {
240 pub fn lock_type(&self) -> LockType {
241 self.lock_type
242 }
243
244 pub fn pid_of_owner(&self) -> ProcessId {
245 self.pid
246 }
247}
248
249#[derive(Debug, Clone, Copy, PartialEq, Eq)]
250enum InternalMode {
251 Blocking,
252 NonBlocking,
253}
254
255impl<'a, T: FileDescriptorBased + Debug> FileLock<'a, T> {
256 fn new(
257 value: T,
258 config: FileLockBuilder,
259 handle: &'a ReadWriteMutexHandle<T>,
260 ) -> Result<Self, ReadWriteMutexCreationError> {
261 Ok(Self {
262 file: fail!(from config, when ReadWriteMutexBuilder::new()
263 .is_interprocess_capable(false)
264 .create(value, handle),
265 "Failed to create ReadWriteMutex for FileLock."),
266 lock_state: AtomicI64::new(0),
267 })
268 }
269
270 pub fn write_lock(&self) -> Result<FileLockWriteGuard<'_, '_, T>, FileWriterGetLockError> {
276 let guard = fail!(from self, when self.file.write_blocking_lock(),
277 "Failed to acquire writer mutex lock in write_lock");
278 self.internal_lock(
279 LockType::Write,
280 InternalMode::Blocking,
281 guard.file_descriptor(),
282 )?;
283
284 Ok(FileLockWriteGuard {
285 file_lock: self,
286 guard,
287 })
288 }
289
290 pub fn write_try_lock(
296 &self,
297 ) -> Result<Option<FileLockWriteGuard<'_, '_, T>>, FileWriterTryLockError> {
298 let guard = fail!(from self, when self.file.write_try_lock(),
299 "Failed while trying to acquire writer mutex lock in write_try_lock");
300
301 if guard.is_none() {
302 return Ok(None);
303 }
304
305 match self.internal_lock(
306 LockType::Write,
307 InternalMode::NonBlocking,
308 guard.as_ref().unwrap().file_descriptor(),
309 )? {
310 true => Ok(Some(FileLockWriteGuard {
311 file_lock: self,
312 guard: guard.unwrap(),
313 })),
314 false => Ok(None),
315 }
316 }
317
318 pub fn read_lock(&self) -> Result<FileLockReadGuard<'_, '_, T>, FileReaderGetLockError> {
323 let guard = fail!(from self, when self.file.read_blocking_lock(),
324 "Failed to acquire reader mutex lock in read_lock");
325
326 self.internal_lock(
327 LockType::Read,
328 InternalMode::Blocking,
329 guard.file_descriptor(),
330 )?;
331
332 Ok(FileLockReadGuard {
333 file_lock: self,
334 guard,
335 })
336 }
337
338 pub fn read_try_lock(
343 &self,
344 ) -> Result<Option<FileLockReadGuard<'_, '_, T>>, FileReaderTryLockError> {
345 let guard = fail!(from self, when self.file.read_try_lock(),
346 "Failed while trying to acquire reader mutex lock in read_try_lock");
347
348 if guard.is_none() {
349 return Ok(None);
350 }
351
352 match self.internal_lock(
353 LockType::Read,
354 InternalMode::NonBlocking,
355 guard.as_ref().unwrap().file_descriptor(),
356 )? {
357 true => Ok(Some(FileLockReadGuard {
358 file_lock: self,
359 guard: guard.unwrap(),
360 })),
361 false => Ok(None),
362 }
363 }
364
365 pub fn get_lock_state(&self) -> Result<LockState, FileLockStateError> {
367 match 0.cmp(&self.lock_state.load(Ordering::Relaxed)) {
368 core::cmp::Ordering::Less => {
369 return Ok(LockState {
370 lock_type: LockType::Read,
371 pid: Process::from_self().id(),
372 })
373 }
374 core::cmp::Ordering::Greater => {
375 return Ok(LockState {
376 lock_type: LockType::Write,
377 pid: Process::from_self().id(),
378 })
379 }
380 core::cmp::Ordering::Equal => (),
381 }
382
383 let msg = "Unable to acquire current file lock state";
384 let mut current_lock_state = posix::flock::new_zeroed();
385 current_lock_state.l_type = posix::F_WRLCK as _;
386
387 let fd_guard = fail!(from self, when self.file.read_blocking_lock(),
388 "{} due to an internal failure in while acquiring the mutex.", msg);
389
390 match unsafe {
391 posix::fcntl(
392 fd_guard.file_descriptor().native_handle(),
393 posix::F_GETLK,
394 &mut current_lock_state,
395 )
396 } != -1
397 {
398 true => Ok(LockState {
399 lock_type: match current_lock_state.l_type as i32 {
400 posix::F_WRLCK => LockType::Write,
401 posix::F_RDLCK => LockType::Read,
402 _ => LockType::Unlock,
403 },
404 pid: ProcessId::new(current_lock_state.l_pid),
405 }),
406 false => handle_errno!(FileLockStateError, from self,
407 Errno::EBADF => (InvalidFileDescriptor, "{} since the file-descriptor is invalid or not opened in the correct mode.", msg),
408 Errno::EINTR => (Interrupt, "{} since an interrupt signal was received.", msg),
409 v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
410 ),
411 }
412 }
413
414 fn release(&self, file_descriptor: &FileDescriptor) -> Result<(), FileUnlockError> {
415 let mut new_lock_state = posix::flock::new_zeroed();
416 new_lock_state.l_type = LockType::Unlock as _;
417 new_lock_state.l_whence = posix::SEEK_SET as _;
418
419 let msg = "Unable to release file-lock";
420 if unsafe {
421 posix::fcntl(
422 file_descriptor.native_handle(),
423 posix::F_SETLK,
424 &mut new_lock_state,
425 )
426 } != -1
427 {
428 self.set_lock_state(LockType::Unlock);
429 return Ok(());
430 }
431
432 handle_errno!(FileUnlockError, from self,
433 Errno::EBADF => (InvalidFileDescriptorOrWrongOpenMode, "{} since the file-descriptor is invalid or not opened in the correct mode.", msg),
434 Errno::EINTR => (Interrupt, "{} since an interrupt signal was received.", msg),
435 v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
436 );
437 }
438
439 fn internal_lock(
440 &self,
441 lock_type: LockType,
442 mode: InternalMode,
443 file_descriptor: &FileDescriptor,
444 ) -> Result<bool, FileTryLockError> {
445 let mut new_lock_state = posix::flock::new_zeroed();
446 new_lock_state.l_type = lock_type as _;
447 new_lock_state.l_whence = posix::SEEK_SET as _;
448
449 if unsafe {
450 posix::fcntl(
451 file_descriptor.native_handle(),
452 if mode == InternalMode::NonBlocking {
453 posix::F_SETLK
454 } else {
455 posix::F_SETLKW
456 },
457 &mut new_lock_state,
458 )
459 } != -1
460 {
461 self.set_lock_state(lock_type);
462 return Ok(true);
463 }
464
465 let msg = match lock_type {
466 LockType::Read => "Unable to acquire read file-lock",
467 _ => "Unable to acquire write file-lock",
468 };
469
470 handle_errno!(FileTryLockError, from self,
471 success Errno::EACCES => false;
472 success Errno::EAGAIN => false,
473 Errno::EBADF => (InvalidFileDescriptorOrWrongOpenMode, "{} since the file-descriptor is invalid or not opened in the correct mode.", msg),
474 Errno::EINTR => (Interrupt, "{} since an interrupt signal was received.", msg),
475 Errno::ENOLCK => (ExceedsMaximumNumberOfLockedRegionsInSystem, "{} since it would exceed the maximum supported number of locked regions in the system..", msg),
476 Errno::EDEADLK => (DeadlockConditionDetected, "{} since a deadlock condition was detected.", msg),
477 v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
478 );
479 }
480
481 fn set_lock_state(&self, value: LockType) {
482 let current_value = self.lock_state.load(Ordering::Relaxed);
483 let adjustment = match value {
484 LockType::Read => 1,
485 LockType::Write => -1,
486 LockType::Unlock => {
487 if current_value > 0 {
488 -1
489 } else {
490 1
491 }
492 }
493 };
494
495 self.lock_state.fetch_add(adjustment, Ordering::Relaxed);
496 }
497}