durusmail: quixote-users: Re: (slightly OT) suggestions for inter-process locks?
(slightly OT) suggestions for inter-process locks?
2004-08-23
2004-08-23
Re: (slightly OT) suggestions for inter-process locks?
2004-08-23
Re: (slightly OT) suggestions for inter-process locks?
Graham Fawcett
2004-08-23
Jim Dukarm wrote:
> On Monday 23 August 2004 11:52, Graham Fawcett wrote:
>
>>I need to implement a lock in a certain block of code, so that only one
>>process at a time is will be able to execute the restricted block. (Note
>>this is an inter-process lock, not an inter-thread lock, or I would just
>>use the standard threading library.)
>>
>
> Would it be practical to run the restricted code in its own process as a
> server (maybe using Medusa or sockets) which responds to only one request
> from your application(s) at a time?  That would be a very portable solution.

Why didn't I think of that? Actually, moving the code into Medusa would
be a pain. But I could write a server to manage semaphores, upon which I
could build the locks. In fact, it could even be multithreaded...

....  ...

Okay, this ought to do the trick. Thanks for the inspiration, Jim!

-- Graham

# =====================================================

"""
ipc_lock.py: lightweight interprocess locks.

Usage:

     from ipc_lock import Lock

     lock = Lock('lock_name')
     lock.acquire()
     try:
         do_something()
     finally:
         lock.release()

You must run a lock server as well:

     ls = LockServer()
     ls.serve_forever()

will do the trick.

It's a basic socket server, multi-threaded, with a basic line protocol.
Commands are of the form

     :=   '\n'
     := '+' | '-' | '*'
     := [^\n]*

where  is a single character command in '+-*'. The message '+fred'
would acquire the lock named 'fred', blocking until it's available.
'-fred' will release the lock. The dangerous '*' command will release
all locks and is for debugging.

"""

from threading import Lock as _Lock
import socket
import SocketServer

class LockServer(SocketServer.ThreadingTCPServer):

     PORT = 5555
     locks = {}
     def __init__(self):
         SocketServer.ThreadingTCPServer.__init__(
             self,
             ('', self.PORT),
             LockHandler)
         print 'serving %s on port %d' % (self, self.PORT)

     def get_lock(self, lockname):
         if not self.locks.has_key(lockname):
             self.locks[lockname] = _Lock()
         return self.locks[lockname]

class LockHandler(SocketServer.BaseRequestHandler):
     def handle(self):
         data = ''
         while not '\n' in data:
             data += self.request.recv(128)
         data = data.rstrip()
         cmd = data[0]
         if cmd == '*':
             self.server.locks.clear()
             self.request.send('*\r\n')
         else:
             key = data[1:]
             lock = self.server.get_lock(key)
             if cmd == '+':
                 lock.acquire()
                 self.request.send('+%s\r\n' % key)
             elif cmd == '-':
                 lock.release()
                 self.request.send('-%s\r\n' % key)
             else:
                 self.request.send('?%s' % data)

class Lock:
     def __init__(self, name):
         self.name = name

     def acquire(self):
         s = socket.socket()
         s.connect(('localhost', 5555))
         s.send('+%s\n' % self.name)
         return s.recv(1024)

     # okay, I copy-pasted. Sue me...

     def release(self):
          s = socket.socket()
          s.connect(('localhost', 5555))
          s.send('-%s\n' % self.name)
          return s.recv(1024)

def clear_all():
      s = socket.socket()
      s.connect(('localhost', 5555))
      s.send('*\n')
      return s.recv(1024)


if __name__ == '__main__':

     s = LockServer()
     s.serve_forever()


reply