Intrusive Containers
Loading...
Searching...
No Matches
Container.h
Go to the documentation of this file.
1/**
2@file Container.h
3@brief Intrusive Container Documentation and Base Class.
4
5A File with some general documentation information for the various @ref IntrusiveContainers "container classes", no code here.
6
7 @warning Most of the use of this library has been with ContainerNoSafety, so there may be errors in the other versions.
8
9@copyright (c) 2014-2024 Richard Damon
10@parblock
11MIT License:
12
13Permission is hereby granted, free of charge, to any person obtaining a copy
14of this software and associated documentation files (the "Software"), to deal
15in the Software without restriction, including without limitation the rights
16to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17copies of the Software, and to permit persons to whom the Software is
18furnished to do so, subject to the following conditions:
19
20The above copyright notice and this permission notice shall be included in
21all copies or substantial portions of the Software.
22
23THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29THE SOFTWARE.
30@endparblock
31
32@ingroup IntrusiveContainers
33
34*/
35#ifndef CONTAINER_H
36#define CONTAINER_H
37
38#define CONTAINER_VERSION_MAJOR 1
39#define CONTAINER_VERSION_MINOR 0
40#define CONTAINER_VERSION_BUILD 0
41#define CONTAINER_VERSION_NUMBER 0x10000
42#define CONTAINER_VERSION_STRING "1.0.0"
43
44
45#ifdef DOXYGEN
46/**
47 * @def CONTAINER_OS
48 * OS Selection for Thread Safety Selection
49 *
50 * One of:
51 * + #CONTAINER_OS_USER
52 * + #CONTAINER_OS_FREERTOS
53 * + #CONTAINER_OS_CMSIS
54 * + #CONTAINER_OS_CMSIS2
55 *
56 * @ingroup IntrusiveContainers
57 */
58
59#ifndef CONTAINER_OS
60#define CONTAINER_OS CONTINER_OS_FREERTOS
61#endif
62#endif
63
64/**
65 * @def CONTAINER_OS_USER
66 * User Defined Safety Layer
67 *
68 * The User Application needs to define the member functions for Container<s> and ContainerNode<s>
69 * for each supported ContainerThreadSafety level it will support except ContainerNoSafety
70 * which will still have the default no locking.
71 *
72 * @ingroup IntrusiveContainers
73 */
74#define CONTAINER_OS_USER 0
75/**
76 * @def CONTAINER_OS_FREERTOS
77 * FreeRTOS based Safety layer
78 *
79 * @ingroup IntrusiveContainers
80 */
81#define CONTAINER_OS_FREERTOS 1
82
83/**
84 * @def CONTAINER_OS_CMSIS
85 * CMSIS based Safety layer
86 *
87 * @todo Implement CMSIS Container Safety layer
88 *
89 * @ingroup IntrusiveContainers
90 */
91#define CONTAINER_OS_CMSIS 2
92
93/**
94 * @def CONTAINER_OS_CMSIS2
95 * CMSIS version 2 based Safety layer
96 *
97 * @todo Implement CMSIS2 Container Safety layer
98 *
99 * @ingroup IntrusiveContainers
100 */
101#define CONTAINER_OS_CMSIS2 3
102
103// See if OS Not defined, and try to detect OS being used, assumes RTOS base header already included
104#ifndef CONTAINER_OS
105# ifdef INC_FREERTOS_H
106# define CONTAINER_OS CONTAINER_OS_FREERTOS
107# endif
108#endif
109
110#ifndef CONTAINER_OS
111// Only fall back to CMSIS if we can't detect the actual OS
112 #ifdef _CMSIS_OS_H
113 #define CONTAINER_OS CONTAINER_OS_CMSIS
114 #endif
115 #ifdef CMSIS_OS2_H_
116 #define CONTAINER_OS CONTAINER_OS_CMSIS2
117 #endif
118#endif
119
120#ifndef CONTAINER_OS
121// Couldn't detect, either warn or apply a global default
122#warning CONTAINER_OS not defined and can not be detected
123#endif
124
125
126/**
127 * Thread Safety selection for container.
128 *
129 * + ContainerNoSafety: No safety provided by container, all needs to be supplied
130 * by the application. Suitable for containers built at startup so have no safety
131 * issues or containers externally managed by the application
132 *
133 * + ContainerMutexSafe: Will use a Mutex to protect the container.
134 *
135 * + ContainerTaskSafe: Critical Sections will be used around updates and internal
136 * searches such that the container is safe to be updated by a task, and possibly
137 * read by an ISR. Global locks are used
138 *
139 * + ContainerISRSafe: Critical Sections will be used around updates and internal
140 * searches such that the container is safe to be updated by either a task or
141 * by an ISR. Note, this may cause longer critical sections.
142 *
143 * Note, it is presumed that while the containers may be shared between tasks / interrupts,
144 * that a given node have its root connections be changed by two different tasks / interrupts.
145 *
146 * TODO: May want to look at implementing a R/W lock version for TaskSafe updates
147 * that has minimal global critical sections.
148 *
149 * | ContainerThreadSafety | ISR? | Read | Write |
150 * | --- | --- | --- | --- |
151 * | #ContainerNoSafety | User Def | | |
152 * | #ContainerReadWrite | No Access | Container Shared | Container Exclusive |
153 * | #ContainerMutexSafe | No Access | Container Exclusive | Container Exclusive |
154 * | #ContainerTaskOnly | No Access | Exclusive Section | Exclusive Section |
155 * | #ContainerTaskSafe | Read Only | Exclusive Section | Critical Section |
156 * | #ContainerISRSafe | Read/Write | Critical Section | Critical Section |
157 *
158 * + Critical Section: Interrupts will be disabled to prevent an ISR for interfacing
159 * + Exclusive Section: Processor Lock held to not let other tasks interfere
160 * + Container Exclusive: A Lock in the container held, keeping other tasks out of the Container
161 * + Container Shared: A Read lock in the container is held, keeping other tasks from changing the Container.
162 *
163 * @warning Most of the use of this library has been with ContainerNoSafety, so there may be errors in the other versions.
164 *
165 * @ingroup IntrusiveContainers
166 */
168 ContainerNoSafety, ///< Thread Safety (if needed) provided by the application
169#ifdef CONTAINER_OS // Only define "safe" versions if we have an OS defined
170 ContainerReadWrite, ///< Container will include a Read/Write lock to protect updates in tasks
171 ContainerMutexSafe, ///< Container will include a Mutex to protect updates in Tasks, but not ISRs
172 ContainerTaskOnly, ///< Container will suspend the scheduler as no need to protect from ISR usage
173 ContainerTaskSafe, ///< Container will include Critical Sections to protect updates in tasks, but not ISRs
174 ContainerISRSafe, ///< Container will include Critical Sections to protect updates in ISR and Tasks
175#endif
176};
177
178template<ContainerThreadSafety s> class ContainerNode;
179
180/**
181 * Base Container Class
182 *
183 * Provides the base locking ability for the container.
184 *
185 * @tparam s The ContainerThreadSafety value to define the thread safety model of the Container
186 *
187 * @note Most of the testing of this library has been done with ContainerNoSafety
188 *
189 * @ingroup IntrusiveContainers
190 */
191template<ContainerThreadSafety s>
193 friend class ContainerNode<s>;
194protected:
197
198 virtual bool check() const = 0;
199
200 /**
201 * Obtain a read lock
202 *
203 * While a read lock is held on a container, the container can not be changed.
204 *
205 * Read locks will not be given while a write lock is held.
206 *
207 * @param upgradable: If called with upgradeable, then the lock can be upgradable to a write lock,
208 * and no other upgradable read lock will be granted.
209 * (Note, the code returned will need to encode the upgradable bit)
210 *
211 * @returns code to give to readUnlock()
212 */
213 unsigned readLock(bool upgradable) const;
214
215 /**
216 * Release the read lock that was held.
217 *
218 * @param code from writeLock();
219 */
220 void readUnlock(unsigned code) const;
221
222 /**
223 * Obtain a write lock
224 *
225 * While a write is held, no other read or write lock will be created.
226 *
227 * @param upgrade: operation already has a read lock that was made with the upgradeable flag
228 */
229 unsigned writeLock(bool upgrade) const;
230 /**
231 * Release write lock
232 *
233 * If was from an upgraded read lock, we return to an upgradable read lock
234 *
235 * @param code the value returned from the previous call to writeLock()
236 */
237 void writeUnlock(unsigned code) const;
238
239private:
240 Container(Container const &) = delete; ///< We are not copyable
241 void operator=(Container const &) = delete; ///< We are not assignable;
242};
243
244/**
245 * Base Container Node Class
246 *
247 * Used to provide the Thread Safety Primitives for a Container Node
248 *
249 * @tparam s The ContainerThreadSafety value to define the thread safety model of the Container
250 * Note, the Nodes must match the safety level of the container.
251 *
252 * @ingroup IntrusiveContainers
253 */
254template<ContainerThreadSafety s>
256 typedef Container<s> C; ///< The type of the Container that we are part of.
257protected:
258 ContainerNode(C* root = nullptr) { setRoot(root); }
260
261 virtual bool check() const = 0;
262 /**
263 * Set our Container
264 *
265 * Used to allow to Node to record what Container it is in.
266 * Used only if safety method need resources from the Container, like a Mutex
267 *
268 * If an operation changes the root of a node, then it needs to save the
269 * original root to exit the critical section on that container that it entered before the
270 * operation.
271 */
272 void setRoot(C* root) { (void)root; }
273
274
275 unsigned readLock(bool upgradable) const;
276
277
278 void readUnlock(unsigned code) const;
279
280
281 unsigned writeLock(bool upgrade) const;
282
283 void writeUnlock(unsigned code) const;
284
285protected:
286 ContainerNode(ContainerNode const&) = delete;
287 void operator=(ContainerNode const&) = delete;
288};
289
290/**
291 * Under ContainerNoSafety application handles all the needed restrictions.
292 */
293template<>
294inline unsigned Container<ContainerNoSafety>::writeLock(bool upgrade) const {
295 return upgrade;
296}
297
298/**
299 * Under ContainerNoSafety application handles all the needed restrictions.
300 */
301template<>
302inline void Container<ContainerNoSafety>::writeUnlock(unsigned save) const {
303 (void) save;
304}
305
306/**
307 * Under ContainerNoSafety application handles all the needed restrictions.
308 */
309template<>
310inline unsigned Container<ContainerNoSafety>::readLock(bool upgradable) const {
311 return upgradable;
312}
313
314/**
315 * Under ContainerNoSafety application handles all the needed restrictions.
316 */
317template<>
318inline void Container<ContainerNoSafety>::readUnlock(unsigned save) const {
319 (void) save;
320}
321
322/**
323 * Under ContainerNoSafety application handles all the needed restrictions.
324 */
325template<>
326inline unsigned ContainerNode<ContainerNoSafety>::writeLock(bool upgrade) const {
327 return upgrade;
328}
329
330/**
331 * Under ContainerNoSafety application handles all the needed restrictions.
332 */
333template<>
334inline void ContainerNode<ContainerNoSafety>::writeUnlock(unsigned save) const {
335 (void) save;
336}
337
338/**
339 * Under ContainerNoSafety application handles all the needed restrictions.
340 */
341template<>
342inline unsigned ContainerNode<ContainerNoSafety>::readLock(bool upgradable) const {
343 return upgradable;
344}
345
346/**
347 * Under ContainerNoSafety application handles all the needed restrictions.
348 */
349template<>
350inline void ContainerNode<ContainerNoSafety>::readUnlock(unsigned save) const {
351 (void) save;
352}
353
354/*****************************************************************************/
355// FreeRTOS implementations
356
357#if CONTAINER_OS == CONTAINER_OS_FREERTOS
358#include <FreeRTOS.h>
359#include <task.h>
360
361#ifdef configUSE_RECURSIVE_MUTEXES
362// Only if Mutexes are configured
363// TODO see if we can work with a pair of non-recurvise mutexes for search/alter
364// This likely will need an "upgrade" flag to writeLock
365#include <semphr.h>
366
367// Define alternate version for Mutex Safety that includes the Mutex
368template<>
370 friend class ContainerNode<ContainerMutexSafe>;
371protected:
372 Container() {
373 #if configSUPPORT_STATIC_ALLOCATION
374 m_mutex = xSemaphoreCreateRecursiveMutexStatic(&m_mutex_buffer);
375 #else
376 m_mutex = xSemaphoreCreateRecursiveMutex();
377 #endif
378 }
379 ~Container() {
380 vSemaphoreDelete(m_mutex);
381 }
382
383 /**
384 * Enter possibly long Read-Only Critical Section
385 *
386 * @returns code to give to readUnlock()
387 */
388 unsigned readLock() const;
389
390 /**
391 * Leave Read Only Critical Section
392 * @param code from writeLock();
393 */
394 void readUnlock(unsigned code) const;
395
396 /**
397 * Enter Write Critical Section
398 */
399 unsigned writeLock() const;
400 /**
401 * Leave
402 * @param code the value returned from the previous call to writeLock()
403 */
404 void writeUnlock(unsigned code) const;
405
406 SemaphoreHandle_t m_mutex;
407#if configSUPPORT_STATIC_ALLOCATION
408 StaticSemaphore_t m_mutex_buffer;
409#endif // configSUPPORT_STATIC_ALLOCATION
410
411private:
412 Container(Container const &) = delete; ///< We are not copyable
413 void operator=(Container const &) = delete; ///< We are not assignable;
414};
415
416// Define alternate version for Mutex Safety that includes the Mutex
417template<>
420protected:
421 ContainerNode(C* root) {
422 setRoot(root);
423 }
425 }
426
427 /**
428 * Set our Container
429 *
430 * Used to allow to Node to record what Containier it is in.
431 * Used only if safety method need resources from the Container, like a Mutex
432 */
433 void setRoot(C* root) { m_root = root; }
434
435 /**
436 * Enter possibly long Read-Only Critical Section
437 *
438 * @returns code to give to readUnlock()
439 */
440 unsigned readLock() const { return m_root->readLock(); }
441
442 /**
443 * Leave Read Only Critical Section
444 * @param code from writeLock();
445 */
446 void readUnlock(unsigned code) const { m_root->readUnlock(code); }
447
448 /**
449 * Enter Write Critical Section
450 */
451 unsigned writeLock() const { return m_root->writeLock(); }
452 /**
453 * Leave
454 * @param code the value returned from the previous call to writeLock()
455 */
456 void writeUnlock(unsigned code) const { m_root->writeUnlock(code); }
457
458
459private:
460 ContainerNode(ContainerNode const &) = delete; ///< We are not copyable
461 void operator=(ContainerNode const &) = delete; ///< We are not assignable;
462
463 C* m_root = nullptr;
464};
465#endif // configUSE_RECURSIVE_MUTEXES
466
467/**********************************/
468// ContainerTaskOnly: Container only used at TaskLevel
469
470template<>
471inline unsigned Container<ContainerTaskOnly>::writeLock(bool upgrade) const {
472 vTaskSuspendAll();
473 return upgrade;
474}
475
476template<>
477inline void Container<ContainerTaskOnly>::writeUnlock(unsigned save) const {
478 (void)save;
479 xTaskResumeAll();
480}
481
482// Search may be longer, so leave interrupts enabled but ISRs can't change things
483template<>
484inline unsigned Container<ContainerTaskOnly>::readLock(bool upgradable) const {
485 vTaskSuspendAll();
486 return upgradable;
487}
488
489template<>
490inline void Container<ContainerTaskOnly>::readUnlock(unsigned save) const {
491 (void)save;
492 xTaskResumeAll();
493}
494
495/*************************/
496// ContainerTaskSafe: Container only changed at Task level, but might be accessed in ISR
497// While only changed at task level, some quick operations might use the ordinary critical
498
499template<>
500inline unsigned Container<ContainerTaskSafe>::writeLock(bool upgrade) const {
501 return taskENTER_CRITICAL_FROM_ISR() | (upgrade << 15);
502}
503
504template<>
505inline void Container<ContainerTaskSafe>::writeUnlock(unsigned save) const {
506 taskEXIT_CRITICAL_FROM_ISR(save & 0xFF);
507}
508
509// Search may be longer, so leave interrupts enabled but ISRs can't change things
510template<>
511inline unsigned Container<ContainerTaskSafe>::readLock(bool upgradable) const {
512
513 if(!xPortIsInsideInterrupt()) vTaskSuspendAll();
514 return upgradable;
515}
516
517template<>
518inline void Container<ContainerTaskSafe>::readUnlock(unsigned save) const {
519 (void)save;
520 if(!xPortIsInsideInterrupt()) xTaskResumeAll();
521}
522
523/*****************************************************************************/
524
525template<>
526inline unsigned Container<ContainerISRSafe>::writeLock(bool upgrade) const {
527
528 return taskENTER_CRITICAL_FROM_ISR() | (upgrade << 15);
529}
530
531template<>
532inline void Container<ContainerISRSafe>::writeUnlock(unsigned save) const {
533 taskEXIT_CRITICAL_FROM_ISR(save & 0xFF);
534}
535
536template<>
537inline unsigned Container<ContainerISRSafe>::readLock(bool upgradable) const {
538 return taskENTER_CRITICAL_FROM_ISR() | (upgradable << 15);
539}
540
541template<>
542inline void Container<ContainerISRSafe>::readUnlock(unsigned save) const {
543 taskEXIT_CRITICAL_FROM_ISR(save & 0xFF);
544}
545#endif // CONTAINER_OS == CONTAINER_OS_FREERTOS
546
547#endif
Base Container Class.
Definition Container.h:192
void operator=(Container const &)=delete
We are not assignable;.
Container(Container const &)=delete
We are not copyable.
Container()
Definition Container.h:195
unsigned writeLock(bool upgrade) const
Obtain a write lock.
unsigned readLock(bool upgradable) const
Obtain a read lock.
~Container()
Definition Container.h:196
void readUnlock(unsigned code) const
Release the read lock that was held.
virtual bool check() const =0
void writeUnlock(unsigned code) const
Release write lock.
Base Container Node Class.
Definition Container.h:255
void operator=(ContainerNode const &)=delete
ContainerNode(C *root=nullptr)
Definition Container.h:258
ContainerNode(ContainerNode const &)=delete
~ContainerNode()
Definition Container.h:259
Container< s > C
The type of the Container that we are part of.
Definition Container.h:256
unsigned readLock(bool upgradable) const
virtual bool check() const =0
void setRoot(C *root)
Set our Container.
Definition Container.h:272
void readUnlock(unsigned code) const
unsigned writeLock(bool upgrade) const
void writeUnlock(unsigned code) const
ContainerThreadSafety
Thread Safety selection for container.
Definition Container.h:167
@ ContainerMutexSafe
Container will include a Mutex to protect updates in Tasks, but not ISRs.
Definition Container.h:171
@ ContainerISRSafe
Container will include Critical Sections to protect updates in ISR and Tasks.
Definition Container.h:174
@ ContainerNoSafety
Thread Safety (if needed) provided by the application.
Definition Container.h:168
@ ContainerTaskSafe
Container will include Critical Sections to protect updates in tasks, but not ISRs.
Definition Container.h:173
@ ContainerTaskOnly
Container will suspend the scheduler as no need to protect from ISR usage.
Definition Container.h:172
@ ContainerReadWrite
Container will include a Read/Write lock to protect updates in tasks.
Definition Container.h:170