mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			404 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			404 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
| /* Copyright (C) 2018-2019 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
 | |
| 
 | |
| #define SIGNAL(advcond) do{						\
 | |
|     INTERN_LOCK (&(advcond)->lock);					\
 | |
|     (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);		\
 | |
|     INTERN_UNLOCK (&(advcond)->lock);					\
 | |
|   } while (0)
 | |
| 
 | |
| #define WAIT_SIGNAL_MUTEX(advcond, condition, mutex) do{		\
 | |
|     __label__ finish;		       					\
 | |
|     INTERN_LOCK (&((advcond)->lock));					\
 | |
|     DEBUG_PRINTF ("%s%-75s %20s():%-5d %18p\n", aio_prefix, DEBUG_BLUE "WAITING: " DEBUG_NORM \
 | |
| 		 #advcond, __FUNCTION__, __LINE__, (void *) advcond);	\
 | |
|     if ((advcond)->pending || (condition)){				\
 | |
|       UNLOCK (mutex);							\
 | |
|       goto finish;							\
 | |
|     }									\
 | |
|     UNLOCK (mutex);							\
 | |
|      while (!__gthread_cond_wait(&(advcond)->signal, &(advcond)->lock)) {	\
 | |
|        { int cond;							\
 | |
| 	 LOCK (mutex); cond = condition; UNLOCK (mutex);	\
 | |
| 	   if (cond){							\
 | |
| 	     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;				\
 | |
| 		 INTERN_UNLOCK (&((advcond)->lock));			\
 | |
| 		 } while (0)
 | |
| 
 | |
| #define REVOKE_SIGNAL(advcond) do{		\
 | |
|     INTERN_LOCK (&(advcond)->lock);		\
 | |
|     (advcond)->pending = 0;			\
 | |
|     INTERN_UNLOCK (&(advcond)->lock);		\
 | |
|   } 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_mutex_t lock;
 | |
|   __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
 |