[m-rev.] for review: deep profiler redesign

Zoltan Somogyi zs at cs.mu.OZ.AU
Mon Nov 11 16:42:39 AEDT 2002


For review by Fergus, the following is the contents of a proposed new file,
deep_profiler/DESIGN. The code changes it describes are not yet complete;
I will post them later. I am seeking feedback on the technical stuff here,
not the English; that will come later.

Zoltan.

This file describes, explains and justifies the changes made in the design
of the deep profiler since the deep profiler paper.

The mdprof_cgi program is the core of the deep profiler. Every web page the
user looks at in a profile viewing session is generated by a separate
invocation of mdprof_cgi. We don't have a choice about this; the design
of the CGI interface to web servers dictates this mode of operation.
The CGI interface also requires each invocation of mdprof_cgi to exit
when it finishes generating a web page; the web server doesn't know
that the web page is complete until the program generating it exits.

Unfortunately, reading and processing a deep profiling data file takes a
significant amount of time, so we don't want to reread the profiling data file
on every query. The solution we have adopted has mdprof_cgi sometimes
forking into two processes after it has generated a web page. The original
process (the parent) exits, to allow the web browser to display the generated
page. The child process sticks around as a server, processing queries from
later invocations of mdprof_cgi that specify the same profiling data file.
This server process communicates with these later invocations through a pair
of named pipes, whose names include the name of the profiling data file
(in a mangled form which translates /s to other characters).

This design has an inherent race condition when two mdprof_cgi processes
are created close together in time, they specify the same profiling data file,
and no server process for that data file exists. (If they specify different
profiling data files, then they are handled independently; if there is already
a server process for that data file, they will both send their queries to it.)
The problem is that the two processes compete to become the server.

The solution we adopt is to create a critical section, a piece of code that can
be executed by only one mdprof_cgi process at a time. To be maximally portable,
we use the `open' system call with the O_CREAT and O_EXCL flags, which make
the open call fail if the file we want to create already exists. Since the rate
at which we get new CGI requests is limited by human typing and/or clicking
speed, we can tolerate the overhead of this method of gaining mutual exclusion.
Releasing the mutual exclusion involves simply removing the lock file.

The code in the critical section includes both checking for the prior existence
of a server (which consists of checking for the existence of the two named
pipes) and, in the absence of an existing server process, the creation of the
named pipes and the commitment to become the server behind those pipes.
This solves the race condition described above: whichever mdprof_cgi process
gains entry to the critical section first will become the server, and all other
mdprof_cgi processes will send their queries to it (i.e. they will become
clients of the server).

Of course, we don't want server processes to live and thus consume resources
forever. The server process therefore has a timeout: it deletes its pipes
and exits when it has been idle for a given amount of time. The timeout
sets up another race condition, since there is a time window between the
delivery of the timeout alarm signal and the deletion of the pipes. If another
mdprof_cgi process tests for the existence of the pipes during this interval,
it could find that they do exist, and therefore commit to becoming a client,
only to find that either the pipes or the process behind don't exist when it
actually tries to send its query to the server.

The solution we use to eliminate this race condition has two parts. First,
we make the timeout code in the server get mutual exclusion on the same lock
file that we use to solve client/client races, thus ensuring that all
operations that create or delete the pipes are guarded by the same mutex.
This is necessary because we use the existence of the pipes to denote the
existence of a server process committed to serving queries on the associated
profiling data file. However, this doesn't prevent the server from getting
the mutex, deleting the pipes and exiting immediately another mdprof_cgi
process got the mutex, found that the pipes existed, committed to becoming
a client, and released the mutex. Therefore inside the critical section
in the timeout code, we check for the existence of waiting clients, and
abort the timeout if any exist. Since all operations on one end of a named
pipe block until another process performs the complementary operation on
the other end, we cannot use tests on the pipes to check for waiting clients.
Instead, each client creates a file with a known prefix to indicate that it
wants to use the services of a server, if one already exists. Would-be clients
create this file before releasing the mutex (actually, before even getting
the mutex) and do not delete it until they exit. (Unless they become the server
instead, in which case they delete it when they make that decision.)
The server aborts the timeout if it finds any of these `want' files.

On a very slow machine, it is possible for the server to abort a timeout
because of a want file that its creator is about to delete just before exiting.
However, this is OK, because when the server aborts its timeout, it sets up
another timeout, so the exit of the server is not delayed indefinitely.

The following is a high level description of mdprof_cgi in pseudocode:

	create "want" file
	get mutual exclusion (i.e. create mutex file)
	if the named pipes exist
		commit to being a client
		send the query to the server through the toserver pipe
		release mutual exclusion (i.e. delete mutex file)
		receive the result from the server through the fromserver pipe
		remove "want" file
	else
		read the profile data file and preprocess it
		if the reading and preprocessing found any errors
			report the error 
			release mutual exclusion (i.e. delete mutex file)
			remove "want" file
		else
			report the result of the initial query
			commit to being a server
			create the named pipes
			release mutual exclusion (i.e. delete mutex file)
			remove "want" file
			loop forever
				setup timeout
				receive query on toserver pipe
				send result to fromserver pipe
			done
		fi
	fi

The initial design of the deep profiler had two separate programs. The old
mdprof_cgi was strictly a client: it *always* sent the query to a process
running the other program, mdprof_server. The problem with this approach
is that having mdprof_cgi invoke mdprof_server via fork and exec made
it very difficult to debug mdprof_server and its interaction with mdprof_cgi,
and to control the level of detail in the diagnostics we print about any
problems we discover (whether in the profiling data file or in the manipulation
of the underlying infrastructure, e.g. the named pipes). The current design
allows a single debugging session to debug any part of the deep profiler
simply by specifying an option that inhibits the fork that normally detaches
the server process, and we can set option flags controlling diagnostics
directly, instead of setting the server's flags indirectly by specifying flags
to the client.
--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list