/* $XConsortium: os2_select.c /main/6 1996/10/27 11:48:55 kaleb $ */ /* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/os2/os2_select.c,v 3.9 2003/03/25 04:18:24 dawes Exp $ */ /* * (c) Copyright 1996 by Sebastien Marineau * * Modified 1999 by Holger.Veit@gmd.de * Modified 2004 by Frank Giessler * * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * HOLGER VEIT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Sebastien Marineau shall not be * used in advertising or otherwise to promote the sale, use or other dealings * in this Software without prior written authorization from Sebastien Marineau. * */ /* os2_select.c: reimplementation of the xserver select(), optimized for speed */ #ifdef HAVE_XORG_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #define I_NEED_OS2_H #define INCL_DOSSEMAPHORES #define INCL_DOSPROFILE #define INCL_DOSPROCESS #define INCL_DOSFILEMGR #define INCL_DOSMISC #define INCL_DOSMODULEMGR #include #include "xf86.h" #include "xf86Priv.h" #include "xf86_OSlib.h" #include "os2_select.h" int os2MouseQueueQuery(); int os2KbdQueueQuery(); void os2RecoverFromPopup(); void os2CheckPopupPending(); void os2SocketMonitorThread(); extern BOOL os2PopupErrorPending; extern HEV hKbdSem; extern HEV hMouseSem; extern HEV hevServerHasFocus; HEV hPipeSem; HEV hSocketSem; HEV hActivateSocketSem; HEV hSwitchToSem; static HMUX hSelectWait; SEMRECORD SelectMuxRecord[5]; HMODULE hmod_so32dll; static struct select_data sd; static int (*os2_tcp_select)(int *,int,int,int,long); static int (*os2_so_cancel)(int); static int (*os2_sock_errno)(); int os2_set_error(ULONG); extern int _files[]; /* This is a new implementation of select, for improved efficiency */ /* This function performs select() on sockets */ /* but uses OS/2 internal fncts to check mouse */ /* and keyboard. S. Marineau, 27/4/96 */ /* This is still VERY messy */ /* A few note on optimizations: this select has been tuned for maximum * performance, and thus has a different approach than a general-purpose * select. It should not be used in another app without modifications. Further, * it may need modifications if the Xserver os code is modified * Assumptions: this is never called with anything in exceptfds. This is * silently ignored. Further, if any pipes are specified in the write mask, it is * because they have just been stuffed full by the xserver. There is not much * in immediately returning with those bits set. Instead, we block on the * semaphore for at least one tick, which will let the client at least start * to flush the pipe. */ int os2PseudoSelect(nfds,readfds,writefds,exceptfds,timeout) int nfds; fd_set *readfds,*writefds,*exceptfds; struct timeval *timeout; { static BOOL FirstTime=TRUE; int n,ns,np; int ready_handles; ULONG timeout_ms; BOOL any_ready; ULONG semKey,postCount; APIRET rc; char faildata[16]; static int Socket_Tid; sd.have_read=FALSE; sd.have_write=FALSE; sd.socket_nread=0; sd.socket_nwrite=0; sd.socket_ntotal=0; sd.max_fds=31; ready_handles=0; any_ready=FALSE; sd.pipe_ntotal=0; sd.pipe_have_write=FALSE; /* Stuff we have to do the first time this is called to set up various parameters */ if (FirstTime) { /* First load the so32dll.dll module and get a pointer to the SELECT fn */ if ((rc=DosLoadModule(faildata,sizeof(faildata),"SO32DLL",&hmod_so32dll))!=0) { FatalError("Could not load module so32dll.dll, rc = %d. Error note %s\n",rc,faildata); } if ((rc = DosQueryProcAddr(hmod_so32dll, 0, "SELECT", (PPFN)&os2_tcp_select))!=0) { FatalError("Could not query address of SELECT, rc = %d.\n",rc); } if ((rc = DosQueryProcAddr(hmod_so32dll, 0, "SO_CANCEL", (PPFN)&os2_so_cancel))!=0) { FatalError("Could not query address of SO_CANCEL, rc = %d.\n",rc); } if ((rc = DosQueryProcAddr(hmod_so32dll, 0, "SOCK_ERRNO", (PPFN)&os2_sock_errno))!=0) { FatalError("Could not query address of SOCK_ERRNO, rc = %d.\n",rc); } /* Call these a first time to set the semaphore */ xf86OsMouseEvents(); xf86KbdEvents(); DosCreateEventSem(NULL, &hSocketSem,DC_SEM_SHARED,FALSE); DosResetEventSem(hSocketSem,&postCount); DosCreateEventSem(NULL, &hActivateSocketSem, DC_SEM_SHARED, FALSE); DosResetEventSem(hActivateSocketSem, &postCount); DosCreateEventSem(NULL, &hSwitchToSem, DC_SEM_SHARED, FALSE); DosResetEventSem(hSwitchToSem, &postCount); Socket_Tid = _beginthread(os2SocketMonitorThread, NULL, 0x2000,(void *) NULL); xf86Msg(X_INFO, "Started Socket monitor thread, TID=%d\n",Socket_Tid); SelectMuxRecord[0].hsemCur = (HSEM)hMouseSem; SelectMuxRecord[0].ulUser = MOUSE_SEM_KEY; SelectMuxRecord[1].hsemCur = (HSEM)hKbdSem; SelectMuxRecord[1].ulUser = KBD_SEM_KEY; SelectMuxRecord[2].hsemCur = (HSEM)hPipeSem; SelectMuxRecord[2].ulUser = PIPE_SEM_KEY; SelectMuxRecord[3].hsemCur = (HSEM)hSocketSem; SelectMuxRecord[3].ulUser = SOCKET_SEM_KEY; SelectMuxRecord[4].hsemCur = (HSEM)hSwitchToSem; SelectMuxRecord[4].ulUser = SWITCHTO_SEM_KEY; rc = DosCreateMuxWaitSem(NULL, &hSelectWait, 5, SelectMuxRecord, DC_SEM_SHARED | DCMW_WAIT_ANY); if (rc) { xf86Msg(X_ERROR,"Could not create MuxWait semaphore, rc=%d\n",rc); } FirstTime = FALSE; } rc = DosResetEventSem(hActivateSocketSem, &postCount); /* Set up the time delay structs */ if (timeout!=NULL) { timeout_ms=timeout->tv_sec*1000+timeout->tv_usec/1000; } else { timeout_ms=1000000; /* This should be large enough... */ } /* Zero our local fd_masks */ {FD_ZERO(&sd.read_copy);} {FD_ZERO(&sd.write_copy);} /* Copy the masks for later use */ if (readfds!=NULL) { XFD_COPYSET(readfds,&sd.read_copy); sd.have_read=TRUE; } if (writefds!=NULL) {XFD_COPYSET(writefds,&sd.write_copy); sd.have_write=TRUE; } /* And zero the original masks */ if (sd.have_read){ FD_ZERO(readfds); } if (sd.have_write) {FD_ZERO(writefds); } if (exceptfds != NULL) {FD_ZERO(exceptfds); } /* Now we parse the fd_sets passed to select and separate pipe/sockets */ n = os2_parse_select(&sd,nfds); /* Now check if we have sockets ready! */ if (sd.socket_ntotal > 0) { ns = os2_poll_sockets(&sd,readfds,writefds); if (ns>0) { ready_handles+=ns; any_ready = TRUE; } else if (ns == -1) { return(-1); } } /* And pipes */ if (sd.pipe_ntotal > 0) { np = os2_check_pipes(&sd,readfds,writefds); if (np > 0) { ready_handles+=np; any_ready = TRUE; } else if (np == -1) { return(-1); } } /* And finally poll input devices */ if(!os2MouseQueueQuery() || !os2KbdQueueQuery() ) any_ready = TRUE; if (xf86Info.vtRequestsPending) any_ready=TRUE; if (os2PopupErrorPending) os2RecoverFromPopup(); if (!any_ready && timeout_ms) { DosResetEventSem(hSocketSem,&postCount); /* Activate the socket thread */ if (sd.socket_ntotal>0) { rc = DosPostEventSem(hActivateSocketSem); } rc = DosWaitMuxWaitSem(hSelectWait, timeout_ms, &semKey); /* If our socket monitor thread is still blocked in os2_tcp_select() * we have to wake it up by calling os2_so_cancel(). * After that, call os2_tcp_select() once more to get rid of * error SOCEINTR (10004) */ if (sd.socket_ntotal>0) { rc = DosQueryEventSem(hSocketSem, &postCount); if (postCount == 0) { /* os2_select still blocked */ int i,f,g; struct select_data *sd_ptr=&sd; if (sd.socket_nread > 0) { for (i=0; itcp_select_mask[i]; os2_so_cancel(f); os2_tcp_select(&g, 1, 0, 0, 0); /* get rid of error 10004 */ } } if (sd.socket_nwrite > 0) { for (i=sd.socket_nread; itcp_select_mask[i]; os2_so_cancel(f); os2_tcp_select(&g, 0, 1, 0, 0); /* get rid of error 10004 */ } } } else { /* not blocked, something must be ready -> get it */ ns = os2_poll_sockets(&sd,readfds,writefds); if (ns>0) { ready_handles+=ns; } else if (ns == -1) { return(-1); } } } if (sd.pipe_ntotal > 0) { rc = DosQueryEventSem(hPipeSem,&postCount); if (postCount > 0) { np = os2_check_pipes(&sd,readfds,writefds); if (np > 0) { ready_handles+=np; } else if (np == -1) { return(-1); } } } } /* The polling of sockets/pipe automatically set the proper bits */ return (ready_handles); } int os2_parse_select(sd,nfds) struct select_data *sd; int nfds; { int i; /* First we determine up to which descriptor we need to check. */ /* No need to check up to 256 if we don't have to (and usually we dont...)*/ /* Note: stuff here is hardcoded for fd_sets which are int[8] as in EMX!!! */ if (nfds > sd->max_fds) { for (i=0;i<((FD_SETSIZE+31)/32);i++) { if (sd->read_copy.fds_bits[i] || sd->write_copy.fds_bits[i]) sd->max_fds=(i*32) +32; } } else { sd->max_fds = nfds; } /* Check if this is greater than specified in select() call */ if(sd->max_fds > nfds) sd->max_fds = nfds; if (sd->have_read) { for (i = 0; i < sd->max_fds; ++i) { if (FD_ISSET (i, &sd->read_copy)) { if(_files[i] & F_SOCKET) { sd->tcp_select_mask[sd->socket_ntotal]=_getsockhandle(i); sd->tcp_emx_handles[sd->socket_ntotal]=i; sd->socket_ntotal++; sd->socket_nread++; } else if (_files[i] & F_PIPE) { sd -> pipe_ntotal++; } } } } if (sd->have_write) { for (i = 0; i < sd->max_fds; ++i) { if (FD_ISSET (i, &sd->write_copy)) { if (_files[i] & F_SOCKET) { sd->tcp_select_mask[sd->socket_ntotal]=_getsockhandle(i); sd->tcp_emx_handles[sd->socket_ntotal]=i; sd->socket_ntotal++; sd->socket_nwrite++; } else if (_files[i] & F_PIPE) { sd -> pipe_ntotal++; sd -> pipe_have_write=TRUE; } } } } return(sd->socket_ntotal); } int os2_poll_sockets(sd,readfds,writefds) struct select_data *sd; fd_set *readfds,*writefds; { int e,i; int j,n; memcpy(sd->tcp_select_copy,sd->tcp_select_mask, sd->socket_ntotal*sizeof(int)); e = os2_tcp_select(sd->tcp_select_copy,sd->socket_nread, sd->socket_nwrite, 0, 0); if (e == 0) return(e); /* We have something ready? */ if (e>0) { j = 0; n = 0; for (i = 0; i < sd->socket_nread; ++i, ++j) if (sd->tcp_select_copy[j] != -1) { FD_SET (sd->tcp_emx_handles[j], readfds); n ++; } for (i = 0; i < sd->socket_nwrite; ++i, ++j) if (sd->tcp_select_copy[j] != -1) { FD_SET (sd->tcp_emx_handles[j], writefds); n ++; } errno = 0; return n; } if (e<0) { /*Error -- TODO */ xf86Msg(X_ERROR,"Error in server select! sock_errno = %d\n",os2_sock_errno()); errno = EBADF; return (-1); } } /* Check to see if anything is ready on pipes */ int os2_check_pipes(sd,readfds,writefds) struct select_data *sd; fd_set *readfds,*writefds; { int i,e; ULONG ulPostCount; PIPESEMSTATE pipeSemState[128]; APIRET rc; e = 0; rc = DosResetEventSem(hPipeSem,&ulPostCount); rc = DosQueryNPipeSemState((HSEM) hPipeSem, (PPIPESEMSTATE)&pipeSemState, sizeof(pipeSemState)); if(rc) xf86Msg(X_ERROR,"SELECT: rc from QueryNPipeSem: %d\n",rc); i=0; while (pipeSemState[i].fStatus != 0) { /* xf86Msg(X_INFO,"SELECT: sem entry, stat=%d, flag=%d, key=%d,avail=%d\n", pipeSemState[i].fStatus,pipeSemState[i].fFlag,pipeSemState[i].usKey, pipeSemState[i].usAvail); */ if ((pipeSemState[i].fStatus == 1) && (FD_ISSET(pipeSemState[i].usKey,&sd->read_copy))) { FD_SET(pipeSemState[i].usKey,readfds); e++; } else if ((pipeSemState[i].fStatus == 2) && (FD_ISSET(pipeSemState[i].usKey,&sd->write_copy))) { FD_SET(pipeSemState[i].usKey,writefds); e++; } else if ((pipeSemState[i].fStatus == 3) && ((FD_ISSET(pipeSemState[i].usKey,&sd->read_copy)) || (FD_ISSET(pipeSemState[i].usKey,&sd->write_copy)) )) { errno = EBADF; /* xf86Msg(X_ERROR,"Pipe has closed down, fd=%d\n",pipeSemState[i].usKey); */ return (-1); } i++; } /* endwhile */ errno = 0; return(e); } void os2SocketMonitorThread(void *arg) { struct select_data *sd_ptr = &sd; ULONG ulPostCount; int e,rc; /* Make thread time critical */ DosSetPriority(2L,3L,0L,0L); while (1) { rc = DosWaitEventSem(hActivateSocketSem, SEM_INDEFINITE_WAIT); if (rc != 0 ) xf86Msg(X_ERROR,"Socket monitor: DosWaitEventSem(hActivateSocketSem..) returned %d\n",rc); rc = DosResetEventSem(hActivateSocketSem,&ulPostCount); if (rc != 0 ) xf86Msg(X_ERROR,"Socket monitor: DosResetEventSem(&hActivateSocketSem..) returned %d\n",rc); /* fg300104: * The next line shouldn't be here, but the DosPostEventSem() * below will return 299 from time to time under heavy load */ /* DosResetEventSem(hSocketSem,&ulPostCount);*/ memcpy(sd_ptr->tcp_select_monitor,sd_ptr->tcp_select_mask, sd_ptr->socket_ntotal*sizeof(int)); /* call os2_select(), return only if either something is ready or * os2_so_cancel() was called */ e = os2_tcp_select(sd_ptr->tcp_select_monitor, sd_ptr->socket_nread, sd_ptr->socket_nwrite, 0, -1); if (e>0) { rc = DosPostEventSem(hSocketSem); if (rc != 0 ) xf86Msg(X_ERROR,"Socket monitor: DosPostEventSem(hSocketSem..) returned %d\n",rc); } else if (e<0) { rc = os2_sock_errno(); if (rc != 10004) xf86Msg(X_ERROR,"Socket monitor: os2_select: sock_errno = %d\n",rc); } rc = DosQueryEventSem(hevServerHasFocus, &ulPostCount); /* no need to rush while switched away */ if ((rc==0) && (ulPostCount==0)) rc == DosWaitEventSem(hevServerHasFocus,31L); } }