1/* Copyright (C) 2018-2020 Free Software Foundation, Inc. 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 32#if defined(__GTHREAD_HAS_COND) && defined(__GTHREADS_CXX0X) 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 213#define DEBUG_LINE(...) __VA_ARGS__ 214 215#else 216#define DEBUG_PRINTF(...) {} 217#define CHECK_LOCK(au, mutex, status) {} 218#define NOTE(str, ...) {} 219#define DEBUG_LINE(...) 220#define T_ERROR(func, ...) func(__VA_ARGS__) 221#define LOCK(mutex) INTERN_LOCK (mutex) 222#define UNLOCK(mutex) INTERN_UNLOCK (mutex) 223#define TRYLOCK(mutex) (__gthread_mutex_trylock (mutex)) 224#endif 225 226#define INTERN_LOCK(mutex) T_ERROR (__gthread_mutex_lock, mutex); 227 228#define INTERN_UNLOCK(mutex) T_ERROR (__gthread_mutex_unlock, mutex); 229 230#if ASYNC_IO 231 232/* au->lock has to be held when calling this macro. */ 233 234#define SIGNAL(advcond) do{ \ 235 (advcond)->pending = 1; \ 236 DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_ORANGE "SIGNAL: " DEBUG_NORM \ 237 #advcond, __FUNCTION__, __LINE__, (void *) advcond); \ 238 T_ERROR (__gthread_cond_broadcast, &(advcond)->signal); \ 239 } while (0) 240 241/* Has to be entered with mutex locked. */ 242 243#define WAIT_SIGNAL_MUTEX(advcond, condition, mutex) do{ \ 244 __label__ finish; \ 245 DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_BLUE "WAITING: " DEBUG_NORM \ 246 #advcond, __FUNCTION__, __LINE__, (void *) advcond); \ 247 if ((advcond)->pending || (condition)) \ 248 goto finish; \ 249 while (1) \ 250 { \ 251 int err_ret = __gthread_cond_wait(&(advcond)->signal, mutex); \ 252 if (err_ret) internal_error (NULL, "WAIT_SIGNAL_MUTEX failed"); \ 253 if (condition) \ 254 { \ 255 DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_ORANGE \ 256 "REC: " DEBUG_NORM \ 257 #advcond, __FUNCTION__, __LINE__, (void *)advcond); \ 258 break; \ 259 } \ 260 } \ 261 finish: \ 262 (advcond)->pending = 0; \ 263 UNLOCK (mutex); \ 264 } while (0) 265 266/* au->lock has to be held when calling this macro. */ 267 268#define REVOKE_SIGNAL(advcond) do{ \ 269 (advcond)->pending = 0; \ 270 } while (0) 271 272#else 273 274#define SIGNAL(advcond) do{} while(0) 275#define WAIT_SIGNAL_MUTEX(advcond, condition, mutex) do{} while(0) 276#define REVOKE_SIGNAL(advcond) do{} while(0) 277 278#endif 279 280#if ASYNC_IO 281DEBUG_LINE (extern __thread const char *aio_prefix); 282 283DEBUG_LINE (typedef struct aio_lock_debug{ 284 __gthread_mutex_t *m; 285 int line; 286 const char *func; 287 struct aio_lock_debug *next; 288 struct aio_lock_debug *prev; 289} aio_lock_debug;) 290 291DEBUG_LINE (extern aio_lock_debug *aio_debug_head;) 292DEBUG_LINE (extern __gthread_mutex_t debug_queue_lock;) 293 294/* Thread - local storage of the current unit we are looking at. Needed for 295 error reporting. */ 296 297extern __thread gfc_unit *thread_unit; 298#endif 299 300enum aio_do { 301 AIO_INVALID = 0, 302 AIO_DATA_TRANSFER_INIT, 303 AIO_TRANSFER_SCALAR, 304 AIO_TRANSFER_ARRAY, 305 AIO_WRITE_DONE, 306 AIO_READ_DONE, 307 AIO_CLOSE 308}; 309 310typedef union transfer_args 311{ 312 struct 313 { 314 void (*transfer) (struct st_parameter_dt *, bt, void *, int, size_t, size_t); 315 bt arg_bt; 316 void *data; 317 int i; 318 size_t s1; 319 size_t s2; 320 } scalar; 321 struct 322 { 323 gfc_array_char *desc; 324 int kind; 325 gfc_charlen_type charlen; 326 } array; 327} transfer_args; 328 329struct adv_cond 330{ 331#if ASYNC_IO 332 int pending; 333 __gthread_cond_t signal; 334#endif 335}; 336 337typedef struct async_unit 338{ 339 __gthread_mutex_t io_lock; /* Lock for doing actual I/O. */ 340 __gthread_mutex_t lock; /* Lock for manipulating the queue structure. */ 341 bool empty; 342 struct 343 { 344 int waiting; 345 int low; 346 int high; 347 struct adv_cond done; 348 } id; 349 350#if ASYNC_IO 351 struct adv_cond work; 352 struct adv_cond emptysignal; 353 struct st_parameter_dt *pdt; 354 pthread_t thread; 355 struct transfer_queue *head; 356 struct transfer_queue *tail; 357 358 struct { 359 const char *message; 360 st_parameter_common *cmp; 361 bool has_error; 362 int last_good_id; 363 int family; 364 bool fatal_error; 365 } error; 366#endif 367} async_unit; 368 369void init_async_unit (gfc_unit *); 370internal_proto (init_async_unit); 371 372bool async_wait (st_parameter_common *, async_unit *); 373internal_proto (async_wait); 374 375bool async_wait_id (st_parameter_common *, async_unit *, int); 376internal_proto (async_wait_id); 377 378bool collect_async_errors (st_parameter_common *, async_unit *); 379internal_proto (collect_async_errors); 380 381void async_close (async_unit *); 382internal_proto (async_close); 383 384void enqueue_transfer (async_unit * au, transfer_args * arg, enum aio_do); 385internal_proto (enqueue_transfer); 386 387void enqueue_done (async_unit *, enum aio_do type); 388internal_proto (enqueue_done); 389 390int enqueue_done_id (async_unit *, enum aio_do type); 391internal_proto (enqueue_done_id); 392 393void enqueue_init (async_unit *); 394internal_proto (enqueue_init); 395 396void enqueue_data_transfer_init (async_unit *, st_parameter_dt *, int); 397internal_proto (enqueue_data_transfer_init); 398 399void enqueue_close (async_unit *); 400internal_proto (enqueue_close); 401 402#endif 403