Skip to content

Commit

Permalink
Merge pull request #577 from elfenpiff/iox2-575-out-of-memory-issue-o…
Browse files Browse the repository at this point in the history
…n-windows

[#575] out of memory issue on windows
  • Loading branch information
elfenpiff authored Jan 9, 2025
2 parents 06c5c21 + 18f7b6a commit 3a3930b
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 27 deletions.
58 changes: 40 additions & 18 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,6 @@ which demonstrates the use of `FixedSizeByteString` and `FixedSizeVec`.
Take a look at the
[publish-subscribe dynamic data size example](examples/rust/publish_subscribe_dynamic_data_size).

The idea is to create a service based on a slice and define at runtime a
`max_slice_len`. Then samples up to a length of the max slice length can be
allocated with `loan_slice{_uninit}`. When it turns out that the slice length is
insufficient, a new publisher with a larger `max_slice_len` can be created.

<!-- markdownlint-disable -->

> [!IMPORTANT]
> Be aware that the history of the old publisher is lost when it is
> removed.
> [!NOTE]
> We are also working on an API that does not require the user to
> explicitly create a new publisher whenever the memory is insufficient. It
> would also solve the history issue.
<!-- markdownlint-enable -->

## How To Make 32-bit and 64-bit iceoryx2 Applications Interoperatable

This is currently not possible since we cannot guarantee to have the same
Expand Down Expand Up @@ -187,3 +169,43 @@ cleanup:
files with the `iox2` prefix from:
* POSIX: `/dev/shm/`, `/tmp/iceoryx2`
* Windows: `c:\Temp\iceoryx2`

## Run Out-Of-Memory When Creating Publisher With A Large Service Payload

Since iceoryx2 is designed to operate in safety-critical systems, it must
ensure that a publisher (or sender) never runs out of memory. To achieve this,
iceoryx2 preallocates a data segment for each new publisher. This segment is
sized to handle the worst-case service requirements, ensuring that every
subscriber can receive and store as many samples as permitted, even under
maximum load.

To minimize the required worst-case memory, you can adjust specific service
settings. For example:

```rust
let service = node
.service_builder(&"some service name".try_into()?)
.publish_subscribe::<[u8]>()
// Limit the maximum number of subscribers
.max_subscribers(1)
// Limit the number of samples a subscriber can hold simultaneously
.subscriber_max_borrowed_samples(1)
// Reduce the subscriber's overall sample buffer size
.subscriber_max_buffer_size(1)
// Limit the size of the publisher's history buffer
.history_size(1)
// ...
```

On the publisher side, you can also configure resource usage:

```rust
let publisher = service
.publisher_builder()
// Limit the number of samples a publisher can loan at once
.max_loaned_samples(1)
// ...
```

All these parameters can also be set globally by using the
[iceoryx2 config file](config).
3 changes: 3 additions & 0 deletions doc/release-notes/iceoryx2-unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

* Completion queue capacity exceeded when history > buffer
[#571](https://github.com/eclipse-iceoryx/iceoryx2/issues/571)
* Increase max supported shared memory size in Windows that restricts
the maximum supported payload size
[#575](https://github.com/eclipse-iceoryx/iceoryx2/issues/575)

### Refactoring

Expand Down
1 change: 1 addition & 0 deletions iceoryx2-bb/posix/src/shared_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ impl SharedMemory {
handle_errno!(SharedMemoryCreationError, from config,
Errno::EAGAIN => (InsufficientMemoryToBeMemoryLocked, "{} since a previous mlockall() enforces all mappings to be memory locked but this mapping cannot be locked due to insufficient memory.", msg),
Errno::EINVAL => (UnsupportedSizeOfZero, "{} since a size of zero is not supported.", msg),
Errno::ENOMEM => (InsufficientMemory, "{} since the system is out-of-memory or does not the support a shared memory with the size of {}.", msg, config.size),
Errno::EMFILE => (MappedRegionLimitReached, "{} since the number of mapped regions would exceed the process or system limit.", msg),
v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
);
Expand Down
2 changes: 1 addition & 1 deletion iceoryx2-pal/posix/src/windows/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub const PROT_EXEC: int = 8;
pub const MCL_CURRENT: int = 16;
pub const MCL_FUTURE: int = 32;
pub const MAP_SHARED: int = 64;
pub const MAP_FAILED: *mut void = u64::MAX as *mut void;
pub const MAP_FAILED: *mut void = 0 as *mut void;
pub const MQ_INVALID: mqd_t = mqd_t::MAX;

pub const PTHREAD_MUTEX_NORMAL: int = 1;
Expand Down
2 changes: 1 addition & 1 deletion iceoryx2-pal/posix/src/windows/fcntl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ pub unsafe fn fstat(fd: int, buf: *mut stat_t) -> int {
return -1;
}

file_stat.st_size = size as i32;
file_stat.st_size = size as _;
file_stat.st_mode = S_IFREG;

let mut info = BY_HANDLE_FILE_INFORMATION::new();
Expand Down
6 changes: 5 additions & 1 deletion iceoryx2-pal/posix/src/windows/mman.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use windows_sys::Win32::{

use super::win32_handle_translator::{FdHandleEntry, FileHandle, HandleTranslator, ShmHandle};

const MAX_SUPPORTED_SHM_SIZE: u64 = 1024 * 1024 * 1024;
const MAX_SUPPORTED_SHM_SIZE: u64 = 128 * 1024 * 1024 * 1024;

pub unsafe fn mlock(addr: *const void, len: size_t) -> int {
-1
Expand Down Expand Up @@ -259,6 +259,10 @@ pub(crate) unsafe fn shm_set_size(fd_handle: HANDLE, shm_size: u64) {
return;
}

if shm_size > MAX_SUPPORTED_SHM_SIZE {
eprintln!("Trying to allocate {shm_size} which is larger than the maximum supported shared memory size of {MAX_SUPPORTED_SHM_SIZE}");
}

let mut bytes_written = 0;

win32call! {SetFilePointer(fd_handle, 0, core::ptr::null_mut::<i32>(), FILE_BEGIN)};
Expand Down
2 changes: 1 addition & 1 deletion iceoryx2-pal/posix/src/windows/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub type long = core::ffi::c_long;
pub type mode_t = u64;
pub type mqd_t = u64;
pub type nlink_t = u64;
pub type off_t = crate::internal::off_t;
pub type off_t = i64;
pub type pid_t = u32;
pub type rlim_t = i64;
pub type __rlim_t = u64;
Expand Down
14 changes: 9 additions & 5 deletions iceoryx2-pal/posix/src/windows/unistd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use windows_sys::Win32::{
Networking::WinSock::closesocket,
Storage::FileSystem::{
FlushFileBuffers, GetFileAttributesA, ReadFile, RemoveDirectoryA, SetEndOfFile,
SetFilePointer, WriteFile, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_READONLY, FILE_BEGIN,
FILE_CURRENT, FILE_END, INVALID_FILE_ATTRIBUTES, INVALID_SET_FILE_POINTER,
SetFilePointerEx, WriteFile, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_READONLY, FILE_BEGIN,
FILE_CURRENT, FILE_END, INVALID_FILE_ATTRIBUTES,
},
System::{
Diagnostics::ToolHelp::{
Expand Down Expand Up @@ -263,9 +263,10 @@ pub unsafe fn lseek(fd: int, offset: off_t, whence: int) -> off_t {
}
};

let (new_position, _) = win32call! {SetFilePointer(handle.handle, offset, core::ptr::null_mut::<i32>(), move_method)};
let mut new_position = 0;
let (has_success, _) = win32call! {SetFilePointerEx(handle.handle, offset, &mut new_position, move_method)};

if new_position == INVALID_SET_FILE_POINTER {
if has_success == 0 {
return -1;
}

Expand Down Expand Up @@ -307,12 +308,15 @@ pub unsafe fn ftruncate(fd: int, length: off_t) -> int {
0
}
Some(FdHandleEntry::File(handle)) => {
win32call! { SetFilePointer(
let (result, _) = win32call! { SetFilePointerEx(
handle.handle,
length as _,
core::ptr::null_mut(),
FILE_BEGIN,
)};
if result == 0 {
return -1;
}
win32call! { SetEndOfFile(handle.handle)};
0
}
Expand Down

0 comments on commit 3a3930b

Please sign in to comment.