FreeRTOScpp
ReadWrite.h
Go to the documentation of this file.
1 /**
2  * @file ReadWrite.h
3  * @brief FreeRTOS Read/Write Lock Wrapper
4  *
5  * This file contains a set of lightweight wrappers for mutexes using FreeRTOS
6  *
7  * @warning This is a fairly new module, and may not be fully tested
8  *
9  * @copyright (c) 2007-2024 Richard Damon
10  * @author Richard Damon <richard.damon@gmail.com>
11  * @parblock
12  * MIT License:
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a copy
15  * of this software and associated documentation files (the "Software"), to deal
16  * in the Software without restriction, including without limitation the rights
17  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18  * copies of the Software, and to permit persons to whom the Software is
19  * furnished to do so, subject to the following conditions:
20  *
21  * The above copyright notice and this permission notice shall be included in
22  * all copies or substantial portions of the Software.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30  * THE SOFTWARE.
31  *
32  * It is requested (but not required by license) that any bugs found or
33  * improvements made be shared, preferably to the author.
34  * @endparblock
35  *
36  * @ingroup FreeRTOSCpp
37  */
38 
39 #ifndef READWRITE_H
40 #define READWRITE_H
41 
42 #include "EventCpp.h"
43 #include "TaskCPP.h"
44 #include "Lock.h"
45 
46 #if FREERTOSCPP_USE_NAMESPACE
47 namespace FreeRTOScpp {
48 #endif
49 
50 class ReadWriteLock;
51 
52 /**
53  * Read-Write Lock Read Side Lockability Base
54  *
55  * Base class to provide Read side Lockability
56 */
57 class Reader : public Lockable {
58  friend class ReadWriteLock;
59 private:
60  Reader() {}
61 public:
62  bool take(TickType_t wait) override;
63  bool give() override;
64 };
65 
66 /**
67  * Read-Write Write Side Lockability Base
68  */
69 class Writer : public Lockable {
70  friend class ReadWriteLock;
71 private:
72  Writer() {}
73 public:
74  bool take(TickType_t wait) override;
75  bool give() override;
76 
77 };
78 
79 
80 /**
81  * Read/Write Lock control
82  *
83  * States:
84  * + Free: readCount == 0
85  * + Read: readCount > 0, reserved == 0
86  * + Reserved: readCount > 0, reserved != 0
87  * + Write: readCount == -1, reserved == 0
88  * + Upgraded: readCount == -1, reserved != 0
89  *
90  * @dot
91  digraph states {
92  compound=true;
93  rankdir="LR";
94  subgraph cluster_0 {
95  Free;
96  }
97 
98  subgraph cluster_1 {
99  Read;
100  Reserved;
101  label="Read";
102 
103  }
104 
105  subgraph cluster_2 {
106  Write;
107  Upgraded;
108  label="Write";
109  }
110 
111  Free:w -> Read [color=green, label="readLock"];
112  Read -> Free:sw [color=red, label="readUnlock"];
113  Free:ne -> Reserved:se [color=green, label="reserveLock"];
114  Read:e -> Reserved:w [color=green, label="reserveRequest"];
115  Reserved:nw -> Read:ne [color=red, label="reserveRelease"];
116  Reserved:sw -> Free:nw [color=red, label="readUnlock"];
117  Free:e -> Write [color=green, label="writeLock"];
118  Reserved:e -> Upgraded:w [color=green, label="writeLock"];
119  Write -> Free:se [color=red, label="writeUnlock"];
120  Upgraded:nw -> Reserved:ne [color=red, label="writeUnlock"];
121  }
122  * @enddot
123  * @warning This is a fairly new module, and may not be fully tested
124  */
125 class ReadWriteLock : public Reader, public Writer {
126 public:
127  ReadWriteLock();
128  ~ReadWriteLock();
129 
130  /**
131  * Get Read Lockable
132  *
133  * @returns a lockable object for using with Read Locks.
134  */
135  Reader& rlock() { return *this; }
136  /**
137  * Get Write Lockable
138  *
139  * @returns a lockable object for using with Write Locks
140  */
141  Writer& wlock() { return *this; }
142 
143  /**
144  * ReadLock
145  *
146  * Get a read lock.
147  * @param wait The maximum number of ticks to wait to get the lock
148  * @returns true if the read lock has been granted
149  *
150  * Algorithm:
151  *
152  * + Loop
153  * + If readCount >=0 and our priority > writeReq
154  * + Increment readCount
155  * + return true
156  * + if Time has expired:
157  * + return false
158  * + else wait a tick for read_bit event.
159  */
160  bool readLock(TickType_t wait = portMAX_DELAY);
161  /**
162  * Get an upgradable Read Lock
163  *
164  * like readLock, but add reserved == nullptr to the conditions.
165  * On success will set reserved to our task handle.
166  *
167  * Only one task can reserve this, as if two are in this state they will deadlock when
168  * they both try to upgrade
169  */
170  bool reservedLock(TickType_t wait = portMAX_DELAY);
171  /**
172  * Task with a Read Lock request upgrade to a reserved lock.
173  * If no reservation current, will be granted, if reservation present, will be rejected.
174  *
175  * Does not check if this task has a read lock, but that is assumed.
176  */
177  bool requestReserved();
178  /**
179  * If we have a reserved lock, down grade to just a read lock
180  */
181  bool releaseReserved();
182  /**
183  * Remove our lock. If we reserved the upgrade, release that.
184  */
185  bool readUnlock();
186 
187  /**
188  * Take the write lock, requires readCount to be 0, or 1 if we reserved the upgrade.
189  */
190  bool writeLock(TickType_t wait = portMAX_DELAY);
191  /**
192  * Release the write lock. If we upgraded go back to a reserved lock which will need to be unlocked.
193  */
194  bool writeUnlock();
195 
196 #if FREERTOSCPP_USE_CHRONO
197  bool readLock(Time_ms delay_ms) { return readLock(ms2ticks(delay_ms)); }
198  bool reservedLock(Time_ms delay_ms) { return reservedLock(ms2ticks(delay_ms)); }
199  bool writeLock(Time_ms delay_ms) { return writeLock(ms2ticks(delay_ms)); }
200 #endif
201 protected:
203  /**
204  * Count of Read Locks
205  *
206  * If 0, then lock is free
207  * if >0, lock is in read mode and is the count of the number of read locks granted
208  * if <0, loci is in write mode.
209  */
210  int readCount = 0;
211  /**
212  * Reserved Lock indicator
213  *
214  * If nullptr, then no reservedLock are currently in existence, so a reservedLock can be granted
215  *
216  * Else, TaskHandle of the task that has reserved the right to upgrade to a write lock.
217  */
218  TaskHandle_t reserved = nullptr;
219  /**
220  * Write Request Pending Priority.
221  *
222  * If in Read mode, and a task with priority below writeReq
223  */
224  int writeReq = -1;
225 };
226 
227 #if FREERTOSCPP_USE_NAMESPACE
228 }
229 #endif
230 
231 #endif
Reader & rlock()
Get Read Lockable.
Definition: ReadWrite.h:135
Writer & wlock()
Get Write Lockable.
Definition: ReadWrite.h:141
bool give() override
Definition: ReadWrite.cpp:54
Read-Write Write Side Lockability Base.
Definition: ReadWrite.h:69
FreeRTOS Lock wrapper.
Read-Write Lock Read Side Lockability Base.
Definition: ReadWrite.h:57
constexpr TickType_t ms2ticks(Time_ms ms)
Definition: FreeRTOScpp.h:81
std::chrono::milliseconds Time_ms
Definition: FreeRTOScpp.h:79
Definition: EventCPP.h:69
Definition: CallBack.h:63
bool take(TickType_t wait) override
Definition: ReadWrite.cpp:50
bool readLock(Time_ms delay_ms)
Definition: ReadWrite.h:197
Reader()
Definition: ReadWrite.h:60
Writer()
Definition: ReadWrite.h:72
FreeRTOS Task Wrapper.
A Base class to provide block based locking capability.
Definition: Lock.h:58
EventGroup event
Definition: ReadWrite.h:202
Read/Write Lock control.
Definition: ReadWrite.h:125
bool writeLock(Time_ms delay_ms)
Definition: ReadWrite.h:199
friend class ReadWriteLock
Definition: ReadWrite.h:58
bool reservedLock(Time_ms delay_ms)
Definition: ReadWrite.h:198