summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSWindows/Poll.c
diff options
context:
space:
mode:
Diffstat (limited to 'mDNSResponder/mDNSWindows/Poll.c')
-rwxr-xr-xmDNSResponder/mDNSWindows/Poll.c728
1 files changed, 728 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSWindows/Poll.c b/mDNSResponder/mDNSWindows/Poll.c
new file mode 100755
index 00000000..9adc632b
--- /dev/null
+++ b/mDNSResponder/mDNSWindows/Poll.c
@@ -0,0 +1,728 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Poll.h"
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <process.h>
+#include "GenLinkedList.h"
+#include "DebugServices.h"
+
+
+typedef struct PollSource_struct
+{
+ SOCKET socket;
+ HANDLE handle;
+ void *context;
+
+ union
+ {
+ mDNSPollSocketCallback socket;
+ mDNSPollEventCallback event;
+ } callback;
+
+ struct Worker_struct *worker;
+ struct PollSource_struct *next;
+
+} PollSource;
+
+
+typedef struct Worker_struct
+{
+ HANDLE thread; // NULL for main worker
+ unsigned id; // 0 for main worker
+
+ HANDLE start; // NULL for main worker
+ HANDLE stop; // NULL for main worker
+ BOOL done; // Not used for main worker
+
+ DWORD numSources;
+ PollSource *sources[ MAXIMUM_WAIT_OBJECTS ];
+ HANDLE handles[ MAXIMUM_WAIT_OBJECTS ];
+ DWORD result;
+ struct Worker_struct *next;
+} Worker;
+
+
+typedef struct Poll_struct
+{
+ mDNSBool setup;
+ HANDLE wakeup;
+ GenLinkedList sources;
+ DWORD numSources;
+ Worker main;
+ GenLinkedList workers;
+ HANDLE workerHandles[ MAXIMUM_WAIT_OBJECTS ];
+ DWORD numWorkers;
+
+} Poll;
+
+
+/*
+ * Poll Methods
+ */
+
+mDNSlocal mStatus PollSetup();
+mDNSlocal mStatus PollRegisterSource( PollSource *source );
+mDNSlocal void PollUnregisterSource( PollSource *source );
+mDNSlocal mStatus PollStartWorkers();
+mDNSlocal mStatus PollStopWorkers();
+mDNSlocal void PollRemoveWorker( Worker *worker );
+
+
+/*
+ * Worker Methods
+ */
+
+mDNSlocal mStatus WorkerInit( Worker *worker );
+mDNSlocal void WorkerFree( Worker *worker );
+mDNSlocal void WorkerRegisterSource( Worker *worker, PollSource *source );
+mDNSlocal int WorkerSourceToIndex( Worker *worker, PollSource *source );
+mDNSlocal void WorkerUnregisterSource( Worker *worker, PollSource *source );
+mDNSlocal void WorkerDispatch( Worker *worker);
+mDNSlocal void CALLBACK WorkerWakeupNotification( HANDLE event, void *context );
+mDNSlocal unsigned WINAPI WorkerMain( LPVOID inParam );
+
+
+static void
+ShiftDown( void * arr, size_t arraySize, size_t itemSize, int index )
+{
+ memmove( ( ( unsigned char* ) arr ) + ( ( index - 1 ) * itemSize ), ( ( unsigned char* ) arr ) + ( index * itemSize ), ( arraySize - index ) * itemSize );
+}
+
+
+#define DEBUG_NAME "[mDNSWin32] "
+#define gMDNSRecord mDNSStorage
+mDNSlocal Poll gPoll = { mDNSfalse, NULL };
+
+#define LogErr( err, FUNC ) LogMsg( "%s:%d - %s failed: %d\n", __FUNCTION__, __LINE__, FUNC, err );
+
+
+mStatus
+mDNSPollRegisterSocket( SOCKET socket, int networkEvents, mDNSPollSocketCallback callback, void *context )
+{
+ PollSource *source = NULL;
+ HANDLE event = INVALID_HANDLE_VALUE;
+ mStatus err = mStatus_NoError;
+
+ if ( !gPoll.setup )
+ {
+ err = PollSetup();
+ require_noerr( err, exit );
+ }
+
+ source = malloc( sizeof( PollSource ) );
+ require_action( source, exit, err = mStatus_NoMemoryErr );
+
+ event = WSACreateEvent();
+ require_action( event, exit, err = mStatus_NoMemoryErr );
+
+ err = WSAEventSelect( socket, event, networkEvents );
+ require_noerr( err, exit );
+
+ source->socket = socket;
+ source->handle = event;
+ source->callback.socket = callback;
+ source->context = context;
+
+ err = PollRegisterSource( source );
+ require_noerr( err, exit );
+
+exit:
+
+ if ( err != mStatus_NoError )
+ {
+ if ( event != INVALID_HANDLE_VALUE )
+ {
+ WSACloseEvent( event );
+ }
+
+ if ( source != NULL )
+ {
+ free( source );
+ }
+ }
+
+ return err;
+}
+
+
+void
+mDNSPollUnregisterSocket( SOCKET socket )
+{
+ PollSource *source;
+
+ for ( source = gPoll.sources.Head; source; source = source->next )
+ {
+ if ( source->socket == socket )
+ {
+ break;
+ }
+ }
+
+ if ( source )
+ {
+ WSACloseEvent( source->handle );
+ PollUnregisterSource( source );
+ free( source );
+ }
+}
+
+
+mStatus
+mDNSPollRegisterEvent( HANDLE event, mDNSPollEventCallback callback, void *context )
+{
+ PollSource *source = NULL;
+ mStatus err = mStatus_NoError;
+
+ if ( !gPoll.setup )
+ {
+ err = PollSetup();
+ require_noerr( err, exit );
+ }
+
+ source = malloc( sizeof( PollSource ) );
+ require_action( source, exit, err = mStatus_NoMemoryErr );
+
+ source->socket = INVALID_SOCKET;
+ source->handle = event;
+ source->callback.event = callback;
+ source->context = context;
+
+ err = PollRegisterSource( source );
+ require_noerr( err, exit );
+
+exit:
+
+ if ( err != mStatus_NoError )
+ {
+ if ( source != NULL )
+ {
+ free( source );
+ }
+ }
+
+ return err;
+}
+
+
+void
+mDNSPollUnregisterEvent( HANDLE event )
+{
+ PollSource *source;
+
+ for ( source = gPoll.sources.Head; source; source = source->next )
+ {
+ if ( source->handle == event )
+ {
+ break;
+ }
+ }
+
+ if ( source )
+ {
+ PollUnregisterSource( source );
+ free( source );
+ }
+}
+
+
+mStatus
+mDNSPoll( DWORD msec )
+{
+ mStatus err = mStatus_NoError;
+
+ if ( gPoll.numWorkers > 0 )
+ {
+ err = PollStartWorkers();
+ require_noerr( err, exit );
+ }
+
+ gPoll.main.result = WaitForMultipleObjects( gPoll.main.numSources, gPoll.main.handles, FALSE, msec );
+ err = translate_errno( ( gPoll.main.result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "WaitForMultipleObjects()" );
+ require_action( gPoll.main.result != WAIT_FAILED, exit, err = ( mStatus ) GetLastError() );
+
+ if ( gPoll.numWorkers > 0 )
+ {
+ err = PollStopWorkers();
+ require_noerr( err, exit );
+ }
+
+ WorkerDispatch( &gPoll.main );
+
+exit:
+
+ return ( err );
+}
+
+
+mDNSlocal mStatus
+PollSetup()
+{
+ mStatus err = mStatus_NoError;
+
+ if ( !gPoll.setup )
+ {
+ memset( &gPoll, 0, sizeof( gPoll ) );
+
+ InitLinkedList( &gPoll.sources, offsetof( PollSource, next ) );
+ InitLinkedList( &gPoll.workers, offsetof( Worker, next ) );
+
+ gPoll.wakeup = CreateEvent( NULL, TRUE, FALSE, NULL );
+ require_action( gPoll.wakeup, exit, err = mStatus_NoMemoryErr );
+
+ err = WorkerInit( &gPoll.main );
+ require_noerr( err, exit );
+
+ gPoll.setup = mDNStrue;
+ }
+
+exit:
+
+ return err;
+}
+
+
+mDNSlocal mStatus
+PollRegisterSource( PollSource *source )
+{
+ Worker *worker = NULL;
+ mStatus err = mStatus_NoError;
+
+ AddToTail( &gPoll.sources, source );
+ gPoll.numSources++;
+
+ // First check our main worker. In most cases, we won't have to worry about threads
+
+ if ( gPoll.main.numSources < MAXIMUM_WAIT_OBJECTS )
+ {
+ WorkerRegisterSource( &gPoll.main, source );
+ }
+ else
+ {
+ // Try to find a thread to use that we've already created
+
+ for ( worker = gPoll.workers.Head; worker; worker = worker->next )
+ {
+ if ( worker->numSources < MAXIMUM_WAIT_OBJECTS )
+ {
+ WorkerRegisterSource( worker, source );
+ break;
+ }
+ }
+
+ // If not, then create a worker and make a thread to run it in
+
+ if ( !worker )
+ {
+ worker = ( Worker* ) malloc( sizeof( Worker ) );
+ require_action( worker, exit, err = mStatus_NoMemoryErr );
+
+ memset( worker, 0, sizeof( Worker ) );
+
+ worker->start = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( worker->start, exit, err = mStatus_NoMemoryErr );
+
+ worker->stop = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( worker->stop, exit, err = mStatus_NoMemoryErr );
+
+ err = WorkerInit( worker );
+ require_noerr( err, exit );
+
+ // Create thread with _beginthreadex() instead of CreateThread() to avoid
+ // memory leaks when using static run-time libraries.
+ // See <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/createthread.asp>.
+
+ worker->thread = ( HANDLE ) _beginthreadex_compat( NULL, 0, WorkerMain, worker, 0, &worker->id );
+ err = translate_errno( worker->thread, ( mStatus ) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ AddToTail( &gPoll.workers, worker );
+ gPoll.workerHandles[ gPoll.numWorkers++ ] = worker->stop;
+
+ WorkerRegisterSource( worker, source );
+ }
+ }
+
+exit:
+
+ if ( err && worker )
+ {
+ WorkerFree( worker );
+ }
+
+ return err;
+}
+
+
+mDNSlocal void
+PollUnregisterSource( PollSource *source )
+{
+ RemoveFromList( &gPoll.sources, source );
+ gPoll.numSources--;
+
+ WorkerUnregisterSource( source->worker, source );
+}
+
+
+mDNSlocal mStatus
+PollStartWorkers()
+{
+ Worker *worker;
+ mStatus err = mStatus_NoError;
+ BOOL ok;
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "starting workers\n" );
+
+ worker = gPoll.workers.Head;
+
+ while ( worker )
+ {
+ Worker *next = worker->next;
+
+ if ( worker->numSources == 1 )
+ {
+ PollRemoveWorker( worker );
+ }
+ else
+ {
+ dlog( kDebugLevelChatty, DEBUG_NAME "waking up worker\n" );
+
+ ok = SetEvent( worker->start );
+ err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "SetEvent()" );
+
+ if ( err )
+ {
+ PollRemoveWorker( worker );
+ }
+ }
+
+ worker = next;
+ }
+
+ err = mStatus_NoError;
+
+ return err;
+}
+
+
+mDNSlocal mStatus
+PollStopWorkers()
+{
+ DWORD result;
+ Worker *worker;
+ BOOL ok;
+ mStatus err = mStatus_NoError;
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "stopping workers\n" );
+
+ ok = SetEvent( gPoll.wakeup );
+ err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "SetEvent()" );
+
+ // Wait For 5 seconds for all the workers to wake up
+
+ result = WaitForMultipleObjects( gPoll.numWorkers, gPoll.workerHandles, TRUE, 5000 );
+ err = translate_errno( ( result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "WaitForMultipleObjects()" );
+
+ ok = ResetEvent( gPoll.wakeup );
+ err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "ResetEvent()" );
+
+ for ( worker = gPoll.workers.Head; worker; worker = worker->next )
+ {
+ WorkerDispatch( worker );
+ }
+
+ err = mStatus_NoError;
+
+ return err;
+}
+
+
+mDNSlocal void
+PollRemoveWorker( Worker *worker )
+{
+ DWORD result;
+ mStatus err;
+ BOOL ok;
+ DWORD i;
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "removing worker %d\n", worker->id );
+
+ RemoveFromList( &gPoll.workers, worker );
+
+ // Remove handle from gPoll.workerHandles
+
+ for ( i = 0; i < gPoll.numWorkers; i++ )
+ {
+ if ( gPoll.workerHandles[ i ] == worker->stop )
+ {
+ ShiftDown( gPoll.workerHandles, gPoll.numWorkers, sizeof( gPoll.workerHandles[ 0 ] ), i + 1 );
+ break;
+ }
+ }
+
+ worker->done = TRUE;
+ gPoll.numWorkers--;
+
+ // Cause the thread to exit.
+
+ ok = SetEvent( worker->start );
+ err = translate_errno( ok, ( OSStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "SetEvent()" );
+
+ result = WaitForSingleObject( worker->thread, 5000 );
+ err = translate_errno( result != WAIT_FAILED, ( OSStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "WaitForSingleObject()" );
+
+ if ( ( result == WAIT_FAILED ) || ( result == WAIT_TIMEOUT ) )
+ {
+ ok = TerminateThread( worker->thread, 0 );
+ err = translate_errno( ok, ( OSStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "TerminateThread()" );
+ }
+
+ CloseHandle( worker->thread );
+ worker->thread = NULL;
+
+ WorkerFree( worker );
+}
+
+
+mDNSlocal void
+WorkerRegisterSource( Worker *worker, PollSource *source )
+{
+ source->worker = worker;
+ worker->sources[ worker->numSources ] = source;
+ worker->handles[ worker->numSources ] = source->handle;
+ worker->numSources++;
+}
+
+
+mDNSlocal int
+WorkerSourceToIndex( Worker *worker, PollSource *source )
+{
+ int index;
+
+ for ( index = 0; index < ( int ) worker->numSources; index++ )
+ {
+ if ( worker->sources[ index ] == source )
+ {
+ break;
+ }
+ }
+
+ if ( index == ( int ) worker->numSources )
+ {
+ index = -1;
+ }
+
+ return index;
+}
+
+
+mDNSlocal void
+WorkerUnregisterSource( Worker *worker, PollSource *source )
+{
+ int sourceIndex = WorkerSourceToIndex( worker, source );
+ DWORD delta;
+
+ if ( sourceIndex == -1 )
+ {
+ LogMsg( "WorkerUnregisterSource: source not found in list" );
+ goto exit;
+ }
+
+ delta = ( worker->numSources - sourceIndex - 1 );
+
+ // If this source is not at the end of the list, then move memory
+
+ if ( delta > 0 )
+ {
+ ShiftDown( worker->sources, worker->numSources, sizeof( worker->sources[ 0 ] ), sourceIndex + 1 );
+ ShiftDown( worker->handles, worker->numSources, sizeof( worker->handles[ 0 ] ), sourceIndex + 1 );
+ }
+
+ worker->numSources--;
+
+exit:
+
+ return;
+}
+
+
+mDNSlocal void CALLBACK
+WorkerWakeupNotification( HANDLE event, void *context )
+{
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "Worker thread wakeup\n" );
+}
+
+
+mDNSlocal void
+WorkerDispatch( Worker *worker )
+{
+ if ( worker->result == WAIT_FAILED )
+ {
+ /* What should we do here? */
+ }
+ else if ( worker->result == WAIT_TIMEOUT )
+ {
+ dlog( kDebugLevelChatty, DEBUG_NAME "timeout\n" );
+ }
+ else
+ {
+ DWORD waitItemIndex = ( DWORD )( ( ( int ) worker->result ) - WAIT_OBJECT_0 );
+ PollSource *source = NULL;
+
+ // Sanity check
+
+ if ( waitItemIndex >= worker->numSources )
+ {
+ LogMsg( "WorkerDispatch: waitItemIndex (%d) is >= numSources (%d)", waitItemIndex, worker->numSources );
+ goto exit;
+ }
+
+ source = worker->sources[ waitItemIndex ];
+
+ if ( source->socket != INVALID_SOCKET )
+ {
+ WSANETWORKEVENTS event;
+
+ if ( WSAEnumNetworkEvents( source->socket, source->handle, &event ) == 0 )
+ {
+ source->callback.socket( source->socket, &event, source->context );
+ }
+ else
+ {
+ source->callback.socket( source->socket, NULL, source->context );
+ }
+ }
+ else
+ {
+ source->callback.event( source->handle, source->context );
+ }
+ }
+
+exit:
+
+ return;
+}
+
+
+mDNSlocal mStatus
+WorkerInit( Worker *worker )
+{
+ PollSource *source = NULL;
+ mStatus err = mStatus_NoError;
+
+ require_action( worker, exit, err = mStatus_BadParamErr );
+
+ source = malloc( sizeof( PollSource ) );
+ require_action( source, exit, err = mStatus_NoMemoryErr );
+
+ source->socket = INVALID_SOCKET;
+ source->handle = gPoll.wakeup;
+ source->callback.event = WorkerWakeupNotification;
+ source->context = NULL;
+
+ WorkerRegisterSource( worker, source );
+
+exit:
+
+ return err;
+}
+
+
+mDNSlocal void
+WorkerFree( Worker *worker )
+{
+ if ( worker->start )
+ {
+ CloseHandle( worker->start );
+ worker->start = NULL;
+ }
+
+ if ( worker->stop )
+ {
+ CloseHandle( worker->stop );
+ worker->stop = NULL;
+ }
+
+ free( worker );
+}
+
+
+mDNSlocal unsigned WINAPI
+WorkerMain( LPVOID inParam )
+{
+ Worker *worker = ( Worker* ) inParam;
+ mStatus err = mStatus_NoError;
+
+ require_action( worker, exit, err = mStatus_BadParamErr );
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME, "entering WorkerMain()\n" );
+
+ while ( TRUE )
+ {
+ DWORD result;
+ BOOL ok;
+
+ dlog( kDebugLevelChatty, DEBUG_NAME, "worker thread %d will wait on main loop\n", worker->id );
+
+ result = WaitForSingleObject( worker->start, INFINITE );
+ err = translate_errno( ( result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) { LogErr( err, "WaitForSingleObject()" ); break; }
+ if ( worker->done ) break;
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "worker thread %d will wait on sockets\n", worker->id );
+
+ worker->result = WaitForMultipleObjects( worker->numSources, worker->handles, FALSE, INFINITE );
+ err = translate_errno( ( worker->result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) { LogErr( err, "WaitForMultipleObjects()" ); break; }
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "worker thread %d did wait on sockets: %d\n", worker->id, worker->result );
+
+ ok = SetEvent( gPoll.wakeup );
+ err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) { LogErr( err, "SetEvent()" ); break; }
+
+ dlog( kDebugLevelChatty, DEBUG_NAME, "worker thread %d preparing to sleep\n", worker->id );
+
+ ok = SetEvent( worker->stop );
+ err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) { LogErr( err, "SetEvent()" ); break; }
+ }
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "exiting WorkerMain()\n" );
+
+exit:
+
+ return 0;
+}