зеркало из https://github.com/mozilla/pjs.git
367 строки
9.5 KiB
C
367 строки
9.5 KiB
C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.1 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
/*
|
|
** File: event.c
|
|
** Description: Test functions in plevent.c
|
|
**
|
|
** This test creates a number of threads. Each thread sends
|
|
** an event to the next higher numbered (in creation order)
|
|
** thread. On receipt of that event, that thread in turn
|
|
** sends an event to the next thread until the event goes
|
|
** around the circle of threads. The test self terminates
|
|
** after a predetermined number of events have circled the
|
|
** circle of threads.
|
|
**
|
|
**
|
|
** Internal Design
|
|
**
|
|
** TState:
|
|
** TState is the Event Handler State Strucuture. It
|
|
** contains the state for an EventThread. There is one TState
|
|
** structure per thread running in the EventThread()
|
|
** function.
|
|
**
|
|
** TestEvents():
|
|
** The TestEvent() function is the "real main()" function of
|
|
** this test. The function creates 'numThreads' instances of
|
|
** a thread running the EventThread() function. He then waits
|
|
** for these threads to complete.
|
|
**
|
|
** EventThread():
|
|
** The EventThread() function runs in the context of a thread
|
|
** instance created by TestEvents(). The function processes
|
|
** events from threads running the same function. An event is
|
|
** created and sent around the ring
|
|
**
|
|
** EventHandler():
|
|
** The EventHandler() function processes events received by
|
|
** EventThread(). His job is to create and send a new event
|
|
** to the next EventThread() thread in the ring of threads.
|
|
**
|
|
** EventDestructor():
|
|
** The EventDestructor() function disposes of the event.
|
|
** Required by the protocol for PLEvent.
|
|
**
|
|
**
|
|
*/
|
|
|
|
#include "nspr.h"
|
|
#include "plgetopt.h"
|
|
#include "plevent.h"
|
|
|
|
/*
|
|
** TState -- Event Handler State Structure
|
|
**
|
|
*/
|
|
typedef struct TState
|
|
{
|
|
PRIntn threadIndex; /* thread number. [0..n] */
|
|
PRIntn quitSwitch; /* when 0, running; when 1, order to quit */
|
|
PLEventQueue *eventQueue; /* event queue for this thread */
|
|
} TState;
|
|
|
|
/*
|
|
** Thread Event structure
|
|
*/
|
|
typedef struct TEvent
|
|
{
|
|
PLEvent plEvent;
|
|
PRIntn tIndex; /* Thread index of thread receiving event */
|
|
PRIntn quitSwitch; /* when 0, keep going; when 1 set TState[me].quitSwitch = 1 */
|
|
} TEvent;
|
|
|
|
static void PR_CALLBACK EventHandler( TEvent *ep );
|
|
static void PR_CALLBACK EventDestructor( TEvent *ep );
|
|
PR_EXTERN(PRIntn) TestEvents( void );
|
|
|
|
/* --- Global data ---------------------------------------*/
|
|
|
|
static PRIntn failed = 0; /* when 1, indicates test failed */
|
|
static PRLogModuleInfo *lm; /* LogModule "test" */
|
|
|
|
static PRIntn numThreads = 4; /* Number of threads in the ring */
|
|
static PRIntn iterations = 7; /* Number of iterations per thread */
|
|
|
|
static PRIntn iterationCount = 0; /* Counting times around the ring */
|
|
|
|
static PRIntn activeThreads = 0; /* Number of active threads */
|
|
static TState *tsa = NULL; /* array of all TStates */
|
|
static PRMonitor *mon;
|
|
static PRBool useMonitoredQueue = PR_FALSE;
|
|
|
|
/*
|
|
** SendEvent() -- Send an event to the next thread
|
|
**
|
|
*/
|
|
static void SendEvent(
|
|
PRIntn threadIndex,
|
|
PRIntn quitSwitch
|
|
)
|
|
{
|
|
PLEventQueue *eq = (tsa+threadIndex)->eventQueue;
|
|
TEvent *event = PR_NEWZAP( TEvent );
|
|
|
|
PR_ASSERT( event != NULL );
|
|
|
|
PR_LOG( lm, PR_LOG_NOTICE, ("SendEvent: event: %p, threadIndex: %d quitSwitch: %d", event, threadIndex, quitSwitch ));
|
|
PL_ENTER_EVENT_QUEUE_MONITOR( eq );
|
|
|
|
PL_InitEvent( &event->plEvent, 0,
|
|
(PLHandleEventProc)EventHandler,
|
|
(PLDestroyEventProc)EventDestructor );
|
|
event->quitSwitch = quitSwitch;
|
|
event->tIndex = threadIndex;
|
|
|
|
PL_PostEvent( eq, &event->plEvent );
|
|
PL_EXIT_EVENT_QUEUE_MONITOR( eq );
|
|
return;
|
|
} /* end SendEvent() */
|
|
|
|
|
|
/*
|
|
** EventHandler() -- Handle events posted from EventThread()
|
|
**
|
|
** if message says quit
|
|
** Tell my thread to quit.
|
|
** create an event, if I quit, pass it on.
|
|
** PostEvent( TEvent.threadIndex+1)
|
|
**
|
|
*/
|
|
static void PR_CALLBACK EventHandler( TEvent *ep )
|
|
{
|
|
TState *ts = tsa+(ep->tIndex);
|
|
PRIntn ndx;
|
|
|
|
PR_LOG( lm, PR_LOG_NOTICE, ("EventHandler: %p, ti: %d", ep, ep->tIndex ));
|
|
if ( ep->quitSwitch == PR_TRUE )
|
|
ts->quitSwitch = PR_TRUE;
|
|
|
|
if ( ts->threadIndex == 0 )
|
|
{
|
|
if ( iterationCount++ >= iterations )
|
|
ep->quitSwitch = PR_TRUE;
|
|
}
|
|
/*
|
|
** Calculate thread index of the next thread to get an event,
|
|
** then send him the event.
|
|
*/
|
|
ndx = ep->tIndex +1;
|
|
if ( ndx >= numThreads )
|
|
ndx = 0;
|
|
|
|
SendEvent( ndx, ep->quitSwitch );
|
|
|
|
return;
|
|
} /* end EventHandler() */
|
|
|
|
/*
|
|
** EventDestructor() -- Free the event handled in EventHandler()
|
|
**
|
|
** free(the event)
|
|
**
|
|
*/
|
|
static void PR_CALLBACK EventDestructor( TEvent *ep )
|
|
{
|
|
PR_LOG( lm, PR_LOG_NOTICE, ("EventDestructor: event: %p", ep ));
|
|
PR_Free( ep );
|
|
return;
|
|
} /* end EventDestructor() */
|
|
|
|
/*
|
|
** EventThread() -- Drive events around the ring of threads
|
|
**
|
|
** do
|
|
** WaitforEvent()
|
|
** HandleEvent()
|
|
** PR_LOG( the event)
|
|
** until told to quit
|
|
**
|
|
*/
|
|
static void EventThread( void *arg )
|
|
{
|
|
TEvent *ep;
|
|
TState *ts = (TState *)arg;
|
|
|
|
|
|
while ( ts->eventQueue == 0 )
|
|
PR_Sleep( PR_MillisecondsToInterval(100));
|
|
|
|
do {
|
|
ep = (TEvent *)PL_WaitForEvent( ts->eventQueue );
|
|
PL_HandleEvent( (PLEvent *)ep );
|
|
PR_LOG( lm, PR_LOG_NOTICE,("EventThread() Handled event: %d", ts->threadIndex ));
|
|
} while( ts->quitSwitch == 0 );
|
|
|
|
PR_EnterMonitor( mon );
|
|
activeThreads--;
|
|
PR_Notify( mon );
|
|
PR_ExitMonitor( mon );
|
|
|
|
PR_LOG( lm, PR_LOG_NOTICE,("EventThread() Exit: %d", ts->threadIndex ));
|
|
|
|
return;
|
|
} /* end EventThread() */
|
|
|
|
/*
|
|
** TestEvents() --
|
|
**
|
|
** Create array of TEvents
|
|
** CreateThreads of EventThreads
|
|
** PostEvent() to the first EventThread
|
|
** monitor for completion.
|
|
** report results.
|
|
**
|
|
*/
|
|
PR_IMPLEMENT( PRIntn ) TestEvents( void )
|
|
{
|
|
PRIntn i;
|
|
|
|
lm = PR_NewLogModule( "test" );
|
|
PR_LOG( lm, PR_LOG_NOTICE,("TestEvent(): Starting" ));
|
|
mon = PR_NewMonitor();
|
|
|
|
/*
|
|
** Allocate array of all TEvents
|
|
*/
|
|
tsa = PR_Calloc( numThreads ,sizeof(TState));
|
|
|
|
/*
|
|
** Initialize this event queue and create its thead
|
|
*/
|
|
PR_ASSERT( tsa != NULL );
|
|
activeThreads = numThreads;
|
|
for ( i = 0 ; i < numThreads; i++ )
|
|
{
|
|
PRThread *me;
|
|
PLEventQueue *eq;
|
|
|
|
(tsa +i)->threadIndex = i;
|
|
(tsa +i)->quitSwitch = 0;
|
|
me = PR_CreateThread( PR_USER_THREAD,
|
|
EventThread,
|
|
(void *)(tsa +i),
|
|
PR_PRIORITY_NORMAL,
|
|
PR_LOCAL_THREAD,
|
|
PR_UNJOINABLE_THREAD,
|
|
0 );
|
|
if ( me == NULL )
|
|
{
|
|
PR_LOG( lm, PR_LOG_ERROR, ("TestEvents: Can't create thread %d", i ));
|
|
exit(1);
|
|
}
|
|
|
|
if ( useMonitoredQueue == PR_TRUE )
|
|
eq = PL_CreateMonitoredEventQueue( "EventThread", me );
|
|
else
|
|
eq = PL_CreateNativeEventQueue( "EventThread", me );
|
|
PR_ASSERT( eq != NULL );
|
|
|
|
(tsa+i)->eventQueue = eq;
|
|
}
|
|
|
|
|
|
/*
|
|
** Post and event to the first thread in the ring
|
|
** to get things started.
|
|
*/
|
|
SendEvent( 0, 0 );
|
|
/*
|
|
** Wait for all threads to exit
|
|
*/
|
|
PR_EnterMonitor( mon );
|
|
while ( activeThreads > 0 )
|
|
PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
|
|
PR_ExitMonitor( mon );
|
|
|
|
/*
|
|
** Release assets: event queue for each thread, the list of TStates.
|
|
*/
|
|
for ( i = 0 ; i < numThreads; i++ )
|
|
{
|
|
PL_DestroyEventQueue( (tsa +i)->eventQueue );
|
|
}
|
|
PR_Free( tsa );
|
|
|
|
/*
|
|
** All done! Log completion and return
|
|
*/
|
|
PR_LOG( lm, PR_LOG_NOTICE,("TestEvent(): Ending" ));
|
|
return 0;
|
|
} /* end TestEvents() */
|
|
|
|
|
|
|
|
|
|
static void Help( void )
|
|
{
|
|
printf( "Event: Help\n"
|
|
"syntax: event [-d][-h]\n"
|
|
"where: -h gives this message\n"
|
|
" -d sets debug mode (a no-op here)\n"
|
|
);
|
|
} /* end Help() */
|
|
|
|
PRIntn main(PRIntn argc, char **argv )
|
|
{
|
|
PLOptStatus os;
|
|
PLOptState *opt = PL_CreateOptState(argc, argv, "dhmt:i:");
|
|
|
|
|
|
while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
|
|
{
|
|
if (PL_OPT_BAD == os) continue;
|
|
switch (opt->option)
|
|
{
|
|
case 'm': /* use monitored event queue */
|
|
useMonitoredQueue = PR_TRUE;
|
|
break;
|
|
case 'd': /* debug mode (noop) */
|
|
break;
|
|
case 't': /* needs guidance */
|
|
numThreads = atol(opt->value);
|
|
break;
|
|
case 'i': /* needs guidance */
|
|
iterations = atol(opt->value);
|
|
break;
|
|
case 'h': /* needs guidance */
|
|
default:
|
|
Help();
|
|
return 2;
|
|
}
|
|
}
|
|
PL_DestroyOptState(opt);
|
|
|
|
TestEvents();
|
|
/*
|
|
** Evaluate results and exit
|
|
*/
|
|
if (failed)
|
|
{
|
|
printf("FAILED\n");
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
printf("PASSED\n");
|
|
return(0);
|
|
}
|
|
} /* end main() */
|
|
|
|
/* end event.c */
|