]> git.ipfire.org Git - thirdparty/gcc.git/blame - libgfortran/io/async.h
libgfortran: Replace mutex with rwlock
[thirdparty/gcc.git] / libgfortran / io / async.h
CommitLineData
83ffe9cd 1/* Copyright (C) 2018-2023 Free Software Foundation, Inc.
2b4c9065
NK
2 Contributed by Nicolas Koenig
3
4 This file is part of the GNU Fortran runtime library (libgfortran).
5
6 Libgfortran is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
10
11 Libgfortran is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 Under Section 7 of GPL version 3, you are granted additional
17 permissions described in the GCC Runtime Library Exception, version
18 3.1, as published by the Free Software Foundation.
19
20 You should have received a copy of the GNU General Public License and
21 a copy of the GCC Runtime Library Exception along with this program;
22 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 <http://www.gnu.org/licenses/>. */
24
25#ifndef ASYNC_H
26#define ASYNC_H
27
28/* Async I/O will not work on targets which do not support
29 __gthread_cond_t and __gthread_equal / __gthread_self. Check
30 this. */
31
464c1bd8 32#if defined(__GTHREAD_HAS_COND) && defined(__GTHREADS_CXX0X)
2b4c9065
NK
33#define ASYNC_IO 1
34#else
35#define ASYNC_IO 0
36#endif
37
38/* Defining DEBUG_ASYNC will enable somewhat verbose debugging
39 output for async I/O. */
40
41#define DEBUG_ASYNC
42#undef DEBUG_ASYNC
43
44#ifdef DEBUG_ASYNC
45
46/* Define this if you want to use ANSI color escape sequences in your
47 debugging output. */
48
49#define DEBUG_COLOR
50
51#ifdef DEBUG_COLOR
52#define MPREFIX "\033[30;46mM:\033[0m "
53#define TPREFIX "\033[37;44mT:\033[0m "
54#define RPREFIX "\033[37;41mR:\033[0m "
55#define DEBUG_RED "\033[31m"
56#define DEBUG_ORANGE "\033[33m"
57#define DEBUG_GREEN "\033[32m"
58#define DEBUG_DARKRED "\033[31;2m"
59#define DEBUG_PURPLE "\033[35m"
60#define DEBUG_NORM "\033[0m"
61#define DEBUG_REVERSE_RED "\033[41;37m"
62#define DEBUG_BLUE "\033[34m"
63
64#else
65
66#define MPREFIX "M: "
67#define TPREFIX "T: "
68#define RPREFIX ""
69#define DEBUG_RED ""
70#define DEBUG_ORANGE ""
71#define DEBUG_GREEN ""
72#define DEBUG_DARKRED ""
73#define DEBUG_PURPLE ""
74#define DEBUG_NORM ""
75#define DEBUG_REVERSE_RED ""
76#define DEBUG_BLUE ""
77
78#endif
79
80#define DEBUG_PRINTF(...) fprintf (stderr,__VA_ARGS__)
81
82#define IN_DEBUG_QUEUE(mutex) ({ \
83 __label__ end; \
84 aio_lock_debug *curr = aio_debug_head; \
85 while (curr) { \
86 if (curr->m == mutex) { \
87 goto end; \
88 } \
89 curr = curr->next; \
90 } \
91 end:; \
92 curr; \
93 })
94
95#define TAIL_DEBUG_QUEUE ({ \
96 aio_lock_debug *curr = aio_debug_head; \
97 while (curr && curr->next) { \
98 curr = curr->next; \
99 } \
100 curr; \
101 })
102
103#define CHECK_LOCK(mutex, status) do { \
104 aio_lock_debug *curr; \
105 INTERN_LOCK (&debug_queue_lock); \
106 if (__gthread_mutex_trylock (mutex)) { \
107 if ((curr = IN_DEBUG_QUEUE (mutex))) { \
108 sprintf (status, DEBUG_RED "%s():%d" DEBUG_NORM, curr->func, curr->line); \
109 } else \
110 sprintf (status, DEBUG_RED "unknown" DEBUG_NORM); \
111 } \
112 else { \
113 __gthread_mutex_unlock (mutex); \
114 sprintf (status, DEBUG_GREEN "unlocked" DEBUG_NORM); \
115 } \
116 INTERN_UNLOCK (&debug_queue_lock); \
117 }while (0)
118
119#define T_ERROR(func, ...) do { \
120 int t_error_temp; \
121 t_error_temp = func(__VA_ARGS__); \
122 if (t_error_temp) \
123 ERROR (t_error_temp, "args: " #__VA_ARGS__ "\n"); \
124 } while (0)
125
126#define NOTE(str, ...) do{ \
127 char note_str[200]; \
128 sprintf (note_str, "%s" DEBUG_PURPLE "NOTE: " DEBUG_NORM str, aio_prefix, ##__VA_ARGS__); \
129 DEBUG_PRINTF ("%-90s %20s():%-5d\n", note_str, __FUNCTION__, __LINE__); \
130 }while (0);
131
132#define ERROR(errnum, str, ...) do{ \
133 char note_str[200]; \
134 sprintf (note_str, "%s" DEBUG_REVERSE_RED "ERROR:" DEBUG_NORM " [%d] " str, aio_prefix, \
135 errnum, ##__VA_ARGS__); \
136 DEBUG_PRINTF ("%-68s %s():%-5d\n", note_str, __FUNCTION__, __LINE__); \
137 }while (0)
138
139#define MUTEX_DEBUG_ADD(mutex) do { \
140 aio_lock_debug *n; \
141 n = malloc (sizeof(aio_lock_debug)); \
142 n->prev = TAIL_DEBUG_QUEUE; \
143 if (n->prev) \
144 n->prev->next = n; \
145 n->next = NULL; \
146 n->line = __LINE__; \
147 n->func = __FUNCTION__; \
148 n->m = mutex; \
149 if (!aio_debug_head) { \
150 aio_debug_head = n; \
151 } \
152 } while (0)
153
154#define UNLOCK(mutex) do { \
155 aio_lock_debug *curr; \
156 DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_GREEN "UNLOCK: " DEBUG_NORM #mutex, \
157 __FUNCTION__, __LINE__, (void *) mutex); \
158 INTERN_LOCK (&debug_queue_lock); \
159 curr = IN_DEBUG_QUEUE (mutex); \
160 if (curr) \
161 { \
162 if (curr->prev) \
163 curr->prev->next = curr->next; \
164 if (curr->next) { \
165 curr->next->prev = curr->prev; \
166 if (curr == aio_debug_head) \
167 aio_debug_head = curr->next; \
168 } else { \
169 if (curr == aio_debug_head) \
170 aio_debug_head = NULL; \
171 } \
172 free (curr); \
173 } \
174 INTERN_UNLOCK (&debug_queue_lock); \
175 INTERN_UNLOCK (mutex); \
176 }while (0)
177
178#define TRYLOCK(mutex) ({ \
179 char status[200]; \
180 int res; \
181 aio_lock_debug *curr; \
182 res = __gthread_mutex_trylock (mutex); \
183 INTERN_LOCK (&debug_queue_lock); \
184 if (res) { \
185 if ((curr = IN_DEBUG_QUEUE (mutex))) { \
186 sprintf (status, DEBUG_RED "%s():%d" DEBUG_NORM, curr->func, curr->line); \
187 } else \
188 sprintf (status, DEBUG_RED "unknown" DEBUG_NORM); \
189 } \
190 else { \
191 sprintf (status, DEBUG_GREEN "unlocked" DEBUG_NORM); \
192 MUTEX_DEBUG_ADD (mutex); \
193 } \
194 DEBUG_PRINTF ("%s%-44s prev: %-35s %20s():%-5d %18p\n", aio_prefix, \
195 DEBUG_DARKRED "TRYLOCK: " DEBUG_NORM #mutex, status, __FUNCTION__, __LINE__, \
196 (void *) mutex); \
197 INTERN_UNLOCK (&debug_queue_lock); \
198 res; \
199 })
200
201#define LOCK(mutex) do { \
202 char status[200]; \
203 CHECK_LOCK (mutex, status); \
204 DEBUG_PRINTF ("%s%-42s prev: %-35s %20s():%-5d %18p\n", aio_prefix, \
205 DEBUG_RED "LOCK: " DEBUG_NORM #mutex, status, __FUNCTION__, __LINE__, (void *) mutex); \
206 INTERN_LOCK (mutex); \
207 INTERN_LOCK (&debug_queue_lock); \
208 MUTEX_DEBUG_ADD (mutex); \
209 INTERN_UNLOCK (&debug_queue_lock); \
210 DEBUG_PRINTF ("%s" DEBUG_RED "ACQ:" DEBUG_NORM " %-30s %78p\n", aio_prefix, #mutex, mutex); \
211 } while (0)
212
b806c88f
LZ
213#ifdef __GTHREAD_RWLOCK_INIT
214#define RWLOCK_DEBUG_ADD(rwlock) do { \
215 aio_rwlock_debug *n; \
216 n = xmalloc (sizeof (aio_rwlock_debug)); \
217 n->prev = TAIL_RWLOCK_DEBUG_QUEUE; \
218 if (n->prev) \
219 n->prev->next = n; \
220 n->next = NULL; \
221 n->line = __LINE__; \
222 n->func = __FUNCTION__; \
223 n->rw = rwlock; \
224 if (!aio_rwlock_debug_head) { \
225 aio_rwlock_debug_head = n; \
226 } \
227 } while (0)
228
229#define CHECK_RDLOCK(rwlock, status) do { \
230 aio_rwlock_debug *curr; \
231 INTERN_WRLOCK (&debug_queue_rwlock); \
232 if (__gthread_rwlock_tryrdlock (rwlock)) { \
233 if ((curr = IN_RWLOCK_DEBUG_QUEUE (rwlock))) { \
234 sprintf (status, DEBUG_RED "%s():%d" DEBUG_NORM, curr->func, curr->line); \
235 } else \
236 sprintf (status, DEBUG_RED "unknown" DEBUG_NORM); \
237 } \
238 else { \
239 __gthread_rwlock_unlock (rwlock); \
240 sprintf (status, DEBUG_GREEN "rwunlocked" DEBUG_NORM); \
241 } \
242 INTERN_RWUNLOCK (&debug_queue_rwlock); \
243 }while (0)
244
245#define CHECK_WRLOCK(rwlock, status) do { \
246 aio_rwlock_debug *curr; \
247 INTERN_WRLOCK (&debug_queue_rwlock); \
248 if (__gthread_rwlock_trywrlock (rwlock)) { \
249 if ((curr = IN_RWLOCK_DEBUG_QUEUE (rwlock))) { \
250 sprintf (status, DEBUG_RED "%s():%d" DEBUG_NORM, curr->func, curr->line); \
251 } else \
252 sprintf (status, DEBUG_RED "unknown" DEBUG_NORM); \
253 } \
254 else { \
255 __gthread_rwlock_unlock (rwlock); \
256 sprintf (status, DEBUG_GREEN "rwunlocked" DEBUG_NORM); \
257 } \
258 INTERN_RWUNLOCK (&debug_queue_rwlock); \
259 }while (0)
260
261#define TAIL_RWLOCK_DEBUG_QUEUE ({ \
262 aio_rwlock_debug *curr = aio_rwlock_debug_head; \
263 while (curr && curr->next) { \
264 curr = curr->next; \
265 } \
266 curr; \
267 })
268
269#define IN_RWLOCK_DEBUG_QUEUE(rwlock) ({ \
270 __label__ end; \
271 aio_rwlock_debug *curr = aio_rwlock_debug_head; \
272 while (curr) { \
273 if (curr->rw == rwlock) { \
274 goto end; \
275 } \
276 curr = curr->next; \
277 } \
278 end:; \
279 curr; \
280 })
281
282#define RDLOCK(rwlock) do { \
283 char status[200]; \
284 CHECK_RDLOCK (rwlock, status); \
285 DEBUG_PRINTF ("%s%-42s prev: %-35s %20s():%-5d %18p\n", aio_prefix, \
286 DEBUG_RED "RDLOCK: " DEBUG_NORM #rwlock, status, __FUNCTION__, __LINE__, (void *) rwlock); \
287 INTERN_RDLOCK (rwlock); \
288 INTERN_WRLOCK (&debug_queue_rwlock); \
289 RWLOCK_DEBUG_ADD (rwlock); \
290 INTERN_RWUNLOCK (&debug_queue_rwlock); \
291 DEBUG_PRINTF ("%s" DEBUG_RED "ACQ:" DEBUG_NORM " %-30s %78p\n", aio_prefix, #rwlock, rwlock); \
292 } while (0)
293
294#define WRLOCK(rwlock) do { \
295 char status[200]; \
296 CHECK_WRLOCK (rwlock, status); \
297 DEBUG_PRINTF ("%s%-42s prev: %-35s %20s():%-5d %18p\n", aio_prefix, \
298 DEBUG_RED "WRLOCK: " DEBUG_NORM #rwlock, status, __FUNCTION__, __LINE__, (void *) rwlock); \
299 INTERN_WRLOCK (rwlock); \
300 INTERN_WRLOCK (&debug_queue_rwlock); \
301 RWLOCK_DEBUG_ADD (rwlock); \
302 INTERN_RWUNLOCK (&debug_queue_rwlock); \
303 DEBUG_PRINTF ("%s" DEBUG_RED "ACQ:" DEBUG_NORM " %-30s %78p\n", aio_prefix, #rwlock, rwlock); \
304 } while (0)
305
306#define RWUNLOCK(rwlock) do { \
307 aio_rwlock_debug *curr; \
308 DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_GREEN "RWUNLOCK: " DEBUG_NORM #rwlock, \
309 __FUNCTION__, __LINE__, (void *) rwlock); \
310 INTERN_WRLOCK (&debug_queue_rwlock); \
311 curr = IN_RWLOCK_DEBUG_QUEUE (rwlock); \
312 if (curr) \
313 { \
314 if (curr->prev) \
315 curr->prev->next = curr->next; \
316 if (curr->next) { \
317 curr->next->prev = curr->prev; \
318 if (curr == aio_rwlock_debug_head) \
319 aio_rwlock_debug_head = curr->next; \
320 } else { \
321 if (curr == aio_rwlock_debug_head) \
322 aio_rwlock_debug_head = NULL; \
323 } \
324 free (curr); \
325 } \
326 INTERN_RWUNLOCK (&debug_queue_rwlock); \
327 INTERN_RWUNLOCK (rwlock); \
328 } while (0)
329
330#define RD_TO_WRLOCK(rwlock) \
331 RWUNLOCK (rwlock); \
332 WRLOCK (rwlock);
333#endif
334
2b4c9065
NK
335#define DEBUG_LINE(...) __VA_ARGS__
336
337#else
338#define DEBUG_PRINTF(...) {}
339#define CHECK_LOCK(au, mutex, status) {}
340#define NOTE(str, ...) {}
341#define DEBUG_LINE(...)
342#define T_ERROR(func, ...) func(__VA_ARGS__)
343#define LOCK(mutex) INTERN_LOCK (mutex)
344#define UNLOCK(mutex) INTERN_UNLOCK (mutex)
345#define TRYLOCK(mutex) (__gthread_mutex_trylock (mutex))
b806c88f
LZ
346#ifdef __GTHREAD_RWLOCK_INIT
347#define RDLOCK(rwlock) INTERN_RDLOCK (rwlock)
348#define WRLOCK(rwlock) INTERN_WRLOCK (rwlock)
349#define RWUNLOCK(rwlock) INTERN_RWUNLOCK (rwlock)
350#define RD_TO_WRLOCK(rwlock) \
351 RWUNLOCK (rwlock); \
352 WRLOCK (rwlock);
353#endif
354#endif
355
356#ifndef __GTHREAD_RWLOCK_INIT
357#define RDLOCK(rwlock) LOCK (rwlock)
358#define WRLOCK(rwlock) LOCK (rwlock)
359#define RWUNLOCK(rwlock) UNLOCK (rwlock)
360#define RD_TO_WRLOCK(rwlock) do {} while (0)
2b4c9065
NK
361#endif
362
363#define INTERN_LOCK(mutex) T_ERROR (__gthread_mutex_lock, mutex);
364
365#define INTERN_UNLOCK(mutex) T_ERROR (__gthread_mutex_unlock, mutex);
366
b806c88f
LZ
367#define INTERN_RDLOCK(rwlock) T_ERROR (__gthread_rwlock_rdlock, rwlock)
368#define INTERN_WRLOCK(rwlock) T_ERROR (__gthread_rwlock_wrlock, rwlock)
369#define INTERN_RWUNLOCK(rwlock) T_ERROR (__gthread_rwlock_unlock, rwlock)
370
2b4c9065
NK
371#if ASYNC_IO
372
3fe19105
TK
373/* au->lock has to be held when calling this macro. */
374
2b4c9065 375#define SIGNAL(advcond) do{ \
2b4c9065
NK
376 (advcond)->pending = 1; \
377 DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_ORANGE "SIGNAL: " DEBUG_NORM \
378 #advcond, __FUNCTION__, __LINE__, (void *) advcond); \
3fe19105 379 T_ERROR (__gthread_cond_broadcast, &(advcond)->signal); \
2b4c9065
NK
380 } while (0)
381
3fe19105
TK
382/* Has to be entered with mutex locked. */
383
2b4c9065
NK
384#define WAIT_SIGNAL_MUTEX(advcond, condition, mutex) do{ \
385 __label__ finish; \
2b4c9065
NK
386 DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_BLUE "WAITING: " DEBUG_NORM \
387 #advcond, __FUNCTION__, __LINE__, (void *) advcond); \
3fe19105 388 if ((advcond)->pending || (condition)) \
2b4c9065 389 goto finish; \
3fe19105
TK
390 while (1) \
391 { \
392 int err_ret = __gthread_cond_wait(&(advcond)->signal, mutex); \
393 if (err_ret) internal_error (NULL, "WAIT_SIGNAL_MUTEX failed"); \
394 if (condition) \
395 { \
396 DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_ORANGE \
397 "REC: " DEBUG_NORM \
398 #advcond, __FUNCTION__, __LINE__, (void *)advcond); \
399 break; \
400 } \
2b4c9065 401 } \
2b4c9065 402 finish: \
3fe19105
TK
403 (advcond)->pending = 0; \
404 UNLOCK (mutex); \
405 } while (0)
406
407/* au->lock has to be held when calling this macro. */
2b4c9065
NK
408
409#define REVOKE_SIGNAL(advcond) do{ \
2b4c9065 410 (advcond)->pending = 0; \
2b4c9065
NK
411 } while (0)
412
413#else
414
415#define SIGNAL(advcond) do{} while(0)
416#define WAIT_SIGNAL_MUTEX(advcond, condition, mutex) do{} while(0)
417#define REVOKE_SIGNAL(advcond) do{} while(0)
418
419#endif
420
421#if ASYNC_IO
422DEBUG_LINE (extern __thread const char *aio_prefix);
423
424DEBUG_LINE (typedef struct aio_lock_debug{
425 __gthread_mutex_t *m;
426 int line;
427 const char *func;
428 struct aio_lock_debug *next;
429 struct aio_lock_debug *prev;
430} aio_lock_debug;)
431
b806c88f
LZ
432DEBUG_LINE (typedef struct aio_rwlock_debug{
433 __gthread_rwlock_t *rw;
434 int line;
435 const char *func;
436 struct aio_rwlock_debug *next;
437 struct aio_rwlock_debug *prev;
438} aio_rwlock_debug;)
439
2b4c9065
NK
440DEBUG_LINE (extern aio_lock_debug *aio_debug_head;)
441DEBUG_LINE (extern __gthread_mutex_t debug_queue_lock;)
b806c88f
LZ
442DEBUG_LINE (extern aio_rwlock_debug *aio_rwlock_debug_head;)
443DEBUG_LINE (extern __gthread_rwlock_t debug_queue_rwlock;)
2b4c9065
NK
444
445/* Thread - local storage of the current unit we are looking at. Needed for
446 error reporting. */
447
448extern __thread gfc_unit *thread_unit;
449#endif
450
451enum aio_do {
452 AIO_INVALID = 0,
453 AIO_DATA_TRANSFER_INIT,
454 AIO_TRANSFER_SCALAR,
455 AIO_TRANSFER_ARRAY,
456 AIO_WRITE_DONE,
457 AIO_READ_DONE,
458 AIO_CLOSE
459};
460
461typedef union transfer_args
462{
463 struct
464 {
465 void (*transfer) (struct st_parameter_dt *, bt, void *, int, size_t, size_t);
466 bt arg_bt;
467 void *data;
468 int i;
469 size_t s1;
470 size_t s2;
471 } scalar;
472 struct
473 {
474 gfc_array_char *desc;
475 int kind;
476 gfc_charlen_type charlen;
477 } array;
478} transfer_args;
479
480struct adv_cond
481{
2532f0f5 482#if ASYNC_IO
2b4c9065 483 int pending;
2b4c9065 484 __gthread_cond_t signal;
2532f0f5 485#endif
2b4c9065
NK
486};
487
488typedef struct async_unit
489{
a7987858
HPN
490 __gthread_mutex_t io_lock; /* Lock for doing actual I/O. */
491 __gthread_mutex_t lock; /* Lock for manipulating the queue structure. */
2532f0f5 492 bool empty;
2b4c9065
NK
493 struct
494 {
495 int waiting;
496 int low;
497 int high;
498 struct adv_cond done;
499 } id;
500
2532f0f5
TK
501#if ASYNC_IO
502 struct adv_cond work;
503 struct adv_cond emptysignal;
504 struct st_parameter_dt *pdt;
decbb5bf 505 __gthread_t thread;
2532f0f5
TK
506 struct transfer_queue *head;
507 struct transfer_queue *tail;
2b4c9065
NK
508
509 struct {
510 const char *message;
511 st_parameter_common *cmp;
512 bool has_error;
513 int last_good_id;
514 int family;
515 bool fatal_error;
516 } error;
2532f0f5 517#endif
2b4c9065
NK
518} async_unit;
519
520void init_async_unit (gfc_unit *);
521internal_proto (init_async_unit);
522
523bool async_wait (st_parameter_common *, async_unit *);
524internal_proto (async_wait);
525
526bool async_wait_id (st_parameter_common *, async_unit *, int);
527internal_proto (async_wait_id);
528
529bool collect_async_errors (st_parameter_common *, async_unit *);
530internal_proto (collect_async_errors);
531
532void async_close (async_unit *);
533internal_proto (async_close);
534
535void enqueue_transfer (async_unit * au, transfer_args * arg, enum aio_do);
536internal_proto (enqueue_transfer);
537
538void enqueue_done (async_unit *, enum aio_do type);
539internal_proto (enqueue_done);
540
541int enqueue_done_id (async_unit *, enum aio_do type);
542internal_proto (enqueue_done_id);
543
544void enqueue_init (async_unit *);
545internal_proto (enqueue_init);
546
547void enqueue_data_transfer_init (async_unit *, st_parameter_dt *, int);
548internal_proto (enqueue_data_transfer_init);
549
550void enqueue_close (async_unit *);
551internal_proto (enqueue_close);
552
553#endif