critical_section.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. /*
  2. * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. /**
  7. * This file provides an abstract OS API for entering and exiting critical sections.
  8. * It furthermore provides macros to define and initialize an optional spinlock
  9. * if the used chip is a multi-core chip. If a single-core chip is used, just disabling interrupts
  10. * is sufficient to guarantee consecutive, non-interrupted execution of a critical section.
  11. * Hence, the spinlock is unneccessary and will be automatically ommitted by the macros.
  12. */
  13. #pragma once
  14. #include "freertos/FreeRTOS.h"
  15. #include "spinlock.h"
  16. #ifdef __cplusplus
  17. extern "C" {
  18. #endif
  19. #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32P4
  20. /**
  21. * This macro also helps users switching between spinlock declarations/definitions for multi-/single core environments
  22. * if the macros below aren't sufficient.
  23. */
  24. #define OS_SPINLOCK 1
  25. #else
  26. #define OS_SPINLOCK 0
  27. #endif
  28. #if OS_SPINLOCK == 1
  29. typedef spinlock_t esp_os_spinlock_t;
  30. #endif
  31. /**
  32. * Define and initialize a static (internal linking) lock for entering critical sections.
  33. *
  34. * Use this when all the critical sections are local inside a file.
  35. * The lock will only be defined if built for a multi-core system, otherwise it is unnecessary.
  36. *
  37. * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
  38. * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core systems.
  39. *
  40. * @param lock_name Variable name of the lock. This will later be used to reference the declared lock.
  41. * @param optional_qualifiers Qualifiers such as DRAM_ATTR and other attributes. Can be omitted if no qualifiers are
  42. * required.
  43. *
  44. * Example usage:
  45. * @code{c}
  46. * ...
  47. * #include "os/critical_section.h"
  48. * ...
  49. * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
  50. * ...
  51. * esp_os_enter_critical(&my_lock);
  52. * ...
  53. * esp_os_exit_critical(&my_lock);
  54. * @endcode
  55. */
  56. #if OS_SPINLOCK == 1
  57. #define DEFINE_CRIT_SECTION_LOCK_STATIC(lock_name, optional_qualifiers...) static optional_qualifiers esp_os_spinlock_t lock_name = SPINLOCK_INITIALIZER
  58. #else
  59. #define DEFINE_CRIT_SECTION_LOCK_STATIC(lock_name, optional_qualifiers...)
  60. #endif
  61. /**
  62. * Define and initialize a non-static (external linking) lock for entering critical sections.
  63. *
  64. * Locks defined by this macro can be linked among object files but this rather exceptional.
  65. * Prefer the static lock definition whenever possible.
  66. * The lock will only be defined if built for a multi-core system, otherwise it is unnecessary.
  67. *
  68. * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
  69. * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core systems.
  70. *
  71. * @param lock_name Variable name of the lock. This will later be used to reference the declared lock.
  72. * @param optional_qualifiers Qualifiers such as DRAM_ATTR and other attributes. Can be omitted if no qualifiers are
  73. * required.
  74. *
  75. * Example usage:
  76. * @code{c}
  77. * ...
  78. * #include "os/critical_section.h"
  79. * ...
  80. * DEFINE_CRIT_SECTION_LOCK(my_lock); // will have external linking (non-static)
  81. * ...
  82. * esp_os_enter_critical(&my_lock);
  83. * ...
  84. * esp_os_exit_critical(&my_lock);
  85. * @endcode
  86. */
  87. #if OS_SPINLOCK == 1
  88. #define DEFINE_CRIT_SECTION_LOCK(lock_name, optional_qualifiers...) optional_qualifiers esp_os_spinlock_t lock_name = SPINLOCK_INITIALIZER
  89. #else
  90. #define DEFINE_CRIT_SECTION_LOCK(lock_name, optional_qualifiers...)
  91. #endif
  92. /**
  93. * @brief This macro initializes a critical section lock at runtime.
  94. *
  95. * This macro basically creates a member of the initialization list, including the trailing comma.
  96. * If the lock is unnecessary because the architecture is single-core, this macro will not do anything.
  97. * This is incompatible with a lock created by DEFINE_CRIT_SECTION_LOCK_STATIC from above.
  98. *
  99. * @param lock_name Pointer to the lock.
  100. *
  101. * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
  102. * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core
  103. * systems.
  104. *
  105. * Example usage:
  106. * @code{c}
  107. * ...
  108. * #include "os/critical_section.h"
  109. * ...
  110. * typedef struct protected_struct_t {
  111. * int member1;
  112. * DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(my_lock)
  113. * int another_member;
  114. * };
  115. * ...
  116. * protected_struct_t my_protected;
  117. * INIT_CRIT_SECTION_LOCK_IN_STRUCT(&(my_protected.my_lock));
  118. * };
  119. * @endcode
  120. */
  121. #if OS_SPINLOCK == 1
  122. #define INIT_CRIT_SECTION_LOCK_RUNTIME(lock_name) spinlock_initialize(lock_name)
  123. #else
  124. #define INIT_CRIT_SECTION_LOCK_RUNTIME(lock_name)
  125. #endif
  126. /**
  127. * @brief This macro declares a critical section lock as a member of a struct.
  128. *
  129. * The critical section lock member is only declared if built for multi-core systems, otherwise it is omitted.
  130. *
  131. * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
  132. * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core
  133. * systems.
  134. * @note Do NOT add any semicolon after declaring the member with this macro.
  135. * The trailing semicolon is included in the macro, otherwise -Wpedantic would complain about
  136. * superfluous ";" if OS_SPINLOCK == 0.
  137. *
  138. * Example usage:
  139. * @code{c}
  140. * ...
  141. * #include "os/critical_section.h"
  142. * ...
  143. * typedef struct protected_struct_t {
  144. * int member1;
  145. * DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(my_lock) // no semicolon!
  146. * int another_member;
  147. * };
  148. * @endcode
  149. */
  150. #if OS_SPINLOCK == 1
  151. #define DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(lock_name) esp_os_spinlock_t lock_name;
  152. #else
  153. #define DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(lock_name)
  154. #endif
  155. /**
  156. * @brief This macro initializes a critical section lock as a member of a struct when using an list initialization.
  157. * It has to be used together with \c DECLARE_CRIT_SECTION_LOCK_IN_STRUCT() to work.
  158. *
  159. * This macro basically creates a member of the initialization list, including the trailing comma.
  160. * If the lock is unnecessary because the architecture is single-core, this macro will not do anything.
  161. * This means that if \c lock_name is still a member of the struct, \c lock_name will be uninitialized.
  162. * Hence, this macro has to be used together with \c DECLARE_CRIT_SECTION_LOCK_IN_STRUCT() to correctly to declare
  163. * or omit the struct member \c lock_name.
  164. *
  165. * @param lock_name The field name of the lock inside the struct.
  166. *
  167. * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
  168. * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core
  169. * systems.
  170. * @note Do NOT add any comma in the initializer list after using this macro.
  171. *
  172. * Example usage:
  173. * @code{c}
  174. * ...
  175. * #include "os/critical_section.h"
  176. * ...
  177. * typedef struct protected_struct_t {
  178. * int member1;
  179. * DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(my_lock)
  180. * int another_member;
  181. * };
  182. * ...
  183. * protected_struct_t my_protected = {
  184. * .member1 = 0,
  185. * INIT_CRIT_SECTION_LOCK_IN_STRUCT(my_lock) // no comma!
  186. * another_member = 47,
  187. * };
  188. * @endcode
  189. */
  190. #if OS_SPINLOCK == 1
  191. #define INIT_CRIT_SECTION_LOCK_IN_STRUCT(lock_name) .lock_name = portMUX_INITIALIZER_UNLOCKED,
  192. #else
  193. #define INIT_CRIT_SECTION_LOCK_IN_STRUCT(lock_name)
  194. #endif
  195. /**
  196. * @brief Enter a critical section, i.e., a section that will not be interrupted by any other task or interrupt.
  197. *
  198. * On multi-core systems, this will disable interrupts and take the spinlock \c lock. On single core systems, a
  199. * spinlock is unncessary, hence \c lock is ignored and interrupts are disabled only.
  200. *
  201. * @note This macro MUST be used together with any of the initialization macros, e.g.
  202. * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
  203. *
  204. * @param lock Pointer to the critical section lock. Ignored if build for single core system.
  205. *
  206. * Example usage with static locks:
  207. * @code{c}
  208. * ...
  209. * #include "os/critical_section.h"
  210. * ...
  211. * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
  212. * ...
  213. * esp_os_enter_critical(&my_lock);
  214. * // code inside critical section
  215. * esp_os_exit_critical(&my_lock);
  216. * @endcode
  217. */
  218. #if OS_SPINLOCK == 1
  219. #define esp_os_enter_critical(lock) portENTER_CRITICAL(lock)
  220. #else
  221. #define esp_os_enter_critical(lock) vPortEnterCritical()
  222. #endif
  223. /**
  224. * @brief Exit a critical section.
  225. *
  226. * On multi-core systems, this will enable interrupts and release the spinlock \c lock. On single core systems, a
  227. * spinlock is unncessary, hence \c lock is ignored and interrupts are enabled only.
  228. *
  229. * @note This macro MUST be used together with any of the initialization macros, e.g.
  230. * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
  231. *
  232. * @param lock Pointer to the critical section lock. Ignored if build for single core system.
  233. *
  234. * Example usage with static locks:
  235. * @code{c}
  236. * ...
  237. * #include "os/critical_section.h"
  238. * ...
  239. * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
  240. * ...
  241. * esp_os_enter_critical(&my_lock);
  242. * // code inside critical section
  243. * esp_os_exit_critical(&my_lock);
  244. * @endcode
  245. */
  246. #if OS_SPINLOCK == 1
  247. #define esp_os_exit_critical(lock) portEXIT_CRITICAL(lock)
  248. #else
  249. #define esp_os_exit_critical(lock) vPortExitCritical()
  250. #endif
  251. /**
  252. * @brief Enter a critical section while from ISR.
  253. *
  254. * On multi-core systems, this will disable interrupts and take the spinlock \c lock. On single core systems, a
  255. * spinlock is unncessary, hence \c lock is ignored and interrupts are disabled only.
  256. *
  257. * @note This macro MUST be used together with any of the initialization macros, e.g.
  258. * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
  259. *
  260. * @param lock Pointer to the critical section lock. Ignored if build for single core system.
  261. *
  262. * Example usage with static locks:
  263. * @code{c}
  264. * ...
  265. * #include "os/critical_section.h"
  266. * ...
  267. * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
  268. * ...
  269. * esp_os_enter_critical(&my_lock);
  270. * // code inside critical section
  271. * esp_os_exit_critical(&my_lock);
  272. * @endcode
  273. */
  274. #if OS_SPINLOCK == 1
  275. #define esp_os_enter_critical_isr(lock) portENTER_CRITICAL_ISR(lock)
  276. #else
  277. #define esp_os_enter_critical_isr(lock) vPortEnterCritical()
  278. #endif
  279. /**
  280. * @brief Exit a critical section after entering from ISR.
  281. *
  282. * On multi-core systems, this will enable interrupts and release the spinlock \c lock. On single core systems, a
  283. * spinlock is unncessary, hence \c lock is ignored and interrupts are enabled only.
  284. *
  285. * @note This macro MUST be used together with any of the initialization macros, e.g.
  286. * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
  287. *
  288. * @param lock Pointer to the critical section lock. Ignored if build for single core system.
  289. *
  290. * Example usage with static locks:
  291. * @code{c}
  292. * ...
  293. * #include "os/critical_section.h"
  294. * ...
  295. * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
  296. * ...
  297. * esp_os_enter_critical(&my_lock);
  298. * // code inside critical section
  299. * esp_os_exit_critical(&my_lock);
  300. * @endcode
  301. */
  302. #if OS_SPINLOCK == 1
  303. #define esp_os_exit_critical_isr(lock) portEXIT_CRITICAL_ISR(lock)
  304. #else
  305. #define esp_os_exit_critical_isr(lock) vPortExitCritical()
  306. #endif
  307. /**
  308. * @brief Enter a critical section from normal task or ISR. This macro will check if the current CPU is processing
  309. * an ISR or not and enter the critical section accordingly.
  310. *
  311. * On multi-core systems, this will disable interrupts and take the spinlock \c lock. On single core systems, a
  312. * spinlock is unncessary, hence \c lock is ignored and interrupts are disabled only.
  313. *
  314. * @note This macro MUST be used together with any of the initialization macros, e.g.
  315. * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
  316. *
  317. * @param lock Pointer to the critical section lock. Ignored if build for single core system.
  318. *
  319. * Example usage with static locks:
  320. * @code{c}
  321. * ...
  322. * #include "os/critical_section.h"
  323. * ...
  324. * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
  325. * ...
  326. * esp_os_enter_critical(&my_lock);
  327. * // code inside critical section
  328. * esp_os_exit_critical(&my_lock);
  329. * @endcode
  330. */
  331. #if OS_SPINLOCK == 1
  332. #define esp_os_enter_critical_safe(lock) portENTER_CRITICAL_SAFE(lock)
  333. #else
  334. #define esp_os_enter_critical_safe(lock) vPortEnterCritical()
  335. #endif
  336. /**
  337. * @brief Exit a critical section after entering via esp_os_enter_critical_safe.
  338. *
  339. * On multi-core systems, this will enable interrupts and release the spinlock \c lock. On single core systems, a
  340. * spinlock is unncessary, hence \c lock is ignored and interrupts are enabled only.
  341. *
  342. * @note This macro MUST be used together with any of the initialization macros, e.g.
  343. * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
  344. *
  345. * @param lock Pointer to the critical section lock. Ignored if build for single core system.
  346. *
  347. * Example usage with static locks:
  348. * @code{c}
  349. * ...
  350. * #include "os/critical_section.h"
  351. * ...
  352. * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
  353. * ...
  354. * esp_os_enter_critical(&my_lock);
  355. * // code inside critical section
  356. * esp_os_exit_critical(&my_lock);
  357. * @endcode
  358. */
  359. #if OS_SPINLOCK == 1
  360. #define esp_os_exit_critical_safe(lock) portEXIT_CRITICAL_SAFE(lock)
  361. #else
  362. #define esp_os_exit_critical_safe(lock) vPortExitCritical()
  363. #endif
  364. #ifdef __cplusplus
  365. }
  366. #endif