Bug #230469 - Implement non-blocking distlocks for --fetchonly. This adds
authorZac Medico <zmedico@gentoo.org>
Thu, 3 Jul 2008 22:48:21 +0000 (22:48 -0000)
committerZac Medico <zmedico@gentoo.org>
Thu, 3 Jul 2008 22:48:21 +0000 (22:48 -0000)
a "flags" keyword parameter to the portage.locks.lock() function. Default
is flags=0. If flags contains os.O_NONBLOCK then lock() will raise
portage.exception.TryAgain instead of blocking. This new flags parameter
is used to implement non-blocking distlocks in fetch() when fetchonly
mode is enabled.

svn path=/main/trunk/; revision=10917

pym/portage/__init__.py
pym/portage/exception.py
pym/portage/locks.py

index 9859e0f5e5f0877b699d463ea939092f8fe742ed..6aa4d266421d154c70bd8f3f3dfef76d1cd0e935 100644 (file)
@@ -3512,15 +3512,27 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks",
                                        from textwrap import wrap
                                        waiting_msg = "\n".join(msg_prefix + line \
                                                for line in wrap(waiting_msg, 65))
+
                                if locks_in_subdir:
-                                       file_lock = portage.locks.lockfile(
-                                               os.path.join(mysettings["DISTDIR"],
-                                               locks_in_subdir, myfile), wantnewlockfile=1,
-                                               waiting_msg=waiting_msg)
+                                       lock_file = os.path.join(mysettings["DISTDIR"],
+                                               locks_in_subdir, myfile)
+                               else:
+                                       lock_file = myfile_path
+
+                               lock_kwargs = {}
+                               if fetchonly:
+                                       lock_kwargs["flags"] = os.O_NONBLOCK
                                else:
-                                       file_lock = portage.locks.lockfile(
-                                               myfile_path, wantnewlockfile=1,
-                                               waiting_msg=waiting_msg)
+                                       lock_kwargs["waiting_msg"] = waiting_msg
+
+                               try:
+                                       file_lock = portage.locks.lockfile(myfile_path,
+                                               wantnewlockfile=1, **lock_kwargs)
+                               except portage.exception.TryAgain:
+                                       writemsg((">>> File '%s' is already locked by " + \
+                                               "another fetcher. Continuing...\n") % myfile,
+                                               noiselevel=-1)
+                                       continue
                try:
                        if not listonly:
 
index d91584b7366c55b391ecf341292a5f7e93112b72..66cd16244135944e4ec45ad173be528b53997403 100644 (file)
@@ -57,6 +57,10 @@ class PermissionDenied(PortageException):
        from errno import EACCES as errno
        """Permission denied"""
 
+class TryAgain(PortageException):
+       from errno import EAGAIN as errno
+       """Try again"""
+
 class ReadOnlyFileSystem(PortageException):
        """Read-only file system"""
 
index 004108eb7515cfaf9ed451dd10af71ff5dd4f701..000cd349521e5a93ffdb8a1c97a546c475f59d09 100644 (file)
@@ -5,7 +5,8 @@
 
 
 import errno, os, stat, time, types
-from portage.exception import InvalidData, DirectoryNotFound, FileNotFound
+from portage.exception import DirectoryNotFound, FileNotFound, \
+       InvalidData, TryAgain
 from portage.data import portage_gid
 from portage.util import writemsg
 from portage.localization import _
@@ -17,7 +18,8 @@ def lockdir(mydir):
 def unlockdir(mylock):
        return unlockfile(mylock)
 
-def lockfile(mypath, wantnewlockfile=0, unlinkfile=0, waiting_msg=None):
+def lockfile(mypath, wantnewlockfile=0, unlinkfile=0,
+       waiting_msg=None, flags=0):
        """Creates all dirs upto, the given dir. Creates a lockfile
        for the given directory as the file: directoryname+'.portage_lockfile'."""
        import fcntl
@@ -54,7 +56,8 @@ def lockfile(mypath, wantnewlockfile=0, unlinkfile=0, waiting_msg=None):
                        except OSError, e:
                                if e[0] == 2: # No such file or directory
                                        return lockfile(mypath, wantnewlockfile=wantnewlockfile,
-                                               unlinkfile=unlinkfile, waiting_msg=waiting_msg)
+                                               unlinkfile=unlinkfile, waiting_msg=waiting_msg,
+                                               flags=flags)
                                else:
                                        writemsg("Cannot chown a lockfile. This could cause inconvenience later.\n");
                        os.umask(old_mask)
@@ -78,6 +81,8 @@ def lockfile(mypath, wantnewlockfile=0, unlinkfile=0, waiting_msg=None):
                        raise
                if e.errno in (errno.EACCES, errno.EAGAIN):
                        # resource temp unavailable; eg, someone beat us to the lock.
+                       if flags & os.O_NONBLOCK:
+                               raise TryAgain(mypath)
                        if waiting_msg is None:
                                if isinstance(mypath, int):
                                        print "waiting for lock on fd %i" % myfd
@@ -114,7 +119,7 @@ def lockfile(mypath, wantnewlockfile=0, unlinkfile=0, waiting_msg=None):
                writemsg("lockfile recurse\n",1)
                lockfilename, myfd, unlinkfile, locking_method = lockfile(
                        mypath, wantnewlockfile=wantnewlockfile, unlinkfile=unlinkfile,
-                       waiting_msg=waiting_msg)
+                       waiting_msg=waiting_msg, flags=flags)
 
        writemsg(str((lockfilename,myfd,unlinkfile))+"\n",1)
        return (lockfilename,myfd,unlinkfile,locking_method)