* Win32 process and thread synchronisation
*
* Copyright 1997 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include "config.h"
+
#include <assert.h>
+#include <errno.h>
#include <signal.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include "heap.h"
-#include "process.h"
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_POLL_H
+# include <sys/poll.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <string.h>
+
+#include "file.h" /* for DOSFS_UnixTimeToFileTime */
#include "thread.h"
#include "winerror.h"
-#include "syslevel.h"
-#include "message.h"
-#include "x11drv.h"
-#include "server.h"
+#include "wine/server.h"
+#include "async.h"
+
+
+/***********************************************************************
+ * get_timeout
+ */
+inline static void get_timeout( struct timeval *when, int timeout )
+{
+ gettimeofday( when, 0 );
+ if (timeout)
+ {
+ long sec = timeout / 1000;
+ if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000)
+ {
+ when->tv_usec -= 1000000;
+ when->tv_sec++;
+ }
+ when->tv_sec += sec;
+ }
+}
+
+/***********************************************************************
+ * check_async_list
+ *
+ * Process a status event from the server.
+ */
+static void WINAPI check_async_list(async_private *asp, DWORD status)
+{
+ async_private *ovp;
+ DWORD ovp_status;
+
+ for( ovp = NtCurrentTeb()->pending_list; ovp && ovp != asp; ovp = ovp->next );
+
+ if(!ovp)
+ return;
+
+ if( status != STATUS_ALERTED )
+ {
+ ovp_status = status;
+ ovp->ops->set_status (ovp, status);
+ }
+ else ovp_status = ovp->ops->get_status (ovp);
+
+ if( ovp_status == STATUS_PENDING ) ovp->func( ovp );
+
+ /* This will destroy all but PENDING requests */
+ register_old_async( ovp );
+}
/***********************************************************************
- * Sleep (KERNEL32.679)
+ * wait_reply
+ *
+ * Wait for a reply on the waiting pipe of the current thread.
+ */
+static int wait_reply( void *cookie )
+{
+ int signaled;
+ struct wake_up_reply reply;
+ for (;;)
+ {
+ int ret;
+ ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) );
+ if (ret == sizeof(reply))
+ {
+ if (!reply.cookie) break; /* thread got killed */
+ if (reply.cookie == cookie) return reply.signaled;
+ /* we stole another reply, wait for the real one */
+ signaled = wait_reply( cookie );
+ /* and now put the wrong one back in the pipe */
+ for (;;)
+ {
+ ret = write( NtCurrentTeb()->wait_fd[1], &reply, sizeof(reply) );
+ if (ret == sizeof(reply)) break;
+ if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret );
+ if (errno == EINTR) continue;
+ server_protocol_perror("wakeup write");
+ }
+ return signaled;
+ }
+ if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret );
+ if (errno == EINTR) continue;
+ server_protocol_perror("wakeup read");
+ }
+ /* the server closed the connection; time to die... */
+ SYSDEPS_AbortThread(0);
+}
+
+
+/***********************************************************************
+ * call_apcs
+ *
+ * Call outstanding APCs.
+ */
+static void call_apcs( BOOL alertable )
+{
+ FARPROC proc = NULL;
+ FILETIME ft;
+ void *args[4];
+
+ for (;;)
+ {
+ int type = APC_NONE;
+ SERVER_START_REQ( get_apc )
+ {
+ req->alertable = alertable;
+ wine_server_set_reply( req, args, sizeof(args) );
+ if (!wine_server_call( req ))
+ {
+ type = reply->type;
+ proc = reply->func;
+ }
+ }
+ SERVER_END_REQ;
+
+ switch(type)
+ {
+ case APC_NONE:
+ return; /* no more APCs */
+ case APC_ASYNC:
+ proc( args[0], args[1]);
+ break;
+ case APC_USER:
+ proc( args[0] );
+ break;
+ case APC_TIMER:
+ /* convert sec/usec to NT time */
+ DOSFS_UnixTimeToFileTime( (time_t)args[0], &ft, (DWORD)args[1] * 10 );
+ proc( args[2], ft.dwLowDateTime, ft.dwHighDateTime );
+ break;
+ case APC_ASYNC_IO:
+ check_async_list ( args[0], (DWORD) args[1]);
+ break;
+ default:
+ server_protocol_error( "get_apc_request: bad type %d\n", type );
+ break;
+ }
+ }
+}
+
+/***********************************************************************
+ * Sleep (KERNEL32.@)
*/
VOID WINAPI Sleep( DWORD timeout )
{
}
/******************************************************************************
- * SleepEx (KERNEL32.680)
+ * SleepEx (KERNEL32.@)
*/
DWORD WINAPI SleepEx( DWORD timeout, BOOL alertable )
{
/***********************************************************************
- * WaitForSingleObject (KERNEL32.723)
+ * WaitForSingleObject (KERNEL32.@)
*/
DWORD WINAPI WaitForSingleObject( HANDLE handle, DWORD timeout )
{
/***********************************************************************
- * WaitForSingleObjectEx (KERNEL32.724)
+ * WaitForSingleObjectEx (KERNEL32.@)
*/
DWORD WINAPI WaitForSingleObjectEx( HANDLE handle, DWORD timeout,
BOOL alertable )
/***********************************************************************
- * WaitForMultipleObjects (KERNEL32.721)
+ * WaitForMultipleObjects (KERNEL32.@)
*/
DWORD WINAPI WaitForMultipleObjects( DWORD count, const HANDLE *handles,
BOOL wait_all, DWORD timeout )
/***********************************************************************
- * WaitForMultipleObjectsEx (KERNEL32.722)
+ * WaitForMultipleObjectsEx (KERNEL32.@)
*/
DWORD WINAPI WaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
BOOL wait_all, DWORD timeout,
BOOL alertable )
{
- struct select_request req;
- struct select_reply reply;
- int server_handle[MAXIMUM_WAIT_OBJECTS];
- void *apc[32];
- int i, len;
+ int ret, cookie;
+ struct timeval tv;
if (count > MAXIMUM_WAIT_OBJECTS)
{
return WAIT_FAILED;
}
- /* FIXME: This is extremely ugly, but needed to avoid endless
- * recursion due to EVENT_Synchronize itself using
- * EnterCriticalSection( &X11DRV_CritSection ) ...
- */
- if ( count == 0 || handles[0] != X11DRV_CritSection.LockSemaphore )
- {
- /* Before we might possibly block, we need to push outstanding
- * graphics output to the X server ... This needs to be done
- * here so that it also works with native USER.
- */
- if ( timeout != 0 )
- EVENT_Synchronize( FALSE );
- }
-
- for (i = 0; i < count; i++) server_handle[i] = handles[i];
-
- req.count = count;
- req.flags = 0;
- req.timeout = timeout;
+ if (timeout == INFINITE) tv.tv_sec = tv.tv_usec = 0;
+ else get_timeout( &tv, timeout );
- if (wait_all) req.flags |= SELECT_ALL;
- if (alertable) req.flags |= SELECT_ALERTABLE;
- if (timeout != INFINITE) req.flags |= SELECT_TIMEOUT;
-
- CLIENT_SendRequest( REQ_SELECT, -1, 2,
- &req, sizeof(req),
- server_handle, count * sizeof(int) );
- CLIENT_WaitReply( &len, NULL, 2, &reply, sizeof(reply),
- apc, sizeof(apc) );
- if ((reply.signaled == STATUS_USER_APC) && (len > sizeof(reply)))
+ for (;;)
{
- int i;
- len -= sizeof(reply);
- for (i = 0; i < len / sizeof(void*); i += 2)
+ SERVER_START_REQ( select )
{
- PAPCFUNC func = (PAPCFUNC)apc[i];
- if ( func ) func( (ULONG_PTR)apc[i+1] );
+ req->flags = SELECT_INTERRUPTIBLE;
+ req->cookie = &cookie;
+ req->sec = tv.tv_sec;
+ req->usec = tv.tv_usec;
+ wine_server_add_data( req, handles, count * sizeof(HANDLE) );
+
+ if (wait_all) req->flags |= SELECT_ALL;
+ if (alertable) req->flags |= SELECT_ALERTABLE;
+ if (timeout != INFINITE) req->flags |= SELECT_TIMEOUT;
+
+ ret = wine_server_call( req );
}
+ SERVER_END_REQ;
+ if (ret == STATUS_PENDING) ret = wait_reply( &cookie );
+ if (ret != STATUS_USER_APC) break;
+ call_apcs( alertable );
+ if (alertable) break;
+ }
+ if (HIWORD(ret)) /* is it an error code? */
+ {
+ SetLastError( RtlNtStatusToDosError(ret) );
+ ret = WAIT_FAILED;
}
- return reply.signaled;
+ return ret;
}
/***********************************************************************
- * WIN16_WaitForSingleObject (KERNEL.460)
+ * WaitForSingleObject (KERNEL.460)
*/
-DWORD WINAPI WIN16_WaitForSingleObject( HANDLE handle, DWORD timeout )
+DWORD WINAPI WaitForSingleObject16( HANDLE handle, DWORD timeout )
{
- DWORD retval;
+ DWORD retval, mutex_count;
- SYSLEVEL_ReleaseWin16Lock();
+ ReleaseThunkLock( &mutex_count );
retval = WaitForSingleObject( handle, timeout );
- SYSLEVEL_RestoreWin16Lock();
-
+ RestoreThunkLock( mutex_count );
return retval;
}
/***********************************************************************
- * WIN16_WaitForMultipleObjects (KERNEL.461)
+ * WaitForMultipleObjects (KERNEL.461)
*/
-DWORD WINAPI WIN16_WaitForMultipleObjects( DWORD count, const HANDLE *handles,
- BOOL wait_all, DWORD timeout )
+DWORD WINAPI WaitForMultipleObjects16( DWORD count, const HANDLE *handles,
+ BOOL wait_all, DWORD timeout )
{
- DWORD retval;
-
- SYSLEVEL_ReleaseWin16Lock();
- retval = WaitForMultipleObjects( count, handles, wait_all, timeout );
- SYSLEVEL_RestoreWin16Lock();
+ DWORD retval, mutex_count;
+ ReleaseThunkLock( &mutex_count );
+ retval = WaitForMultipleObjectsEx( count, handles, wait_all, timeout, FALSE );
+ RestoreThunkLock( mutex_count );
return retval;
}
/***********************************************************************
- * WIN16_WaitForMultipleObjectsEx (KERNEL.495)
+ * WaitForMultipleObjectsEx (KERNEL.495)
*/
-DWORD WINAPI WIN16_WaitForMultipleObjectsEx( DWORD count,
- const HANDLE *handles,
- BOOL wait_all, DWORD timeout,
- BOOL alertable )
+DWORD WINAPI WaitForMultipleObjectsEx16( DWORD count, const HANDLE *handles,
+ BOOL wait_all, DWORD timeout, BOOL alertable )
{
- DWORD retval;
-
- SYSLEVEL_ReleaseWin16Lock();
- retval = WaitForMultipleObjectsEx( count, handles,
- wait_all, timeout, alertable );
- SYSLEVEL_RestoreWin16Lock();
+ DWORD retval, mutex_count;
+ ReleaseThunkLock( &mutex_count );
+ retval = WaitForMultipleObjectsEx( count, handles, wait_all, timeout, alertable );
+ RestoreThunkLock( mutex_count );
return retval;
}
-