chiark / gitweb /
Makefile: Use check_PROGRAMS target.
[mLib] / lock.c
diff --git a/lock.c b/lock.c
index 9dfabab6f715ec0ef8e2cd232c6c624f8ae2dc51..19392a8e7a1ea63c2639ce7f1bb17247df501a3a 100644 (file)
--- a/lock.c
+++ b/lock.c
@@ -1,13 +1,13 @@
 /* -*-c-*-
  *
- * $Id: lock.c,v 1.2 1999/05/26 20:53:40 mdw Exp $
+ * $Id: lock.c,v 1.6 2004/04/08 01:36:13 mdw Exp $
  *
  * Simplified POSIX locking interface
  *
  * (c) 1997 Straylight/Edgeware
  */
 
-/*----- Licensing notice --------------------------------------------------* 
+/*----- Licensing notice --------------------------------------------------*
  *
  * This file is part of the mLib utilities library.
  *
  * it under the terms of the GNU Library General Public License as
  * published by the Free Software Foundation; either version 2 of the
  * License, or (at your option) any later version.
- * 
+ *
  * mLib is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU Library General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Library General Public
  * License along with mLib; if not, write to the Free
  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  * MA 02111-1307, USA.
  */
 
-/*----- Revision history --------------------------------------------------*
- *
- * $Log: lock.c,v $
- * Revision 1.2  1999/05/26 20:53:40  mdw
- * Fixes for stupid bugs.
- *
- * Revision 1.1  1999/05/15 10:33:53  mdw
- * Add simplified locking code.
- *
- */
-
 /*----- 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 +62,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 +82,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 +100,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 +111,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);
 }