chiark / gitweb /
New hex encoding stuff. Rename test programs.
[mLib] / lock.c
diff --git a/lock.c b/lock.c
index 9dfabab..335c3ac 100644 (file)
--- a/lock.c
+++ b/lock.c
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * $Id: lock.c,v 1.2 1999/05/26 20:53:40 mdw Exp $
+ * $Id: lock.c,v 1.5 1999/06/19 20:35:45 mdw Exp $
  *
  * Simplified POSIX locking interface
  *
 /*----- Revision history --------------------------------------------------*
  *
  * $Log: lock.c,v $
+ * Revision 1.5  1999/06/19 20:35:45  mdw
+ * Whoops.  I'd left the type of the jump buffer as `sigjmp_buf'.
+ *
+ * Revision 1.4  1999/06/19 20:33:16  mdw
+ * More sophisticated and excessive signal and alarm handling.
+ *
+ * Revision 1.3  1999/06/06 01:23:00  mdw
+ * Fix signal handling.
+ *
  * Revision 1.2  1999/05/26 20:53:40  mdw
  * Fixes for stupid bugs.
  *
@@ -41,6 +50,7 @@
 /*----- Header files ------------------------------------------------------*/
 
 #include <errno.h>
+#include <setjmp.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 #define LOCK_TIMEOUT 10                        /* Maximum time in seconds to wait */
 
+/*----- Static variables --------------------------------------------------*/
+
+static jmp_buf jmp;                    /* Jump here to interrup @fcntl@ */
+
 /*----- Main code ---------------------------------------------------------*/
 
-/* --- @lock_alarm@ --- *
+/* --- @sigalrm@ --- *
  *
  * Arguments:  @int sig@ = signal number
  *
@@ -68,7 +82,7 @@
  * Use:                Makes sure that a @SIGALRM@ signal interrupts system calls.
  */
 
-static void lock_alarm(int sig) { ; }
+static void sigalrm(int sig) { longjmp(jmp, 1); }
 
 /* --- @lock_file@ --- *
  *
@@ -88,8 +102,10 @@ static void lock_alarm(int sig) { ; }
 int lock_file(int fd, unsigned how)
 {
   struct flock fk;
-  void (*alrm)(int);
+  struct sigaction sa, osa;
+  sigset_t ss, oss; 
   int e;
+  int al, d, left;
 
   /* --- Fill in the easy bits --- */
 
@@ -104,7 +120,7 @@ int lock_file(int fd, unsigned how)
     return (fcntl(fd, F_SETLK, &fk));
   }
 
-  /* --- Set an alarm handler --- */
+  /* --- Decide how to do the locking --- */
 
   if (how == LOCK_EXCL)
     fk.l_type = F_WRLCK;
@@ -115,14 +131,64 @@ int lock_file(int fd, unsigned how)
     return (-1);
   }
 
-  alrm = signal(SIGALRM, lock_alarm);
-  alarm(LOCK_TIMEOUT);
-  if ((e = fcntl(fd, F_SETLKW, &fk)) != 0) {
-    if (errno == EINTR)
-      errno = EAGAIN;
+  /* --- Block @SIGALRM@ for a while --- *
+   *
+   * I don't want stray alarms going off while I'm busy here.
+   */
+
+  sigemptyset(&ss);
+  sigaddset(&ss, SIGALRM);
+  if (sigprocmask(SIG_BLOCK, &ss, &oss))
+    return (-1);
+
+  /* --- Set up the signal handler --- */
+
+  sa.sa_handler = sigalrm;
+  sa.sa_flags = 0;
+#ifdef SA_INTERRUPT
+  sa.sa_flags |= SA_INTERRUPT;
+#endif
+  sigemptyset(&sa.sa_mask);
+  if (sigaction(SIGALRM, &sa, &osa))
+    return (-1);
+
+  /* --- Set up the alarm, remembering when it's meant to go off --- */
+
+  al = alarm(0);
+  if (al && al < LOCK_TIMEOUT)
+    d = al;
+  else
+    d = LOCK_TIMEOUT;
+  alarm(d);
+
+  /* --- Set up the return context for the signal handler --- */
+
+  if (setjmp(jmp)) {
+    sigprocmask(SIG_SETMASK, &oss, 0);
+    errno = EINTR;
+    e = -1;
+    goto done;
+  }
+
+  /* --- Unblock the signal and we're ready --- */
+
+  if (sigprocmask(SIG_SETMASK, &oss, 0)) {
+    alarm(al);
+    e = -1;
+    goto done;
   }
-  alarm(0);
-  signal(SIGALRM, alrm);
+
+  /* --- Do it --- */
+
+  e = fcntl(fd, F_SETLKW, &fk);
+
+  /* --- Tidy up the mess I left --- */
+
+  left = alarm(0);
+  if (al)
+    alarm(al - d + left);
+done:
+  sigaction(SIGALRM, &osa, 0);
   return (e);
 }