Skip to content

Commit

Permalink
Merge pull request #2348 from elBoberido/iox-2301-mixed-mode-32-and-6…
Browse files Browse the repository at this point in the history
…4-bit

iox-#2301 Mixed mode 32 and 64 bit
  • Loading branch information
elBoberido authored Sep 27, 2024
2 parents 05f686b + 8f8bf24 commit 00c5349
Show file tree
Hide file tree
Showing 33 changed files with 1,246 additions and 302 deletions.
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
108 changes: 108 additions & 0 deletions doc/website/advanced/iceoray-on-32-bit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# 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 simplest way to build iceoryx is via the `iceoryx_build_test.sh` script

```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 applications share the same bitness, there is no need for special consideration.
However, when connecting 32-bit and 64-bit applications via shared memory, both must adhere to a common memory layout.
If the layout differs, it can lead to unpredictable behavior and errors in the applications.
The simplest 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.
## Build steps
Similar to the 32-bit build, the simplest way to build for the 32-64 bit mix-mode is the `iceoryx_build_test.sh` script
```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

0 comments on commit 00c5349

Please sign in to comment.