Cygwin: console: Make I/O functions thread-safe
- POSIX states I/O functions shall be thread-safe, however, cygwin console I/O functions were not. This patch makes console I/O functions thread-safe.
This commit is contained in:
		
				
					committed by
					
						 Corinna Vinschen
						Corinna Vinschen
					
				
			
			
				
	
			
			
			
						parent
						
							8382778cdb
						
					
				
				
					commit
					f4b47827cf
				
			| @@ -1698,6 +1698,12 @@ class fhandler_serial: public fhandler_base | ||||
|   } | ||||
| }; | ||||
|  | ||||
| #define acquire_input_mutex(ms) \ | ||||
|   __acquire_input_mutex (__PRETTY_FUNCTION__, __LINE__, ms) | ||||
|  | ||||
| #define release_input_mutex() \ | ||||
|   __release_input_mutex (__PRETTY_FUNCTION__, __LINE__) | ||||
|  | ||||
| #define acquire_output_mutex(ms) \ | ||||
|   __acquire_output_mutex (__PRETTY_FUNCTION__, __LINE__, ms) | ||||
|  | ||||
| @@ -1897,6 +1903,7 @@ private: | ||||
|   static const unsigned MAX_WRITE_CHARS; | ||||
|   static console_state *shared_console_info; | ||||
|   static bool invisible_console; | ||||
|   HANDLE input_mutex, output_mutex; | ||||
|  | ||||
|   /* Used when we encounter a truncated multi-byte sequence.  The | ||||
|      lead bytes are stored here and revisited in the next write call. */ | ||||
| @@ -1966,8 +1973,11 @@ private: | ||||
|   bool focus_aware () {return shared_console_info->con.use_focus;} | ||||
|   bool get_cons_readahead_valid () | ||||
|   { | ||||
|     return shared_console_info->con.cons_rapoi != NULL && | ||||
|     acquire_input_mutex (INFINITE); | ||||
|     bool ret = shared_console_info->con.cons_rapoi != NULL && | ||||
|       *shared_console_info->con.cons_rapoi; | ||||
|     release_input_mutex (); | ||||
|     return ret; | ||||
|   } | ||||
|  | ||||
|   select_record *select_read (select_stuff *); | ||||
| @@ -2002,6 +2012,12 @@ private: | ||||
|     return fh; | ||||
|   } | ||||
|   input_states process_input_message (); | ||||
|   void setup_io_mutex (void); | ||||
|   DWORD __acquire_input_mutex (const char *fn, int ln, DWORD ms); | ||||
|   void __release_input_mutex (const char *fn, int ln); | ||||
|   DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms); | ||||
|   void __release_output_mutex (const char *fn, int ln); | ||||
|  | ||||
|   friend tty_min * tty_list::get_cttyp (); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -232,6 +232,45 @@ tty_list::get_cttyp () | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| void | ||||
| fhandler_console::setup_io_mutex (void) | ||||
| { | ||||
|   char buf[MAX_PATH]; | ||||
|   DWORD res; | ||||
|  | ||||
|   res = WAIT_FAILED; | ||||
|   if (!input_mutex || WAIT_FAILED == (res = acquire_input_mutex (0))) | ||||
|     { | ||||
|       shared_name (buf, "cygcons.input.mutex", get_minor ()); | ||||
|       input_mutex = OpenMutex (MAXIMUM_ALLOWED, TRUE, buf); | ||||
|       if (!input_mutex) | ||||
| 	input_mutex = CreateMutex (&sec_none, FALSE, buf); | ||||
|       if (!input_mutex) | ||||
| 	{ | ||||
| 	  __seterrno (); | ||||
| 	  return; | ||||
| 	} | ||||
|     } | ||||
|   if (res == WAIT_OBJECT_0) | ||||
|     release_input_mutex (); | ||||
|  | ||||
|   res = WAIT_FAILED; | ||||
|   if (!output_mutex || WAIT_FAILED == (res = acquire_output_mutex (0))) | ||||
|     { | ||||
|       shared_name (buf, "cygcons.output.mutex", get_minor ()); | ||||
|       output_mutex = OpenMutex (MAXIMUM_ALLOWED, TRUE, buf); | ||||
|       if (!output_mutex) | ||||
| 	output_mutex = CreateMutex (&sec_none, FALSE, buf); | ||||
|       if (!output_mutex) | ||||
| 	{ | ||||
| 	  __seterrno (); | ||||
| 	  return; | ||||
| 	} | ||||
|     } | ||||
|   if (res == WAIT_OBJECT_0) | ||||
|     release_output_mutex (); | ||||
| } | ||||
|  | ||||
| inline DWORD | ||||
| dev_console::con_to_str (char *d, int dlen, WCHAR w) | ||||
| { | ||||
| @@ -362,7 +401,9 @@ fhandler_console::read (void *pv, size_t& buflen) | ||||
| #define buf ((char *) pv) | ||||
|  | ||||
|       int ret; | ||||
|       acquire_input_mutex (INFINITE); | ||||
|       ret = process_input_message (); | ||||
|       release_input_mutex (); | ||||
|       switch (ret) | ||||
| 	{ | ||||
| 	case input_error: | ||||
| @@ -382,6 +423,7 @@ fhandler_console::read (void *pv, size_t& buflen) | ||||
|     } | ||||
|  | ||||
|   /* Check console read-ahead buffer filled from terminal requests */ | ||||
|   acquire_input_mutex (INFINITE); | ||||
|   while (con.cons_rapoi && *con.cons_rapoi && buflen) | ||||
|     { | ||||
|       buf[copied_chars++] = *con.cons_rapoi++; | ||||
| @@ -393,6 +435,7 @@ fhandler_console::read (void *pv, size_t& buflen) | ||||
|  | ||||
|   if (!ralen) | ||||
|     input_ready = false; | ||||
|   release_input_mutex (); | ||||
|  | ||||
| #undef buf | ||||
|  | ||||
| @@ -903,6 +946,8 @@ fhandler_console::open (int flags, mode_t) | ||||
|     } | ||||
|   set_output_handle (h); | ||||
|  | ||||
|   setup_io_mutex (); | ||||
|  | ||||
|   if (con.fillin (get_output_handle ())) | ||||
|     { | ||||
|       con.current_win32_attr = con.b.wAttributes; | ||||
| @@ -953,6 +998,11 @@ fhandler_console::close () | ||||
| { | ||||
|   debug_printf ("closing: %p, %p", get_handle (), get_output_handle ()); | ||||
|  | ||||
|   CloseHandle (input_mutex); | ||||
|   input_mutex = NULL; | ||||
|   CloseHandle (output_mutex); | ||||
|   output_mutex = NULL; | ||||
|  | ||||
|   if (shared_console_info && getpid () == con.owner && | ||||
|       wincap.has_con_24bit_colors ()) | ||||
|     { | ||||
| @@ -980,6 +1030,7 @@ fhandler_console::ioctl (unsigned int cmd, void *arg) | ||||
|   int res = fhandler_termios::ioctl (cmd, arg); | ||||
|   if (res <= 0) | ||||
|     return res; | ||||
|   acquire_output_mutex (INFINITE); | ||||
|   switch (cmd) | ||||
|     { | ||||
|       case TIOCGWINSZ: | ||||
| @@ -995,20 +1046,25 @@ fhandler_console::ioctl (unsigned int cmd, void *arg) | ||||
| 	    syscall_printf ("WINSZ: (row=%d,col=%d)", | ||||
| 			   ((struct winsize *) arg)->ws_row, | ||||
| 			   ((struct winsize *) arg)->ws_col); | ||||
| 	    release_output_mutex (); | ||||
| 	    return 0; | ||||
| 	  } | ||||
| 	else | ||||
| 	  { | ||||
| 	    syscall_printf ("WINSZ failed"); | ||||
| 	    __seterrno (); | ||||
| 	    release_output_mutex (); | ||||
| 	    return -1; | ||||
| 	  } | ||||
| 	release_output_mutex (); | ||||
| 	return 0; | ||||
|       case TIOCSWINSZ: | ||||
| 	bg_check (SIGTTOU); | ||||
| 	release_output_mutex (); | ||||
| 	return 0; | ||||
|       case KDGKBMETA: | ||||
| 	*(int *) arg = (con.metabit) ? K_METABIT : K_ESCPREFIX; | ||||
| 	release_output_mutex (); | ||||
| 	return 0; | ||||
|       case KDSKBMETA: | ||||
| 	if ((intptr_t) arg == K_METABIT) | ||||
| @@ -1018,16 +1074,20 @@ fhandler_console::ioctl (unsigned int cmd, void *arg) | ||||
| 	else | ||||
| 	  { | ||||
| 	    set_errno (EINVAL); | ||||
| 	    release_output_mutex (); | ||||
| 	    return -1; | ||||
| 	  } | ||||
| 	release_output_mutex (); | ||||
| 	return 0; | ||||
|       case TIOCLINUX: | ||||
| 	if (*(unsigned char *) arg == 6) | ||||
| 	  { | ||||
| 	    *(unsigned char *) arg = (unsigned char) con.nModifiers; | ||||
| 	    release_output_mutex (); | ||||
| 	    return 0; | ||||
| 	  } | ||||
| 	set_errno (EINVAL); | ||||
| 	release_output_mutex (); | ||||
| 	return -1; | ||||
|       case FIONREAD: | ||||
| 	{ | ||||
| @@ -1040,17 +1100,20 @@ fhandler_console::ioctl (unsigned int cmd, void *arg) | ||||
| 	  if (!PeekConsoleInputW (get_handle (), inp, INREC_SIZE, &n)) | ||||
| 	    { | ||||
| 	      set_errno (EINVAL); | ||||
| 	      release_output_mutex (); | ||||
| 	      return -1; | ||||
| 	    } | ||||
| 	  while (n-- > 0) | ||||
| 	    if (inp[n].EventType == KEY_EVENT && inp[n].Event.KeyEvent.bKeyDown) | ||||
| 	      ++ret; | ||||
| 	  *(int *) arg = ret; | ||||
| 	  release_output_mutex (); | ||||
| 	  return 0; | ||||
| 	} | ||||
| 	break; | ||||
|     } | ||||
|  | ||||
|   release_output_mutex (); | ||||
|   return fhandler_base::ioctl (cmd, arg); | ||||
| } | ||||
|  | ||||
| @@ -1075,6 +1138,7 @@ fhandler_console::output_tcsetattr (int, struct termios const *t) | ||||
| { | ||||
|   /* All the output bits we can ignore */ | ||||
|  | ||||
|   acquire_output_mutex (INFINITE); | ||||
|   DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; | ||||
|   /* If system has 24 bit color capability, use xterm compatible mode. */ | ||||
|   if (wincap.has_con_24bit_colors ()) | ||||
| @@ -1087,6 +1151,7 @@ fhandler_console::output_tcsetattr (int, struct termios const *t) | ||||
|   int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1; | ||||
|   if (res) | ||||
|     __seterrno_from_win_error (GetLastError ()); | ||||
|   release_output_mutex (); | ||||
|   syscall_printf ("%d = tcsetattr(,%p) (ENABLE FLAGS %y) (lflag %y oflag %y)", | ||||
| 		  res, t, flags, t->c_lflag, t->c_oflag); | ||||
|   return res; | ||||
| @@ -1097,6 +1162,7 @@ fhandler_console::input_tcsetattr (int, struct termios const *t) | ||||
| { | ||||
|   /* Ignore the optional_actions stuff, since all output is emitted | ||||
|      instantly */ | ||||
|   acquire_input_mutex (INFINITE); | ||||
|  | ||||
|   DWORD oflags; | ||||
|  | ||||
| @@ -1158,6 +1224,7 @@ fhandler_console::input_tcsetattr (int, struct termios const *t) | ||||
|     } | ||||
|  | ||||
|   get_ttyp ()->rstcons (false); | ||||
|   release_input_mutex (); | ||||
|   return res; | ||||
| } | ||||
|  | ||||
| @@ -1210,7 +1277,8 @@ fhandler_console::tcgetattr (struct termios *t) | ||||
| } | ||||
|  | ||||
| fhandler_console::fhandler_console (fh_devices unit) : | ||||
|   fhandler_termios (), input_ready (false) | ||||
|   fhandler_termios (), input_ready (false), | ||||
|   input_mutex (NULL), output_mutex (NULL) | ||||
| { | ||||
|   if (unit > 0) | ||||
|     dev ().parse (unit); | ||||
| @@ -2132,9 +2200,11 @@ fhandler_console::char_command (char c) | ||||
| 	 fhandler_console object associated with standard input. | ||||
| 	 So puts_readahead does not work. | ||||
| 	 Use a common console read-ahead buffer instead. */ | ||||
|       acquire_input_mutex (INFINITE); | ||||
|       con.cons_rapoi = NULL; | ||||
|       strcpy (con.cons_rabuf, buf); | ||||
|       con.cons_rapoi = con.cons_rabuf; | ||||
|       release_input_mutex (); | ||||
|       /* Wake up read() or select() by sending a message | ||||
| 	 which has no effect */ | ||||
|       PostMessageW (GetConsoleWindow (), WM_SETFOCUS, 0, 0); | ||||
| @@ -2147,9 +2217,11 @@ fhandler_console::char_command (char c) | ||||
| 	  y -= con.b.srWindow.Top; | ||||
| 	  /* x -= con.b.srWindow.Left;		// not available yet */ | ||||
| 	  __small_sprintf (buf, "\033[%d;%dR", y + 1, x + 1); | ||||
| 	  acquire_input_mutex (INFINITE); | ||||
| 	  con.cons_rapoi = NULL; | ||||
| 	  strcpy (con.cons_rabuf, buf); | ||||
| 	  con.cons_rapoi = con.cons_rabuf; | ||||
| 	  release_input_mutex (); | ||||
| 	  /* Wake up read() or select() by sending a message | ||||
| 	     which has no effect */ | ||||
| 	  PostMessageW (GetConsoleWindow (), WM_SETFOCUS, 0, 0); | ||||
| @@ -2487,6 +2559,7 @@ fhandler_console::write (const void *vsrc, size_t len) | ||||
|     return (ssize_t) bg; | ||||
|  | ||||
|   push_process_state process_state (PID_TTYOU); | ||||
|   acquire_output_mutex (INFINITE); | ||||
|  | ||||
|   /* Run and check for ansi sequences */ | ||||
|   unsigned const char *src = (unsigned char *) vsrc; | ||||
| @@ -2507,7 +2580,10 @@ fhandler_console::write (const void *vsrc, size_t len) | ||||
| 	case normal: | ||||
| 	  src = write_normal (src, end); | ||||
| 	  if (!src) /* write_normal failed */ | ||||
| 	    return -1; | ||||
| 	    { | ||||
| 	      release_output_mutex (); | ||||
| 	      return -1; | ||||
| 	    } | ||||
| 	  break; | ||||
| 	case gotesc: | ||||
| 	  if (*src == '[')		/* CSI Control Sequence Introducer */ | ||||
| @@ -2676,6 +2752,7 @@ fhandler_console::write (const void *vsrc, size_t len) | ||||
| 	  break; | ||||
| 	} | ||||
|     } | ||||
|   release_output_mutex (); | ||||
|  | ||||
|   syscall_printf ("%ld = fhandler_console::write(...)", len); | ||||
|  | ||||
| @@ -2803,6 +2880,7 @@ void | ||||
| fhandler_console::fixup_after_fork_exec (bool execing) | ||||
| { | ||||
|   set_unit (); | ||||
|   setup_io_mutex (); | ||||
| } | ||||
|  | ||||
| // #define WINSTA_ACCESS (WINSTA_READATTRIBUTES | STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | WINSTA_CREATEDESKTOP | WINSTA_EXITWINDOWS) | ||||
| @@ -2986,3 +3064,57 @@ fhandler_console::need_invisible () | ||||
|   debug_printf ("invisible_console %d", invisible_console); | ||||
|   return b; | ||||
| } | ||||
|  | ||||
| DWORD | ||||
| fhandler_console::__acquire_input_mutex (const char *fn, int ln, DWORD ms) | ||||
| { | ||||
| #ifdef DEBUGGING | ||||
|   strace.prntf (_STRACE_TERMIOS, fn, "(%d): trying to get input_mutex", ln); | ||||
| #endif | ||||
|   DWORD res = WaitForSingleObject (input_mutex, ms); | ||||
|   if (res != WAIT_OBJECT_0) | ||||
|     strace.prntf (_STRACE_TERMIOS, fn, | ||||
| 		  "(%d): Failed to acquire input_mutex %08x", | ||||
| 		  ln, GetLastError ()); | ||||
| #ifdef DEBUGGING | ||||
|   else | ||||
|     strace.prntf (_STRACE_TERMIOS, fn, "(%d): got input_mutex", ln); | ||||
| #endif | ||||
|   return res; | ||||
| } | ||||
|  | ||||
| void | ||||
| fhandler_console::__release_input_mutex (const char *fn, int ln) | ||||
| { | ||||
|   ReleaseMutex (input_mutex); | ||||
| #ifdef DEBUGGING | ||||
|   strace.prntf (_STRACE_TERMIOS, fn, "(%d): release input_mutex", ln); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| DWORD | ||||
| fhandler_console::__acquire_output_mutex (const char *fn, int ln, DWORD ms) | ||||
| { | ||||
| #ifdef DEBUGGING | ||||
|   strace.prntf (_STRACE_TERMIOS, fn, "(%d): trying to get output_mutex", ln); | ||||
| #endif | ||||
|   DWORD res = WaitForSingleObject (output_mutex, ms); | ||||
|   if (res != WAIT_OBJECT_0) | ||||
|     strace.prntf (_STRACE_TERMIOS, fn, | ||||
| 		  "(%d): Failed to acquire output_mutex %08x", | ||||
| 		  ln, GetLastError ()); | ||||
| #ifdef DEBUGGING | ||||
|   else | ||||
|     strace.prntf (_STRACE_TERMIOS, fn, "(%d): got output_mutex", ln); | ||||
| #endif | ||||
|   return res; | ||||
| } | ||||
|  | ||||
| void | ||||
| fhandler_console::__release_output_mutex (const char *fn, int ln) | ||||
| { | ||||
|   ReleaseMutex (output_mutex); | ||||
| #ifdef DEBUGGING | ||||
|   strace.prntf (_STRACE_TERMIOS, fn, "(%d): release output_mutex", ln); | ||||
| #endif | ||||
| } | ||||
|   | ||||
| @@ -1039,15 +1039,20 @@ peek_console (select_record *me, bool) | ||||
|   set_handle_or_return_if_not_open (h, me); | ||||
|  | ||||
|   while (!fh->input_ready && !fh->get_cons_readahead_valid ()) | ||||
|     if (fh->bg_check (SIGTTIN, true) <= bg_eof) | ||||
|       return me->read_ready = true; | ||||
|     else if (!PeekConsoleInputW (h, &irec, 1, &events_read) || !events_read) | ||||
|       break; | ||||
|     else if (fhandler_console::input_winch == fh->process_input_message ()) | ||||
|       { | ||||
| 	set_sig_errno (EINTR); | ||||
| 	return -1; | ||||
|       } | ||||
|     { | ||||
|       if (fh->bg_check (SIGTTIN, true) <= bg_eof) | ||||
| 	return me->read_ready = true; | ||||
|       else if (!PeekConsoleInputW (h, &irec, 1, &events_read) || !events_read) | ||||
| 	break; | ||||
|       fh->acquire_input_mutex (INFINITE); | ||||
|       if (fhandler_console::input_winch == fh->process_input_message ()) | ||||
| 	{ | ||||
| 	  set_sig_errno (EINTR); | ||||
| 	  fh->release_input_mutex (); | ||||
| 	  return -1; | ||||
| 	} | ||||
|       fh->release_input_mutex (); | ||||
|     } | ||||
|   if (fh->input_ready || fh->get_cons_readahead_valid ()) | ||||
|     return me->read_ready = true; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user