Skip to content

Commit 239944a

Browse files
authored
Merge pull request #134 from cactusdynamics/examples-update
Batch 1 of example updates
2 parents 4450f9c + f88257c commit 239944a

File tree

20 files changed

+207
-167
lines changed

20 files changed

+207
-167
lines changed

CMakeLists.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,10 @@ if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME})
183183

184184
if (ENABLE_EXAMPLES)
185185
message(STATUS "Building example programs. Turn it off via ENABLE_EXAMPLES=OFF")
186-
add_subdirectory(examples/lockless_example)
186+
add_subdirectory(examples/lockless_examples)
187187
add_subdirectory(examples/logging_example)
188188
add_subdirectory(examples/message_passing_example)
189189
add_subdirectory(examples/mutex_example)
190-
add_subdirectory(examples/signal_handling_example)
191190
add_subdirectory(examples/simple_deadline_example)
192191
add_subdirectory(examples/simple_example)
193192
add_subdirectory(examples/random_example)

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,22 @@ writing a real-time Linux application. Some key features are:
2929
Examples
3030
--------
3131

32-
See each example's README for more details on what they do.
33-
3432
* [`simple_example`](examples/simple_example/): The most basic example showing
35-
a single real-time looping thread.
36-
* [`signal_handling_example`](examples/signal_handling_example/): Same as
37-
`simple_example`, except the program respond to SIGTERM and SIGINT and quit
38-
upon receiving the signal.
39-
* [`logging_example`](examples/logging_example/): Demonstrates setting up custom
40-
logging configuration via `cactus_rt::App`.
33+
a single real-time looping thread running at 1000 Hz.
34+
* [`tracing_example`](examples/tracing_example/): This demonstrates how to use
35+
the real-time-safe tracing system built into cactus-rt. This is probably a
36+
good thing to undrestand immediately after the above example.
4137
* [`mutex_example`](examples/mutex_example/): Demonstrates the usage of
4238
priority-inheritence mutex (`cactus_rt::mutex`) to pass data between real-time
43-
and non-real-time threads.
39+
and non-real-time threads via the implementation of a mutex-based double
40+
buffer.
41+
* [`logging_example`](examples/logging_example/): Demonstrates setting up custom
42+
logging configuration via `cactus_rt::App`.
43+
4444
* [`simple_deadline_example`](examples/simple_deadline_example/): Same as
4545
`simple_example`, except it uses `SCHED_DEADLINE` as opposed to `SCHED_FIFO`.
4646
This is for a more advanced use case.
47-
* [`tracing_example`](examples/tracing_example/): Shows how to dynamically start and stop tracing, as well as trace custom application functions.
47+
4848
* [`tracing_example_no_rt`](examples/tracing_example_no_rt/): Shows how to using the tracing library in `cactus_rt` without using `cactus_rt::App`.
4949

5050

examples/lockless_example/CMakeLists.txt

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
add_executable(rt_lockless_realtime_read_example
2+
realtime_read.cc
3+
)
4+
5+
target_link_libraries(rt_lockless_realtime_read_example
6+
PRIVATE
7+
cactus_rt
8+
)
9+
10+
setup_cactus_rt_target_options(rt_lockless_realtime_read_example)

examples/lockless_examples/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Lockless examples
2+
=================
3+

examples/mutex_example/README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@
44
This program shows the usage of sharing data between an RT and a non-RT thread
55
via the `cactus_rt::mutex`, which is a priority-inheritence mutex compatible
66
with the [`Lockable`](https://en.cppreference.com/w/cpp/named_req/Lockable)
7-
interface.
7+
interface. This means you can use this `mutex` just like you would a normal
8+
mutex from STL and expect that priority inheritance is enabled on it.
89

9-
Specifically, this example implements a very naive double buffer. This data
10-
structure has 2 data slots guarded by the priority-inheriting mutex. The RT
10+
In this example, we use the `cactus_rt::mutex` to implement a very naive double
11+
buffer. It has 2 data slots guarded by the priority-inheriting mutex. The RT
1112
thread writes to the double buffer at 1 kHz and the non-RT thread reads it every
1213
half second. Once it is read, it is logged via the `cactus_rt` logging
1314
capability.
1415

16+
_Note: a lockless version of this double buffer is implemented by the cactus-rt framework under `cactus_rt::experimental::lockless::spsc::AtomicWritableValue` which doesn't require a lock. That serves as an alternative to this code without the usage of a mutex._
17+
1518
To run:
1619

1720
```bash

examples/mutex_example/double_buffer.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
#include <cactus_rt/mutex.h>
55

6-
#include <cstdint>
76
#include <mutex>
87

98
/**
@@ -12,7 +11,11 @@
1211
* Also only uses a single mutex so reads, writes, and swaps contend on a
1312
* single lock, which is no good for performance.
1413
*
15-
* Realistically, you would implement this in a lock-free manner, like this:
14+
* cactus-rt already has something for this use case (real-time thread writes
15+
* and non-real-time thread reads) implemented via the
16+
* cactus_rt::experimental::lockless::spsc::RealtimeWritableValue.
17+
*
18+
* There could also be alternative implementations like:
1619
* https://stackoverflow.com/questions/23666069/single-producer-single-consumer-data-structure-with-double-buffer-in-c
1720
*/
1821
template <typename T>

examples/mutex_example/main.cc

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ using cactus_rt::App;
1010
using cactus_rt::CyclicThread;
1111
using cactus_rt::Thread;
1212

13+
// This is the data structure we are passing between the RT and non-RT thread.
14+
// It is big enough such that it cannot be atomically changed with a single
15+
// instruction to necessitate a mutex.
1316
struct Data {
1417
double v1 = 0.0;
1518
double v2 = 0.0;
@@ -21,8 +24,8 @@ class RTThread : public CyclicThread {
2124
NaiveDoubleBuffer<Data>& buf_;
2225

2326
public:
24-
explicit RTThread(const char* name, cactus_rt::CyclicThreadConfig config, NaiveDoubleBuffer<Data>& buf)
25-
: CyclicThread(name, config),
27+
explicit RTThread(NaiveDoubleBuffer<Data>& buf)
28+
: CyclicThread("RTThread", CreateConfig()),
2629
buf_(buf) {}
2730

2831
protected:
@@ -40,14 +43,24 @@ class RTThread : public CyclicThread {
4043

4144
return LoopControl::Continue;
4245
}
46+
47+
private:
48+
static cactus_rt::CyclicThreadConfig CreateConfig() {
49+
cactus_rt::CyclicThreadConfig thread_config;
50+
thread_config.period_ns = 1'000'000;
51+
thread_config.SetFifoScheduler(80);
52+
53+
return thread_config;
54+
}
4355
};
4456

4557
class NonRTThread : public Thread {
4658
NaiveDoubleBuffer<Data>& buf_;
4759

4860
public:
49-
explicit NonRTThread(const char* name, cactus_rt::CyclicThreadConfig config, NaiveDoubleBuffer<Data>& buf)
50-
: Thread(name, config), buf_(buf) {}
61+
explicit NonRTThread(NaiveDoubleBuffer<Data>& buf)
62+
: Thread("NonRTThread", CreateConfig()),
63+
buf_(buf) {}
5164

5265
protected:
5366
void Run() final {
@@ -58,32 +71,33 @@ class NonRTThread : public Thread {
5871
std::this_thread::sleep_for(std::chrono::milliseconds(500));
5972
}
6073
}
74+
75+
private:
76+
static cactus_rt::ThreadConfig CreateConfig() {
77+
cactus_rt::CyclicThreadConfig rt_thread_config;
78+
rt_thread_config.SetOtherScheduler(0 /* niceness */);
79+
return rt_thread_config;
80+
}
6181
};
6282

83+
// Trivial demonstration of the double buffer.
6384
void TrivialDemo() {
64-
// Trivial demonstration that the double buffer does work..
6585
NaiveDoubleBuffer<int> buf;
6686
buf.Write(2);
6787
auto a = buf.SwapAndRead();
6888
std::cout << "a is " << a << std::endl;
6989
}
7090

91+
// The actual application running.
7192
void ThreadedDemo() {
72-
cactus_rt::CyclicThreadConfig rt_thread_config;
73-
rt_thread_config.period_ns = 1'000'000;
74-
rt_thread_config.SetFifoScheduler(80 /* priority */);
75-
76-
cactus_rt::CyclicThreadConfig non_rt_thread_config;
77-
non_rt_thread_config.SetOtherScheduler(0 /* niceness */);
78-
7993
// The double buffer is shared between the two threads, so we pass a reference
8094
// into the thread and maintain the object lifetime to this function.
8195
NaiveDoubleBuffer<Data> buf;
8296

8397
App app;
8498

85-
auto rt_thread = app.CreateThread<RTThread>("RTThread", rt_thread_config, buf);
86-
auto non_rt_thread = app.CreateThread<NonRTThread>("NonRTThread", non_rt_thread_config, buf);
99+
auto rt_thread = app.CreateThread<RTThread>(buf);
100+
auto non_rt_thread = app.CreateThread<NonRTThread>(buf);
87101

88102
constexpr unsigned int time = 10;
89103
app.Start();

examples/signal_handling_example/CMakeLists.txt

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)