Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iox-#2301 Mixed mode 32 and 64 bit #2348

Merged
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,18 @@ jobs:
uses: ./.github/actions/install-iceoryx-deps-and-clang
- run: ./tools/ci/build-test-ubuntu.sh 32-bit-x86

build-test-ubuntu-32-64-bit-mix-mode:
# prevent stuck jobs consuming runners for 6 hours
timeout-minutes: 60
runs-on: ubuntu-latest
needs: pre-flight-check
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install iceoryx dependencies and clang-tidy
uses: ./.github/actions/install-iceoryx-deps-and-clang
- run: ./tools/ci/build-test-32-64-bit-mix-mode.sh

# Bazel sanity check
build-test-ubuntu-bazel:
# prevent stuck jobs consuming runners for 6 hours
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
build/
build-*/
build_out_of_tree/
build_package/
install/
Expand Down
107 changes: 107 additions & 0 deletions doc/website/advanced/iceoray-on-32-bit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Status

iceoryx works on 32-bit hardware, but only as technology preview and is not meant for production.

See also https://github.com/eclipse-iceoryx/iceoryx/issues/2301 for more details and the limitations sections in this document.

# Dependencies

For 32-bit support, the following packages need to be installed on ubuntu

```bash
sudo dpkg --add-architecture i386
sudo apt install libacl1-dev:i386 libc6-dev-i386 libc6-dev-i386-cross libstdc++6-i386-cross gcc-multilib g++-multilib
```

# iceoryx as 32-bit library

## Build steps

The most simple way to build iceoryx is via the `iceoryx_build_test.sh` script
elBoberido marked this conversation as resolved.
Show resolved Hide resolved

```bash
tools/iceoryx_build_test.sh release 32-bit-x86
```

If the script cannot be used, this are the steps with `cmake` on x86

```bash
cmake -S iceoryx_meta -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-m32 -malign-double" -DCMAKE_CXX_FLAGS="-m32 -malign-double"
cmake --build build
```

The `-m32` flag tells GCC to build iceoryx as 32-bit library on a 64-bit system.
The `-malign-double` flag is required to have 64-bit atomics on an 8 byte boundary.
Furthermore, it is required for the 32-64 bit mix-mode to enforce the same data layout when 32-bit application communicate with 64-bit applications.

## Limitations

An internal data structure, the `UsedChunkList`, might be left in a corrupt state when an application terminates abnormally when writing to this data structure.
In order to detect torn-writes on 32-bit, the data structure needs to be refactored.

# iceoryx for communication between 32-bit and 64-bit applications aka 32-64 bit mix-mode

## Attention

Mixing 32-bit and 64-bit applications in a shared-memory environment is a non-trivial endeavor.
Since the data structures are shared between applications with varying bitness, one has to take special care of the layout of the data structures shared between the applications.

For example, the following struct has a size of 16 bytes and is aligned to 8 byte on common 64-bit architectures like x86-64.
But on common 32-bit architectures like x86, it has a size of 12 bytes and is aligned to 4 bytes.

```cpp
struct Foo {
bool bar {false};
uint64_t baz {0};
};
```

As long as the bitness it the same, one does not have to care and but with shared memory, both applications have to agree to a common layout, else the applications will misbehave.
elBoberido marked this conversation as resolved.
Show resolved Hide resolved
In the worst case, the application keeps running with invalid data and in the best case it leads to an immediate segmentation fault.

The most simple way to fix this specific alignment issue, is to use the `-malign-double` flag, which enforces an 8 byte alignment boundary for 64-bit data types on 32-bit architectures.
elBoberido marked this conversation as resolved.
Show resolved Hide resolved

## Build steps

Similar to the 32-bit build, the most simple way to build for the 32-64 bit mix-mode is the `iceoryx_build_test.sh` script
elBoberido marked this conversation as resolved.
Show resolved Hide resolved

```bash
tools/iceoryx_build_test.sh release examples 32-bit-x86 experimental-32-64-bit-mix-mode --build-dir build-32
tools/iceoryx_build_test.sh release examples experimental-32-64-bit-mix-mode --build-dir build-64
```

If the script cannot be used, this are the steps with `cmake` on x86

```bash
cmake -S iceoryx_meta -B build-32 -DCMAKE_BUILD_TYPE=Release -DEXAMPLES=ON -DCMAKE_C_FLAGS="-m32 -malign-double" -DCMAKE_CXX_FLAGS="-m32 -malign-double" -DIOX_EXPERIMENTAL_32_64_BIT_MIX_MODE=ON
cmake --build build-32

cmake -S iceoryx_meta -B build-64 -DCMAKE_BUILD_TYPE=Release -DEXAMPLES=ON -DIOX_EXPERIMENTAL_32_64_BIT_MIX_MODE=ON
cmake --build build-64
```

## Running the examples

You can now mix and match 32-bit and 64-bit applications

```bash
# terminal 1
build-32/iox-roudi

# terminal 2
build-64/iceoryx_examples/request_response/iox-cpp-request-response-listener-server

# terminal 3
build-32/iceoryx_examples/request_response/iox-cpp-request-response-waitset-client
```

## Limitations

In addition to the limitations of the 32-bit iceoryx, the mix-mode needs to ensure that all the data structures in shared memory have the same layout.
While the `-malign-double` flag can be used for the iceoryx data types, it does not work for POSIX data structures like `sem_t`.
These data types also have a different size for 32-bit and 64-bit architecture and are used in iceoryx in the shared-memory, e.g. for the `WaitSet`.
In order to make the iceoryx applications interoperable between 32-bit and 64-bit, a spin lock and a spin semaphore is used for their POSIX counterparts.
This can increase the CPU load and also the latency.

For a production environment, the spin semaphore and spin lock needs to be replaced by a `futex` on Linux and a `WaitOnAddress` call on Windows.
For other OSes, a proper solution is yet to be found.
2 changes: 1 addition & 1 deletion doc/website/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ All iceoryx libraries are deployed as independent CMake packages. Posh is using

### Dependencies

- 64-bit hardware (e.g. x86_64 or aarch64; 32-bit hardware might work, but is not supported)
- 64-bit hardware (e.g. x86_64 or aarch64; 32-bit hardware works, but only as technology preview and not meant for production)
- [CMake](https://cmake.org), 3.16 or later
- One of the following compilers:
- [GCC](https://gcc.gnu.org), 8.3 or later
Expand Down
3 changes: 2 additions & 1 deletion doc/website/release-notes/iceoryx-unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- The minimal supported GCC compiler is now 8.3
- The required C++ standard is now C++17
- Experimental 32-bit support for all platforms supporting 64-bit atomic operations
- Experimental support for communication between 32-bit and 64-bit applications

**Features:**

Expand Down Expand Up @@ -726,7 +727,7 @@
// after
iox::optional<iox::mutex> myMutex;
iox::MutexBuilder()
.mutexType(iox::MutexType::RECURSIVE)
.lock_behavior(iox::LockBehavior::RECURSIVE)
.create(myMutex);
myMutex->lock();
```
Expand Down
1 change: 1 addition & 0 deletions iceoryx_hoofs/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ cc_library(
srcs = glob([
"cli/source/*.cpp",
"concurrent/buffer/source/*.cpp",
"concurrent/sync/source/*.cpp",
"design/source/*.cpp",
"filesystem/source/*.cpp",
"memory/source/*.cpp",
Expand Down
4 changes: 3 additions & 1 deletion iceoryx_hoofs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ iox_add_library(
cli/source/option_definition.cpp
cli/source/option_manager.cpp
concurrent/buffer/source/mpmc_loffli.cpp
concurrent/sync/source/spin_lock.cpp
concurrent/sync/source/spin_semaphore.cpp
filesystem/source/file_reader.cpp
filesystem/source/filesystem.cpp
memory/source/bump_allocator.cpp
Expand Down Expand Up @@ -134,7 +136,7 @@ iox_add_library(
posix/sync/source/named_semaphore.cpp
posix/sync/source/signal_handler.cpp
posix/sync/source/signal_watcher.cpp
posix/sync/source/semaphore_interface.cpp
posix/sync/source/semaphore_helper.cpp
posix/sync/source/thread.cpp
posix/sync/source/unnamed_semaphore.cpp
posix/time/source/adaptive_wait.cpp
Expand Down
98 changes: 98 additions & 0 deletions iceoryx_hoofs/concurrent/sync/include/iox/spin_lock.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) 2024 by ekxide IO GmbH. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#ifndef IOX_HOOFS_CONCURRENT_SYNC_SPIN_LOCK_HPP
#define IOX_HOOFS_CONCURRENT_SYNC_SPIN_LOCK_HPP

#include "iceoryx_platform/unistd.hpp"
#include "iox/atomic.hpp"
#include "iox/lock_interface.hpp"

#include <thread>

namespace iox
{
namespace concurrent
{
class SpinLockBuilder;

/// @brief A spin lock implementation as drop-in replacement for a mutex
class SpinLock : public LockInterface<SpinLock>
{
public:
using Builder = SpinLockBuilder;

SpinLock(const SpinLock&) = delete;
SpinLock(SpinLock&&) = delete;
SpinLock& operator=(const SpinLock&) = delete;
SpinLock& operator=(SpinLock&&) = delete;

~SpinLock() noexcept = default;

private:
friend class optional<SpinLock>;
friend class LockInterface<SpinLock>;

explicit SpinLock(const LockBehavior lock_behavior) noexcept;

expected<void, LockError> lock_impl() noexcept;

expected<void, UnlockError> unlock_impl() noexcept;

expected<TryLock, TryLockError> try_lock_impl() noexcept;

struct LockInfo
{
pid_t tid;
uint32_t recursive_count;
};

private:
concurrent::AtomicFlag m_lock_flag =
ATOMIC_FLAG_INIT; // NOTE: only initialization via assignment is guaranteed to work
const concurrent::Atomic<bool> m_recursive{false};
concurrent::Atomic<pid_t> m_pid{0};
concurrent::Atomic<uint64_t> m_recursive_count{0};
concurrent::Atomic<std::thread::id> m_tid{};
};

class SpinLockBuilder
{
public:
enum class Error : uint8_t
{
LOCK_ALREADY_INITIALIZED,
INTER_PROCESS_LOCK_UNSUPPORTED_BY_PLATFORM,
UNKNOWN_ERROR
};

/// @brief Defines if the SpinLock should be usable in an inter process context. Default: true
IOX_BUILDER_PARAMETER(bool, is_inter_process_capable, true)

/// @brief Sets the LockBehavior, default: LockBehavior::RECURSIVE
IOX_BUILDER_PARAMETER(LockBehavior, lock_behavior, LockBehavior::RECURSIVE)

public:
/// @brief Initializes a provided uninitialized SpinLock
/// @param[in] uninitializedLock the uninitialized SpinLock which should be initialized
/// @return On failure LockCreationError which explains the error
expected<void, Error> create(optional<SpinLock>& uninitializedLock) noexcept;
};

} // namespace concurrent
} // namespace iox

#endif // IOX_HOOFS_CONCURRENT_SYNC_SPIN_LOCK_HPP
86 changes: 86 additions & 0 deletions iceoryx_hoofs/concurrent/sync/include/iox/spin_semaphore.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@

// Copyright (c) 2024 by ekxide IO GmbH. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#ifndef IOX_HOOFS_CONCURRENT_SYNC_SPIN_SEMAPHORE_HPP
#define IOX_HOOFS_CONCURRENT_SYNC_SPIN_SEMAPHORE_HPP

#include "iox/atomic.hpp"
#include "iox/deadline_timer.hpp"
#include "iox/detail/adaptive_wait.hpp"
#include "iox/optional.hpp"
#include "iox/semaphore_interface.hpp"
#include "iox/spin_lock.hpp"

namespace iox
{
namespace concurrent
{
class SpinSemaphoreBuilder;

class SpinSemaphore : public detail::SemaphoreInterface<SpinSemaphore>
{
public:
using Builder = SpinSemaphoreBuilder;

SpinSemaphore(const SpinSemaphore&) = delete;
SpinSemaphore(SpinSemaphore&&) = delete;
SpinSemaphore& operator=(const SpinSemaphore&) = delete;
SpinSemaphore& operator=(SpinSemaphore&&) = delete;

~SpinSemaphore() noexcept;

private:
friend class optional<SpinSemaphore>;
friend class detail::SemaphoreInterface<SpinSemaphore>;

explicit SpinSemaphore(int32_t initial_value) noexcept;

expected<void, SemaphoreError> post_impl() noexcept;

expected<void, SemaphoreError> wait_impl() noexcept;

expected<bool, SemaphoreError> try_wait_impl() noexcept;

expected<SemaphoreWaitState, SemaphoreError> timed_wait_impl(const units::Duration& timeout) noexcept;

private:
concurrent::Atomic<int32_t> m_count{0};
concurrent::Atomic<bool> m_to_be_destroyed{false};
optional<concurrent::SpinLock> m_spinlock;
};

class SpinSemaphoreBuilder
{
/// @brief Set the initial value of the spin semaphore
IOX_BUILDER_PARAMETER(uint32_t, initialValue, 0U)

/// @brief Set if the spin semaphore can be stored in the shared memory
/// for inter process usage
IOX_BUILDER_PARAMETER(bool, isInterProcessCapable, true)

public:
/// @brief Create a spin semaphore
/// @param[in] uninitializedSemaphore since the semaphore is not movable the user has to provide
/// memory to store the semaphore into - packed in an optional
/// @return an error describing the failure or success
expected<void, SemaphoreError> create(optional<SpinSemaphore>& uninitializedSemaphore) const noexcept;
};

} // namespace concurrent
} // namespace iox

#endif // IOX_HOOFS_CONCURRENT_SYNC_SPIN_LOCK_HPP
Loading
Loading