From: skibud2
Subject: locking files with common lisp
Date: 
Message-ID: <1157513284.594551.126600@h48g2000cwc.googlegroups.com>
I have two lisp processes that want to access a common file. I would
like to lock that file when one of the processes is writing to the
file. What is the best way of doing this? I would prefer not to use a
database to save complexity. 

Thanks in advance,

Mike

From: ·············@gmail.com
Subject: Re: locking files with common lisp
Date: 
Message-ID: <1157513762.407782.49760@e3g2000cwe.googlegroups.com>
skibud2 wrote:
> I have two lisp processes that want to access a common file. I would
> like to lock that file when one of the processes is writing to the
> file. What is the best way of doing this?

You can find a nice tutorial in:

Keene, Sonya E. Object-Oriented Programming in Common Lisp: A
Programmer's Guide to CLOS. Addison-Wesley (Reading, Massachusetts,
1989).

She walks the reader through writing a locks library.  It might take
some work to get it working on your implementation though, since this
isn't in the Common Lisp standard.
From: Kaz Kylheku
Subject: Re: locking files with common lisp
Date: 
Message-ID: <1157521445.334807.69160@m79g2000cwm.googlegroups.com>
skibud2 wrote:
> I have two lisp processes that want to access a common file. I would
> like to lock that file when one of the processes is writing to the
> file. What is the best way of doing this? I would prefer not to use a
> database to save complexity.

The best way is to do it the way it is supposed to be done on the
operating system platform you are  developing for. Just because you are
using Lisp to create these programs doesn't mean that there is some
special Lisp way to do file locking.

On a POSIX system, you have the fcntl() function: specifically, its
F_SETLK and F_UNLCK opcodes used in conjunction with struct flock.
From: Pascal Bourguignon
Subject: Re: locking files with common lisp
Date: 
Message-ID: <87y7sx64o0.fsf@thalassa.informatimago.com>
"skibud2" <·············@gmail.com> writes:

> I have two lisp processes that want to access a common file. I would
> like to lock that file when one of the processes is writing to the
> file. What is the best way of doing this? I would prefer not to use a
> database to save complexity. 

You will have to use implementation dependant stuff.
There's nothing in the Common Lisp standard about file locking.

There may be some indirect behavior accessible from standard
functions, but it really depends on how the implementation implement
these standard functions.  

For example you could try:

(let ((file (open "/some/path.lock" 
                  :direction :output
                  :if-does-not-exist :create
                  :if-exists nil)))
  (if file
     (unwind-protect
         (do-mutex-stuff file)
       (close file)
       (delete-file "/some/path.data")
       (rename-file file "/some/path.data"))
     (format t "~&File is locked.~%")))

and check that your implementation does the right thing.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"Logiciels libres : nourris au code source sans farine animale."
From: dpapathanasiou
Subject: Re: locking files with common lisp
Date: 
Message-ID: <1157547333.899161.191120@e3g2000cwe.googlegroups.com>
Here's a quick-and-dirty way of doing this, if you don't want to use
FFI to access POSIX functions:

(defun with-file-lock (folder fn &rest args)
  "Perform file I/O on folder (i.e. apply fn and its args), while using
a lock to prevent simultaneous writes/race conditions."
  (let ((file-lock (make-pathname :directory (pathname-directory
folder) :name ".lock"))
	(result nil))
    (loop (handler-case
	    (progn
	      (with-open-file (out file-lock :direction :output
					     :if-exists :error
					     :if-does-not-exist :create)
		(unwind-protect
		  (setf result (apply fn args))))
	      (delete-file file-lock)
	      (return-from with-file-lock result))
	    (error ()
		   (sleep 0.2))))))

Basically, it creates a hidden file named ".lock" in the folder you're
working in, executes whatever function you want, then removes the
".lock" file.

If it's invoked when the ".lock" file is present in that folder, the
:if-exists will cause it to fall into an error state, which means it'll
sleep and try again.

Two warnings about this (it is quick-and-dirty, after all):

(1) Make sure the function you apply inside unwind-protect is active in
the same folder as where the ".lock" file is created -- otherwise,
there's no point in creating the lock

(2) Make sure the function you apply inside unwind-protect returns --
if you hit an unaccounted for error condition, the ".lock" file will
not get cleaned up, and other calls will loop endlessly
From: skibud2
Subject: Re: locking files with common lisp
Date: 
Message-ID: <1157553046.706300.250800@b28g2000cwb.googlegroups.com>
Thanks everyone for responding. I think I am good for now.

Mike


dpapathanasiou wrote:
> Here's a quick-and-dirty way of doing this, if you don't want to use
> FFI to access POSIX functions:
>
> (defun with-file-lock (folder fn &rest args)
>   "Perform file I/O on folder (i.e. apply fn and its args), while using
> a lock to prevent simultaneous writes/race conditions."
>   (let ((file-lock (make-pathname :directory (pathname-directory
> folder) :name ".lock"))
> 	(result nil))
>     (loop (handler-case
> 	    (progn
> 	      (with-open-file (out file-lock :direction :output
> 					     :if-exists :error
> 					     :if-does-not-exist :create)
> 		(unwind-protect
> 		  (setf result (apply fn args))))
> 	      (delete-file file-lock)
> 	      (return-from with-file-lock result))
> 	    (error ()
> 		   (sleep 0.2))))))
>
> Basically, it creates a hidden file named ".lock" in the folder you're
> working in, executes whatever function you want, then removes the
> ".lock" file.
>
> If it's invoked when the ".lock" file is present in that folder, the
> :if-exists will cause it to fall into an error state, which means it'll
> sleep and try again.
>
> Two warnings about this (it is quick-and-dirty, after all):
>
> (1) Make sure the function you apply inside unwind-protect is active in
> the same folder as where the ".lock" file is created -- otherwise,
> there's no point in creating the lock
>
> (2) Make sure the function you apply inside unwind-protect returns --
> if you hit an unaccounted for error condition, the ".lock" file will
> not get cleaned up, and other calls will loop endlessly
From: Tim Bradshaw
Subject: Re: locking files with common lisp
Date: 
Message-ID: <1157564956.543600.48940@e3g2000cwe.googlegroups.com>
dpapathanasiou wrote:

> Two warnings about this (it is quick-and-dirty, after all):

And a third (which likely does not affect the OP but is worth bearing
in mind): this is likely to go horribly wrong if using NFS, or really
if using NFS with multiple clients.  NFS locking is a saga of pain.

--tim
From: Johan Ur Riise
Subject: Re: locking files with common lisp
Date: 
Message-ID: <87lkovwb2u.fsf@morr.riise-data.net>
"skibud2" <·············@gmail.com> writes:

> I have two lisp processes that want to access a common file. I would
> like to lock that file when one of the processes is writing to the
> file. What is the best way of doing this? I would prefer not to use a
> database to save complexity. 

If speed in the seconds are is sufficient, you can use the lockfile
command in Linux:

LOCKFILE(1)                                                        LOCKFILE(1)

NAME
       lockfile - conditional semaphore-file creator

SYNOPSIS
       lockfile -sleeptime | -r retries |
            -l locktimeout | -s suspend | -!  | -ml | -mu | filename ...

DESCRIPTION
       lockfile  can  be used to create one or more semaphore files.  If lock-
       file can't create all the specified files (in the specified order),  it
       waits  sleeptime (defaults to 8) seconds and retries the last file that
       didn't succeed.  You can specify the number  of  retries  to  do  until
       failure  is  returned.   If the number of retries is -1 (default, i.e.,
       -r-1) lockfile will retry forever.

[...]

It is in the procmail package.