1 /* Copyright (C) 2018-2024 Free Software Foundation, Inc.
2 Contributed by Nicolas Koenig
4 This file is part of the GNU Fortran runtime library (libgfortran).
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)
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.
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.
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/>. */
28 /* Async I/O will not work on targets which do not support
29 __gthread_cond_t and __gthread_equal / __gthread_self. Check
32 #if defined(__GTHREAD_HAS_COND) && defined(__GTHREADS_CXX0X)
38 /* Defining DEBUG_ASYNC will enable somewhat verbose debugging
39 output for async I/O. */
46 /* Define this if you want to use ANSI color escape sequences in your
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"
70 #define DEBUG_ORANGE ""
71 #define DEBUG_GREEN ""
72 #define DEBUG_DARKRED ""
73 #define DEBUG_PURPLE ""
75 #define DEBUG_REVERSE_RED ""
80 #define DEBUG_PRINTF(...) fprintf (stderr,__VA_ARGS__)
82 #define IN_DEBUG_QUEUE(mutex) ({ \
84 aio_lock_debug *curr = aio_debug_head; \
86 if (curr->m == mutex) { \
95 #define TAIL_DEBUG_QUEUE ({ \
96 aio_lock_debug *curr = aio_debug_head; \
97 while (curr && curr->next) { \
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); \
110 sprintf (status, DEBUG_RED "unknown" DEBUG_NORM); \
113 __gthread_mutex_unlock (mutex); \
114 sprintf (status, DEBUG_GREEN "unlocked" DEBUG_NORM); \
116 INTERN_UNLOCK (&debug_queue_lock); \
119 #define T_ERROR(func, ...) do { \
121 t_error_temp = func(__VA_ARGS__); \
123 ERROR (t_error_temp, "args: " #__VA_ARGS__ "\n"); \
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__); \
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__); \
139 #define MUTEX_DEBUG_ADD(mutex) do { \
141 n = malloc (sizeof(aio_lock_debug)); \
142 n->prev = TAIL_DEBUG_QUEUE; \
146 n->line = __LINE__; \
147 n->func = __FUNCTION__; \
149 if (!aio_debug_head) { \
150 aio_debug_head = n; \
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); \
163 curr->prev->next = curr->next; \
165 curr->next->prev = curr->prev; \
166 if (curr == aio_debug_head) \
167 aio_debug_head = curr->next; \
169 if (curr == aio_debug_head) \
170 aio_debug_head = NULL; \
174 INTERN_UNLOCK (&debug_queue_lock); \
175 INTERN_UNLOCK (mutex); \
178 #define TRYLOCK(mutex) ({ \
181 aio_lock_debug *curr; \
182 res = __gthread_mutex_trylock (mutex); \
183 INTERN_LOCK (&debug_queue_lock); \
185 if ((curr = IN_DEBUG_QUEUE (mutex))) { \
186 sprintf (status, DEBUG_RED "%s():%d" DEBUG_NORM, curr->func, curr->line); \
188 sprintf (status, DEBUG_RED "unknown" DEBUG_NORM); \
191 sprintf (status, DEBUG_GREEN "unlocked" DEBUG_NORM); \
192 MUTEX_DEBUG_ADD (mutex); \
194 DEBUG_PRINTF ("%s%-44s prev: %-35s %20s():%-5d %18p\n", aio_prefix, \
195 DEBUG_DARKRED "TRYLOCK: " DEBUG_NORM #mutex, status, __FUNCTION__, __LINE__, \
197 INTERN_UNLOCK (&debug_queue_lock); \
201 #define LOCK(mutex) do { \
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); \
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; \
221 n->line = __LINE__; \
222 n->func = __FUNCTION__; \
224 if (!aio_rwlock_debug_head) { \
225 aio_rwlock_debug_head = n; \
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); \
236 sprintf (status, DEBUG_RED "unknown" DEBUG_NORM); \
239 __gthread_rwlock_unlock (rwlock); \
240 sprintf (status, DEBUG_GREEN "rwunlocked" DEBUG_NORM); \
242 INTERN_RWUNLOCK (&debug_queue_rwlock); \
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); \
252 sprintf (status, DEBUG_RED "unknown" DEBUG_NORM); \
255 __gthread_rwlock_unlock (rwlock); \
256 sprintf (status, DEBUG_GREEN "rwunlocked" DEBUG_NORM); \
258 INTERN_RWUNLOCK (&debug_queue_rwlock); \
261 #define TAIL_RWLOCK_DEBUG_QUEUE ({ \
262 aio_rwlock_debug *curr = aio_rwlock_debug_head; \
263 while (curr && curr->next) { \
269 #define IN_RWLOCK_DEBUG_QUEUE(rwlock) ({ \
271 aio_rwlock_debug *curr = aio_rwlock_debug_head; \
273 if (curr->rw == rwlock) { \
282 #define RDLOCK(rwlock) do { \
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); \
294 #define WRLOCK(rwlock) do { \
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); \
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); \
315 curr->prev->next = curr->next; \
317 curr->next->prev = curr->prev; \
318 if (curr == aio_rwlock_debug_head) \
319 aio_rwlock_debug_head = curr->next; \
321 if (curr == aio_rwlock_debug_head) \
322 aio_rwlock_debug_head = NULL; \
326 INTERN_RWUNLOCK (&debug_queue_rwlock); \
327 INTERN_RWUNLOCK (rwlock); \
330 #define RD_TO_WRLOCK(rwlock) \
335 #define DEBUG_LINE(...) __VA_ARGS__
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))
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) \
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)
363 #define INTERN_LOCK(mutex) T_ERROR (__gthread_mutex_lock, mutex);
365 #define INTERN_UNLOCK(mutex) T_ERROR (__gthread_mutex_unlock, mutex);
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)
373 /* au->lock has to be held when calling this macro. */
375 #define SIGNAL(advcond) do{ \
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); \
379 T_ERROR (__gthread_cond_broadcast, &(advcond)->signal); \
382 /* Has to be entered with mutex locked. */
384 #define WAIT_SIGNAL_MUTEX(advcond, condition, mutex) do{ \
386 DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_BLUE "WAITING: " DEBUG_NORM \
387 #advcond, __FUNCTION__, __LINE__, (void *) advcond); \
388 if ((advcond)->pending || (condition)) \
392 int err_ret = __gthread_cond_wait(&(advcond)->signal, mutex); \
393 if (err_ret) internal_error (NULL, "WAIT_SIGNAL_MUTEX failed"); \
396 DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_ORANGE \
398 #advcond, __FUNCTION__, __LINE__, (void *)advcond); \
403 (advcond)->pending = 0; \
407 /* au->lock has to be held when calling this macro. */
409 #define REVOKE_SIGNAL(advcond) do{ \
410 (advcond)->pending = 0; \
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)
422 DEBUG_LINE (extern __thread
const char *aio_prefix
);
424 DEBUG_LINE (typedef struct aio_lock_debug
{
425 __gthread_mutex_t
*m
;
428 struct aio_lock_debug
*next
;
429 struct aio_lock_debug
*prev
;
432 DEBUG_LINE (typedef struct aio_rwlock_debug
{
433 __gthread_rwlock_t
*rw
;
436 struct aio_rwlock_debug
*next
;
437 struct aio_rwlock_debug
*prev
;
440 DEBUG_LINE (extern aio_lock_debug
*aio_debug_head
;)
441 DEBUG_LINE (extern __gthread_mutex_t debug_queue_lock
;)
442 DEBUG_LINE (extern aio_rwlock_debug
*aio_rwlock_debug_head
;)
443 DEBUG_LINE (extern __gthread_rwlock_t debug_queue_rwlock
;)
445 /* Thread - local storage of the current unit we are looking at. Needed for
448 extern __thread gfc_unit
*thread_unit
;
453 AIO_DATA_TRANSFER_INIT
,
461 typedef union transfer_args
465 void (*transfer
) (struct st_parameter_dt
*, bt
, void *, int, size_t, size_t);
474 gfc_array_char
*desc
;
476 gfc_charlen_type charlen
;
484 __gthread_cond_t signal
;
488 typedef struct async_unit
490 __gthread_mutex_t io_lock
; /* Lock for doing actual I/O. */
491 __gthread_mutex_t lock
; /* Lock for manipulating the queue structure. */
498 struct adv_cond done
;
502 struct adv_cond work
;
503 struct adv_cond emptysignal
;
504 struct st_parameter_dt
*pdt
;
506 struct transfer_queue
*head
;
507 struct transfer_queue
*tail
;
511 st_parameter_common
*cmp
;
520 void init_async_unit (gfc_unit
*);
521 internal_proto (init_async_unit
);
523 bool async_wait (st_parameter_common
*, async_unit
*);
524 internal_proto (async_wait
);
526 bool async_wait_id (st_parameter_common
*, async_unit
*, int);
527 internal_proto (async_wait_id
);
529 bool collect_async_errors (st_parameter_common
*, async_unit
*);
530 internal_proto (collect_async_errors
);
532 void async_close (async_unit
*);
533 internal_proto (async_close
);
535 void enqueue_transfer (async_unit
* au
, transfer_args
* arg
, enum aio_do
);
536 internal_proto (enqueue_transfer
);
538 void enqueue_done (async_unit
*, enum aio_do type
);
539 internal_proto (enqueue_done
);
541 int enqueue_done_id (async_unit
*, enum aio_do type
);
542 internal_proto (enqueue_done_id
);
544 void enqueue_init (async_unit
*);
545 internal_proto (enqueue_init
);
547 void enqueue_data_transfer_init (async_unit
*, st_parameter_dt
*, int);
548 internal_proto (enqueue_data_transfer_init
);
550 void enqueue_close (async_unit
*);
551 internal_proto (enqueue_close
);