[Mono-list] [PATCH] robust mono-handle-d?

Dennis Haney davh@davh.dk
11 Jun 2002 17:19:32 +0200


--=-=-=

>>>>> "Dick" == Dick Porter <dick@ximian.com> writes:

Dick> On Fri, 2002-06-07 at 22:28, Dennis Haney wrote: They're called
Dick> "file descriptors" in Linux :)
>>  heh, thats the word(s) I was looking for all along ;) Shall I
>> change it?  If so it will be after the commit...

Dick> Yes please.

Done and patch attached with these changes..

Dick> This change shouldnt go in.  The daemon shouldn't quit if it's
Dick> still servicing clients, and it should clean up when it does
Dick> quit.
>>  assert does not quit (and never has in any implementation I've
>> seen), g_assert is implemented as:
>> 
>> #define g_assert(expr) G_STMT_START{ \ if (expr) { } else \ g_log
>> (G_LOG_DOMAIN, \ G_LOG_LEVEL_ERROR, \ "file %s: line %d (%s):
>> assertion failed: (%s)", \ __FILE__, \ __LINE__, \
>> __PRETTY_FUNCTION__, \ #expr); }G_STMT_END

Dick> In glib, it quits due to the call to g_log() with
Dick> G_LOG_LEVEL_ERROR severity.  In the standard C library, assert()
Dick> calls abort().

Hmm.. How could I miss something so fundamentally wrong?!? :( Well at
least I can say I learned something new ;)

Dick> - Dick

-- 
Dennis
use Inline C => qq{void p(char*g){
printf("Just Another %s Hacker\n",g);}};p("Perl");

--=-=-=
Content-Disposition: attachment; filename=mono_handle_d_diff1.diff

Index: daemon-messages.c
===================================================================
RCS file: /mono/mono/mono/io-layer/daemon-messages.c,v
retrieving revision 1.3
diff -u -b -B -r1.3 daemon-messages.c
--- daemon-messages.c	31 May 2002 09:54:14 -0000	1.3
+++ daemon-messages.c	11 Jun 2002 15:18:14 -0000
@@ -25,7 +25,7 @@
 
 /* Send request on fd, wait for response (called by applications, not
  * the daemon)
-*/
+ */
 void _wapi_daemon_request_response (int fd, WapiHandleRequest *req,
 				    WapiHandleResponse *resp)
 {
@@ -41,7 +41,7 @@
 	ret=send (fd, req, sizeof(WapiHandleRequest), MSG_NOSIGNAL);
 	if(ret!=sizeof(WapiHandleRequest)) {
 		if(errno==EPIPE) {
-			g_warning (G_GNUC_PRETTY_FUNCTION ": The handle daemon vanished!");
+			g_critical (G_GNUC_PRETTY_FUNCTION ": The handle daemon vanished!");
 			exit (-1);
 		} else {
 			g_warning (G_GNUC_PRETTY_FUNCTION ": Send error: %s",
@@ -53,7 +53,7 @@
 	ret=recv (fd, resp, sizeof(WapiHandleResponse), MSG_NOSIGNAL);
 	if(ret==-1) {
 		if(errno==EPIPE) {
-			g_warning (G_GNUC_PRETTY_FUNCTION ": The handle daemon vanished!");
+			g_critical (G_GNUC_PRETTY_FUNCTION ": The handle daemon vanished!");
 			exit (-1);
 		} else {
 			g_warning (G_GNUC_PRETTY_FUNCTION ": Send error: %s",
@@ -71,7 +71,9 @@
 	int ret;
 	
 	ret=recv (fd, req, sizeof(WapiHandleRequest), MSG_NOSIGNAL);
-	if(ret==-1) {
+	if(ret==-1 || ret != sizeof(WapiHandleRequest) {
+		/* make sure we dont do anything with this response */
+		req->type=WapiHandleRequestType_Error;
 #ifdef DEBUG
 		g_warning (G_GNUC_PRETTY_FUNCTION ": Recv error: %s",
 			   strerror (errno));
@@ -86,11 +88,11 @@
 	int ret;
 	
 	ret=send (fd, resp, sizeof(WapiHandleResponse), MSG_NOSIGNAL);
-	if(ret==-1) {
 #ifdef DEBUG
+	if(ret==-1 || ret != sizeof(WapiHandleResponse)) {
 		g_warning (G_GNUC_PRETTY_FUNCTION ": Send error: %s",
 			   strerror (errno));
-#endif
 		/* The next loop around poll() should tidy up */
 	}
+#endif
 }
Index: daemon-messages.h
===================================================================
RCS file: /mono/mono/mono/io-layer/daemon-messages.h,v
retrieving revision 1.2
diff -u -b -B -r1.2 daemon-messages.h
--- daemon-messages.h	9 May 2002 13:10:18 -0000	1.2
+++ daemon-messages.h	11 Jun 2002 15:18:14 -0000
@@ -13,6 +13,7 @@
 #include <mono/io-layer/wapi-private.h>
 
 typedef enum {
+	WapiHandleRequestType_Error,
 	WapiHandleRequestType_New,
 	WapiHandleRequestType_Open,
 	WapiHandleRequestType_Close,
Index: daemon.c
===================================================================
RCS file: /mono/mono/mono/io-layer/daemon.c,v
retrieving revision 1.4
diff -u -b -B -r1.4 daemon.c
--- daemon.c	31 May 2002 10:41:30 -0000	1.4
+++ daemon.c	11 Jun 2002 15:18:14 -0000
@@ -27,9 +27,13 @@
 
 #undef DEBUG
 
+/* Array to hold the filedescr. we are currently polling */
 static struct pollfd *pollfds=NULL;
+/* Keep track of the size and usage of pollfds */
 static int nfds=0, maxfds=0;
-static gpointer *handle_refs=NULL;
+/* Array to keep track of arrays of handles that belong to a given client */
+static guint32 **handle_refs=NULL;
+/* The sockey which we listen to new connections on */
 static int main_sock;
 
 /* Deletes the shared memory segment.  If we're exiting on error,
@@ -40,12 +44,23 @@
 	_wapi_shm_destroy ();
 }
 
+/*
+ * signal_handler:
+ * @unused: unused
+ *
+ * Called if daemon receives a SIGTERM or SIGINT
+ */
 static void signal_handler (int unused)
 {
 	cleanup ();
 	exit (-1);
 }
 
+/*
+ * startup:
+ *
+ * Bind signals and attach to shared memory
+ */
 static void startup (void)
 {
 	struct sigaction sa;
@@ -76,6 +91,15 @@
 		  time (NULL));
 }
 
+
+/*
+ * ref_handle:
+ * @idx: idx into pollfds
+ * @handle: handle to inc refcnt
+ *
+ * Increase ref count of handle for idx's filedescr. and the
+ * shm. Handle 0 is ignored.
+ */
 static void ref_handle (guint32 idx, guint32 handle)
 {
 	guint32 *open_handles=handle_refs[idx];
@@ -95,6 +119,15 @@
 #endif
 }
 
+/*
+ * unref_handle:
+ * @idx: idx into pollfds
+ * @handle: handle to inc refcnt
+ *
+ * Decrease ref count of handle for idx's filedescr. and the shm. If
+ * global ref count reaches 0 it is free'ed. Return TRUE if the local
+ * ref count is 0. Handle 0 is ignored.
+ */
 static gboolean unref_handle (guint32 idx, guint32 handle)
 {
 	guint32 *open_handles=handle_refs[idx];
@@ -104,6 +137,13 @@
 		return(FALSE);
 	}
 	
+	if (open_handles[handle] == 0) {
+                g_warning(G_GNUC_PRETTY_FUNCTION
+                          ": unref on %d called when ref was already 0", 
+                          handle);
+                return TRUE;
+        }
+
 	_wapi_shared_data->handles[handle].ref--;
 	open_handles[handle]--;
 	
@@ -140,13 +180,21 @@
 	return(destroy);
 }
 
+/*
+ * add_fd:
+ * @fd: Filehandle to add
+ *
+ * Add filedescriptor to the pollfds array, expand if necessary
+ */
 static void add_fd(int fd)
 {
 	if(nfds==maxfds) {
 		/* extend the array */
 		maxfds+=10;
+		/* no need to memset the extra memory, we init it
+		 * before use anyway */
 		pollfds=g_renew (struct pollfd, pollfds, maxfds);
-		handle_refs=g_renew (gpointer, handle_refs, maxfds);
+		handle_refs=g_renew (guint32*, handle_refs, maxfds);
 	}
 
 	pollfds[nfds].fd=fd;
@@ -158,6 +206,14 @@
 	nfds++;
 }
 
+/*
+ * rem_fd:
+ * @idx: idx into pollfds to remove
+ *
+ * Close filedescriptor and remove it from the pollfds array. Closes
+ * all handles that it may have open. If only main_sock is open, the
+ * daemon is shut down.
+ */
 static void rem_fd(int idx)
 {
 	guint32 *open_handles=handle_refs[idx], handle_count;
@@ -205,12 +261,28 @@
 	}
 }
 
+
+/*
+ * send_reply:
+ * @idx: idx into pollfds.
+ * @rest: Package to send
+ *
+ * Send a package to a client
+ */
 static void send_reply (guint32 idx, WapiHandleResponse *resp)
 {
 	/* send message */
 	_wapi_daemon_response (pollfds[idx].fd, resp);
 }
 
+/*
+ * process_new:
+ * @idx: idx into pollfds.
+ * @type: type to init handle to
+ *
+ * Find a free handle and initialize it to 'type', increase refcnt and
+ * send back a reply to the client.
+ */
 static void process_new (guint32 idx, WapiHandleType type)
 {
 	guint32 handle;
@@ -231,6 +303,14 @@
 	send_reply (idx, &resp);
 }
 
+/*
+ * process_open:
+ * @idx: idx into pollfds.
+ * @handle: handle no.
+ *
+ * Increase refcnt on a previously created handle and send back a
+ * response to the client.
+ */
 static void process_open (guint32 idx, guint32 handle)
 {
 	WapiHandleResponse resp;
@@ -259,6 +339,14 @@
 	send_reply (idx, &resp);
 }
 
+/*
+ * process_close:
+ * @idx: idx into pollfds.
+ * @handle: handle no.
+ *
+ * Decrease refcnt on a previously created handle and send back a
+ * response to the client with notice of it being destroyed.
+ */
 static void process_close (guint32 idx, guint32 handle)
 {
 	WapiHandleResponse resp;
@@ -273,6 +361,13 @@
 	send_reply (idx, &resp);
 }
 
+/*
+ * process_scratch:
+ * @idx: idx into pollfds.
+ * @length: allocate this much scratch space
+ *
+ * Allocate some scratch space and send a reply to the client.
+ */
 static void process_scratch (guint32 idx, guint32 length)
 {
 	WapiHandleResponse resp;
@@ -288,6 +383,13 @@
 	send_reply (idx, &resp);
 }
 
+/*
+ * process_scratch_free:
+ * @idx: idx into pollfds.
+ * @scratch_idx: deallocate this scratch space
+ *
+ * Deallocate scratch space and send a reply to the client.
+ */
 static void process_scratch_free (guint32 idx, guint32 scratch_idx)
 {
 	WapiHandleResponse resp;
@@ -303,6 +405,13 @@
 	send_reply (idx, &resp);
 }
 
+/*
+ * read_message:
+ * @idx: idx into pollfds.
+ *
+ * Read a message (A WapiHandleRequest) from a client and dispatch
+ * whatever it wants to the process_* calls.
+ */
 static void read_message (guint32 idx)
 {
 	WapiHandleRequest req;
@@ -314,9 +423,15 @@
 		process_new (idx, req.u.new.type);
 		break;
 	case WapiHandleRequestType_Open:
+#ifdef DEBUG
+		g_assert(req.u.open.handle < _WAPI_MAX_HANDLES);
+#endif
 		process_open (idx, req.u.open.handle);
 		break;
 	case WapiHandleRequestType_Close:
+#ifdef DEBUG
+		g_assert(req.u.close.handle < _WAPI_MAX_HANDLES);
+#endif
 		process_close (idx, req.u.close.handle);
 		break;
 	case WapiHandleRequestType_Scratch:
@@ -325,9 +440,19 @@
 	case WapiHandleRequestType_ScratchFree:
 		process_scratch_free (idx, req.u.scratch_free.idx);
 		break;
+	case WapiHandleRequestType_Error: /* Falltrough */
+	default: /* Catch faulty packages */
+		/*FIXME: call rem_fd?*/
+		break;
 	}
 }
 
+/*
+ * main:
+ *
+ * Open socket, create shared mem segment and begin listening for
+ * clients.
+ */
 int main(int argc, char **argv)
 {
 	struct sockaddr_un main_socket_address;
@@ -347,8 +472,8 @@
 	ret=bind(main_sock, (struct sockaddr *)&main_socket_address,
 		 sizeof(struct sockaddr_un));
 	if(ret==-1) {
-		g_warning ("bind failed: %s", strerror (errno));
-		_wapi_shared_data->daemon_running=2;
+		g_critical ("bind failed: %s", strerror (errno));
+		_wapi_shared_data->daemon_running=DAEMON_DIED_AT_STARTUP;
 		exit(-1);
 	}
 
@@ -358,8 +483,8 @@
 
 	ret=listen(main_sock, 5);
 	if(ret==-1) {
-		g_warning ("listen failed: %s", strerror (errno));
-		_wapi_shared_data->daemon_running=2;
+		g_critical ("listen failed: %s", strerror (errno));
+		_wapi_shared_data->daemon_running=DAEMON_DIED_AT_STARTUP;
 		exit(-1);
 	}
 
@@ -373,7 +498,7 @@
 	 * ready.  From now on, it's up to us to delete the shared
 	 * memory segment when appropriate.
 	 */
-	_wapi_shared_data->daemon_running=1;
+	_wapi_shared_data->daemon_running=DAEMON_RUNNING;
 
 	while(TRUE) {
 		int i;
@@ -385,21 +510,29 @@
 		/* Block until something happens */
 		ret=poll(pollfds, nfds, -1);
 		if(ret==-1) {
-			g_message ("poll error: %s", strerror (errno));
+			g_critical ("poll error: %s", strerror (errno));
 			cleanup ();
 			exit(-1);
 		}
 
 		for(i=0; i<nfds; i++) {
-			if(((pollfds[i].revents&POLLHUP)==POLLHUP) ||
-			   ((pollfds[i].revents&POLLERR)==POLLERR) ||
-			   ((pollfds[i].revents&POLLNVAL)==POLLNVAL)) {
+			if(((pollfds[i].revents & POLLHUP)  == POLLHUP) ||
+			   ((pollfds[i].revents & POLLERR)  == POLLERR) ||
+			   ((pollfds[i].revents & POLLNVAL) == POLLNVAL)) {
 #ifdef DEBUG
 			   	g_message ("fd[%d] %d error", i,
 					   pollfds[i].fd);
 #endif
 				rem_fd(i);
 			} else if((pollfds[i].revents&POLLIN)==POLLIN) {
+				/* If a client is connecting, accept
+				 * it and begin listening to that
+				 * socket too otherwise it must be a
+				 * client we already have that wants
+				 * something
+				 */
+
+				/* FIXME: Isn't this only true for i==0??? */
 				if(pollfds[i].fd==main_sock) {
 					int newsock;
 					struct sockaddr addr;
@@ -407,7 +540,7 @@
 					newsock=accept(main_sock, &addr,
 						       &addrlen);
 					if(newsock==-1) {
-						g_message("accept error: %s",
+						g_critical("accept error: %s",
 							  strerror (errno));
 						cleanup ();
 						exit(-1);
Index: handles.c
===================================================================
RCS file: /mono/mono/mono/io-layer/handles.c,v
retrieving revision 1.13
diff -u -b -B -r1.13 handles.c
--- handles.c	8 Jun 2002 17:57:49 -0000	1.13
+++ handles.c	11 Jun 2002 15:18:14 -0000
@@ -135,6 +135,13 @@
 	thread_handle=_wapi_handle_new (WAPI_HANDLE_THREAD);
 }
 
+/*
+ * _wapi_handle_new_internal:
+ * @type: Init handle to this type
+ *
+ * Search for a free handle and initialize it. Return the handle on
+ * succes and 0 on failure.
+ */
 guint32 _wapi_handle_new_internal (WapiHandleType type)
 {
 	guint32 i;
@@ -149,7 +156,7 @@
 		struct _WapiHandleShared *shared=&_wapi_shared_data->handles[i];
 		
 		if(shared->type==WAPI_HANDLE_UNUSED) {
-			last=i;
+			last=i+1;
 			shared->type=type;
 			shared->signalled=FALSE;
 			mono_mutex_init (&_wapi_shared_data->handles[i].signal_mutex, &mutex_shared_attr);
@@ -163,7 +170,7 @@
 		/* Try again from the beginning */
 		last=1;
 		goto again;
-	}
+}
 	
 
 	return(0);
@@ -350,6 +357,13 @@
 
 #define HDRSIZE sizeof(struct _WapiScratchHeader)
 
+/*
+ * _wapi_handle_scratch_store_internal:
+ * @bytes: Allocate no. bytes
+ *
+ * Like malloc(3) except its for the shared memory segment's scratch
+ * part. Memory block returned is zeroed out.
+ */
 guint32 _wapi_handle_scratch_store_internal (guint32 bytes)
 {
 	guint32 idx=0, last_idx=0;
@@ -400,6 +414,10 @@
 			g_message (G_GNUC_PRETTY_FUNCTION ": new header at %d, length %d", idx+bytes, hdr->length);
 #endif
 			
+			/*
+			 * It was memset(0..) when free/made so no need to do it here
+			 */
+
 			return(idx);
 		} else if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
 			  last_was_free == FALSE) {
@@ -516,6 +534,13 @@
 	return(str);
 }
 
+/*
+ * _wapi_handle_scratch_delete_internal:
+ * @idx: Index to free block
+ *
+ * Like free(3) except its for the shared memory segment's scratch
+ * part.
+ */
 void _wapi_handle_scratch_delete_internal (guint32 idx)
 {
 	struct _WapiScratchHeader *hdr;
Index: shared.c
===================================================================
RCS file: /mono/mono/mono/io-layer/shared.c,v
retrieving revision 1.4
diff -u -b -B -r1.4 shared.c
--- shared.c	9 May 2002 13:10:18 -0000	1.4
+++ shared.c	11 Jun 2002 15:18:14 -0000
@@ -56,6 +56,17 @@
 
 #undef DEBUG
 
+/*
+ * _wapi_shm_attach:
+ * @daemon: Is it the daemon trying to attach to the segment
+ * @success: Was it a success
+ * @shm_id: The ID of the segment created/attached to
+ *
+ * Attach to the shared memory segment or create it if it did not
+ * exist. If it was created and daemon was FALSE a new daemon is
+ * forked into existence. Returns the memory area the segment was
+ * attached to.
+ */
 gpointer _wapi_shm_attach (gboolean daemon, gboolean *success, int *shm_id)
 {
 	gpointer shm_seg;
@@ -99,7 +110,7 @@
 		 */
 	} else {
 		/* Some error other than EEXIST */
-		g_message (G_GNUC_PRETTY_FUNCTION ": shmget error: %s",
+		g_critical (G_GNUC_PRETTY_FUNCTION ": shmget error: %s",
 			   strerror (errno));
 		exit (-1);
 	}
@@ -110,7 +121,7 @@
 	 */
 	shm_seg=shmat (*shm_id, NULL, 0);
 	if(shm_seg==(gpointer)-1) {
-		g_message (G_GNUC_PRETTY_FUNCTION ": shmat error: %s",
+		g_critical (G_GNUC_PRETTY_FUNCTION ": shmat error: %s",
 			   strerror (errno));
 		if(fork_daemon==TRUE) {
 			_wapi_shm_destroy ();
@@ -131,7 +142,7 @@
 			
 		pid=fork ();
 		if(pid==-1) {
-			g_message (G_GNUC_PRETTY_FUNCTION ": fork error: %s",
+			g_critical (G_GNUC_PRETTY_FUNCTION ": fork error: %s",
 				   strerror (errno));
 			_wapi_shm_destroy ();
 			exit (-1);
@@ -140,9 +151,9 @@
 			setsid ();
 			execl (MONO_BINDIR "/mono-handle-d", "mono-handle-d",
 			       NULL);
-			g_warning (": exec of %s/mono-handle-d failed: %s",
+			g_critical (": exec of %s/mono-handle-d failed: %s",
 				   MONO_BINDIR, strerror (errno));
-			data->daemon_running=2;
+			data->daemon_running=DAEMON_DIED_AT_STARTUP;
 			exit (-1);
 		}
 		/* parent carries on */
@@ -150,8 +161,9 @@
 		/* Do some sanity checking on the shared memory we
 		 * attached
 		 */
-		if(!(data->daemon_running==0 || data->daemon_running==1 ||
-		     data->daemon_running==2) ||
+		if(!(data->daemon_running==DAEMON_STARTING || 
+		     data->daemon_running==DAEMON_RUNNING ||
+		     data->daemon_running==DAEMON_DIED_AT_STARTUP) ||
 		   (strncmp (data->daemon+1, "mono-handle-daemon-", 19)!=0)) {
 			g_warning ("Shared memory sanity check failed.");
 			*success=FALSE;
@@ -159,7 +171,7 @@
 		}
 	}
 		
-	for(tries=0; data->daemon_running==0 && tries < 100; tries++) {
+	for(tries=0; data->daemon_running==DAEMON_STARTING && tries < 100; tries++) {
 		/* wait for the daemon to sort itself out.  To be
 		 * completely safe, we should have a timeout before
 		 * giving up.
@@ -171,7 +183,7 @@
 			
 		nanosleep (&sleepytime, NULL);
 	}
-	if(tries==100 && data->daemon_running==0) {
+	if(tries==100 && data->daemon_running==DAEMON_STARTING) {
 		/* Daemon didnt get going */
 		if(fork_daemon==TRUE) {
 			_wapi_shm_destroy ();
@@ -181,7 +193,7 @@
 		return(NULL);
 	}
 	
-	if(data->daemon_running==2) {
+	if(data->daemon_running==DAEMON_DIED_AT_STARTUP) {
 		/* Oh dear, the daemon had an error starting up */
 		if(fork_daemon==TRUE) {
 			_wapi_shm_destroy ();
Index: wapi-private.h
===================================================================
RCS file: /mono/mono/mono/io-layer/wapi-private.h,v
retrieving revision 1.12
diff -u -b -B -r1.12 wapi-private.h
--- wapi-private.h	9 May 2002 13:10:19 -0000	1.12
+++ wapi-private.h	11 Jun 2002 15:18:14 -0000
@@ -95,6 +95,12 @@
 #define _WAPI_MAX_HANDLES 4096
 #define _WAPI_HANDLE_INVALID (gpointer)-1
 
+typedef enum {
+	DAEMON_STARTING = 0,
+	DAEMON_RUNNING  = 1,
+	DAEMON_DIED_AT_STARTUP = 2,
+} _wapi_daemon_status;
+
 /*
  * This is the layout of the shared memory segment
  */
@@ -104,7 +110,7 @@
 	 * header file
 	 */
 	guchar daemon[108];
-	guint32 daemon_running;
+	_wapi_daemon_status daemon_running;
 	
 #ifdef _POSIX_THREAD_PROCESS_SHARED
 	mono_mutex_t signal_mutex;
--- /dev/null	Thu Jan  1 01:00:00 1970
+++ docs/.cvsignore	Sun Jun  2 00:00:05 2002
@@ -0,0 +1 @@
+Makefile Makefile.in
--- /dev/null	Thu Jan  1 01:00:00 1970
+++ docs/mono_handle_d	Tue Jun 11 17:17:18 2002
@@ -0,0 +1,98 @@
+=pod
+
+=head1 Internal design document for the mono_handle_d
+
+This document is designed to hold the design of the mono_handle_d and
+not as an api reference.
+
+=head2 Primary goal and purpose
+
+The mono_handle_d is a process which takes care of the (de)allocation
+of scratch shared memory and handles (of files, threads, mutexes,
+sockets etc. see L<WapiHandleType>) and refcounts of the
+filehandles. It is designed to be run by a user and to be fast, thus
+minimal error checking on input is done and will most likely crash if
+given a faulty package. No effort has been, or should be, made to have
+the daemon talking to machine of different endianness/size of int.
+
+=head2 How to start the daemon
+
+To start the daemon you either run the mono_handle_d executable or try
+to attach to the shared memory segment via L<_wapi_shm_attach> which
+will start a daemon if one does not exist.
+
+=head1 Internal details
+
+The daemon works by opening a socket and listening to clients. These
+clients send packages over the socket complying to L<struct
+WapiHandleRequest>.
+
+=head2 Possible requests
+
+=over
+
+=item WapiHandleRequest_New
+
+Find a handle in the shared memory segment that is free and allocate
+it to the specified type. To destroy use
+L</WapiHandleRequest_Close>. A L<WapiHandleResponse> with
+.type=WapiHandleResponseType_New will be sent back with .u.new.handle
+set to the handle that was allocated. .u.new.type is the type that was
+requested.
+
+=item WapiHandleRequestType_Open
+
+Increase the ref count of an already created handle. A
+L<WapiHandleResponse> with .type=WapiHandleResponseType_Open will be sent
+back with .u.new.handle set to the handle, .u.new.type is set to the
+type of handle this is.
+
+=item WapiHandleRequestType_Close
+
+Decrease the ref count of an already created handle. A
+L<WapiHandleResponse> with .type=WapiHandleResponseType_Close will be
+sent back with .u.close.destroy set to TRUE if ref count for this
+client reached 0.
+
+=item WapiHandleRequestType_Scratch
+
+Allocate a shared memory area of size .u.scratch.length in bytes. A
+L<WapiHandleResponse> with .type=WapiHandleResponseType_Scratch will be
+sent back with .u.scratch.idx set to the index into the shared
+memory's scratch area where to memory begins. (works just like
+malloc(3))
+
+=item WapiHandleRequestType_Scratch
+
+Deallocate a shared memory area, this must have been allocated before
+deallocating. A L<WapiHandleResponse> with
+.type=WapiHandleResponseType_ScratchFree will be sent back (works just
+like free(3))
+
+=back
+
+=head1 Why a daemon
+
+From an email:
+
+Dennis: I just have one question about the daemon... Why does it
+exist? Isn't it better performancewise to just protect the shared area
+with a mutex when allocation a new handle/shared mem segment or
+changing refcnt? It will however be a less resilient to clients that
+crash (the deamon cleans up ref'd handles if socket closes)
+
+Dick: It's precisely because with a mutex the shared memory segment
+can be left in a locked state. Also, it's not so easy to clean up
+shared memory without it (you can't just mark it deleted when creating
+it, because you can't attach any more readers to the same segment
+after that).  I did some minimal performance testing, and I don't
+think the daemon is particularly slow.
+
+
+=head1 Authors
+
+Documentaion: Dennis Haney
+
+Implementation: Dick Porter
+
+=cut

--=-=-=--