ADDED utils/softlock.scm Index: utils/softlock.scm ================================================================== --- /dev/null +++ utils/softlock.scm @@ -0,0 +1,123 @@ +;;====================================================================== +;; Copyright 2019, Matthew Welland. +;; +;; This file is part of Megatest. +;; +;; Megatest is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Megatest 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Megatest. If not, see . +;; +;;====================================================================== + +(use posix srfi-18) + + +(if (< (length (command-line-arguments)) 2) ;; require at least lockfile command + (begin + (print "Usage: +softlock lockfile command args ... + +Softlock does weak, transient locking. This is useful to slow down a +deluge of events that can overwhelm hardware or software systems. + +Locks are only good for two seconds, just enough time to spread things +out. + +On NFS file locking works well but the lock handling on the filers can +be overwhelmed by too many locks occuring too quickly. Jobs that must +use NFS file locks can use softlock to minimize the rate that the file +locks are created, preventing the NFS filer from being swamped. + +Environment variables: + + SOFTLOCK_DEBUG_MODE - if defined enable some messages + +WARNING: the file .softlock will be overwritten and removed by softlock! + +Part of the Megatest project http://www.kiatoa.com/fossils/megatest") + (exit 1))) + +(define (read-lock-file fname) + (handle-exceptions + exn + (begin + (if (get-environment-variable "SOFTLOCK_DEBUG_MODE") + (print "Exception on reading lock file. exn=" exn)) + #f) + (with-input-from-file fname + read-line))) + +(define (lock-file-old fname) + (and (file-exists? fname) + (> (- (current-seconds)(file-modification-time fname)) 1))) ;; hard coded to one second + +(define (check-locked-by-me fname mykey) + (if (file-exists? fname) + (let ((lock-data (read-lock-file fname))) + (if (and lock-data + (equal? mykey lock-data)) + #t + (not (lock-file-old fname)))) ;; if the lockfile is old we are NOT locked. + #f)) + +(define (check-locked-by-someone-else fname mykey) + (if (file-exists? fname) + (let ((lock-data (read-lock-file fname))) + (and lock-data + (not (equal? mykey lock-data)) + (not (lock-file-old fname)))) ;; if the lockfile is old we are NOT locked. + #f)) + +(define (take-lock fname mykey) + (with-output-to-file fname + (lambda () + (print mykey)))) + +(define (run-the-command command params) + (process-wait (process-run command params))) + +(let* ((lockfile (car (command-line-arguments))) + (fulllock (conc lockfile ".softlock")) ;; prevent accidentally removing important files + (lockfdir (pathname-directory lockfile)) + (command (cadr (command-line-arguments))) + (params (cddr (command-line-arguments))) + (mykey (conc (get-host-name) "-" (current-process-id)))) + + ;; sanity checks + (cond + ((not lockfdir) + (print "ERROR: lock file parameter must include path component, e.g. ./mylock") + (exit 1)) + ((not (file-write-access? lockfdir)) + (print "ERROR: Can not access directory for lock " lockfdir) + (exit 1)) + ;; add more sanity checks here + ) + + (let loop ((remtries 10)) + (if (> remtries 0) + (if (check-locked-by-someone-else fulllock mykey) + (begin + (print "... lock " fulllock " exists, waiting...") + (thread-sleep! 1.9) + (loop (- remtries 1))) + (begin + (take-lock fulllock mykey) + (if (check-locked-by-me fulllock mykey) + (run-the-command command params) + (begin ;; didn't get the lock + (thread-sleep! (+ 1.9 (/ 1 (+ 1 (random 20))))) ;; add some noise to prevent nyquist problems + (loop (- remtries 1)))))) + (begin + (print "ERROR: not able to get the lock. Gonna take it and proceed...") + (take-lock fulllock mykey) + (run-the-command command params)))))