locks.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include <sys/lock.h>
  15. #include <stdlib.h>
  16. #include <sys/reent.h>
  17. #include "esp_attr.h"
  18. #include "soc/cpu.h"
  19. #include "freertos/FreeRTOS.h"
  20. #include "freertos/semphr.h"
  21. #include "freertos/task.h"
  22. #include "freertos/portable.h"
  23. #include "esp_heap_caps.h"
  24. /* Notes on our newlib lock implementation:
  25. *
  26. * - Use FreeRTOS mutex semaphores as locks.
  27. * - lock_t is int, but we store an xSemaphoreHandle there.
  28. * - Locks are no-ops until the FreeRTOS scheduler is running.
  29. * - Due to this, locks need to be lazily initialised the first time
  30. * they are acquired. Initialisation/deinitialisation of locks is
  31. * protected by lock_init_spinlock.
  32. * - Race conditions around lazy initialisation (via lock_acquire) are
  33. * protected against.
  34. * - Anyone calling lock_close is reponsible for ensuring noone else
  35. * is holding the lock at this time.
  36. * - Race conditions between lock_close & lock_init (for the same lock)
  37. * are the responsibility of the caller.
  38. */
  39. static portMUX_TYPE lock_init_spinlock = portMUX_INITIALIZER_UNLOCKED;
  40. /* Initialize the given lock by allocating a new mutex semaphore
  41. as the _lock_t value.
  42. Called by _lock_init*, also called by _lock_acquire* to lazily initialize locks that might have
  43. been initialised (to zero only) before the RTOS scheduler started.
  44. */
  45. static void IRAM_ATTR lock_init_generic(_lock_t *lock, uint8_t mutex_type) {
  46. portENTER_CRITICAL(&lock_init_spinlock);
  47. if (*lock) {
  48. /* Lock already initialised (either we didn't check earlier,
  49. or it got initialised while we were waiting for the
  50. spinlock.) */
  51. }
  52. else
  53. {
  54. /* Create a new semaphore
  55. this is a bit of an API violation, as we're calling the
  56. private function xQueueCreateMutex(x) directly instead of
  57. the xSemaphoreCreateMutex / xSemaphoreCreateRecursiveMutex
  58. wrapper functions...
  59. The better alternative would be to pass pointers to one of
  60. the two xSemaphoreCreate___Mutex functions, but as FreeRTOS
  61. implements these as macros instead of inline functions
  62. (*party like it's 1998!*) it's not possible to do this
  63. without writing wrappers. Doing it this way seems much less
  64. spaghetti-like.
  65. */
  66. xSemaphoreHandle new_sem = xQueueCreateMutex(mutex_type);
  67. if (!new_sem) {
  68. abort(); /* No more semaphores available or OOM */
  69. }
  70. *lock = (_lock_t)new_sem;
  71. }
  72. portEXIT_CRITICAL(&lock_init_spinlock);
  73. }
  74. void IRAM_ATTR _lock_init(_lock_t *lock) {
  75. *lock = 0; // In case lock's memory is uninitialized
  76. lock_init_generic(lock, queueQUEUE_TYPE_MUTEX);
  77. }
  78. void IRAM_ATTR _lock_init_recursive(_lock_t *lock) {
  79. *lock = 0; // In case lock's memory is uninitialized
  80. lock_init_generic(lock, queueQUEUE_TYPE_RECURSIVE_MUTEX);
  81. }
  82. /* Free the mutex semaphore pointed to by *lock, and zero it out.
  83. Note that FreeRTOS doesn't account for deleting mutexes while they
  84. are held, and neither do we... so take care not to delete newlib
  85. locks while they may be held by other tasks!
  86. Also, deleting a lock in this way will cause it to be lazily
  87. re-initialised if it is used again. Caller has to avoid doing
  88. this!
  89. */
  90. void IRAM_ATTR _lock_close(_lock_t *lock) {
  91. portENTER_CRITICAL(&lock_init_spinlock);
  92. if (*lock) {
  93. xSemaphoreHandle h = (xSemaphoreHandle)(*lock);
  94. #if (INCLUDE_xSemaphoreGetMutexHolder == 1)
  95. configASSERT(xSemaphoreGetMutexHolder(h) == NULL); /* mutex should not be held */
  96. #endif
  97. vSemaphoreDelete(h);
  98. *lock = 0;
  99. }
  100. portEXIT_CRITICAL(&lock_init_spinlock);
  101. }
  102. void _lock_close_recursive(_lock_t *lock) __attribute__((alias("_lock_close")));
  103. /* Acquire the mutex semaphore for lock. wait up to delay ticks.
  104. mutex_type is queueQUEUE_TYPE_RECURSIVE_MUTEX or queueQUEUE_TYPE_MUTEX
  105. */
  106. static int IRAM_ATTR lock_acquire_generic(_lock_t *lock, uint32_t delay, uint8_t mutex_type) {
  107. xSemaphoreHandle h = (xSemaphoreHandle)(*lock);
  108. if (!h) {
  109. if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
  110. return 0; /* locking is a no-op before scheduler is up, so this "succeeds" */
  111. }
  112. /* lazy initialise lock - might have had a static initializer (that we don't use) */
  113. lock_init_generic(lock, mutex_type);
  114. h = (xSemaphoreHandle)(*lock);
  115. configASSERT(h != NULL);
  116. }
  117. if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
  118. return 0; /* locking is a no-op before scheduler is up, so this "succeeds" */
  119. }
  120. BaseType_t success;
  121. if (!xPortCanYield()) {
  122. /* In ISR Context */
  123. if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
  124. abort(); /* recursive mutexes make no sense in ISR context */
  125. }
  126. BaseType_t higher_task_woken = false;
  127. success = xSemaphoreTakeFromISR(h, &higher_task_woken);
  128. if (!success && delay > 0) {
  129. abort(); /* Tried to block on mutex from ISR, couldn't... rewrite your program to avoid libc interactions in ISRs! */
  130. }
  131. if (higher_task_woken) {
  132. portYIELD_FROM_ISR();
  133. }
  134. }
  135. else {
  136. /* In task context */
  137. if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
  138. success = xSemaphoreTakeRecursive(h, delay);
  139. } else {
  140. success = xSemaphoreTake(h, delay);
  141. }
  142. }
  143. return (success == pdTRUE) ? 0 : -1;
  144. }
  145. void IRAM_ATTR _lock_acquire(_lock_t *lock) {
  146. lock_acquire_generic(lock, portMAX_DELAY, queueQUEUE_TYPE_MUTEX);
  147. }
  148. void IRAM_ATTR _lock_acquire_recursive(_lock_t *lock) {
  149. lock_acquire_generic(lock, portMAX_DELAY, queueQUEUE_TYPE_RECURSIVE_MUTEX);
  150. }
  151. int IRAM_ATTR _lock_try_acquire(_lock_t *lock) {
  152. return lock_acquire_generic(lock, 0, queueQUEUE_TYPE_MUTEX);
  153. }
  154. int IRAM_ATTR _lock_try_acquire_recursive(_lock_t *lock) {
  155. return lock_acquire_generic(lock, 0, queueQUEUE_TYPE_RECURSIVE_MUTEX);
  156. }
  157. /* Release the mutex semaphore for lock.
  158. mutex_type is queueQUEUE_TYPE_RECURSIVE_MUTEX or queueQUEUE_TYPE_MUTEX
  159. */
  160. static void IRAM_ATTR lock_release_generic(_lock_t *lock, uint8_t mutex_type) {
  161. if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
  162. return; /* locking is a no-op before scheduler is up */
  163. }
  164. xSemaphoreHandle h = (xSemaphoreHandle)(*lock);
  165. assert(h);
  166. if (!xPortCanYield()) {
  167. if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
  168. abort(); /* indicates logic bug, it shouldn't be possible to lock recursively in ISR */
  169. }
  170. BaseType_t higher_task_woken = false;
  171. xSemaphoreGiveFromISR(h, &higher_task_woken);
  172. if (higher_task_woken) {
  173. portYIELD_FROM_ISR();
  174. }
  175. } else {
  176. if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
  177. xSemaphoreGiveRecursive(h);
  178. } else {
  179. xSemaphoreGive(h);
  180. }
  181. }
  182. }
  183. void IRAM_ATTR _lock_release(_lock_t *lock) {
  184. lock_release_generic(lock, queueQUEUE_TYPE_MUTEX);
  185. }
  186. void IRAM_ATTR _lock_release_recursive(_lock_t *lock) {
  187. lock_release_generic(lock, queueQUEUE_TYPE_RECURSIVE_MUTEX);
  188. }
  189. /* No-op function, used to force linking this file,
  190. instead of the dummy locks implementation from newlib.
  191. */
  192. void newlib_include_locks_impl(void)
  193. {
  194. }