Skip to content

Commit 97c4cb4

Browse files
committed
[fiber] Implement std concurrency interfaces
1 parent b521355 commit 97c4cb4

9 files changed

Lines changed: 812 additions & 0 deletions

File tree

src/modm/processing/fiber.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@
1212
#include "fiber/fiber.hpp"
1313
#include "fiber/scheduler.hpp"
1414
#include "fiber/functions.hpp"
15+
#include "fiber/mutex.hpp"
16+
#include "fiber/shared_mutex.hpp"
17+
#include "fiber/semaphore.hpp"

src/modm/processing/fiber/module.lb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ def build(env):
7474
env.copy("context.h")
7575
env.template("stack.hpp.in")
7676
env.template("scheduler.hpp.in")
77+
env.template("mutex.hpp.in")
78+
env.template("semaphore.hpp.in")
79+
env.template("shared_mutex.hpp.in")
7780
env.copy("task.hpp")
7881
env.copy("functions.hpp")
7982
env.copy("fiber.hpp")
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright (c) 2023, Niklas Hauser
3+
*
4+
* This file is part of the modm project.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
// ----------------------------------------------------------------------------
11+
12+
#pragma once
13+
14+
#include "functions.hpp"
15+
%% if multicore
16+
#include <modm/platform/core/multicore.hpp>
17+
%% endif
18+
19+
namespace modm::fiber
20+
{
21+
22+
/// @ingroup modm_processing_fiber
23+
/// @{
24+
25+
/// Implements the `std::mutex` interface for fibers.
26+
/// @see https://en.cppreference.com/w/cpp/thread/mutex
27+
class mutex
28+
{
29+
mutex(const mutex&) = delete;
30+
mutex& operator=(const mutex&) = delete;
31+
32+
volatile bool locked{false};
33+
public:
34+
constexpr mutex() = default;
35+
36+
[[nodiscard]] bool inline
37+
try_lock()
38+
{
39+
%% if multicore
40+
modm::platform::multicore::SystemSpinLockGuard g;
41+
%% endif
42+
if (locked) return false;
43+
locked = true;
44+
return true;
45+
}
46+
47+
void inline
48+
lock()
49+
{
50+
while(not try_lock()) modm::this_fiber::yield();
51+
}
52+
53+
void inline
54+
unlock()
55+
{
56+
locked = false;
57+
}
58+
};
59+
60+
/// Implements the `std::timed_mutex` interface for fibers.
61+
/// @see https://en.cppreference.com/w/cpp/thread/timed_mutex
62+
class timed_mutex : public mutex
63+
{
64+
public:
65+
template< typename Rep, typename Period >
66+
[[nodiscard]] bool
67+
try_lock_for(std::chrono::duration<Rep, Period> sleep_duration)
68+
{
69+
return this_fiber::sleep_condition_for(sleep_duration, [this](){ return try_lock(); });
70+
}
71+
72+
template< class Clock, class Duration >
73+
[[nodiscard]] bool
74+
try_lock_until(std::chrono::time_point<Clock, Duration> sleep_time)
75+
{
76+
return this_fiber::sleep_condition_until(sleep_time, [this](){ return try_lock(); });
77+
}
78+
};
79+
80+
/// Implements the `std::recursive_mutex` interface for fibers.
81+
/// @see https://en.cppreference.com/w/cpp/thread/recursive_mutex
82+
class recursive_mutex
83+
{
84+
recursive_mutex(const recursive_mutex&) = delete;
85+
recursive_mutex& operator=(const recursive_mutex&) = delete;
86+
using count_t = uint16_t;
87+
88+
static constexpr fiber::id NoOwner{fiber::id(-1)};
89+
volatile fiber::id owner{NoOwner};
90+
static constexpr count_t countMax{count_t(-1)};
91+
volatile count_t count{1};
92+
93+
public:
94+
constexpr recursive_mutex() = default;
95+
96+
[[nodiscard]] bool inline
97+
try_lock()
98+
{
99+
%% if multicore
100+
modm::platform::multicore::SystemSpinLockGuard g;
101+
%% endif
102+
const auto id = modm::this_fiber::get_id();
103+
if (owner == NoOwner) {
104+
owner = id;
105+
// count = 1; is implicit
106+
return true;
107+
}
108+
if (owner == id and count < countMax) {
109+
count++;
110+
return true;
111+
}
112+
return false;
113+
}
114+
115+
void inline
116+
lock()
117+
{
118+
while(not try_lock()) modm::this_fiber::yield();
119+
}
120+
121+
void inline
122+
unlock()
123+
{
124+
if (count > 1) count--;
125+
else {
126+
// count = 1; is implicit
127+
owner = NoOwner;
128+
}
129+
}
130+
};
131+
132+
/// Implements the `std::timed_recursive_mutex` interface for fibers.
133+
/// @see https://en.cppreference.com/w/cpp/thread/recursive_mutex
134+
class timed_recursive_mutex : public recursive_mutex
135+
{
136+
public:
137+
template< typename Rep, typename Period >
138+
[[nodiscard]] bool
139+
try_lock_for(std::chrono::duration<Rep, Period> sleep_duration)
140+
{
141+
return this_fiber::sleep_condition_for(sleep_duration, [this](){ return try_lock(); });
142+
}
143+
144+
template< class Clock, class Duration >
145+
[[nodiscard]] bool
146+
try_lock_until(std::chrono::time_point<Clock, Duration> sleep_time)
147+
{
148+
return this_fiber::sleep_condition_until(sleep_time, [this](){ return try_lock(); });
149+
}
150+
};
151+
152+
/// @}
153+
154+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright (c) 2023, Niklas Hauser
3+
*
4+
* This file is part of the modm project.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
// ----------------------------------------------------------------------------
11+
12+
#pragma once
13+
14+
#include "functions.hpp"
15+
%% if multicore
16+
#include <modm/platform/core/multicore.hpp>
17+
%% endif
18+
19+
namespace modm::fiber
20+
{
21+
22+
/// @ingroup modm_processing_fiber
23+
/// @{
24+
25+
/// Implements the `std::counting_semaphore` interface for fibers.
26+
/// @see https://en.cppreference.com/w/cpp/thread/counting_semaphore
27+
template< std::ptrdiff_t LeastMaxValue = 255 >
28+
class counting_semaphore
29+
{
30+
counting_semaphore(const counting_semaphore&) = delete;
31+
counting_semaphore& operator=(const counting_semaphore&) = delete;
32+
using count_t = std::conditional_t<LeastMaxValue < 256, uint8_t, uint16_t>;
33+
34+
volatile count_t count{};
35+
static_assert(LeastMaxValue < 65'536, "counting_semaphore uses a 16-bit counter!");
36+
public:
37+
constexpr explicit
38+
counting_semaphore(count_t desired)
39+
: count(desired) {}
40+
41+
[[nodiscard]] static constexpr std::ptrdiff_t
42+
max() { return count_t(-1); }
43+
44+
[[nodiscard]] bool inline
45+
try_acquire()
46+
{
47+
%% if multicore
48+
modm::platform::multicore::SystemSpinLockGuard g;
49+
%% endif
50+
if (count == 0) return false;
51+
count--;
52+
return true;
53+
}
54+
55+
void inline
56+
acquire()
57+
{
58+
while(not try_acquire()) modm::this_fiber::yield();
59+
}
60+
61+
void inline
62+
release()
63+
{
64+
count++;
65+
}
66+
67+
template< typename Rep, typename Period >
68+
[[nodiscard]] bool
69+
try_acquire_for(std::chrono::duration<Rep, Period> sleep_duration)
70+
{
71+
return this_fiber::sleep_condition_for(sleep_duration, [this](){ return try_acquire(); });
72+
}
73+
74+
template< class Clock, class Duration >
75+
[[nodiscard]] bool
76+
try_acquire_until(std::chrono::time_point<Clock, Duration> sleep_time)
77+
{
78+
return this_fiber::sleep_condition_until(sleep_time, [this](){ return try_acquire(); });
79+
}
80+
};
81+
82+
/// Implements the `std::binary_semaphore` interface for fibers.
83+
/// @see https://en.cppreference.com/w/cpp/thread/counting_semaphore
84+
using binary_semaphore = counting_semaphore<1>;
85+
86+
/// @}
87+
88+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright (c) 2023, Niklas Hauser
3+
*
4+
* This file is part of the modm project.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
// ----------------------------------------------------------------------------
11+
12+
#pragma once
13+
14+
#include "functions.hpp"
15+
%% if multicore
16+
#include <modm/platform/core/multicore.hpp>
17+
%% endif
18+
19+
namespace modm::fiber
20+
{
21+
22+
/// @ingroup modm_processing_fiber
23+
/// @{
24+
25+
/// Implements the `std::shared_mutex` interface for fibers.
26+
/// @see https://en.cppreference.com/w/cpp/thread/shared_mutex
27+
class shared_mutex
28+
{
29+
shared_mutex(const shared_mutex&) = delete;
30+
shared_mutex& operator=(const shared_mutex&) = delete;
31+
32+
static constexpr fiber::id NoOwner{fiber::id(-1)};
33+
static constexpr fiber::id SharedOwner{fiber::id(-2)};
34+
volatile fiber::id owner{NoOwner};
35+
public:
36+
constexpr shared_mutex() = default;
37+
38+
[[nodiscard]] bool inline
39+
try_lock()
40+
{
41+
%% if multicore
42+
modm::platform::multicore::SystemSpinLockGuard g;
43+
%% endif
44+
if (owner != NoOwner) return false;
45+
owner = modm::this_fiber::get_id();
46+
return true;
47+
}
48+
49+
void inline
50+
lock()
51+
{
52+
while(not try_lock()) modm::this_fiber::yield();
53+
}
54+
55+
void inline
56+
unlock()
57+
{
58+
owner = NoOwner;
59+
}
60+
61+
[[nodiscard]] bool inline
62+
try_lock_shared()
63+
{
64+
%% if multicore
65+
modm::platform::multicore::SystemSpinLockGuard g;
66+
%% endif
67+
if (owner < SharedOwner) return false;
68+
owner = SharedOwner;
69+
return true;
70+
}
71+
72+
void inline
73+
lock_shared()
74+
{
75+
while(not try_lock_shared()) modm::this_fiber::yield();
76+
}
77+
78+
void inline
79+
unlock_shared()
80+
{
81+
owner = NoOwner;
82+
}
83+
};
84+
85+
/// Implements the `std::timed_shared_mutex` interface for fibers.
86+
/// @see https://en.cppreference.com/w/cpp/thread/timed_shared_mutex
87+
class timed_shared_mutex : public shared_mutex
88+
{
89+
public:
90+
template< typename Rep, typename Period >
91+
[[nodiscard]] bool
92+
try_lock_for(std::chrono::duration<Rep, Period> sleep_duration)
93+
{
94+
return this_fiber::sleep_condition_for(sleep_duration, [this](){ return try_lock(); });
95+
}
96+
97+
template< class Clock, class Duration >
98+
[[nodiscard]] bool
99+
try_lock_until(std::chrono::time_point<Clock, Duration> sleep_time)
100+
{
101+
return this_fiber::sleep_condition_until(sleep_time, [this](){ return try_lock(); });
102+
}
103+
104+
template< typename Rep, typename Period >
105+
[[nodiscard]] bool
106+
try_lock_shared_for(std::chrono::duration<Rep, Period> sleep_duration)
107+
{
108+
return this_fiber::sleep_condition_for(sleep_duration, [this](){ return try_lock_shared(); });
109+
}
110+
111+
template< class Clock, class Duration >
112+
[[nodiscard]] bool
113+
try_lock_shared_until(std::chrono::time_point<Clock, Duration> sleep_time)
114+
{
115+
return this_fiber::sleep_condition_until(sleep_time, [this](){ return try_lock_shared(); });
116+
}
117+
};
118+
119+
/// @}
120+
121+
}

0 commit comments

Comments
 (0)