/* file: synctest.c */ /* author: MLehr */ /* * Copyright (C) 1994 * by the Rector and Board of Visitors of * the University of Virginia * * for more information: * Sanghyuk Son * Department of Computer Science * School of Engineering and Applied Science * University of Virginia * Charlottesville, VA 22903 */ /* The idea behind synctest is to determine what the differences in performance between Mach's lightweight spin_locks and RT-Mach's RT-Sync are, if there are any. The program creates two client real-time threads which contend for a single lock, globalLock, for a certain number of iterations. When the time is up, the program displays how long it took. */ #include "MachUseful.h" /* The definitions in MachUseful.h make prototyping RT-Mach programs easier. MachUseful.h contains: * #includes of all necessary RT-Mach headers, so the programmer doesn't have to hunt * macros to initialize RT-Mach data structures so the programmer doesn't have to remember the precise format of the data structures * macros to call RT-Mach functions check return values automatically especially useful for functions which don't have man pages no additional overhead */ /* We need the definitions for spin_locks which are given in cthreads.h. Unfortunately cthreads.h won't compile without errors in MK83g (it redefines any_t), so we hack our own here. */ #include "mycthreads.h" /* prototypes to keep gcc's -Wall happy */ int atoi (char *); void printf (char *, ...); void puts (char *); /******************************/ /* Depending on whether the program is to be compiled with conventional or real-time synchronization, we use different definitions for message-passing routines, e.g., LOCK () uses real-time mutex lock if USE_REAL_TIME is #defined, otherwise it uses a spin_lock lock. Many of these macro definitions are defined in terms of other macros from MachUseful.h. */ #ifdef USE_REAL_TIME /* use RT-Mach's RT-Sync */ typedef rt_mutex_t sb_lock_t; #define LOCK(l,exp,status) LOCK_MUTEX (l, exp, status); #define UNLOCK(l,status) UNLOCK_MUTEX (l, status); #else /* USE_REAL_TIME */ /* use Mach's C-Threads synchronization */ typedef spin_lock_t sb_lock_t; #define LOCK(l,exp,status) spin_lock (&(l)) #define UNLOCK(l,status) spin_unlock (&(l)) #endif /* USE_REAL_TIME */ /********************************/ #define ITERATIONS 0xffff typedef struct { mach_msg_header_t header; mach_msg_type_t descriptor; } BlockMsg; /******************************/ /* client data structures */ thread_t thread1; rt_thread_attr_data_t thread1Attr; boolean_t thread1Done; thread_t thread2; rt_thread_attr_data_t thread2Attr; boolean_t thread2Done; /* global data structures */ mach_port_t blockPort; /* to synchronize before the program ends */ sb_lock_t globalLock; /* both clients contend for this lock */ unsigned int counter; unsigned int iterations; /******************************/ /* DoIt () is the entry point for the two client threads. After initializing, each client contends for the lock until it has been acquired a certain number of times. At that point it sends a message back to the root thread signalling that it has finished. */ void DoIt (boolean_t *done) { kern_return_t status; BlockMsg msg; for (;;) { LOCK (globalLock, NULL, status); /* if we're done */ if (counter >= iterations) { /* send synchronizing message back to root thread */ INIT_MESSAGE_HEADER (msg, msg.header, msg.descriptor, 13); SEND_MESSAGE (blockPort, msg.header, status); UNLOCK (globalLock, status); /* client terminates here */ return; } counter++; UNLOCK (globalLock, status); } } /******************************/ void main (int argc, char *argv []) { kern_return_t status; BlockMsg msg; device_t realTimeClock; timespec_t startTime; timespec_t endTime; #ifdef USE_REAL_TIME rt_mutex_attr_data_t lockAttr; #endif if (argc == 1) iterations = ITERATIONS; else if (argc == 2) { iterations = atoi (argv [1]); if (iterations <= 0) { puts ("iterations must be a postive integer"); exit (-1); } } else { puts ("usage: synctest [iterations]"); exit (-1); } #ifdef USE_REAL_TIME puts ("using rt_mutex..."); #else puts ("using spin_locks..."); #endif puts ("opening clock device..."); counter = 0; timespec_init_zero (startTime); thread1Done = FALSE; thread2Done = FALSE; OPEN_RT_CLOCK (realTimeClock, status); puts ("initializing mutex variable..."); /* initialize the globalLock */ #ifdef USE_REAL_TIME INIT_RT_MUTEX_ATTR (lockAttr, PRI_BPI, startTime, startTime, 1); CREATE_MUTEX (globalLock, lockAttr, status); #else spin_lock_init (&globalLock); #endif /* create the synchronizing port */ puts ("creating synchronizing port..."); CREATE_PORT (blockPort, status); INIT_MESSAGE_HEADER (msg, msg.header, msg.descriptor, 13); /* start the client threads */ puts ("initializing threads..."); INIT_RT_THREAD_ATTR (thread1Attr, startTime, startTime, 12, FALSE, DoIt, &thread1Done, 8092, status); INIT_RT_THREAD_ATTR (thread2Attr, startTime, startTime, 12, FALSE, DoIt, &thread2Done, 8092, status); puts ("starting threads..."); /* lock the globalLock so that the clients have same chance at starting at the same moment */ LOCK (globalLock, NULL, status); CREATE_RT_THREAD (thread1, thread1Attr, status); CREATE_RT_THREAD (thread2, thread2Attr, status); /* start timing */ READ_TIME (realTimeClock, startTime, status); /* release the clients */ UNLOCK (globalLock, status); /* wait for two synchronizing messages--one from each client */ RECEIVE_MESSAGE (blockPort, msg.header, status); RECEIVE_MESSAGE (blockPort, msg.header, status); /* stop timing */ READ_TIME (realTimeClock, endTime, status); /* display how we did */ printf ("counter:\t%u\n", counter); printf ("end time:\t"); timespec_print (stdout, endTime); puts (" -"); printf ("start time:\t"); timespec_print (stdout, startTime); timespec_sub (endTime, startTime); printf ("\n====================================\ntotal time:\t"); timespec_print (stdout, endTime); printf ("\n====================================\nmean time:\t"); timespec_div (endTime, iterations); timespec_print (stdout, endTime); putchar ('\n'); exit (0); }