]>
Commit | Line | Data |
---|---|---|
d0e1a0ae NH |
1 | =pod |
2 | ||
3 | =head1 NAME | |
4 | ||
5 | ossl_rcu_lock_new, | |
6 | ossl_rcu_lock_free, ossl_rcu_read_lock, | |
7 | ossl_rcu_read_unlock, ossl_rcu_write_lock, | |
8 | ossl_rcu_write_unlock, ossl_synchronize_rcu, | |
9 | ossl_rcu_call, ossl_rcu_deref, | |
10 | ossl_rcu_assign_ptr, ossl_rcu_uptr_deref, | |
11 | ossl_rcu_assign_uptr | |
12 | - perform read-copy-update locking | |
13 | ||
14 | =head1 SYNOPSIS | |
15 | ||
24d16d3a | 16 | CRYPTO_RCU_LOCK *ossl_rcu_lock_new(int num_writers, OSSL_LIB_CTX *ctx); |
d0e1a0ae NH |
17 | void ossl_rcu_read_lock(CRYPTO_RCU_LOCK *lock); |
18 | void ossl_rcu_write_lock(CRYPTO_RCU_LOCK *lock); | |
19 | void ossl_rcu_write_unlock(CRYPTO_RCU_LOCK *lock); | |
20 | void ossl_rcu_read_unlock(CRYPTO_RCU_LOCK *lock); | |
21 | void ossl_synchronize_rcu(CRYPTO_RCU_LOCK *lock); | |
22 | void ossl_rcu_call(CRYPTO_RCU_LOCK *lock, rcu_cb_fn cb, void *data); | |
23 | void *ossl_rcu_deref(void **p); | |
24 | void ossl_rcu_uptr_deref(void **p); | |
25 | void ossl_rcu_assign_ptr(void **p, void **v); | |
26 | void ossl_rcu_assign_uptr(void **p, void **v); | |
27 | void ossl_rcu_lock_free(CRYPTO_RCU_LOCK *lock); | |
28 | ||
29 | =head1 DESCRIPTION | |
30 | ||
31 | OpenSSL can be safely used in multi-threaded applications provided that | |
32 | support for the underlying OS threading API is built-in. Currently, OpenSSL | |
33 | supports the pthread and Windows APIs. OpenSSL can also be built without | |
34 | any multi-threading support, for example on platforms that don't provide | |
35 | any threading support or that provide a threading API that is not yet | |
36 | supported by OpenSSL. | |
37 | ||
38 | In addition to more traditional Read/Write locks, OpenSSL provides | |
39 | Read-Copy-Update (RCU) locks, which allow for always nonblocking read paths. | |
40 | ||
41 | The following multi-threading functions are provided: | |
42 | ||
43 | =over 2 | |
44 | ||
45 | =item * | |
46 | ||
47 | ossl_rcu_assign_uptr() assigns the value pointed to by v to the | |
48 | location pointed to by p. This function should typically not be used, rely | |
49 | instead on the ossl_rcu_assign_ptr() macro. | |
50 | ||
51 | =item * | |
52 | ||
53 | ossl_rcu_uptr_deref() returns the value stored at the | |
54 | location pointed to by p. This function should typically not be used, rely | |
55 | instead on the ossl_rcu_deref() macro. | |
56 | ||
57 | =item * | |
58 | ||
59 | ossl_rcu_assign_ptr() assigns the value pointed to by v to | |
60 | location pointed to by p. | |
61 | ||
62 | =item * | |
63 | ||
64 | ossl_rcu_lock_new() allocates a new RCU lock. The I<num_writers> param | |
65 | indicates the number of write side threads which may execute | |
66 | ossl_synchronize_rcu() in parallel. The value must be at least 1, but may be | |
67 | larger to obtain increased write side throughput at the cost of additional | |
24d16d3a NH |
68 | internal memory usage. A value of 1 is generally recommended. The I<ctx> |
69 | parameter references the library context in which the lock is allocated. | |
d0e1a0ae NH |
70 | |
71 | =item * | |
72 | ||
73 | ossl_rcu_read_lock() acquires a read side hold on data protected by | |
74 | the lock. | |
75 | ||
76 | =item * | |
77 | ||
78 | ossl_rcu_read_unlock() releases a read side hold on data protected by | |
79 | the lock. | |
80 | ||
81 | =item * | |
82 | ||
83 | ossl_rcu_write_lock() acquires a write side hold on data protected by | |
84 | the lock. Note only one writer per lock is permitted, as with read/write locks. | |
85 | ||
86 | =item * | |
87 | ||
88 | ossl_rcu_write_unlock() releases a write side hold on data protected | |
89 | by the lock. | |
90 | ||
91 | =item * | |
92 | ||
93 | ossl_synchronize_rcu() blocks the calling thread until all read side | |
94 | holds on the lock have been released, guaranteeing that any old data updated by | |
95 | the write side thread is safe to free. | |
96 | ||
97 | =item * | |
98 | ||
99 | ossl_rcu_call() enqueues a callback function to the lock, to be called | |
100 | when the next synchronization completes. Note: It is not guaranteed that the | |
101 | thread which enqueued the callback will be the thread which executes the | |
102 | callback | |
103 | ||
104 | =item * | |
105 | ||
106 | ossl_rcu_deref(p) atomically reads a pointer under an RCU locks | |
107 | protection | |
108 | ||
109 | =item * | |
110 | ||
111 | ossl_rcu_assign_ptr(p,v) atomically writes to a pointer under an | |
112 | RCU locks protection | |
113 | ||
114 | =item * | |
115 | ||
116 | ossl_rcu_lock_free() frees an allocated RCU lock | |
117 | ||
118 | =back | |
119 | ||
120 | =head1 RETURN VALUES | |
121 | ||
122 | ossl_rcu_lock_new() returns a pointer to a newly created RCU lock structure. | |
123 | ||
124 | ossl_rcu_deref() and ossl_rcu_uptr_deref() return the value pointed | |
125 | to by the passed in value v. | |
126 | ||
127 | All other functions return no value. | |
128 | ||
129 | =head1 EXAMPLES | |
130 | ||
131 | You can find out if OpenSSL was configured with thread support: | |
132 | ||
133 | #include <openssl/opensslconf.h> | |
134 | #if defined(OPENSSL_THREADS) | |
135 | /* thread support enabled */ | |
136 | #else | |
137 | /* no thread support */ | |
138 | #endif | |
139 | ||
140 | This example safely initializes and uses a lock. | |
141 | ||
142 | #include "internal/rcu.h" | |
143 | ||
144 | struct foo { | |
145 | int aval; | |
146 | char *name; | |
147 | }; | |
148 | ||
149 | static CRYPTO_ONCE once = CRYPTO_ONCE_STATIC_INIT; | |
150 | static CRYPTO_RCU_LOCK *lock; | |
151 | static struct foo *fooptr = NULL; | |
152 | ||
153 | static void myinit(void) | |
154 | { | |
155 | lock = ossl_rcu_lock_new(1); | |
156 | } | |
157 | ||
158 | static int initlock(void) | |
159 | { | |
160 | if (!RUN_ONCE(&once, myinit) || lock == NULL) | |
161 | return 0; | |
162 | return 1; | |
163 | } | |
164 | ||
165 | static void writer_thread() | |
166 | { | |
167 | struct foo *newfoo; | |
168 | struct foo *oldfoo; | |
169 | ||
170 | initlock(); | |
171 | ||
172 | /* | |
173 | * update steps in an rcu model | |
174 | */ | |
175 | ||
176 | /* | |
177 | * 1) create a new shared object | |
178 | */ | |
179 | newfoo = OPENSSL_zalloc(sizeof(struct foo)); | |
180 | ||
181 | /* | |
182 | * acquire the write side lock | |
183 | */ | |
184 | ossl_rcu_write_lock(lock); | |
185 | ||
186 | /* | |
187 | * 2) read the old pointer | |
188 | */ | |
189 | oldfoo = ossl_rcu_deref(&fooptr); | |
190 | ||
191 | /* | |
192 | * 3) Copy the old pointer to the new object, and | |
193 | * make any needed adjustments | |
194 | */ | |
195 | memcpy(newfoo, oldfoo, sizeof(struct foo)); | |
196 | newfoo->aval++; | |
197 | ||
198 | /* | |
199 | * 4) Update the shared pointer to the new value | |
200 | */ | |
201 | ossl_rcu_assign_ptr(&fooptr, &newfoo); | |
202 | ||
203 | /* | |
204 | * 5) Release the write side lock | |
205 | */ | |
206 | ossl_rcu_write_unlock(lock); | |
207 | ||
208 | /* | |
209 | * 6) wait for any read side holds on the old data | |
210 | * to be released | |
211 | */ | |
212 | ossl_synchronize_rcu(lock); | |
213 | ||
214 | /* | |
215 | * 7) free the old pointer, now that there are no | |
216 | * further readers | |
217 | */ | |
218 | OPENSSL_free(oldfoo); | |
219 | } | |
220 | ||
221 | static void reader_thread() | |
222 | { | |
223 | struct foo *myfoo = NULL; | |
224 | int a; | |
225 | /* | |
226 | * 1) Acquire a read side hold on the shared data | |
227 | */ | |
228 | ossl_rcu_read_lock(lock); | |
229 | ||
230 | /* | |
231 | * 2) Access the shared data pointer | |
232 | */ | |
233 | myfoo = ossl_rcu_deref(&fooptr); | |
234 | ||
235 | /* | |
236 | * 3) Read the data from the pointer | |
237 | */ | |
238 | a = myfoo->aval; | |
239 | ||
240 | /* | |
241 | * 4) Indicate our hold on the shared data is complete | |
242 | */ | |
243 | ossl_rcu_read_unlock(lock); | |
244 | } | |
245 | ||
246 | =head1 SEE ALSO | |
247 | ||
248 | L<crypto(7)>, L<openssl-threads(7)>. | |
249 | ||
250 | =head1 COPYRIGHT | |
251 | ||
b6461792 | 252 | Copyright 2023-2024 The OpenSSL Project Authors. All Rights Reserved. |
d0e1a0ae NH |
253 | |
254 | Licensed under the Apache License 2.0 (the "License"). You may not use | |
255 | this file except in compliance with the License. You can obtain a copy | |
256 | in the file LICENSE in the source distribution or at | |
257 | L<https://www.openssl.org/source/license.html>. | |
258 | ||
259 | =cut |