Re: [PATCH 0/8] FIFO: bug fixes and small improvements

2020-08-04 Thread Corinna Vinschen
On Aug  4 08:54, Ken Brown via Cygwin-patches wrote:
> The second patch in this series fixes a serious bug that has resulted
> in observable hangs.  The other patches are minor bug fixes or minor
> performance improvements.
> 
> Ken Brown (8):
>   Cygwin: FIFO: lock fixes
>   Cygwin: FIFO: fix timing issue with owner change
>   Cygwin: FIFO: add a timeout to take_ownership
>   Cygwin: FIFO: reorganize some fifo_client_handler methods
>   Cygwin: FIFO: don't read from pipes that are closing
>   Cygwin: FIFO: synchronize the fifo_reader and fifosel threads
>   Cygwin: FIFO: fix indentation
>   Cygwin: FIFO: add a third pass to raw_read
> 
>  winsup/cygwin/fhandler.h   |  24 +--
>  winsup/cygwin/fhandler_fifo.cc | 358 ++---
>  winsup/cygwin/select.cc|  38 ++--
>  3 files changed, 268 insertions(+), 152 deletions(-)
> 
> -- 
> 2.28.0

LGTM


Corinna


[PATCH 5/8] Cygwin: FIFO: don't read from pipes that are closing

2020-08-04 Thread Ken Brown via Cygwin-patches
Don't try to read from fifo_client_handlers that are in the fc_closing
state.  Experiments have shown that this always yields
STATUS_PIPE_BROKEN, so it just wastes a Windows system call.

Re-order the values in enum fifo_client_connect_state to reflect the
new status of fc_closing.
---
 winsup/cygwin/fhandler.h   | 9 +
 winsup/cygwin/fhandler_fifo.cc | 6 +++---
 winsup/cygwin/select.cc| 2 +-
 3 files changed, 5 insertions(+), 12 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index f64eabda4..40e201b0f 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1276,20 +1276,13 @@ public:
 
 #define CYGWIN_FIFO_PIPE_NAME_LEN 47
 
-/* We view the fc_closing state as borderline between open and closed
-   for a writer at the other end of a fifo_client_handler.
-
-   We still attempt to read from the writer when the handler is in
-   this state, and we don't declare a reader to be at EOF if there's a
-   handler in this state.  But the existence of a handler in this
-   state is not sufficient to unblock a reader trying to open. */
 enum fifo_client_connect_state
 {
   fc_unknown,
   fc_error,
   fc_disconnected,
-  fc_listening,
   fc_closing,
+  fc_listening,
   fc_connected,
   fc_input_avail,
 };
diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc
index c816c692a..1e1140f53 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -365,7 +365,7 @@ fhandler_fifo::cleanup_handlers ()
 
   while (i < nhandlers)
 {
-  if (fc_handler[i].get_state () < fc_closing)
+  if (fc_handler[i].get_state () < fc_connected)
delete_client_handler (i);
   else
i++;
@@ -1280,7 +1280,7 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
   for (j = 0; j < nhandlers; j++)
if (fc_handler[j].last_read)
  break;
-  if (j < nhandlers && fc_handler[j].get_state () >= fc_closing)
+  if (j < nhandlers && fc_handler[j].get_state () >= fc_connected)
{
  NTSTATUS status;
  IO_STATUS_BLOCK io;
@@ -1315,7 +1315,7 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
 
   /* Second pass. */
   for (int i = 0; i < nhandlers; i++)
-   if (fc_handler[i].get_state () >= fc_closing)
+   if (fc_handler[i].get_state () >= fc_connected)
  {
NTSTATUS status;
IO_STATUS_BLOCK io;
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 0c94f6c45..9ee305f64 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -878,7 +878,7 @@ peek_fifo (select_record *s, bool from_select)
{
  fifo_client_handler &fc = fh->get_fc_handler (i);
  fc.query_and_set_state ();
- if (fc.get_state () >= fc_closing)
+ if (fc.get_state () >= fc_connected)
{
  nconnected++;
  if (fc.get_state () == fc_input_avail)
-- 
2.28.0



[PATCH 4/8] Cygwin: FIFO: reorganize some fifo_client_handler methods

2020-08-04 Thread Ken Brown via Cygwin-patches
Rename the existing set_state() to query_and_set_state() to reflect
what it really does.  (It queries the O/S for the pipe state.)  Add a
new set_state() method, which is a standard setter, and a
corresponding getter get_state().
---
 winsup/cygwin/fhandler.h   |  9 --
 winsup/cygwin/fhandler_fifo.cc | 50 +++---
 winsup/cygwin/select.cc| 28 +++
 3 files changed, 50 insertions(+), 37 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 5488327a2..f64eabda4 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1297,11 +1297,14 @@ enum fifo_client_connect_state
 struct fifo_client_handler
 {
   HANDLE h;
-  fifo_client_connect_state state;
+  fifo_client_connect_state _state;
   bool last_read;  /* true if our last successful read was from this client. */
-  fifo_client_handler () : h (NULL), state (fc_unknown), last_read (false) {}
+  fifo_client_handler () : h (NULL), _state (fc_unknown), last_read (false) {}
   void close () { NtClose (h); }
-  fifo_client_connect_state set_state ();
+  fifo_client_connect_state get_state () const { return _state; }
+  void set_state (fifo_client_connect_state s) { _state = s; }
+  /* Query O/S.  Return previous state. */
+  fifo_client_connect_state query_and_set_state ();
 };
 
 class fhandler_fifo;
diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc
index b8a47f27f..c816c692a 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -340,7 +340,7 @@ fhandler_fifo::add_client_handler (bool new_pipe_instance)
   if (!ph)
return -1;
   fc.h = ph;
-  fc.state = fc_listening;
+  fc.set_state (fc_listening);
 }
   fc_handler[nhandlers++] = fc;
   return 0;
@@ -365,7 +365,7 @@ fhandler_fifo::cleanup_handlers ()
 
   while (i < nhandlers)
 {
-  if (fc_handler[i].state < fc_closing)
+  if (fc_handler[i].get_state () < fc_closing)
delete_client_handler (i);
   else
i++;
@@ -377,7 +377,7 @@ void
 fhandler_fifo::record_connection (fifo_client_handler& fc,
  fifo_client_connect_state s)
 {
-  fc.state = s;
+  fc.set_state (s);
   set_pipe_non_blocking (fc.h, true);
 }
 
@@ -414,13 +414,13 @@ fhandler_fifo::update_my_handlers ()
{
  debug_printf ("Can't duplicate handle of previous owner, %E");
  __seterrno ();
- fc.state = fc_error;
+ fc.set_state (fc_error);
  fc.last_read = false;
  ret = -1;
}
   else
{
- fc.state = shared_fc_handler[i].state;
+ fc.set_state (shared_fc_handler[i].get_state ());
  fc.last_read = shared_fc_handler[i].last_read;
}
 }
@@ -614,7 +614,7 @@ owner_listen:
break;
  default:
debug_printf ("NtFsControlFile status %y after failing to 
connect bogus client or real client", io.Status);
-   fc.state = fc_unknown;
+   fc.set_state (fc_unknown);
break;
  }
  break;
@@ -1280,7 +1280,7 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
   for (j = 0; j < nhandlers; j++)
if (fc_handler[j].last_read)
  break;
-  if (j < nhandlers && fc_handler[j].state >= fc_closing)
+  if (j < nhandlers && fc_handler[j].get_state () >= fc_closing)
{
  NTSTATUS status;
  IO_STATUS_BLOCK io;
@@ -1303,11 +1303,11 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
case STATUS_PIPE_EMPTY:
  break;
case STATUS_PIPE_BROKEN:
- fc_handler[j].state = fc_disconnected;
+ fc_handler[j].set_state (fc_disconnected);
  break;
default:
  debug_printf ("NtReadFile status %y", status);
- fc_handler[j].state = fc_error;
+ fc_handler[j].set_state (fc_error);
  break;
}
  fc_handler[j].last_read = false;
@@ -1315,7 +1315,7 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
 
   /* Second pass. */
   for (int i = 0; i < nhandlers; i++)
-   if (fc_handler[i].state >= fc_closing)
+   if (fc_handler[i].get_state () >= fc_closing)
  {
NTSTATUS status;
IO_STATUS_BLOCK io;
@@ -1339,12 +1339,12 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
  case STATUS_PIPE_EMPTY:
break;
  case STATUS_PIPE_BROKEN:
-   fc_handler[i].state = fc_disconnected;
+   fc_handler[i].set_state (fc_disconnected);
nconnected--;
break;
  default:
debug_printf ("NtReadFile status %y", status);
-   fc_handler[i].state = fc_error;
+   fc_handler[i].set_state (fc_error);
nconnected--;
break;
  }
@@ -1417,45 +1417,51 @@ fhandler

[PATCH 3/8] Cygwin: FIFO: add a timeout to take_ownership

2020-08-04 Thread Ken Brown via Cygwin-patches
fhandler_fifo::take_ownership() is called from select.cc::peek_fifo
and fhandler_fifo::raw_read and could potentially block indefinitely
if something goes wrong.  This is always undesirable in peek_fifo, and
it is undesirable in a nonblocking read.  Fix this by adding a timeout
parameter to take_ownership.

Arbitrarily use a 1 ms timeout in peek_fifo and a 10 ms timeout in
raw_read.  These numbers may have to be tweaked based on experience.

Replace the call to cygwait in take_ownership by a call to WFSO.
There's no need to allow interruption now that we have a timeout.
---
 winsup/cygwin/fhandler.h   |  2 +-
 winsup/cygwin/fhandler_fifo.cc | 74 +-
 winsup/cygwin/select.cc|  7 +---
 3 files changed, 30 insertions(+), 53 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 60bd27e00..5488327a2 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1487,7 +1487,7 @@ public:
   void fifo_client_lock () { _fifo_client_lock.lock (); }
   void fifo_client_unlock () { _fifo_client_lock.unlock (); }
 
-  DWORD take_ownership ();
+  int take_ownership (DWORD timeout = INFINITE);
   void reading_lock () { shmem->reading_lock (); }
   void reading_unlock () { shmem->reading_unlock (); }
 
diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc
index 7b87aab00..b8a47f27f 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -1214,16 +1214,17 @@ fhandler_fifo::raw_write (const void *ptr, size_t len)
   return ret;
 }
 
-/* Called from raw_read and select.cc:peek_fifo.  Return WAIT_OBJECT_0
-   on success.  */
-DWORD
-fhandler_fifo::take_ownership ()
+/* Called from raw_read and select.cc:peek_fifo. */
+int
+fhandler_fifo::take_ownership (DWORD timeout)
 {
+  int ret = 0;
+
   owner_lock ();
   if (get_owner () == me)
 {
   owner_unlock ();
-  return WAIT_OBJECT_0;
+  return 0;
 }
   set_pending_owner (me);
   /* Wake up my fifo_reader_thread. */
@@ -1233,18 +1234,25 @@ fhandler_fifo::take_ownership ()
 SetEvent (update_needed_evt);
   owner_unlock ();
   /* The reader threads should now do the transfer. */
-  DWORD waitret = cygwait (owner_found_evt, cw_cancel | cw_sig_eintr);
-  owner_lock ();
-  if (waitret == WAIT_OBJECT_0
-  && (get_owner () != me || get_pending_owner ()))
+  switch (WaitForSingleObject (owner_found_evt, timeout))
 {
-  /* Something went wrong.  Return WAIT_TIMEOUT, which can't be
-returned by the above cygwait call. */
-  set_pending_owner (null_fr_id);
-  waitret = WAIT_TIMEOUT;
+case WAIT_OBJECT_0:
+  owner_lock ();
+  if (get_owner () != me)
+   {
+ debug_printf ("owner_found_evt signaled, but I'm not the owner");
+ ret = -1;
+   }
+  owner_unlock ();
+  break;
+case WAIT_TIMEOUT:
+  debug_printf ("timed out");
+  ret = -1;
+default:
+  debug_printf ("WFSO failed, %E");
+  ret = -1;
 }
-  owner_unlock ();
-  return waitret;
+  return ret;
 }
 
 void __reg3
@@ -1255,38 +1263,12 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
 
   while (1)
 {
+  int nconnected = 0;
+
   /* No one else can take ownership while we hold the reading_lock. */
   reading_lock ();
-  switch (take_ownership ())
-   {
-   case WAIT_OBJECT_0:
- break;
-   case WAIT_SIGNALED:
- if (_my_tls.call_signal_handler ())
-   {
- reading_unlock ();
- continue;
-   }
- else
-   {
- set_errno (EINTR);
- reading_unlock ();
- goto errout;
-   }
- break;
-   case WAIT_CANCELED:
- reading_unlock ();
- pthread::static_cancel_self ();
- break;
-   case WAIT_TIMEOUT:
- reading_unlock ();
- debug_printf ("take_ownership returned an unexpected result; retry");
- continue;
-   default:
- reading_unlock ();
- debug_printf ("unknown error while trying to take ownership, %E");
- goto errout;
-   }
+  if (take_ownership (10) < 0)
+   goto maybe_retry;
 
   /* Poll the connected clients for input.  Make two passes.  On
 the first pass, just try to read from the client from which
@@ -1332,7 +1314,6 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
}
 
   /* Second pass. */
-  int nconnected = 0;
   for (int i = 0; i < nhandlers; i++)
if (fc_handler[i].state >= fc_closing)
  {
@@ -1375,6 +1356,7 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
  len = 0;
  return;
}
+maybe_retry:
   reading_unlock ();
   if (is_nonblocking ())
{
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 3f3f33fb5..1ba76c817 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -867,16 +867,11 @@ peek_fifo (select_record *s, bo

[PATCH 6/8] Cygwin: FIFO: synchronize the fifo_reader and fifosel threads

2020-08-04 Thread Ken Brown via Cygwin-patches
The fifo_reader thread function and the function select.cc:peek_fifo()
can both change the state of a fifo_client_handler.  These changes are
made under fifo_client_lock, so there is no race, but the changes can
still be incompatible.

Add code to make sure that only one of these functions can change the
state from its initial fc_listening state.  Whichever function does
this calls the fhandler_fifo::record_connection method, which is now
public so that peek_fifo can call it.

Slightly modify that method to make it suitable for being called by
peek_fifo.

Make a few other small changes to the fifo_reader thread function to
change how it deals with the STATUS_PIPE_CLOSING value that can
(rarely) be returned by NtFsControlFile.

Add commentary to fhandler_fifo.cc to explain fifo_client connect
states and where they can be changed.
---
 winsup/cygwin/fhandler.h   |  4 +--
 winsup/cygwin/fhandler_fifo.cc | 60 ++
 winsup/cygwin/select.cc|  5 ++-
 3 files changed, 60 insertions(+), 9 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 40e201b0f..a577ca542 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1413,8 +1413,6 @@ class fhandler_fifo: public fhandler_base
   void cleanup_handlers ();
   void close_all_handlers ();
   void cancel_reader_thread ();
-  void record_connection (fifo_client_handler&,
- fifo_client_connect_state = fc_connected);
 
   int create_shmem (bool only_open = false);
   int reopen_shmem ();
@@ -1482,6 +1480,8 @@ public:
   DWORD fifo_reader_thread_func ();
   void fifo_client_lock () { _fifo_client_lock.lock (); }
   void fifo_client_unlock () { _fifo_client_lock.unlock (); }
+  void record_connection (fifo_client_handler&, bool = true,
+ fifo_client_connect_state = fc_connected);
 
   int take_ownership (DWORD timeout = INFINITE);
   void reading_lock () { shmem->reading_lock (); }
diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc
index 1e1140f53..2b829eb6c 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -37,11 +37,42 @@
  "fifo_client_handler" structures, one for each writer.  A
  fifo_client_handler contains the handle for the pipe server
  instance and information about the state of the connection with
- the writer.  Each writer holds the pipe instance's client handle.
+ the writer.  Access to the list is controlled by a
+ "fifo_client_lock".
 
  The reader runs a "fifo_reader_thread" that creates new pipe
  instances as needed and listens for client connections.
 
+ The connection state of a fifo_client_handler has one of the
+ following values, in which order is important:
+
+   fc_unknown
+   fc_error
+   fc_disconnected
+   fc_closing
+   fc_listening
+   fc_connected
+   fc_input_avail
+
+ It can be changed in the following places:
+
+   - It is set to fc_listening when the pipe instance is created.
+
+   - It is set to fc_connected when the fifo_reader_thread detects
+ a connection.
+
+   - It is set to a value reported by the O/S when
+ query_and_set_state is called.  This can happen in
+ select.cc:peek_fifo and a couple other places.
+
+   - It is set to fc_disconnected by raw_read when an attempt to
+ read yields STATUS_PIPE_BROKEN.
+
+   - It is set to fc_error in various places when unexpected
+ things happen.
+
+ State changes are always guarded by fifo_client_lock.
+
  If there are multiple readers open, only one of them, called the
  "owner", maintains the fifo_client_handler list.  The owner is
  therefore the only reader that can read at any given time.  If a
@@ -374,10 +405,11 @@ fhandler_fifo::cleanup_handlers ()
 
 /* Always called with fifo_client_lock in place. */
 void
-fhandler_fifo::record_connection (fifo_client_handler& fc,
+fhandler_fifo::record_connection (fifo_client_handler& fc, bool set,
  fifo_client_connect_state s)
 {
-  fc.set_state (s);
+  if (set)
+fc.set_state (s);
   set_pipe_non_blocking (fc.h, true);
 }
 
@@ -583,6 +615,11 @@ owner_listen:
   NTSTATUS status1;
 
   fifo_client_lock ();
+  if (fc.get_state () != fc_listening)
+   /* select.cc:peek_fifo has already recorded a connection. */
+   ;
+  else
+  {
   switch (status)
{
case STATUS_SUCCESS:
@@ -590,7 +627,12 @@ owner_listen:
  record_connection (fc);
  break;
case STATUS_PIPE_CLOSING:
- record_connection (fc, fc_closing);
+ debug_printf ("NtFsControlFile got STATUS_PIPE_CLOSING...");
+ /* Maybe a writer already connected, wrote, and closed.
+Just query the O/S. */
+ fc.query_and_set_state ();
+ debug_printf ("...O/S reports state %d", fc.get_state ());
+ record_conne

[PATCH 0/8] FIFO: bug fixes and small improvements

2020-08-04 Thread Ken Brown via Cygwin-patches
The second patch in this series fixes a serious bug that has resulted
in observable hangs.  The other patches are minor bug fixes or minor
performance improvements.

Ken Brown (8):
  Cygwin: FIFO: lock fixes
  Cygwin: FIFO: fix timing issue with owner change
  Cygwin: FIFO: add a timeout to take_ownership
  Cygwin: FIFO: reorganize some fifo_client_handler methods
  Cygwin: FIFO: don't read from pipes that are closing
  Cygwin: FIFO: synchronize the fifo_reader and fifosel threads
  Cygwin: FIFO: fix indentation
  Cygwin: FIFO: add a third pass to raw_read

 winsup/cygwin/fhandler.h   |  24 +--
 winsup/cygwin/fhandler_fifo.cc | 358 ++---
 winsup/cygwin/select.cc|  38 ++--
 3 files changed, 268 insertions(+), 152 deletions(-)

-- 
2.28.0



[PATCH 8/8] Cygwin: FIFO: add a third pass to raw_read

2020-08-04 Thread Ken Brown via Cygwin-patches
Currently raw_read makes two passes through the list of clients.  On
the first pass it tries to read from the client from which it last
read successfully.  On the second pass it tries to read from all
connected clients.

Add a new pass in between these two, in which raw_read tries to read
from all clients that are in the fc_input_avail case.  This should be
more efficient in case select was previously called and detected input
available.

Slightly tweak the first pass.  If a client is marked as having the
last successful read but reading from it now finds no input, don't
unmark it unless we successfully read from a different client on one
of the later passes.
---
 winsup/cygwin/fhandler_fifo.cc | 66 ++
 1 file changed, 60 insertions(+), 6 deletions(-)

diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc
index 017d44e54..a33c32b73 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -1318,17 +1318,30 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
   if (take_ownership (10) < 0)
goto maybe_retry;
 
-  /* Poll the connected clients for input.  Make two passes.  On
-the first pass, just try to read from the client from which
-we last read successfully.  This should minimize
-interleaving of writes from different clients. */
   fifo_client_lock ();
+  /* Poll the connected clients for input.  Make three passes.
+
+On the first pass, just try to read from the client from
+which we last read successfully.  This should minimize
+interleaving of writes from different clients.
+
+On the second pass, just try to read from the clients in the
+state fc_input_avail.  This should be more efficient if
+select has been called and detected input available.
+
+On the third pass, try to read from all connected clients. */
+
   /* First pass. */
   int j;
   for (j = 0; j < nhandlers; j++)
if (fc_handler[j].last_read)
  break;
-  if (j < nhandlers && fc_handler[j].get_state () >= fc_connected)
+  if (j < nhandlers && fc_handler[j].get_state () < fc_connected)
+   {
+ fc_handler[j].last_read = false;
+ j = nhandlers;
+   }
+  if (j < nhandlers)
{
  NTSTATUS status;
  IO_STATUS_BLOCK io;
@@ -1349,6 +1362,8 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
}
  break;
case STATUS_PIPE_EMPTY:
+ /* Update state in case it's fc_input_avail. */
+ fc_handler[j].set_state (fc_connected);
  break;
case STATUS_PIPE_BROKEN:
  fc_handler[j].set_state (fc_disconnected);
@@ -1358,10 +1373,47 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
  fc_handler[j].set_state (fc_error);
  break;
}
- fc_handler[j].last_read = false;
}
 
   /* Second pass. */
+  for (int i = 0; i < nhandlers; i++)
+   if (fc_handler[i].get_state () == fc_input_avail)
+ {
+   NTSTATUS status;
+   IO_STATUS_BLOCK io;
+
+   status = NtReadFile (fc_handler[i].h, NULL, NULL, NULL,
+&io, in_ptr, len, NULL, NULL);
+   switch (status)
+ {
+ case STATUS_SUCCESS:
+ case STATUS_BUFFER_OVERFLOW:
+   if (io.Information > 0)
+ {
+   len = io.Information;
+   if (j < nhandlers)
+ fc_handler[j].last_read = false;
+   fc_handler[i].last_read = true;
+   fifo_client_unlock ();
+   reading_unlock ();
+   return;
+ }
+   break;
+ case STATUS_PIPE_EMPTY:
+   /* No input available after all. */
+   fc_handler[i].set_state (fc_connected);
+   break;
+ case STATUS_PIPE_BROKEN:
+   fc_handler[i].set_state (fc_disconnected);
+   break;
+ default:
+   debug_printf ("NtReadFile status %y", status);
+   fc_handler[i].set_state (fc_error);
+   break;
+ }
+ }
+
+  /* Third pass. */
   for (int i = 0; i < nhandlers; i++)
if (fc_handler[i].get_state () >= fc_connected)
  {
@@ -1378,6 +1430,8 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
if (io.Information > 0)
  {
len = io.Information;
+   if (j < nhandlers)
+ fc_handler[j].last_read = false;
fc_handler[i].last_read = true;
fifo_client_unlock ();
reading_unlock ();
-- 
2.28.0



[PATCH 7/8] Cygwin: FIFO: fix indentation

2020-08-04 Thread Ken Brown via Cygwin-patches
---
 winsup/cygwin/fhandler_fifo.cc | 96 +-
 1 file changed, 48 insertions(+), 48 deletions(-)

diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc
index 2b829eb6c..017d44e54 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -619,56 +619,56 @@ owner_listen:
/* select.cc:peek_fifo has already recorded a connection. */
;
   else
-  {
-  switch (status)
{
-   case STATUS_SUCCESS:
-   case STATUS_PIPE_CONNECTED:
- record_connection (fc);
- break;
-   case STATUS_PIPE_CLOSING:
- debug_printf ("NtFsControlFile got STATUS_PIPE_CLOSING...");
- /* Maybe a writer already connected, wrote, and closed.
-Just query the O/S. */
- fc.query_and_set_state ();
- debug_printf ("...O/S reports state %d", fc.get_state ());
- record_connection (fc, false);
- break;
-   case STATUS_THREAD_IS_TERMINATING:
-   case STATUS_WAIT_1:
- /* Try to connect a bogus client.  Otherwise fc is still
-listening, and the next connection might not get recorded. */
- status1 = open_pipe (ph);
- WaitForSingleObject (conn_evt, INFINITE);
- if (NT_SUCCESS (status1))
-   /* Bogus cilent connected. */
-   delete_client_handler (nhandlers - 1);
- else
-   /* Did a real client connect? */
-   switch (io.Status)
- {
- case STATUS_SUCCESS:
- case STATUS_PIPE_CONNECTED:
-   record_connection (fc);
-   break;
- case STATUS_PIPE_CLOSING:
-   debug_printf ("got STATUS_PIPE_CLOSING when trying to connect 
bogus client...");
-   fc.query_and_set_state ();
-   debug_printf ("...O/S reports state %d", fc.get_state ());
-   record_connection (fc, false);
-   break;
- default:
-   debug_printf ("NtFsControlFile status %y after failing to 
connect bogus client or real client", io.Status);
-   fc.set_state (fc_error);
-   break;
- }
- break;
-   default:
- debug_printf ("NtFsControlFile got unexpected status %y", status);
- fc.set_state (fc_error);
- break;
+ switch (status)
+   {
+   case STATUS_SUCCESS:
+   case STATUS_PIPE_CONNECTED:
+ record_connection (fc);
+ break;
+   case STATUS_PIPE_CLOSING:
+ debug_printf ("NtFsControlFile got STATUS_PIPE_CLOSING...");
+ /* Maybe a writer already connected, wrote, and closed.
+Just query the O/S. */
+ fc.query_and_set_state ();
+ debug_printf ("...O/S reports state %d", fc.get_state ());
+ record_connection (fc, false);
+ break;
+   case STATUS_THREAD_IS_TERMINATING:
+   case STATUS_WAIT_1:
+ /* Try to connect a bogus client.  Otherwise fc is still
+listening, and the next connection might not get recorded. */
+ status1 = open_pipe (ph);
+ WaitForSingleObject (conn_evt, INFINITE);
+ if (NT_SUCCESS (status1))
+   /* Bogus cilent connected. */
+   delete_client_handler (nhandlers - 1);
+ else
+   /* Did a real client connect? */
+   switch (io.Status)
+ {
+ case STATUS_SUCCESS:
+ case STATUS_PIPE_CONNECTED:
+   record_connection (fc);
+   break;
+ case STATUS_PIPE_CLOSING:
+   debug_printf ("got STATUS_PIPE_CLOSING when trying to 
connect bogus client...");
+   fc.query_and_set_state ();
+   debug_printf ("...O/S reports state %d", fc.get_state ());
+   record_connection (fc, false);
+   break;
+ default:
+   debug_printf ("NtFsControlFile status %y after failing to 
connect bogus client or real client", io.Status);
+   fc.set_state (fc_error);
+   break;
+ }
+ break;
+   default:
+ debug_printf ("NtFsControlFile got unexpected status %y", status);
+ fc.set_state (fc_error);
+ break;
+   }
}
-  }
   if (ph)
NtClose (ph);
   if (update)
-- 
2.28.0



[PATCH 2/8] Cygwin: FIFO: fix timing issue with owner change

2020-08-04 Thread Ken Brown via Cygwin-patches
fhandler_fifo::take_ownership() tacitly assumes that the current
owner's fifo_reader_thread will be woken up from WFMO when
update_needed_evt is signaled.  But it's possible that the the current
owner's fifo_reader_thread is at the beginning of its main loop rather
than in its WFMO call when that event is signaled.

In this case the owner will never see that the event has been
signaled, and it will never update the shared fifo_client_handlers.
The reader that wants to take ownership will then spin its wheels
forever.

Fix this by having the current owner call update_shared_handlers at
the beginning of its loop, if necessary.
---
 winsup/cygwin/fhandler_fifo.cc | 25 +
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc
index ee7f47c0c..7b87aab00 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -472,17 +472,34 @@ fhandler_fifo::fifo_reader_thread_func ()
 
   if (pending_owner)
{
- if (pending_owner != me)
+ if (pending_owner == me)
+   take_ownership = true;
+ else if (cur_owner != me)
idle = true;
  else
-   take_ownership = true;
+   {
+ /* I'm the owner but someone else wants to be.  Have I
+already seen and reacted to update_needed_evt? */
+ if (WaitForSingleObject (update_needed_evt, 0) == WAIT_OBJECT_0)
+   {
+ /* No, I haven't. */
+ fifo_client_lock ();
+ if (update_shared_handlers () < 0)
+   api_fatal ("Can't update shared handlers, %E");
+ fifo_client_unlock ();
+   }
+ owner_unlock ();
+ /* Yield to pending owner. */
+ Sleep (1);
+ continue;
+   }
}
   else if (!cur_owner)
take_ownership = true;
   else if (cur_owner != me)
idle = true;
   else
-   /* I'm the owner. */
+   /* I'm the owner and there's no pending owner. */
goto owner_listen;
   if (idle)
{
@@ -1212,7 +1229,7 @@ fhandler_fifo::take_ownership ()
   /* Wake up my fifo_reader_thread. */
   owner_needed ();
   if (get_owner ())
-/* Wake up owner's fifo_reader_thread. */
+/* Wake up the owner and request an update of the shared fc_handlers. */
 SetEvent (update_needed_evt);
   owner_unlock ();
   /* The reader threads should now do the transfer. */
-- 
2.28.0



[PATCH 1/8] Cygwin: FIFO: lock fixes

2020-08-04 Thread Ken Brown via Cygwin-patches
Add some missing locks and remove one extra unlock.  Clarify for some
functions whether caller or callee acquires lock, and add appropriate
comments.
---
 winsup/cygwin/fhandler_fifo.cc | 23 +--
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc
index e9d0187d4..ee7f47c0c 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -316,6 +316,7 @@ fhandler_fifo::wait_open_pipe (HANDLE& ph)
   return status;
 }
 
+/* Always called with fifo_client_lock in place. */
 int
 fhandler_fifo::add_client_handler (bool new_pipe_instance)
 {
@@ -345,6 +346,7 @@ fhandler_fifo::add_client_handler (bool new_pipe_instance)
   return 0;
 }
 
+/* Always called with fifo_client_lock in place. */
 void
 fhandler_fifo::delete_client_handler (int i)
 {
@@ -354,7 +356,8 @@ fhandler_fifo::delete_client_handler (int i)
 (nhandlers - i) * sizeof (fc_handler[i]));
 }
 
-/* Delete handlers that we will never read from. */
+/* Delete handlers that we will never read from.  Always called with
+   fifo_client_lock in place. */
 void
 fhandler_fifo::cleanup_handlers ()
 {
@@ -369,6 +372,7 @@ fhandler_fifo::cleanup_handlers ()
 }
 }
 
+/* Always called with fifo_client_lock in place. */
 void
 fhandler_fifo::record_connection (fifo_client_handler& fc,
  fifo_client_connect_state s)
@@ -398,6 +402,7 @@ fhandler_fifo::update_my_handlers ()
   if (!prev_proc)
 api_fatal ("Can't open process of previous owner, %E");
 
+  fifo_client_lock ();
   for (int i = 0; i < get_shared_nhandlers (); i++)
 {
   if (add_client_handler (false) < 0)
@@ -419,10 +424,12 @@ fhandler_fifo::update_my_handlers ()
  fc.last_read = shared_fc_handler[i].last_read;
}
 }
+  fifo_client_unlock ();
   set_prev_owner (null_fr_id);
   return ret;
 }
 
+/* Always called with fifo_client_lock and owner_lock in place. */
 int
 fhandler_fifo::update_shared_handlers ()
 {
@@ -435,9 +442,7 @@ fhandler_fifo::update_shared_handlers ()
   set_shared_nhandlers (nhandlers);
   memcpy (shared_fc_handler, fc_handler, nhandlers * sizeof (fc_handler[0]));
   shared_fc_handler_updated (true);
-  owner_lock ();
   set_prev_owner (me);
-  owner_unlock ();
   return 0;
 }
 
@@ -509,7 +514,6 @@ fhandler_fifo::fifo_reader_thread_func ()
  if (update_my_handlers () < 0)
debug_printf ("error updating my handlers, %E");
  owner_found ();
- owner_unlock ();
  /* Fall through to owner_listen. */
}
}
@@ -602,8 +606,13 @@ owner_listen:
}
   if (ph)
NtClose (ph);
-  if (update && update_shared_handlers () < 0)
-   api_fatal ("Can't update shared handlers, %E");
+  if (update)
+   {
+ owner_lock ();
+ if (get_owner () == me && update_shared_handlers () < 0)
+   api_fatal ("Can't update shared handlers, %E");
+ owner_unlock ();
+   }
   fifo_client_unlock ();
   if (cancel)
goto canceled;
@@ -1402,9 +1411,11 @@ fhandler_fifo::fstatvfs (struct statvfs *sfs)
 void
 fhandler_fifo::close_all_handlers ()
 {
+  fifo_client_lock ();
   for (int i = 0; i < nhandlers; i++)
 fc_handler[i].close ();
   nhandlers = 0;
+  fifo_client_unlock ();
 }
 
 fifo_client_connect_state
-- 
2.28.0



Re: [PATCH] fhandler_proc.cc(format_proc_cpuinfo): add SERIALIZE instruction flag

2020-08-04 Thread Corinna Vinschen
On Aug  3 10:22, Brian Inglis wrote:
> CPUID 7:0 EDX[14] serialize added in linux-next 5.8 by Ricardo Neri-Calderon:
> The Intel architecture defines a set of Serializing Instructions (a
> detailed definition can be found in Vol.3 Section 8.3 of the Intel "main"
> manual, SDM). However, these instructions do more than what is required,
> have side effects and/or may be rather invasive. Furthermore, some of
> these instructions are only available in kernel mode or may cause VMExits.
> Thus, software using these instructions only to serialize execution (as
> defined in the manual) must handle the undesired side effects.
> 
> As indicated in the name, SERIALIZE is a new Intel architecture
> Serializing Instruction. Crucially, it does not have any of the mentioned
> side effects. Also, it does not cause VMExit and can be used in user mode.
> 
> This new instruction is currently documented in the latest "extensions"
> manual (ISE). It will appear in the "main" manual in the future.
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/arch/x86/include/asm/cpufeatures.h?id=85b23fbc7d88f8c6e3951721802d7845bc39663d
> ---
>  winsup/cygwin/fhandler_proc.cc | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc
> index 4bb8bea1766c..72ffa89cdc79 100644
> --- a/winsup/cygwin/fhandler_proc.cc
> +++ b/winsup/cygwin/fhandler_proc.cc
> @@ -1578,6 +1578,7 @@ format_proc_cpuinfo (void *, char *&destbuf)
>ftcprint (features1,  4, "fsrm"); /* fast short REP 
> MOVSB */
>ftcprint (features1,  8, "avx512_vp2intersect"); /* vec intcpt d/q 
> */
>ftcprint (features1, 10, "md_clear");/* verw clear buf 
> */
> +  ftcprint (features1, 14, "serialize");   /* SERIALIZE 
> instruction */
>ftcprint (features1, 18, "pconfig");  /* platform 
> config */
>ftcprint (features1, 19, "arch_lbr"); /* last 
> branch records */
>ftcprint (features1, 28, "flush_l1d");/* flush l1d cache */
> -- 
> 2.27.0

Pushed.


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer


Re: [PATCH] fhandler_proc.cc(format_proc_cpuinfo): use _small_sprintf %X for microcode

2020-08-04 Thread Corinna Vinschen
On Aug  4 00:51, Brian Inglis wrote:
> microcode is unsigned long long, printed by _small_sprintf using %x;
> Cygwin32 used last 4 bytes of microcode for next field MHz, printing 0;
> use correct _small_sprintf format %X to print microcode, producing
> correct MHz value under Cygwin32
> ---
>  winsup/cygwin/fhandler_proc.cc | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc
> index 72ffa89cdc79..9a20c23d4b65 100644
> --- a/winsup/cygwin/fhandler_proc.cc
> +++ b/winsup/cygwin/fhandler_proc.cc
> @@ -833,7 +833,7 @@ format_proc_cpuinfo (void *, char *&destbuf)
>"model\t\t: %d\n"
>"model name\t: %s\n"
>"stepping\t: %d\n"
> -  "microcode\t: 0x%x\n"
> +  "microcode\t: 0x%X\n"
>"cpu MHz\t\t: %d.000\n",
>family,
>model,
> -- 
> 2.28.0

Pushed.


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer