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