1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
|
/* Copyright (C) 2018-2022 Free Software Foundation, Inc.
Contributed by Nicolas Koenig
This file is part of the GNU Fortran runtime library (libgfortran).
Libgfortran is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
Libgfortran is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
#ifndef ASYNC_H
#define ASYNC_H
/* Async I/O will not work on targets which do not support
__gthread_cond_t and __gthread_equal / __gthread_self. Check
this. */
#if defined(__GTHREAD_HAS_COND) && defined(__GTHREADS_CXX0X)
#define ASYNC_IO 1
#else
#define ASYNC_IO 0
#endif
/* Defining DEBUG_ASYNC will enable somewhat verbose debugging
output for async I/O. */
#define DEBUG_ASYNC
#undef DEBUG_ASYNC
#ifdef DEBUG_ASYNC
/* Define this if you want to use ANSI color escape sequences in your
debugging output. */
#define DEBUG_COLOR
#ifdef DEBUG_COLOR
#define MPREFIX "\033[30;46mM:\033[0m "
#define TPREFIX "\033[37;44mT:\033[0m "
#define RPREFIX "\033[37;41mR:\033[0m "
#define DEBUG_RED "\033[31m"
#define DEBUG_ORANGE "\033[33m"
#define DEBUG_GREEN "\033[32m"
#define DEBUG_DARKRED "\033[31;2m"
#define DEBUG_PURPLE "\033[35m"
#define DEBUG_NORM "\033[0m"
#define DEBUG_REVERSE_RED "\033[41;37m"
#define DEBUG_BLUE "\033[34m"
#else
#define MPREFIX "M: "
#define TPREFIX "T: "
#define RPREFIX ""
#define DEBUG_RED ""
#define DEBUG_ORANGE ""
#define DEBUG_GREEN ""
#define DEBUG_DARKRED ""
#define DEBUG_PURPLE ""
#define DEBUG_NORM ""
#define DEBUG_REVERSE_RED ""
#define DEBUG_BLUE ""
#endif
#define DEBUG_PRINTF(...) fprintf (stderr,__VA_ARGS__)
#define IN_DEBUG_QUEUE(mutex) ({ \
__label__ end; \
aio_lock_debug *curr = aio_debug_head; \
while (curr) { \
if (curr->m == mutex) { \
goto end; \
} \
curr = curr->next; \
} \
end:; \
curr; \
})
#define TAIL_DEBUG_QUEUE ({ \
aio_lock_debug *curr = aio_debug_head; \
while (curr && curr->next) { \
curr = curr->next; \
} \
curr; \
})
#define CHECK_LOCK(mutex, status) do { \
aio_lock_debug *curr; \
INTERN_LOCK (&debug_queue_lock); \
if (__gthread_mutex_trylock (mutex)) { \
if ((curr = IN_DEBUG_QUEUE (mutex))) { \
sprintf (status, DEBUG_RED "%s():%d" DEBUG_NORM, curr->func, curr->line); \
} else \
sprintf (status, DEBUG_RED "unknown" DEBUG_NORM); \
} \
else { \
__gthread_mutex_unlock (mutex); \
sprintf (status, DEBUG_GREEN "unlocked" DEBUG_NORM); \
} \
INTERN_UNLOCK (&debug_queue_lock); \
}while (0)
#define T_ERROR(func, ...) do { \
int t_error_temp; \
t_error_temp = func(__VA_ARGS__); \
if (t_error_temp) \
ERROR (t_error_temp, "args: " #__VA_ARGS__ "\n"); \
} while (0)
#define NOTE(str, ...) do{ \
char note_str[200]; \
sprintf (note_str, "%s" DEBUG_PURPLE "NOTE: " DEBUG_NORM str, aio_prefix, ##__VA_ARGS__); \
DEBUG_PRINTF ("%-90s %20s():%-5d\n", note_str, __FUNCTION__, __LINE__); \
}while (0);
#define ERROR(errnum, str, ...) do{ \
char note_str[200]; \
sprintf (note_str, "%s" DEBUG_REVERSE_RED "ERROR:" DEBUG_NORM " [%d] " str, aio_prefix, \
errnum, ##__VA_ARGS__); \
DEBUG_PRINTF ("%-68s %s():%-5d\n", note_str, __FUNCTION__, __LINE__); \
}while (0)
#define MUTEX_DEBUG_ADD(mutex) do { \
aio_lock_debug *n; \
n = malloc (sizeof(aio_lock_debug)); \
n->prev = TAIL_DEBUG_QUEUE; \
if (n->prev) \
n->prev->next = n; \
n->next = NULL; \
n->line = __LINE__; \
n->func = __FUNCTION__; \
n->m = mutex; \
if (!aio_debug_head) { \
aio_debug_head = n; \
} \
} while (0)
#define UNLOCK(mutex) do { \
aio_lock_debug *curr; \
DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_GREEN "UNLOCK: " DEBUG_NORM #mutex, \
__FUNCTION__, __LINE__, (void *) mutex); \
INTERN_LOCK (&debug_queue_lock); \
curr = IN_DEBUG_QUEUE (mutex); \
if (curr) \
{ \
if (curr->prev) \
curr->prev->next = curr->next; \
if (curr->next) { \
curr->next->prev = curr->prev; \
if (curr == aio_debug_head) \
aio_debug_head = curr->next; \
} else { \
if (curr == aio_debug_head) \
aio_debug_head = NULL; \
} \
free (curr); \
} \
INTERN_UNLOCK (&debug_queue_lock); \
INTERN_UNLOCK (mutex); \
}while (0)
#define TRYLOCK(mutex) ({ \
char status[200]; \
int res; \
aio_lock_debug *curr; \
res = __gthread_mutex_trylock (mutex); \
INTERN_LOCK (&debug_queue_lock); \
if (res) { \
if ((curr = IN_DEBUG_QUEUE (mutex))) { \
sprintf (status, DEBUG_RED "%s():%d" DEBUG_NORM, curr->func, curr->line); \
} else \
sprintf (status, DEBUG_RED "unknown" DEBUG_NORM); \
} \
else { \
sprintf (status, DEBUG_GREEN "unlocked" DEBUG_NORM); \
MUTEX_DEBUG_ADD (mutex); \
} \
DEBUG_PRINTF ("%s%-44s prev: %-35s %20s():%-5d %18p\n", aio_prefix, \
DEBUG_DARKRED "TRYLOCK: " DEBUG_NORM #mutex, status, __FUNCTION__, __LINE__, \
(void *) mutex); \
INTERN_UNLOCK (&debug_queue_lock); \
res; \
})
#define LOCK(mutex) do { \
char status[200]; \
CHECK_LOCK (mutex, status); \
DEBUG_PRINTF ("%s%-42s prev: %-35s %20s():%-5d %18p\n", aio_prefix, \
DEBUG_RED "LOCK: " DEBUG_NORM #mutex, status, __FUNCTION__, __LINE__, (void *) mutex); \
INTERN_LOCK (mutex); \
INTERN_LOCK (&debug_queue_lock); \
MUTEX_DEBUG_ADD (mutex); \
INTERN_UNLOCK (&debug_queue_lock); \
DEBUG_PRINTF ("%s" DEBUG_RED "ACQ:" DEBUG_NORM " %-30s %78p\n", aio_prefix, #mutex, mutex); \
} while (0)
#define DEBUG_LINE(...) __VA_ARGS__
#else
#define DEBUG_PRINTF(...) {}
#define CHECK_LOCK(au, mutex, status) {}
#define NOTE(str, ...) {}
#define DEBUG_LINE(...)
#define T_ERROR(func, ...) func(__VA_ARGS__)
#define LOCK(mutex) INTERN_LOCK (mutex)
#define UNLOCK(mutex) INTERN_UNLOCK (mutex)
#define TRYLOCK(mutex) (__gthread_mutex_trylock (mutex))
#endif
#define INTERN_LOCK(mutex) T_ERROR (__gthread_mutex_lock, mutex);
#define INTERN_UNLOCK(mutex) T_ERROR (__gthread_mutex_unlock, mutex);
#if ASYNC_IO
/* au->lock has to be held when calling this macro. */
#define SIGNAL(advcond) do{ \
(advcond)->pending = 1; \
DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_ORANGE "SIGNAL: " DEBUG_NORM \
#advcond, __FUNCTION__, __LINE__, (void *) advcond); \
T_ERROR (__gthread_cond_broadcast, &(advcond)->signal); \
} while (0)
/* Has to be entered with mutex locked. */
#define WAIT_SIGNAL_MUTEX(advcond, condition, mutex) do{ \
__label__ finish; \
DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_BLUE "WAITING: " DEBUG_NORM \
#advcond, __FUNCTION__, __LINE__, (void *) advcond); \
if ((advcond)->pending || (condition)) \
goto finish; \
while (1) \
{ \
int err_ret = __gthread_cond_wait(&(advcond)->signal, mutex); \
if (err_ret) internal_error (NULL, "WAIT_SIGNAL_MUTEX failed"); \
if (condition) \
{ \
DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_ORANGE \
"REC: " DEBUG_NORM \
#advcond, __FUNCTION__, __LINE__, (void *)advcond); \
break; \
} \
} \
finish: \
(advcond)->pending = 0; \
UNLOCK (mutex); \
} while (0)
/* au->lock has to be held when calling this macro. */
#define REVOKE_SIGNAL(advcond) do{ \
(advcond)->pending = 0; \
} while (0)
#else
#define SIGNAL(advcond) do{} while(0)
#define WAIT_SIGNAL_MUTEX(advcond, condition, mutex) do{} while(0)
#define REVOKE_SIGNAL(advcond) do{} while(0)
#endif
#if ASYNC_IO
DEBUG_LINE (extern __thread const char *aio_prefix);
DEBUG_LINE (typedef struct aio_lock_debug{
__gthread_mutex_t *m;
int line;
const char *func;
struct aio_lock_debug *next;
struct aio_lock_debug *prev;
} aio_lock_debug;)
DEBUG_LINE (extern aio_lock_debug *aio_debug_head;)
DEBUG_LINE (extern __gthread_mutex_t debug_queue_lock;)
/* Thread - local storage of the current unit we are looking at. Needed for
error reporting. */
extern __thread gfc_unit *thread_unit;
#endif
enum aio_do {
AIO_INVALID = 0,
AIO_DATA_TRANSFER_INIT,
AIO_TRANSFER_SCALAR,
AIO_TRANSFER_ARRAY,
AIO_WRITE_DONE,
AIO_READ_DONE,
AIO_CLOSE
};
typedef union transfer_args
{
struct
{
void (*transfer) (struct st_parameter_dt *, bt, void *, int, size_t, size_t);
bt arg_bt;
void *data;
int i;
size_t s1;
size_t s2;
} scalar;
struct
{
gfc_array_char *desc;
int kind;
gfc_charlen_type charlen;
} array;
} transfer_args;
struct adv_cond
{
#if ASYNC_IO
int pending;
__gthread_cond_t signal;
#endif
};
typedef struct async_unit
{
__gthread_mutex_t io_lock; /* Lock for doing actual I/O. */
__gthread_mutex_t lock; /* Lock for manipulating the queue structure. */
bool empty;
struct
{
int waiting;
int low;
int high;
struct adv_cond done;
} id;
#if ASYNC_IO
struct adv_cond work;
struct adv_cond emptysignal;
struct st_parameter_dt *pdt;
pthread_t thread;
struct transfer_queue *head;
struct transfer_queue *tail;
struct {
const char *message;
st_parameter_common *cmp;
bool has_error;
int last_good_id;
int family;
bool fatal_error;
} error;
#endif
} async_unit;
void init_async_unit (gfc_unit *);
internal_proto (init_async_unit);
bool async_wait (st_parameter_common *, async_unit *);
internal_proto (async_wait);
bool async_wait_id (st_parameter_common *, async_unit *, int);
internal_proto (async_wait_id);
bool collect_async_errors (st_parameter_common *, async_unit *);
internal_proto (collect_async_errors);
void async_close (async_unit *);
internal_proto (async_close);
void enqueue_transfer (async_unit * au, transfer_args * arg, enum aio_do);
internal_proto (enqueue_transfer);
void enqueue_done (async_unit *, enum aio_do type);
internal_proto (enqueue_done);
int enqueue_done_id (async_unit *, enum aio_do type);
internal_proto (enqueue_done_id);
void enqueue_init (async_unit *);
internal_proto (enqueue_init);
void enqueue_data_transfer_init (async_unit *, st_parameter_dt *, int);
internal_proto (enqueue_data_transfer_init);
void enqueue_close (async_unit *);
internal_proto (enqueue_close);
#endif
|