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

merge develop #75

Merged
merged 3 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
Binary file not shown.
Binary file not shown.
6 changes: 4 additions & 2 deletions .github/workflows/code_testing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ jobs:
compiler: clang-17
- os: ubuntu-22.04
compiler: clang-18
- os: ubuntu-22.04
compiler: clang-19

- os: ubuntu-22.04
compiler: gcc-12
Expand Down Expand Up @@ -45,15 +47,15 @@ jobs:
- name: Configure conan
uses: dice-group/cpp-conan-release-reusable-workflow/.github/actions/configure_conan@main
with:
conan-version: 2.3.1
conan-version: 2.12.2

- name: add conan user
run: |
conan remote add -f dice-group https://conan.dice-research.org/artifactory/api/conan/tentris

- name: Cache conan data
id: cache-conan
uses: actions/cache@v4.0.2
uses: actions/cache@v4
with:
path: ~/.conan2/p
key: ${{ matrix.config.os }}-${{ matrix.config.compiler }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/detect-pobr-diff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ jobs:
uses: dice-group/cpp-conan-release-reusable-workflow/.github/workflows/abi-diff.yml@main
with:
os: ubuntu-22.04
compiler: clang-16
compiler: clang-17
cmake-version: 3.24.0
conan-version: 2.3.1
conan-version: 2.12.2
base-branch: ${{ github.base_ref }}
search-path: >
include/dice/template-library/flex_array.hpp
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish-conan-branch-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ jobs:
with:
public_artifactory: true
os: ubuntu-22.04
compiler: clang-18
compiler: clang-17
cmake-version: 3.24.0
conan-version: 2.3.1
conan-version: 2.12.2
secrets:
CONAN_USER: ${{ secrets.CONAN_USER }}
CONAN_PW: ${{ secrets.CONAN_PW }}
4 changes: 2 additions & 2 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ jobs:
with:
public_artifactory: true
os: ubuntu-22.04
compiler: clang-14
compiler: clang-17
cmake-version: 3.24.0
conan-version: 2.3.0
conan-version: 2.12.2
secrets:
CONAN_USER: ${{ secrets.CONAN_USER }}
CONAN_PW: ${{ secrets.CONAN_PW }}
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.24)

project(
dice-template-library
VERSION 1.10.1
VERSION 1.11.0
DESCRIPTION
"This template library is a collection of template-oriented code that we, the Data Science Group at UPB, found pretty handy. It contains: `switch_cases` (Use runtime values in compile-time context), `integral_template_tuple` (Create a tuple-like structure that instantiates a template for a range of values), `integral_template_variant` (A wrapper type for `std::variant` guarantees to only contain variants of the form `T<IX>` and `for_{types,values,range}` (Compile time for loops for types, values or ranges))."
HOMEPAGE_URL "https://dice-research.org/")
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ It contains:
- `generator`: The reference implementation of `std::generator` from [P2502R2](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2502r2.pdf)
- `channel`: A single producer, single consumer queue
- `variant2`: Like `std::variant` but optimized for exactly two types
- `mutex`/`shared_mutex`: Rust inspired mutex interfaces that hold their data instead of living next to it

## Usage

Expand Down Expand Up @@ -107,6 +108,11 @@ Additionally, `visit` does not involve any virtual function calls.
### `type_traits.hpp`
Things that are missing in the standard library `<type_traits>` header.

### `mutex`/`shared_mutex`
Rust inspired mutex interfaces that hold their data instead of living next to it.
The benefit of this approach is that it makes it harder (impossible in rust) to access the
data without holding the mutex.

### Further Examples

Compilable code examples can be found in [examples](./examples). The example build requires the cmake
Expand Down
16 changes: 16 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,19 @@ target_link_libraries(example_pool_allocator
PRIVATE
dice-template-library::dice-template-library
)


add_executable(example_mutex
example_mutex.cpp)
target_link_libraries(example_mutex
PRIVATE
dice-template-library::dice-template-library
)

add_executable(example_shared_mutex
example_shared_mutex.cpp)
target_link_libraries(example_shared_mutex
PRIVATE
dice-template-library::dice-template-library
)

15 changes: 15 additions & 0 deletions examples/example_mutex.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <dice/template-library/mutex.hpp>

#include <cassert>
#include <thread>

int main() {
dice::template_library::mutex<int> mut{0};

std::thread thrd{[&]() {
*mut.lock() = 5;
}};

thrd.join();
assert(*mut.lock() == 5);
}
22 changes: 22 additions & 0 deletions examples/example_shared_mutex.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <dice/template-library/shared_mutex.hpp>

#include <cassert>
#include <thread>

int main() {
dice::template_library::shared_mutex<int> mut{0};

std::thread thrd1{[&]() {
*mut.lock() = 5;
}};

thrd1.join();

std::thread thrd2{[&]() {
assert(*mut.lock_shared() == 5);
}};

thrd2.join();

assert(*mut.lock() == 5);
}
134 changes: 134 additions & 0 deletions include/dice/template-library/mutex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#ifndef DICE_TEMPLATELIBRARY_MUTEX_HPP
#define DICE_TEMPLATELIBRARY_MUTEX_HPP

#include <mutex>
#include <optional>
#include <type_traits>
#include <utility>

namespace dice::template_library {

template<typename T, typename Mutex = std::mutex>
struct mutex;

/**
* An RAII guard for a value behind a mutex.
* When this mutex_guard is dropped the lock is automatically released.
*
* @note to use this correctly it is important to understand that unlike rust, C++ cannot
* enforce that you do not use pointers given out by this type beyond its lifetime.
* You may not store pointers or references given out via this wrapper beyond its lifetime, otherwise behaviour is undefined.
*
* @tparam T the value type protected by the mutex
* @tparam Mutex the mutex type
*/
template<typename T, typename Mutex = std::mutex>
struct mutex_guard {
using value_type = T;
using mutex_type = Mutex;

private:
friend struct mutex<T, Mutex>;

value_type *value_ptr_;
std::unique_lock<mutex_type> lock_;

mutex_guard(std::unique_lock<mutex_type> &&lock, T &value) noexcept
: value_ptr_{&value},
lock_{std::move(lock)} {
}

public:
mutex_guard() = delete;
mutex_guard(mutex_guard const &other) noexcept = delete;
mutex_guard &operator=(mutex_guard const &other) noexcept = delete;
mutex_guard(mutex_guard &&other) noexcept = default;
mutex_guard &operator=(mutex_guard &&other) noexcept = default;
~mutex_guard() = default;

value_type *operator->() const noexcept {
return value_ptr_;
}

value_type &operator*() const noexcept {
return *value_ptr_;
}

friend void swap(mutex_guard const &lhs, mutex_guard const &rhs) noexcept {
using std::swap;
swap(lhs.value_ptr_, rhs.value_ptr_);
swap(lhs.lock_, rhs.lock_);
}
};

/**
* A rust-like mutex type (https://doc.rust-lang.org/std/sync/struct.Mutex.html) that holds its data instead of living next to it.
*
* @note Because this is C++ it cannot be fully safe, like the rust version is, for more details see doc comment on mutex_guard.
* @note This type is non-movable and non-copyable, if you need to do either of these things use `std::unique_ptr<mutex<T>>` or `std::shared_ptr<mutex<T>>`
*
* @tparam T value type stored
* @tparam Mutex the mutex type
*/
template<typename T, typename Mutex>
struct mutex {
using value_type = T;
using mutex_type = Mutex;

private:
value_type value_;
mutex_type mutex_;

public:
constexpr mutex() noexcept(std::is_nothrow_default_constructible_v<value_type>) = default;
constexpr ~mutex() noexcept(std::is_nothrow_destructible_v<value_type>) = default;

mutex(mutex const &other) = delete;
mutex(mutex &&other) = delete;
mutex &operator=(mutex const &other) = delete;
mutex &operator=(mutex &&other) = delete;

explicit constexpr mutex(value_type const &value) noexcept(std::is_nothrow_copy_constructible_v<value_type>)
: value_{value} {
}

explicit constexpr mutex(value_type &&value) noexcept(std::is_nothrow_move_constructible_v<value_type>)
: value_{std::move(value)} {
}

template<typename ...Args>
explicit constexpr mutex(std::in_place_t, Args &&...args) noexcept(std::is_nothrow_constructible_v<value_type, decltype(std::forward<Args>(args))...>)
: value_{std::forward<Args>(args)...} {
}

/**
* Lock the mutex and return a guard that will keep it locked until it goes out of scope and
* allows access to the inner value.
*
* @return mutex guard for the inner value
* @throws std::system_error in case the underlying mutex implementation throws it
*/
[[nodiscard]] mutex_guard<value_type> lock() {
return mutex_guard<value_type>{std::unique_lock<mutex_type>{mutex_}, value_};
}

/**
* Attempt to lock the mutex and return a guard that will keep it locked until it goes out of scope and
* allows access to the inner value.
*
* @return nullopt in case the mutex could not be locked, otherwise a mutex guard for the inner value
* @throws std::system_error in case the underlying mutex implementation throws it
*/
[[nodiscard]] std::optional<mutex_guard<value_type>> try_lock() {
std::unique_lock<mutex_type> lock{mutex_, std::try_to_lock};
if (!lock.owns_lock()) {
return std::nullopt;
}

return mutex_guard<value_type>{std::move(lock), value_};
}
};

} // namespace dice::template_library

#endif // DICE_TEMPLATELIBRARY_MUTEX_HPP
Loading