iceoryx2_bb_posix/
memory_lock.rs

1// Copyright (c) 2023 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13//! A [`MemoryLock`] excludes a specific range in the memory from paging, e.g. makes it non-swapable.
14//! This may increase the runtime and reduce jitter in a realtime applications since the specific
15//! region inside the memory is not moved into the swap space.
16
17use crate::system_configuration::SystemInfo;
18use iceoryx2_bb_elementary::enum_gen;
19use iceoryx2_log::fatal_panic;
20use iceoryx2_pal_posix::posix::errno::Errno;
21use iceoryx2_pal_posix::*;
22
23#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
24pub enum MemoryLockCreationError {
25    InvalidAddressRange,
26    UnableToLock,
27    AddressNotAMultipleOfThePageSize,
28    InsufficientPermissions,
29    UnknownError(i32),
30}
31
32#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
33pub enum MemoryLockAllError {
34    UnableToLock,
35    WouldExceedMainMemory,
36    InsufficientPermissions,
37    UnknownError(i32),
38}
39
40enum_gen! {
41    /// The MemoryLockError enum is a generalization when one doesn't require the fine-grained error
42    /// handling enums. One can forward MemoryLockError as more generic return value when a method
43    /// returns a MemoryLock***Error.
44    /// On a higher level it is again convertable to [`crate::Error`].
45    MemoryLockError
46  generalization:
47    LockFailed <= MemoryLockCreationError; MemoryLockAllError
48}
49
50/// Required by [`MemoryLock::lock_all()`]. Defines what memory should be locked, either the
51/// current memory or all memory that will become mapped in the future.
52#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
53#[repr(i32)]
54pub enum LockMode {
55    LockAllPagesCurrentlyMapped = posix::MCL_CURRENT,
56    LockAllPagesThatBecomeMapped = posix::MCL_FUTURE,
57}
58
59/// A MemoryLock excludes a specific range in the memory from paging, e.g. makes it non-swapable.
60/// This may increase the runtime and reduce jitter in a realtime applications since the specific
61/// region inside the memory is not moved into the swap space.
62#[derive(Debug)]
63pub struct MemoryLock {
64    address: *const posix::void,
65    len: usize,
66}
67
68impl MemoryLock {
69    /// Locks a provided memory region. As soon as the memory lock goes out of scope the memory
70    /// region is unlocked again.
71    ///
72    /// # Safety
73    ///   * the memory range [address, len] be a valid address during the lifetime of [`MemoryLock`]
74    ///
75    pub unsafe fn new(
76        address: *const posix::void,
77        len: usize,
78    ) -> Result<MemoryLock, MemoryLockCreationError> {
79        if unsafe { posix::mlock(address, len) } == 0 {
80            return Ok(MemoryLock { address, len });
81        }
82
83        let msg = "Unable to lock memory";
84        handle_errno!(MemoryLockCreationError, from "MemoryLock::new",
85            Errno::ENOMEM => (InvalidAddressRange, "{} since the specified range beginning from {:#16X} with a length of {} is not contained in the valid mapped pages in the address spaces of the current process.", msg, address as usize, len),
86            Errno::EAGAIN => (UnableToLock, "{} since some or all memory could not be locked.", msg),
87            Errno::EINVAL => (AddressNotAMultipleOfThePageSize, "{} since the address {:#16X} is not a multiple of the page-size {}.", msg, address as usize, SystemInfo::PageSize.value()),
88            Errno::EPERM => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
89            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
90        );
91    }
92
93    /// Locks all pages with defined [`LockMode`]
94    pub fn lock_all(mode: LockMode) -> Result<(), MemoryLockAllError> {
95        if unsafe { posix::mlockall(mode as i32) } != -1 {
96            return Ok(());
97        }
98
99        let msg = "Unable to lock all memory with the mode";
100        handle_errno!(MemoryLockAllError, from "MemoryLock::lock_all",
101            Errno::EAGAIN => (UnableToLock, "{} {:?} since the memory could not be locked.", msg, mode),
102            Errno::ENOMEM => (WouldExceedMainMemory, "{} {:?} due to insufficient main memory.", msg, mode),
103            Errno::EPERM => (InsufficientPermissions, "{} {:?} due to insufficient permissions.", msg, mode),
104            v => (UnknownError(v as i32), "{} {:?} since an unknown error occurred ({}).", msg, mode, v)
105        );
106    }
107
108    /// Unlocks all pages.
109    pub fn unlock_all() {
110        unsafe { posix::munlockall() };
111    }
112}
113
114impl Drop for MemoryLock {
115    fn drop(&mut self) {
116        if unsafe { posix::munlock(self.address, self.len) } != 0 {
117            fatal_panic!(from self, "This should never happen! Unable to unlock memory region.");
118        }
119    }
120}