/* file: ClockSyncClient.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 */ #include #include "ClockSync.h" /* This program is inspired by "Time, Clocks, and the Ordering of Events in a Distributed System" by Lamport. After running the server portion of this program on two hosts, start up this client on both. The clients will synchronize every five seconds. */ static ClockSyncReplyMessage reply; static ClockSyncRequestMessage request; static boolean_t initialize; static mach_port_t clock; static mach_port_t replyPort; static mach_port_t servicePort; static rt_priority_data_t priority; void Synchronize () { kern_return_t status; timespec_t startTime; timespec_t endTime; timespec_t latency; if (initialize) { /* in order for Basic Priority Inheritance to work properly, RT-IPC has to know the set of potential receivers on the port in question */ RT_PORT_ASSOCIATE (mach_thread_self (), replyPort, priority, status); initialize = FALSE; } /* we take half of the round-trip time as an estimate of Lamport's minimum delay, mu[m] */ READ_TIME (clock, startTime, status); SEND_RT_MESSAGE (servicePort, request.header, status); RECEIVE_RT_MESSAGE (replyPort, reply.header, status); /* to work correctly, the rest of the loop should be executed atomically with the receive, but we're not going to fool with disabling interrupts here */ READ_TIME (clock, endTime, status); /* compute the communication latency to estimate mu[m] */ latency = endTime; timespec_sub (latency, startTime); latency.nanoseconds = ((latency.seconds % 2) * NSEC_PER_SEC + (latency.nanoseconds / 2)); /* estimate the other host's clock value when this message was sent */ timespec_add (reply.timeStamp, latency); /* "IR2: Upon receiving a message m at physical time t', process P[j] sets C[j] equal to maximum (C[j](t' - 0), T[m] + mu[m])." */ if (timespec_ge (reply.timeStamp, endTime)) { /* Note: we may not be able to set the time for all hardware clocks, particularly the one provided by the STAT! timer board */ status = clock_set_time (clock, &reply.timeStamp); if (status != KERN_SUCCESS) MACH_CRASH ("clock_set_time", status); #ifdef DEBUG printf ("reset time from "); timespec_print (stdout, endTime); printf (" to "); timespec_print (stdout, reply.timeStamp); putchar ('\n'); #endif } } void main (int argc, char *argv []) { kern_return_t status; rt_mach_port_attr_t portAttrs; rt_thread_attr_data_t threadAttrs; thread_t thread; timespec_t deadline; timespec_t period; if (argc != 2) { puts ("usage: ts hostname"); exit (-1); } OPEN_RT_CLOCK (clock, status); /* 100 millisecond deadline for scheduling purposes only--we don't enforce this */ timespec_init_msec (deadline, 100); /* five second period */ timespec_init_sec (period, 5); INIT_RT_PRIORITY_ATTR (priority, deadline, period, CRITICALITY); INIT_RT_MESSAGE_HEADER (request, request.header, request.descriptor, 0, priority.rt_deadline, priority.rt_period, CRITICALITY); INIT_RT_MESSAGE_HEADER (reply, reply.header, reply.descriptor, 0, priority.rt_deadline, priority.rt_period, 0); /* create the client's reply port and locate where the server is */ INIT_RT_PORT_ATTR (portAttrs, sizeof (ClockSyncReplyMessage), 1, DISP_PRI, PRI_BPI, HANDOFF_MSG); CREATE_RT_PORT (replyPort, portAttrs, status); request.header.msgh_local_port = replyPort; status = netname_look_up (name_server_port, argv [1], "ClockSyncServer", &servicePort); if (status != KERN_SUCCESS) { printf ("problems contacting time server at \"%s\"\n", argv [1]); MACH_CRASH ("netname_look_up", status); } /* since main () isn't easily made re-entrant, we'll spawn off a separate periodic real-time thread to do the synchronization on a regular basis */ INIT_RT_THREAD_ATTR (threadAttrs, priority.rt_deadline, priority.rt_period, CRITICALITY, FALSE, Synchronize, NULL, vm_page_size, status); initialize = TRUE; CREATE_RT_THREAD (thread, threadAttrs, status); /* terminate main--it's served its purpose */ status = thread_terminate (mach_thread_self ()); if (status != KERN_SUCCESS) MACH_CRASH ("thread_terminate", status); assert (FALSE); }