/* -*- 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 */