From ad7538bfc5873811678becf3e858128236a7f210 Mon Sep 17 00:00:00 2001
From: Marcus Meissner <marcus@jet.franken.de>
Date: Mon, 14 Dec 1998 18:15:10 +0000
Subject: [PATCH] Made async IO (SIGIO) stuff from WINSOCK generic useable.
 Added async IO support to FILE and CONSOLE objects.

---
 files/Makefile.in  |   1 +
 files/async.c      | 186 +++++++++++++++++++++++++++++++++++++++++++++
 files/file.c       |  27 +++++--
 include/async.h    |   6 ++
 include/file.h     |   3 +
 loader/signal.c    |   4 +-
 misc/winsock_dns.c |  86 ++++++---------------
 win32/console.c    |  40 ++++++++--
 8 files changed, 274 insertions(+), 79 deletions(-)
 create mode 100644 files/async.c
 create mode 100644 include/async.h

diff --git a/files/Makefile.in b/files/Makefile.in
index eb37b8965e1..b1937e1c7ca 100644
--- a/files/Makefile.in
+++ b/files/Makefile.in
@@ -8,6 +8,7 @@ VPATH      = @srcdir@
 MODULE     = files
 
 C_SRCS = \
+	async.c \
 	change.c \
 	directory.c \
 	dos_fs.c \
diff --git a/files/async.c b/files/async.c
new file mode 100644
index 00000000000..7f1c69fec03
--- /dev/null
+++ b/files/async.c
@@ -0,0 +1,186 @@
+/*
+ * Generic async UNIX file IO handling
+ *
+ * Copyright 1996,1997 Alex Korobka
+ * Copyright 1998 Marcus Meissner
+ */
+/*
+ * This file handles asynchronous signaling for UNIX filedescriptors. 
+ * The passed handler gets called when input arrived for the filedescriptor.
+ * 
+ * This is done either by the kernel or (in the WINSOCK case) by the pipe
+ * handler, since pipes do not support asynchronous signaling.
+ * (Not all possible filedescriptors support async IO. Generic files do not
+ *  for instance, sockets do, ptys don't.)
+ * 
+ * To make this a bit better, we would need an additional thread doing select()
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+#ifdef __svr4__
+# include <sys/file.h>
+#endif
+
+#include "xmalloc.h"
+#include "wintypes.h"
+#include "miscemu.h"
+#include "selectors.h"
+#include "sig_context.h"
+#include "async.h"
+#include "debug.h"
+
+typedef struct _async_fd {
+	int	unixfd;
+	void	(*handler)(int fd,void *private);
+	void	*private;
+} ASYNC_FD;
+
+static ASYNC_FD	*asyncfds = NULL;
+static int	 nrofasyncfds = 0;
+
+/***************************************************************************
+ *		ASYNC_sigio				[internal]
+ * 
+ * Signal handler for asynchronous IO.
+ *
+ * Note: This handler and the function it calls may not block. Neither they
+ * are allowed to use blocking IO (write/read). No memory management.
+ * No possible blocking synchronization of any kind.
+ */
+HANDLER_DEF(ASYNC_sigio) {
+	struct timeval	timeout;
+	fd_set	rset,wset;
+	int	i,maxfd=0;
+
+	HANDLER_INIT();
+
+	if (!nrofasyncfds) 
+		return;
+	FD_ZERO(&rset);
+	FD_ZERO(&wset);
+	for (i=nrofasyncfds;i--;) {
+		if (asyncfds[i].unixfd == -1)
+			continue;
+		FD_SET(asyncfds[i].unixfd,&rset);
+		FD_SET(asyncfds[i].unixfd,&wset);
+		if (maxfd<asyncfds[i].unixfd)
+			maxfd=asyncfds[i].unixfd;
+	}
+	/* select() with timeout values set to 0 is nonblocking. */
+	memset(&timeout,0,sizeof(timeout));
+	if (select(maxfd+1,&rset,&wset,NULL,&timeout)<=0)
+		return; /* Can't be. hmm */
+	for (i=nrofasyncfds;i--;)
+		if (	(FD_ISSET(asyncfds[i].unixfd,&rset)) ||
+			(FD_ISSET(asyncfds[i].unixfd,&wset))
+		)
+			asyncfds[i].handler(asyncfds[i].unixfd,asyncfds[i].private);
+}
+
+/***************************************************************************
+ *		ASYNC_MakeFDAsync			[internal]
+ *
+ * Makes the passed filedescriptor async (or not) depending on flag.
+ */
+static BOOL32 ASYNC_MakeFDAsync(int unixfd,int async) {
+    int	flags;
+
+#if !defined(FASYNC) && defined(FIOASYNC)
+#define FASYNC FIOASYNC
+#endif
+
+#ifdef F_SETOWN
+    if (-1==fcntl(unixfd,F_SETOWN,getpid()))
+    	perror("fcntl F_SETOWN <pid>");
+#endif
+#ifdef FASYNC
+    if (-1==fcntl(unixfd,F_GETFL,&flags)) {
+	perror("fcntl F_GETFL");
+	return FALSE;
+    }
+    if (async)
+	flags|=FASYNC;
+    else
+	flags&=~FASYNC;
+    if (-1==fcntl(unixfd,F_SETFL,&flags)) {
+	perror("fcntl F_SETFL FASYNC");
+	return FALSE;
+    }
+    return TRUE;
+#else
+    return FALSE;
+#endif
+}
+
+/***************************************************************************
+ *		ASYNC_RegisterFD			[internal]
+ *
+ * Register a UNIX filedescriptor with handler and private data pointer. 
+ * this function is _NOT_ safe to be called from a signal handler.
+ *
+ * Additional Constraint:  The handler passed to this function _MUST_ adhere
+ * to the same signalsafeness as ASYNC_sigio itself. (nonblocking, no thread/
+ * signal unsafe operations, no blocking synchronization)
+ */
+void ASYNC_RegisterFD(int unixfd,void (*handler)(int fd,void *private),void *private) {
+    int	i;
+
+    SIGNAL_MaskAsyncEvents( TRUE );
+    for (i=0;i<nrofasyncfds;i++) {
+	if (asyncfds[i].unixfd==unixfd) {
+	    /* Might be a leftover entry. Make fd async anyway... */
+	    if (asyncfds[i].handler==handler) {
+	        ASYNC_MakeFDAsync(unixfd,1);
+		SIGNAL_MaskAsyncEvents( FALSE );
+	    	return;
+	    }
+	}
+    }
+   for (i=0;i<nrofasyncfds;i++)
+	if (asyncfds[i].unixfd == -1)
+		break;
+   if (i==nrofasyncfds) {
+       if (nrofasyncfds)
+	   asyncfds=(ASYNC_FD*)xrealloc(asyncfds,sizeof(ASYNC_FD)*(nrofasyncfds+1));
+       else
+	   asyncfds=(ASYNC_FD*)xmalloc(sizeof(ASYNC_FD)*1);
+       nrofasyncfds++;
+   }
+   asyncfds[i].unixfd	= unixfd;
+   asyncfds[i].handler	= handler;
+   asyncfds[i].private	= private;
+   ASYNC_MakeFDAsync(unixfd,1);
+   SIGNAL_MaskAsyncEvents( FALSE );
+}
+
+/***************************************************************************
+ *		ASYNC_UnregisterFD			[internal]
+ *
+ * Unregister a UNIX filedescriptor with handler. This function is basically
+ * signal safe, but try to not call it in the signal handler anyway.
+ */
+void ASYNC_UnregisterFD(int unixfd,void (*handler)(int fd,void *private)) {
+    int	i;
+
+    for (i=nrofasyncfds;i--;)
+	if ((asyncfds[i].unixfd==unixfd)||(asyncfds[i].handler==handler))
+	    break;
+    if (i==nrofasyncfds)
+    	return;
+    asyncfds[i].unixfd	= -1;
+    asyncfds[i].handler	= NULL;
+    asyncfds[i].private	= NULL;
+    return;
+}
diff --git a/files/file.c b/files/file.c
index c4ddb55b747..6a5cac09813 100644
--- a/files/file.c
+++ b/files/file.c
@@ -35,6 +35,7 @@
 #include "ldt.h"
 #include "process.h"
 #include "task.h"
+#include "async.h"
 #include "debug.h"
 
 #if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
@@ -97,6 +98,7 @@ HFILE32 FILE_Alloc( FILE_OBJECT **file )
     (*file)->type = FILE_TYPE_DISK;
     (*file)->pos = 0;
     (*file)->mode = 0;
+    (*file)->wait_queue = NULL;
 
     handle = HANDLE_Alloc( PROCESS_Current(), &(*file)->header,
                            FILE_ALL_ACCESS | GENERIC_READ |
@@ -106,6 +108,16 @@ HFILE32 FILE_Alloc( FILE_OBJECT **file )
     return handle;
 }
 
+/***********************************************************************
+ *		FILE_async_handler			[internal]
+ */
+static void
+FILE_async_handler(int unixfd,void *private) {
+	FILE_OBJECT *file = (FILE_OBJECT*)private;
+
+	SYNC_WakeUp(&file->wait_queue,INFINITE32);
+}
+
 static BOOL32 FILE_Signaled(K32OBJ *ptr, DWORD thread_id)
 {
 	fd_set	fds,*readfds = NULL,*writefds = NULL;
@@ -127,20 +139,23 @@ static BOOL32 FILE_Signaled(K32OBJ *ptr, DWORD thread_id)
 
 static void FILE_AddWait(K32OBJ *ptr, DWORD thread_id)
 {
-	TRACE(file,"(),stub\n");
-	return;
+	FILE_OBJECT *file = (FILE_OBJECT*)ptr;
+	if (!file->wait_queue)
+		ASYNC_RegisterFD(file->unix_handle,FILE_async_handler,file);
+	THREAD_AddQueue(&file->wait_queue,thread_id);
 }
 
 static void FILE_RemoveWait(K32OBJ *ptr, DWORD thread_id)
 {
-	TRACE(file,"(),stub\n");
-	return;
+	FILE_OBJECT *file = (FILE_OBJECT*)ptr;
+	THREAD_RemoveQueue(&file->wait_queue,thread_id);
+	if (!file->wait_queue)
+		ASYNC_UnregisterFD(file->unix_handle,FILE_async_handler);
 }
 
 static BOOL32 FILE_Satisfied(K32OBJ *ptr, DWORD thread_id)
 {
-	TRACE(file,"(),stub\n");
-	return TRUE;
+	return FALSE; /* not abandoned. Hmm? */
 }
 
 /* FIXME: lpOverlapped is ignored */
diff --git a/include/async.h b/include/async.h
new file mode 100644
index 00000000000..52c0bbad733
--- /dev/null
+++ b/include/async.h
@@ -0,0 +1,6 @@
+#ifndef __WINE_ASYNC_H
+#define __WINE_ASYNC_H
+
+extern void ASYNC_RegisterFD(int unixfd,void (*handler)(int fd,void *private),void *private);
+extern void ASYNC_UnregisterFD(int unixfd,void (*handler)(int fd,void *private));
+#endif
diff --git a/include/file.h b/include/file.h
index b727a209ec0..e7827243ddb 100644
--- a/include/file.h
+++ b/include/file.h
@@ -10,6 +10,7 @@
 #include <time.h>
 #include "windows.h"
 #include "k32obj.h"
+#include "thread.h"
 
 #define MAX_PATHNAME_LEN   1024
 
@@ -22,6 +23,8 @@ typedef struct
     char     *unix_name;
     DWORD     type;         /* Type for win32 apps */
     DWORD     pos;	    /* workaround to emulate weird DOS error handling */
+
+    THREAD_QUEUE	wait_queue;
 } FILE_OBJECT;
 
 /* Definition of a full DOS file name */
diff --git a/loader/signal.c b/loader/signal.c
index c6267219d89..ed18a3a48bc 100644
--- a/loader/signal.c
+++ b/loader/signal.c
@@ -148,6 +148,7 @@ void SIGNAL_SetHandler( int sig, void (*func)(), int flags )
 
 extern void stop_wait(int a);
 extern void WINSOCK_sigio(int a);
+extern void ASYNC_sigio(int a);
 
 
 /**********************************************************************
@@ -178,7 +179,8 @@ BOOL32 SIGNAL_Init(void)
 #endif
 #ifdef SIGIO
     sigaddset(&async_signal_set, SIGIO);
-    SIGNAL_SetHandler( SIGIO,   (void (*)())WINSOCK_sigio, 0); 
+/*    SIGNAL_SetHandler( SIGIO,   (void (*)())WINSOCK_sigio, 0);  */
+    SIGNAL_SetHandler( SIGIO,   (void (*)())ASYNC_sigio, 0); 
 #endif
 
     sigaddset(&async_signal_set, SIGALRM);
diff --git a/misc/winsock_dns.c b/misc/winsock_dns.c
index 113c50e0d04..5408c952cc5 100644
--- a/misc/winsock_dns.c
+++ b/misc/winsock_dns.c
@@ -11,6 +11,7 @@
 
 #include "config.h"
 
+#include <assert.h>
 #include <unistd.h>
 #include <string.h>
 #include <signal.h>
@@ -38,14 +39,11 @@
 #include "heap.h"
 #include "ldt.h"
 #include "message.h"
-#include "selectors.h"
 #include "miscemu.h"
-#include "sig_context.h"
+#include "async.h"
 #include "debug.h"
 
-#ifndef FASYNC
-#define FASYNC FIOASYNC
-#endif
+static void WINSOCK_async_handler(int unixfd,void *private);
 
 /* async DNS op control struct */
 typedef struct          
@@ -67,9 +65,6 @@ extern void*	__ws_memalloc( int size );
 extern void	__ws_memfree( void* ptr );
 
 /* NOTE: ws_async_op list is traversed inside the SIGIO handler! */
-
-static int		__async_io_max_fd = 0;
-static fd_set		__async_io_fdset;
 static ws_async_op*	__async_op_list = NULL;
 
 static void fixup_wshe(struct ws_hostent* p_wshe, void* base);
@@ -78,20 +73,6 @@ static void fixup_wsse(struct ws_servent* p_wsse, void* base);
 
 /* ----------------------------------- async/non-blocking I/O */
 
-int WINSOCK_async_io(int fd, int async)
-{
-    int fd_flags;
-
-#ifndef __EMX__
-    fcntl(fd, F_SETOWN, getpid());
-#endif
-
-    fd_flags = fcntl(fd, F_GETFL, 0);
-    if (fcntl(fd, F_SETFL, (async)? fd_flags | FASYNC
-                                  : fd_flags & ~FASYNC ) != -1) return 0;
-    return -1;
-}
-
 int WINSOCK_unblock_io(int fd, int noblock)
 {
     int fd_flags;
@@ -191,14 +172,12 @@ void WINSOCK_link_async_op(ws_async_op* p_aop)
 	  p = p->next;
       }
   }
-  else FD_ZERO(&__async_io_fdset);
   p_aop->next = __async_op_list;
   __async_op_list = p_aop;
+
   SIGNAL_MaskAsyncEvents( FALSE );
 
-  FD_SET(p_aop->fd[0], &__async_io_fdset);
-  if( p_aop->fd[0] > __async_io_max_fd ) 
-		     __async_io_max_fd = p_aop->fd[0];
+  ASYNC_RegisterFD(p_aop->fd[0],WINSOCK_async_handler,p_aop);
 }
 
 void WINSOCK_unlink_async_op(ws_async_op* p_aop)
@@ -210,9 +189,7 @@ void WINSOCK_unlink_async_op(ws_async_op* p_aop)
       p_aop->prev->next = p_aop->next;
   if( p_aop->next ) p_aop->next->prev = p_aop->prev;
 
-  FD_CLR(p_aop->fd[0], &__async_io_fdset); 
-  if( p_aop->fd[0] == __async_io_max_fd )
-		      __async_io_max_fd--;
+  ASYNC_UnregisterFD(p_aop->fd[0],WINSOCK_async_handler);
 }
 
 /* ----------------------------------- SIGIO handler -
@@ -223,42 +200,23 @@ void WINSOCK_unlink_async_op(ws_async_op* p_aop)
  * Note: pipe-based handlers must raise explicit SIGIO with kill(2).
  */
 
-HANDLER_DEF(WINSOCK_sigio)
+static void WINSOCK_async_handler(int unixfd,void *private)
 {
- struct timeval         timeout;
- fd_set                 check_set;
- ws_async_op*		p_aop;
-
- HANDLER_INIT();
-
- check_set = __async_io_fdset;
- memset(&timeout, 0, sizeof(timeout));
-
- while( select(__async_io_max_fd + 1,
-              &check_set, NULL, NULL, &timeout) > 0)
- {
-   for( p_aop = __async_op_list;
-	p_aop ; p_aop = p_aop->next )
-      if( FD_ISSET(p_aop->fd[0], &check_set) )
-          if( p_aop->aop_control(p_aop, AOP_IO) == AOP_CONTROL_REMOVE )
-	  {
-	     /* NOTE: memory management is signal-unsafe, therefore
-	      * we can only set a flag to remove this p_aop later on.
-	      */
-
-	      p_aop->flags = WSMSG_DEAD_AOP;
-	      close(p_aop->fd[0]);
-	      FD_CLR(p_aop->fd[0],&__async_io_fdset);
-	      if( p_aop->fd[0] == __async_io_max_fd )
-				 __async_io_max_fd = p_aop->fd[0];
-	      if( p_aop->pid ) 
-	      { 
-		  kill(p_aop->pid, SIGKILL); 
-		  waitpid(p_aop->pid, NULL, WNOHANG);
-		  p_aop->pid = 0;
-	      }
-	  }
-   check_set = __async_io_fdset;
+  ws_async_op*		p_aop = (ws_async_op*)private;
+
+  if( p_aop->aop_control(p_aop, AOP_IO) == AOP_CONTROL_REMOVE )
+  {
+     /* NOTE: memory management is signal-unsafe, therefore
+      * we can only set a flag to remove this p_aop later on.
+      */
+      p_aop->flags = WSMSG_DEAD_AOP;
+      close(p_aop->fd[0]);
+      if( p_aop->pid ) 
+      { 
+	  kill(p_aop->pid, SIGKILL); 
+	  waitpid(p_aop->pid, NULL, WNOHANG);
+	  p_aop->pid = 0;
+      }
   }
 }
 
diff --git a/win32/console.c b/win32/console.c
index 9c7d289419f..48aba956220 100644
--- a/win32/console.c
+++ b/win32/console.c
@@ -32,8 +32,11 @@
 #include <sys/errno.h>
 #include <signal.h>
 #include <assert.h>
+
 #include "windows.h"
 #include "k32obj.h"
+#include "thread.h"
+#include "async.h"
 #include "file.h"
 #include "process.h"
 #include "winerror.h"
@@ -53,6 +56,7 @@ typedef struct _CONSOLE {
         LPSTR   		title;	/* title of console */
 	INPUT_RECORD		*irs;	/* buffered input records */
 	int			nrofirs;/* nr of buffered input records */
+	THREAD_QUEUE   		wait_queue;
 } CONSOLE;
 
 static void CONSOLE_AddWait(K32OBJ *ptr, DWORD thread_id);
@@ -377,6 +381,16 @@ CONSOLE_drain_input(CONSOLE *console,int n) {
     }
 }
 
+/***********************************************************************
+ *		CONSOLE_async_handler			[internal]
+ */
+static void
+CONSOLE_async_handler(int unixfd,void *private) {
+	CONSOLE *console = (CONSOLE*)private;
+
+	SYNC_WakeUp(&console->wait_queue,INFINITE32);
+}
+
 /***********************************************************************
  *		CONSOLE_Signaled			[internal]
  *
@@ -389,7 +403,10 @@ CONSOLE_Signaled(K32OBJ *ptr,DWORD tid) {
 	if (ptr->type!= K32OBJ_CONSOLE)
 		return FALSE;
 	CONSOLE_get_input(console);
-	return console->nrofirs!=0;
+	if (console->nrofirs!=0)
+		return TRUE;
+	/* addref console */
+	return FALSE;
 }
 
 /***********************************************************************
@@ -399,19 +416,26 @@ CONSOLE_Signaled(K32OBJ *ptr,DWORD tid) {
  */
 static void CONSOLE_AddWait(K32OBJ *ptr, DWORD thread_id)
 {
-	WARN(console,"(),stub. Expect hang.\n");
-	return;
+    CONSOLE *console = (CONSOLE *)ptr;
+
+    /* register our unix filedescriptors for async IO */
+    if (!console->wait_queue)
+    	ASYNC_RegisterFD(console->infd,CONSOLE_async_handler,console);
+    THREAD_AddQueue( &console->wait_queue, THREAD_ID_TO_THDB(thread_id) );
 }
 
 /***********************************************************************
- *		CONSOLE_AddWait			[internal]
+ *		CONSOLE_RemoveWait			[internal]
  *
  * Remove thread from our waitqueue.
  */
 static void CONSOLE_RemoveWait(K32OBJ *ptr, DWORD thread_id)
 {
-	TRACE(console,"(),stub\n");
-	return;
+    CONSOLE *console = (CONSOLE *)ptr;
+
+    THREAD_RemoveQueue( &console->wait_queue, THREAD_ID_TO_THDB(thread_id) );
+    if (!console->wait_queue)
+    	ASYNC_UnregisterFD(console->infd,CONSOLE_async_handler);
 }
 
 /***********************************************************************
@@ -421,8 +445,7 @@ static void CONSOLE_RemoveWait(K32OBJ *ptr, DWORD thread_id)
  */
 static BOOL32 CONSOLE_Satisfied(K32OBJ *ptr, DWORD thread_id)
 {
-	TRACE(console,"(),stub\n");
-	return TRUE;
+    return FALSE;
 }
 
 
@@ -784,6 +807,7 @@ BOOL32 WINAPI AllocConsole(VOID)
         console->pid             = -1;
         console->title           = NULL;
 	console->nrofirs	 = 0;
+	console->wait_queue	 = NULL;
 	console->irs	 	 = HeapAlloc(GetProcessHeap(),0,1);;
     	console->mode		 =   ENABLE_PROCESSED_INPUT
 				   | ENABLE_LINE_INPUT
-- 
GitLab