1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
28 #include "GenLinkedList.h"
29 #include "DebugServices.h"
32 typedef struct PollSource_struct
40 mDNSPollSocketCallback socket
;
41 mDNSPollEventCallback event
;
44 struct Worker_struct
*worker
;
45 struct PollSource_struct
*next
;
50 typedef struct Worker_struct
52 HANDLE thread
; // NULL for main worker
53 unsigned id
; // 0 for main worker
55 HANDLE start
; // NULL for main worker
56 HANDLE stop
; // NULL for main worker
57 BOOL done
; // Not used for main worker
60 PollSource
*sources
[ MAXIMUM_WAIT_OBJECTS
];
61 HANDLE handles
[ MAXIMUM_WAIT_OBJECTS
];
63 struct Worker_struct
*next
;
67 typedef struct Poll_struct
71 GenLinkedList sources
;
74 GenLinkedList workers
;
75 HANDLE workerHandles
[ MAXIMUM_WAIT_OBJECTS
];
85 mDNSlocal mStatus
PollSetup();
86 mDNSlocal mStatus
PollRegisterSource( PollSource
*source
);
87 mDNSlocal
void PollUnregisterSource( PollSource
*source
);
88 mDNSlocal mStatus
PollStartWorkers();
89 mDNSlocal mStatus
PollStopWorkers();
90 mDNSlocal
void PollRemoveWorker( Worker
*worker
);
97 mDNSlocal mStatus
WorkerInit( Worker
*worker
);
98 mDNSlocal
void WorkerFree( Worker
*worker
);
99 mDNSlocal
void WorkerRegisterSource( Worker
*worker
, PollSource
*source
);
100 mDNSlocal
int WorkerSourceToIndex( Worker
*worker
, PollSource
*source
);
101 mDNSlocal
void WorkerUnregisterSource( Worker
*worker
, PollSource
*source
);
102 mDNSlocal
void WorkerDispatch( Worker
*worker
);
103 mDNSlocal
void CALLBACK
WorkerWakeupNotification( HANDLE event
, void *context
);
104 mDNSlocal
unsigned WINAPI
WorkerMain( LPVOID inParam
);
108 ShiftDown( void * arr
, size_t arraySize
, size_t itemSize
, int index
)
110 memmove( ( ( unsigned char* ) arr
) + ( ( index
- 1 ) * itemSize
), ( ( unsigned char* ) arr
) + ( index
* itemSize
), ( arraySize
- index
) * itemSize
);
114 #define DEBUG_NAME "[mDNSWin32] "
115 #define gMDNSRecord mDNSStorage
116 mDNSlocal Poll gPoll
= { mDNSfalse
, NULL
};
118 #define LogErr( err, FUNC ) LogMsg( "%s:%d - %s failed: %d\n", __FUNCTION__, __LINE__, FUNC, err );
122 mDNSPollRegisterSocket( SOCKET socket
, int networkEvents
, mDNSPollSocketCallback callback
, void *context
)
124 PollSource
*source
= NULL
;
125 HANDLE event
= INVALID_HANDLE_VALUE
;
126 mStatus err
= mStatus_NoError
;
131 require_noerr( err
, exit
);
134 source
= malloc( sizeof( PollSource
) );
135 require_action( source
, exit
, err
= mStatus_NoMemoryErr
);
137 event
= WSACreateEvent();
138 require_action( event
, exit
, err
= mStatus_NoMemoryErr
);
140 err
= WSAEventSelect( socket
, event
, networkEvents
);
141 require_noerr( err
, exit
);
143 source
->socket
= socket
;
144 source
->handle
= event
;
145 source
->callback
.socket
= callback
;
146 source
->context
= context
;
148 err
= PollRegisterSource( source
);
149 require_noerr( err
, exit
);
153 if ( err
!= mStatus_NoError
)
155 if ( event
!= INVALID_HANDLE_VALUE
)
157 WSACloseEvent( event
);
160 if ( source
!= NULL
)
171 mDNSPollUnregisterSocket( SOCKET socket
)
175 for ( source
= gPoll
.sources
.Head
; source
; source
= source
->next
)
177 if ( source
->socket
== socket
)
185 WSACloseEvent( source
->handle
);
186 PollUnregisterSource( source
);
193 mDNSPollRegisterEvent( HANDLE event
, mDNSPollEventCallback callback
, void *context
)
195 PollSource
*source
= NULL
;
196 mStatus err
= mStatus_NoError
;
201 require_noerr( err
, exit
);
204 source
= malloc( sizeof( PollSource
) );
205 require_action( source
, exit
, err
= mStatus_NoMemoryErr
);
207 source
->socket
= INVALID_SOCKET
;
208 source
->handle
= event
;
209 source
->callback
.event
= callback
;
210 source
->context
= context
;
212 err
= PollRegisterSource( source
);
213 require_noerr( err
, exit
);
217 if ( err
!= mStatus_NoError
)
219 if ( source
!= NULL
)
230 mDNSPollUnregisterEvent( HANDLE event
)
234 for ( source
= gPoll
.sources
.Head
; source
; source
= source
->next
)
236 if ( source
->handle
== event
)
244 PollUnregisterSource( source
);
251 mDNSPoll( DWORD msec
)
253 mStatus err
= mStatus_NoError
;
255 if ( gPoll
.numWorkers
> 0 )
257 err
= PollStartWorkers();
258 require_noerr( err
, exit
);
261 gPoll
.main
.result
= WaitForMultipleObjects( gPoll
.main
.numSources
, gPoll
.main
.handles
, FALSE
, msec
);
262 err
= translate_errno( ( gPoll
.main
.result
!= WAIT_FAILED
), ( mStatus
) GetLastError(), kUnknownErr
);
263 if ( err
) LogErr( err
, "WaitForMultipleObjects()" );
264 require_action( gPoll
.main
.result
!= WAIT_FAILED
, exit
, err
= ( mStatus
) GetLastError() );
266 if ( gPoll
.numWorkers
> 0 )
268 err
= PollStopWorkers();
269 require_noerr( err
, exit
);
272 WorkerDispatch( &gPoll
.main
);
283 mStatus err
= mStatus_NoError
;
287 memset( &gPoll
, 0, sizeof( gPoll
) );
289 InitLinkedList( &gPoll
.sources
, offsetof( PollSource
, next
) );
290 InitLinkedList( &gPoll
.workers
, offsetof( Worker
, next
) );
292 gPoll
.wakeup
= CreateEvent( NULL
, TRUE
, FALSE
, NULL
);
293 require_action( gPoll
.wakeup
, exit
, err
= mStatus_NoMemoryErr
);
295 err
= WorkerInit( &gPoll
.main
);
296 require_noerr( err
, exit
);
298 gPoll
.setup
= mDNStrue
;
308 PollRegisterSource( PollSource
*source
)
310 Worker
*worker
= NULL
;
311 mStatus err
= mStatus_NoError
;
313 AddToTail( &gPoll
.sources
, source
);
316 // First check our main worker. In most cases, we won't have to worry about threads
318 if ( gPoll
.main
.numSources
< MAXIMUM_WAIT_OBJECTS
)
320 WorkerRegisterSource( &gPoll
.main
, source
);
324 // Try to find a thread to use that we've already created
326 for ( worker
= gPoll
.workers
.Head
; worker
; worker
= worker
->next
)
328 if ( worker
->numSources
< MAXIMUM_WAIT_OBJECTS
)
330 WorkerRegisterSource( worker
, source
);
335 // If not, then create a worker and make a thread to run it in
339 worker
= ( Worker
* ) malloc( sizeof( Worker
) );
340 require_action( worker
, exit
, err
= mStatus_NoMemoryErr
);
342 memset( worker
, 0, sizeof( Worker
) );
344 worker
->start
= CreateEvent( NULL
, FALSE
, FALSE
, NULL
);
345 require_action( worker
->start
, exit
, err
= mStatus_NoMemoryErr
);
347 worker
->stop
= CreateEvent( NULL
, FALSE
, FALSE
, NULL
);
348 require_action( worker
->stop
, exit
, err
= mStatus_NoMemoryErr
);
350 err
= WorkerInit( worker
);
351 require_noerr( err
, exit
);
353 // Create thread with _beginthreadex() instead of CreateThread() to avoid
354 // memory leaks when using static run-time libraries.
355 // See <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/createthread.asp>.
357 worker
->thread
= ( HANDLE
) _beginthreadex_compat( NULL
, 0, WorkerMain
, worker
, 0, &worker
->id
);
358 err
= translate_errno( worker
->thread
, ( mStatus
) GetLastError(), kUnknownErr
);
359 require_noerr( err
, exit
);
361 AddToTail( &gPoll
.workers
, worker
);
362 gPoll
.workerHandles
[ gPoll
.numWorkers
++ ] = worker
->stop
;
364 WorkerRegisterSource( worker
, source
);
372 WorkerFree( worker
);
380 PollUnregisterSource( PollSource
*source
)
382 RemoveFromList( &gPoll
.sources
, source
);
385 WorkerUnregisterSource( source
->worker
, source
);
393 mStatus err
= mStatus_NoError
;
396 dlog( kDebugLevelChatty
, DEBUG_NAME
"starting workers\n" );
398 worker
= gPoll
.workers
.Head
;
402 Worker
*next
= worker
->next
;
404 if ( worker
->numSources
== 1 )
406 PollRemoveWorker( worker
);
410 dlog( kDebugLevelChatty
, DEBUG_NAME
"waking up worker\n" );
412 ok
= SetEvent( worker
->start
);
413 err
= translate_errno( ok
, ( mStatus
) GetLastError(), kUnknownErr
);
414 if ( err
) LogErr( err
, "SetEvent()" );
418 PollRemoveWorker( worker
);
425 err
= mStatus_NoError
;
437 mStatus err
= mStatus_NoError
;
439 dlog( kDebugLevelChatty
, DEBUG_NAME
"stopping workers\n" );
441 ok
= SetEvent( gPoll
.wakeup
);
442 err
= translate_errno( ok
, ( mStatus
) GetLastError(), kUnknownErr
);
443 if ( err
) LogErr( err
, "SetEvent()" );
445 // Wait For 5 seconds for all the workers to wake up
447 result
= WaitForMultipleObjects( gPoll
.numWorkers
, gPoll
.workerHandles
, TRUE
, 5000 );
448 err
= translate_errno( ( result
!= WAIT_FAILED
), ( mStatus
) GetLastError(), kUnknownErr
);
449 if ( err
) LogErr( err
, "WaitForMultipleObjects()" );
451 ok
= ResetEvent( gPoll
.wakeup
);
452 err
= translate_errno( ok
, ( mStatus
) GetLastError(), kUnknownErr
);
453 if ( err
) LogErr( err
, "ResetEvent()" );
455 for ( worker
= gPoll
.workers
.Head
; worker
; worker
= worker
->next
)
457 WorkerDispatch( worker
);
460 err
= mStatus_NoError
;
467 PollRemoveWorker( Worker
*worker
)
474 dlog( kDebugLevelChatty
, DEBUG_NAME
"removing worker %d\n", worker
->id
);
476 RemoveFromList( &gPoll
.workers
, worker
);
478 // Remove handle from gPoll.workerHandles
480 for ( i
= 0; i
< gPoll
.numWorkers
; i
++ )
482 if ( gPoll
.workerHandles
[ i
] == worker
->stop
)
484 ShiftDown( gPoll
.workerHandles
, gPoll
.numWorkers
, sizeof( gPoll
.workerHandles
[ 0 ] ), i
+ 1 );
492 // Cause the thread to exit.
494 ok
= SetEvent( worker
->start
);
495 err
= translate_errno( ok
, ( OSStatus
) GetLastError(), kUnknownErr
);
496 if ( err
) LogErr( err
, "SetEvent()" );
498 result
= WaitForSingleObject( worker
->thread
, 5000 );
499 err
= translate_errno( result
!= WAIT_FAILED
, ( OSStatus
) GetLastError(), kUnknownErr
);
500 if ( err
) LogErr( err
, "WaitForSingleObject()" );
502 if ( ( result
== WAIT_FAILED
) || ( result
== WAIT_TIMEOUT
) )
504 ok
= TerminateThread( worker
->thread
, 0 );
505 err
= translate_errno( ok
, ( OSStatus
) GetLastError(), kUnknownErr
);
506 if ( err
) LogErr( err
, "TerminateThread()" );
509 CloseHandle( worker
->thread
);
510 worker
->thread
= NULL
;
512 WorkerFree( worker
);
517 WorkerRegisterSource( Worker
*worker
, PollSource
*source
)
519 source
->worker
= worker
;
520 worker
->sources
[ worker
->numSources
] = source
;
521 worker
->handles
[ worker
->numSources
] = source
->handle
;
522 worker
->numSources
++;
527 WorkerSourceToIndex( Worker
*worker
, PollSource
*source
)
531 for ( index
= 0; index
< ( int ) worker
->numSources
; index
++ )
533 if ( worker
->sources
[ index
] == source
)
539 if ( index
== ( int ) worker
->numSources
)
549 WorkerUnregisterSource( Worker
*worker
, PollSource
*source
)
551 int sourceIndex
= WorkerSourceToIndex( worker
, source
);
554 if ( sourceIndex
== -1 )
556 LogMsg( "WorkerUnregisterSource: source not found in list" );
560 delta
= ( worker
->numSources
- sourceIndex
- 1 );
562 // If this source is not at the end of the list, then move memory
566 ShiftDown( worker
->sources
, worker
->numSources
, sizeof( worker
->sources
[ 0 ] ), sourceIndex
+ 1 );
567 ShiftDown( worker
->handles
, worker
->numSources
, sizeof( worker
->handles
[ 0 ] ), sourceIndex
+ 1 );
570 worker
->numSources
--;
578 mDNSlocal
void CALLBACK
579 WorkerWakeupNotification( HANDLE event
, void *context
)
581 DEBUG_UNUSED( event
);
582 DEBUG_UNUSED( context
);
584 dlog( kDebugLevelChatty
, DEBUG_NAME
"Worker thread wakeup\n" );
589 WorkerDispatch( Worker
*worker
)
591 if ( worker
->result
== WAIT_FAILED
)
593 /* What should we do here? */
595 else if ( worker
->result
== WAIT_TIMEOUT
)
597 dlog( kDebugLevelChatty
, DEBUG_NAME
"timeout\n" );
601 DWORD waitItemIndex
= ( DWORD
)( ( ( int ) worker
->result
) - WAIT_OBJECT_0
);
602 PollSource
*source
= NULL
;
606 if ( waitItemIndex
>= worker
->numSources
)
608 LogMsg( "WorkerDispatch: waitItemIndex (%d) is >= numSources (%d)", waitItemIndex
, worker
->numSources
);
612 source
= worker
->sources
[ waitItemIndex
];
614 if ( source
->socket
!= INVALID_SOCKET
)
616 WSANETWORKEVENTS event
;
618 if ( WSAEnumNetworkEvents( source
->socket
, source
->handle
, &event
) == 0 )
620 source
->callback
.socket( source
->socket
, &event
, source
->context
);
624 source
->callback
.socket( source
->socket
, NULL
, source
->context
);
629 source
->callback
.event( source
->handle
, source
->context
);
640 WorkerInit( Worker
*worker
)
642 PollSource
*source
= NULL
;
643 mStatus err
= mStatus_NoError
;
645 require_action( worker
, exit
, err
= mStatus_BadParamErr
);
647 source
= malloc( sizeof( PollSource
) );
648 require_action( source
, exit
, err
= mStatus_NoMemoryErr
);
650 source
->socket
= INVALID_SOCKET
;
651 source
->handle
= gPoll
.wakeup
;
652 source
->callback
.event
= WorkerWakeupNotification
;
653 source
->context
= NULL
;
655 WorkerRegisterSource( worker
, source
);
664 WorkerFree( Worker
*worker
)
668 CloseHandle( worker
->start
);
669 worker
->start
= NULL
;
674 CloseHandle( worker
->stop
);
682 mDNSlocal
unsigned WINAPI
683 WorkerMain( LPVOID inParam
)
685 Worker
*worker
= ( Worker
* ) inParam
;
686 mStatus err
= mStatus_NoError
;
688 require_action( worker
, exit
, err
= mStatus_BadParamErr
);
690 dlog( kDebugLevelVerbose
, DEBUG_NAME
, "entering WorkerMain()\n" );
697 dlog( kDebugLevelChatty
, DEBUG_NAME
, "worker thread %d will wait on main loop\n", worker
->id
);
699 result
= WaitForSingleObject( worker
->start
, INFINITE
);
700 err
= translate_errno( ( result
!= WAIT_FAILED
), ( mStatus
) GetLastError(), kUnknownErr
);
701 if ( err
) { LogErr( err
, "WaitForSingleObject()" ); break; }
702 if ( worker
->done
) break;
704 dlog( kDebugLevelChatty
, DEBUG_NAME
"worker thread %d will wait on sockets\n", worker
->id
);
706 worker
->result
= WaitForMultipleObjects( worker
->numSources
, worker
->handles
, FALSE
, INFINITE
);
707 err
= translate_errno( ( worker
->result
!= WAIT_FAILED
), ( mStatus
) GetLastError(), kUnknownErr
);
708 if ( err
) { LogErr( err
, "WaitForMultipleObjects()" ); break; }
710 dlog( kDebugLevelChatty
, DEBUG_NAME
"worker thread %d did wait on sockets: %d\n", worker
->id
, worker
->result
);
712 ok
= SetEvent( gPoll
.wakeup
);
713 err
= translate_errno( ok
, ( mStatus
) GetLastError(), kUnknownErr
);
714 if ( err
) { LogErr( err
, "SetEvent()" ); break; }
716 dlog( kDebugLevelChatty
, DEBUG_NAME
, "worker thread %d preparing to sleep\n", worker
->id
);
718 ok
= SetEvent( worker
->stop
);
719 err
= translate_errno( ok
, ( mStatus
) GetLastError(), kUnknownErr
);
720 if ( err
) { LogErr( err
, "SetEvent()" ); break; }
723 dlog( kDebugLevelVerbose
, DEBUG_NAME
"exiting WorkerMain()\n" );