1# Locking in Picolibc 2 3When newlib-multithread and newlib-retargetable-locking are enabled, 4Picolibc uses the following locking API. Picolibc provides stubs for 5all of these functions that do not actually perform any locking, so 6that a single-threaded application can still use a Picolibc compiled 7with locking enabled. An application needing locking must provide a 8real implementation of the locking API. 9 10## Where Picolibc uses locking 11 12Picolibc has a single global lock for APIs that share global 13data. That includes: 14 15 * malloc family 16 * onexit/atexit 17 * arc4random 18 * functions using timezones (localtime, et al) 19 * legacy stdio struct reent globals 20 21Tinystdio (the default stdio) uses per-file locks for the buffered 22POSIX file backend, but it doesn't require any locks for the bulk of 23the implementation. It uses atomic exchanges to handle the one 24reentrancy issue related to ungetc/getc. 25 26The legacy stdio implementation is full of locking, and has per-file 27locks for every operation. 28 29## Configuration options controlling locking 30 31There are two configuration options related to locking: 32 33 * newlib-retargetable-locking. When 'true', locking operations are 34 enabled and performed by the retargetable locking API described below. 35 36 * newlib-multithread. When 'true', the library is built with 37 multithreading support enabled and uses the locking operations. 38 39Picolibc inherits these options from newlib, and they're so 40interrelated as to make them effectively co-dependent, so users must 41set them to the same value. 42 43## Retargetable locking API 44 45When newlib-multithread and newlib-retargetable-locking are enabled 46enabled, Picolibc uses the following interface. Picolibc provides 47stubs for all of these functions that do not perform locking, so a 48single threaded application can still use a library compiled to enable 49locking. An application needing locking would provide a real 50implementation of the API. 51 52This API requires recursive mutexes; if the underlying implementation 53only provides non-recursive mutexes, a suitable wrapper implementing 54recursive mutexes will be required for the recursive APIs. All APIs 55involving recursive mutexes contain `recursive` in their names. 56 57In this section, *the locking implementation* refers to the external 58implementation of this API. 59 60### `struct __lock; typedef struct __lock *_LOCK_T;` 61 62This struct is only referenced by picolibc, not defined. The 63locking implementation may define it as necessary. 64 65### `extern struct __lock __libc_recursive_mutex;` 66 67This is the single global lock used for most libc locking. It must be 68defined in the locking implementation in such a way as to not require 69any runtime initialization. 70 71### `void __retarget_lock_init(_LOCK_T *lock)` 72 73This is used by tinystdio to initialize the lock in a newly allocated 74FILE. 75 76### `void __retarget_lock_acquire(_LOCK_T lock)` 77 78Acquire a non-recursive mutex. A thread will only acquire the mutex once. 79 80### `void __retarget_lock_release(_LOCK_T lock)` 81 82Release a non-recursive mutex. 83 84### `void __retarget_lock_close(_LOCK_T lock)` 85 86This is used by tinystdio to de-initialize a lock from a FILE which is 87being closed. 88 89### `void __retarget_lock_init_recursive(_LOCK_T *lock)` 90 91This is used by the legacy stdio code to initialize the lock in a 92newly allocated FILE. 93 94### `void __retarget_lock_acquire_recursive(_LOCK_T lock)` 95 96Acquire a recursive mutex. A thread may acquire a recursive 97mutex multiple times. 98 99### `void __retarget_lock_release_recursive(_LOCK_T lock)` 100 101Release a recursive mutex. A thread thread must release the mutex as 102many times as it has acquired it before the mutex is unlocked. 103 104### `void __retarget_lock_close_recursive(_LOCK_T lock)` 105 106This is used by the legacy stdio code to de-initialize a lock from a 107FILE which is being closed. 108