FreeRTOS C-Addons  1.1.0
C-Addon functionality to FreeRTOS
read_write_lock.c
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2017, Michael Becker (michael.f.becker@gmail.com)
4  *
5  * This file is part of the FreeRTOS Add-ons project.
6  *
7  * Source Code:
8  * https://github.com/michaelbecker/freertos-addons
9  *
10  * Project Page:
11  * http://michaelbecker.github.io/freertos-addons/
12  *
13  * On-line Documentation:
14  * http://michaelbecker.github.io/freertos-addons/docs/html/index.html
15  *
16  * Permission is hereby granted, free of charge, to any person obtaining a
17  * copy of this software and associated documentation files
18  * (the "Software"), to deal in the Software without restriction, including
19  * without limitation the rights to use, copy, modify, merge, publish,
20  * distribute, sublicense, and/or sell copies of the Software, and to
21  * permit persons to whom the Software is furnished to do so,subject to the
22  * following conditions:
23  *
24  * + The above copyright notice and this permission notice shall be included
25  * in all copies or substantial portions of the Software.
26  * + Credit is appreciated, but not required, if you find this project
27  * useful enough to include in your application, product, device, etc.
28  *
29  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
30  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
32  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
33  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
34  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
35  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36  *
37  ***************************************************************************/
38 
39 
40 #include <stdlib.h>
41 #include "FreeRTOS.h"
42 #include "semphr.h"
43 #include "read_write_lock.h"
44 
45 
46 /*
47  * We need to differentiate which type of lock this is. We can
48  * also use this as a sanity check, signature.
49  */
51 
52  RwPreferReader = 0x72656164, /* "read" */
53  RwPreferWriter = 0x77726974 /* "writ" */
54 };
55 
56 
57 typedef struct RWLockPreferReader_t_ {
58 
63 
67  int ReadCount;
68 
72  SemaphoreHandle_t ReadLock;
73 
78  SemaphoreHandle_t ResourceLock;
79 
81 
82 
83 typedef struct RWLockPreferWriter_t_ {
84 
85  /*
86  * --------------------------------------------------------
87  * THIS PORTION MUST MIRROR THE PREFER READER STRUCTURE
88  */
90 
94  int ReadCount;
95 
99  SemaphoreHandle_t ReadLock;
100 
105  SemaphoreHandle_t ResourceLock;
106 
107  /*
108  * --------------------------------------------------------
109  * WRITER ONLY ADDITION
110  */
111 
117 
121  SemaphoreHandle_t WriteLock;
122 
126  SemaphoreHandle_t BlockReadersLock;
127 
129 
130 
131 
133 {
134  RWLockPreferReader_t *lock;
135 
136  lock = (RWLockPreferReader_t *)malloc(sizeof(RWLockPreferReader_t));
137  if (!lock) {
138  /*
139  * If the initial malloc fails, just get out.
140  */
141  return NULL;
142  }
143 
144  lock->Type = RwPreferReader;
145 
146  lock->ReadCount = 0;
147 
148  /*
149  * Preload these with NULLs to allow simplified error handling.
150  */
151  lock->ReadLock = NULL;
152  lock->ResourceLock = NULL;
153 
154 
155  lock->ReadLock = xSemaphoreCreateMutex();
156  if (lock->ReadLock == NULL) {
157  goto ERROR;
158  }
159 
160  /*
161  * ResourceLock CANNOT be a mutex. In FreeRTOS, as in most OS's,
162  * a thread is not allowed to unlock another thread's mutex. But
163  * the very nature of a Reader Lock allows an arbitrary ordering
164  * of unlocks when multiple threads hold the reader lock.
165  * Semaphores are not subject to this constraint.
166  */
167  lock->ResourceLock = xSemaphoreCreateBinary();
168  if (lock->ResourceLock == NULL) {
169  goto ERROR;
170  }
171 
172  /*
173  * Initialize it as "full", so it behaves similar to a mutex.
174  */
175  xSemaphoreGive(lock->ResourceLock);
176 
177  return (ReadWriteLock_t *)lock;
178 
179 
180  /*
181  * Handle most error cases here. This simplifies cleanup
182  * and is a common pattern in most OSs.
183  */
184 ERROR:
185  if (!lock->ReadLock) {
186  vSemaphoreDelete(lock->ReadLock);
187  }
188 
189  if (!lock->ResourceLock) {
190  vSemaphoreDelete(lock->ResourceLock);
191  }
192 
193  free(lock);
194  return NULL;
195 }
196 
197 
199 {
200  RWLockPreferWriter_t *lock;
201 
202  lock = (RWLockPreferWriter_t *)malloc(sizeof(RWLockPreferWriter_t));
203  if (!lock) {
204  /*
205  * If the initial malloc fails, just get out.
206  */
207  return NULL;
208  }
209 
210  lock->Type = RwPreferWriter;
211 
212  lock->ReadCount = 0;
213  lock->WriteCount = 0;
214 
215  /*
216  * Preload these with NULLs to allow simplified error handling.
217  */
218  lock->ReadLock = NULL;
219  lock->WriteLock = NULL;
220  lock->ResourceLock = NULL;
221  lock->BlockReadersLock = NULL;
222 
223  lock->ReadLock = xSemaphoreCreateMutex();
224  if (lock->ReadLock == NULL) {
225  goto ERROR;
226  }
227 
228  lock->WriteLock = xSemaphoreCreateMutex();
229  if (lock->WriteLock == NULL) {
230  goto ERROR;
231  }
232 
233  /*
234  * ResourceLock CANNOT be a mutex. In FreeRTOS, as in most OS's,
235  * a thread is not allowed to unlock another thread's mutex. But
236  * the very nature of a Reader Lock allows an arbitrary ordering
237  * of unlocks when multiple threads hold the reader lock.
238  * Semaphores are not subject to this constraint.
239  */
240  lock->ResourceLock = xSemaphoreCreateBinary();
241  if (lock->ResourceLock == NULL) {
242  goto ERROR;
243  }
244 
245  /*
246  * Initialize it as "full", so it behaves similar to a mutex.
247  */
248  xSemaphoreGive(lock->ResourceLock);
249 
250  /*
251  * BlockReadersLock CANNOT be a mutex. In FreeRTOS, as in most OS's,
252  * a thread is not allowed to unlock another thread's mutex. But
253  * the very nature of a Reader Lock allows an arbitrary ordering
254  * of unlocks when multiple threads hold the reader lock.
255  * Semaphores are not subject to this constraint.
256  */
257  lock->BlockReadersLock = xSemaphoreCreateBinary();
258  if (lock->BlockReadersLock == NULL) {
259  goto ERROR;
260  }
261 
262  /*
263  * Initialize it as "full", so it behaves similar to a mutex.
264  */
265  xSemaphoreGive(lock->BlockReadersLock);
266 
267  return (ReadWriteLock_t *)lock;
268 
269 
270  /*
271  * Handle most error cases here. This simplifies cleanup
272  * and is a common pattern in most OSs.
273  */
274 ERROR:
275  if (!lock->ReadLock) {
276  vSemaphoreDelete(lock->ReadLock);
277  }
278 
279  if (!lock->WriteLock) {
280  vSemaphoreDelete(lock->WriteLock);
281  }
282 
283  if (!lock->ResourceLock) {
284  vSemaphoreDelete(lock->ResourceLock);
285  }
286 
287  if (!lock->BlockReadersLock) {
288  vSemaphoreDelete(lock->BlockReadersLock);
289  }
290 
291  free(lock);
292  return NULL;
293 }
294 
295 
297 {
300 
301  configASSERT(lockR != NULL);
302 
303  if (lockR->Type == RwPreferReader) {
304  vSemaphoreDelete(lockR->ReadLock);
305  vSemaphoreDelete(lockR->ResourceLock);
306  free(lockR);
307  }
308  else if (lockW->Type == RwPreferWriter) {
309  vSemaphoreDelete(lockW->ReadLock);
310  vSemaphoreDelete(lockW->WriteLock);
311  vSemaphoreDelete(lockW->ResourceLock);
312  vSemaphoreDelete(lockW->BlockReadersLock);
313  free(lockW);
314  }
315  else {
316  configASSERT(!"Bad RW Lock type!");
317  }
318 }
319 
320 
322 {
325 
326  configASSERT(lockR != NULL);
327 
328  if (lockR->Type == RwPreferReader) {
329 
330  xSemaphoreTake(lockR->ReadLock, portMAX_DELAY);
331 
332  lockR->ReadCount++;
333  if (lockR->ReadCount == 1) {
334  xSemaphoreTake(lockR->ResourceLock, portMAX_DELAY);
335  }
336 
337  xSemaphoreGive(lockR->ReadLock);
338  }
339  else if (lockW->Type == RwPreferWriter) {
340 
341  xSemaphoreTake(lockW->BlockReadersLock, portMAX_DELAY);
342  xSemaphoreTake(lockW->ReadLock, portMAX_DELAY);
343 
344  lockW->ReadCount++;
345  if (lockW->ReadCount == 1) {
346  xSemaphoreTake(lockW->ResourceLock, portMAX_DELAY);
347  }
348 
349  xSemaphoreGive(lockW->ReadLock);
350  xSemaphoreGive(lockW->BlockReadersLock);
351 
352  }
353  else {
354  configASSERT(!"Bad RW Lock type!");
355  }
356 }
357 
358 
360 {
363 
364  configASSERT(lockR != NULL);
365 
366  if (lockR->Type == RwPreferReader) {
367 
368  xSemaphoreTake(lockR->ReadLock, portMAX_DELAY);
369 
370  lockR->ReadCount--;
371  if (lockR->ReadCount == 0) {
372  xSemaphoreGive(lockR->ResourceLock);
373  }
374 
375  xSemaphoreGive(lockR->ReadLock);
376  }
377  else if (lockW->Type == RwPreferWriter) {
378 
379  xSemaphoreTake(lockW->ReadLock, portMAX_DELAY);
380 
381  lockW->ReadCount--;
382  if (lockW->ReadCount == 0) {
383  xSemaphoreGive(lockW->ResourceLock);
384  }
385 
386  xSemaphoreGive(lockW->ReadLock);
387 
388  }
389  else {
390  configASSERT(!"Bad RW Lock type!");
391  }
392 }
393 
394 
396 {
399 
400  configASSERT(lockR != NULL);
401 
402  if (lockR->Type == RwPreferReader) {
403 
404  xSemaphoreTake(lockR->ResourceLock, portMAX_DELAY);
405 
406  }
407  else if (lockW->Type == RwPreferWriter) {
408 
409  xSemaphoreTake(lockW->WriteLock, portMAX_DELAY);
410 
411  lockW->WriteCount++;
412  if (lockW->WriteCount == 1) {
413  xSemaphoreTake(lockW->BlockReadersLock, portMAX_DELAY);
414  }
415 
416  xSemaphoreGive(lockW->WriteLock);
417 
418  xSemaphoreTake(lockW->ResourceLock, portMAX_DELAY);
419 
420  }
421  else {
422  configASSERT(!"Bad RW Lock type!");
423  }
424 }
425 
426 
428 {
431 
432  configASSERT(lockR != NULL);
433 
434  if (lockR->Type == RwPreferReader) {
435 
436  xSemaphoreGive(lockR->ResourceLock);
437 
438  }
439  else if (lockW->Type == RwPreferWriter) {
440 
441  xSemaphoreGive(lockW->ResourceLock);
442 
443  xSemaphoreTake(lockW->WriteLock, portMAX_DELAY);
444 
445  lockW->WriteCount--;
446  if (lockW->WriteCount == 0) {
447  xSemaphoreGive(lockW->BlockReadersLock);
448  }
449 
450  xSemaphoreGive(lockW->WriteLock);
451 
452  }
453  else {
454  configASSERT(!"Bad RW Lock type!");
455  }
456 }
457 
458 
459 
SemaphoreHandle_t ReadLock
void WriterLock(ReadWriteLock_t *lock)
SemaphoreHandle_t WriteLock
SemaphoreHandle_t ResourceLock
ReadWriteLock_t * CreateReadWriteLockPreferWriter(void)
SemaphoreHandle_t BlockReadersLock
void ReaderUnlock(ReadWriteLock_t *lock)
void WriterUnlock(ReadWriteLock_t *lock)
enum ReaderWriterLockType Type
struct RWLockPreferWriter_t_ RWLockPreferWriter_t
void ReaderLock(ReadWriteLock_t *lock)
SemaphoreHandle_t ReadLock
void * ReadWriteLock_t
struct RWLockPreferReader_t_ RWLockPreferReader_t
enum ReaderWriterLockType Type
ReadWriteLock_t * CreateReadWriteLockPreferReader(void)
void FreeReadWriteLock(ReadWriteLock_t *lock)
ReaderWriterLockType
SemaphoreHandle_t ResourceLock