public inbox for gentoo-portage-dev@lists.gentoo.org
 help / color / mirror / Atom feed
From: Alec Warner <antarus@gentoo.org>
To: gentoo-portage-dev@lists.gentoo.org
Cc: Zac Medico <zmedico@gentoo.org>
Subject: Re: [gentoo-portage-dev] [PATCH] bindbapi: add reentrant lock method (bug 685236)
Date: Tue, 7 May 2019 10:55:16 -0400	[thread overview]
Message-ID: <CAAr7Pr9t2wYzfYbu6gsqsCsNUxm0irr-i_4uYDsN+XEJOtxeuw@mail.gmail.com> (raw)
In-Reply-To: <20190507001437.25448-1-zmedico@gentoo.org>

[-- Attachment #1: Type: text/plain, Size: 7815 bytes --]

On Mon, May 6, 2019 at 8:15 PM Zac Medico <zmedico@gentoo.org> wrote:

> Add a reentrant lock/unlock method which is useful for things like
> emaint binhost and eclean-pkg. The vardbapi class already provides
> lock/unlock methods that behave the same way.
>

Curious why we are not doing more encapsulation here.

I'd suggest a new class type in locks.py:

class RefCountedLock(object): ...
# This implements the Lock() / Unlock() interface, as well as perhaps
__enter__ and __exit__.
# It uses a lockfile at path and a reference count.

Then in bintree's init, set self.lock to
locks.refCountedLock(self.bintree._pkgindex_file)

def lock(self): # on bintree
  self._lock.Lock()

def unlock(self):
  self._lock.Unlock()

Also curious why we are not implementing enter and exit so we can avoid
unbalanced pairs by using context managers.

e.g. in match(), we could likely write:

with self.dbapi.lock():
  # try to match
  update_pkgindex = self._populate_local()
  if update_pkgindex:
    self._pkgindex_write(update_pkgindex)

If the block raises an exception, __exit__ is still called, so we can
eliminate the finally: blocks and replace them with a careful __exit__
implementation?

-A


>
> Bug: https://bugs.gentoo.org/685236
> Signed-off-by: Zac Medico <zmedico@gentoo.org>
> ---
>  lib/portage/dbapi/bintree.py                  | 46 ++++++++++++++-----
>  lib/portage/emaint/modules/binhost/binhost.py |  9 ++--
>  2 files changed, 38 insertions(+), 17 deletions(-)
>
> diff --git a/lib/portage/dbapi/bintree.py b/lib/portage/dbapi/bintree.py
> index 9c2d877e7..707958858 100644
> --- a/lib/portage/dbapi/bintree.py
> +++ b/lib/portage/dbapi/bintree.py
> @@ -1,4 +1,4 @@
> -# Copyright 1998-2018 Gentoo Foundation
> +# Copyright 1998-2019 Gentoo Authors
>  # Distributed under the terms of the GNU General Public License v2
>
>  from __future__ import unicode_literals
> @@ -94,6 +94,8 @@ class bindbapi(fakedbapi):
>                         ])
>                 self._aux_cache_slot_dict =
> slot_dict_class(self._aux_cache_keys)
>                 self._aux_cache = {}
> +               self._lock = None
> +               self._lock_count = 0
>
>         @property
>         def writable(self):
> @@ -106,6 +108,34 @@ class bindbapi(fakedbapi):
>                 """
>                 return os.access(first_existing(self.bintree.pkgdir),
> os.W_OK)
>
> +       def lock(self):
> +               """
> +               Acquire a reentrant lock, blocking, for cooperation with
> concurrent
> +               processes.
> +               """
> +               if self._lock_count <= 0:
> +                       if self._lock is not None:
> +                               raise AssertionError("already locked")
> +                       # At least the parent needs to exist for the lock
> file.
> +                       ensure_dirs(self.bintree.pkgdir)
> +                       self._lock = lockfile(self.bintree._pkgindex_file,
> wantnewlockfile=True)
> +
> +               self._lock_count += 1
> +
> +       def unlock(self):
> +               """
> +               Release a lock, decrementing the recursion level. Each
> unlock() call
> +               must be matched with a prior lock() call, or else an
> AssertionError
> +               will be raised if unlock() is called while not locked.
> +               """
> +               if self._lock_count <= 1:
> +                       if self._lock is None:
> +                               raise AssertionError("not locked")
> +                       unlockfile(self._lock)
> +                       self._lock = None
> +
> +               self._lock_count -= 1
> +
>         def match(self, *pargs, **kwargs):
>                 if self.bintree and not self.bintree.populated:
>                         self.bintree.populate()
> @@ -545,16 +575,13 @@ class binarytree(object):
>                                 # that changes made by a concurrent
> process cannot be lost. This
>                                 # case is avoided when possible, in order
> to minimize lock
>                                 # contention.
> -                               pkgindex_lock = None
> +                               self.dbapi.lock()
>                                 try:
> -                                       pkgindex_lock =
> lockfile(self._pkgindex_file,
> -                                               wantnewlockfile=True)
>                                         update_pkgindex =
> self._populate_local()
>                                         if update_pkgindex:
>
> self._pkgindex_write(update_pkgindex)
>                                 finally:
> -                                       if pkgindex_lock:
> -                                               unlockfile(pkgindex_lock)
> +                                       self.dbapi.unlock()
>
>                         if getbinpkgs:
>                                 if not
> self.settings.get("PORTAGE_BINHOST"):
> @@ -1110,10 +1137,8 @@ class binarytree(object):
>
>                 # Reread the Packages index (in case it's been changed by
> another
>                 # process) and then updated it, all while holding a lock.
> -               pkgindex_lock = None
> +               self.dbapi.lock()
>                 try:
> -                       pkgindex_lock = lockfile(self._pkgindex_file,
> -                               wantnewlockfile=1)
>                         if filename is not None:
>                                 new_filename = self.getname(cpv,
> allocate_new=True)
>                                 try:
> @@ -1154,8 +1179,7 @@ class binarytree(object):
>                         self._pkgindex_write(pkgindex)
>
>                 finally:
> -                       if pkgindex_lock:
> -                               unlockfile(pkgindex_lock)
> +                       self.dbapi.unlock()
>
>                 # This is used to record BINPKGMD5 in the installed package
>                 # database, for a package that has just been built.
> diff --git a/lib/portage/emaint/modules/binhost/binhost.py
> b/lib/portage/emaint/modules/binhost/binhost.py
> index d3df0cbce..ceb9e87f3 100644
> --- a/lib/portage/emaint/modules/binhost/binhost.py
> +++ b/lib/portage/emaint/modules/binhost/binhost.py
> @@ -1,4 +1,4 @@
> -# Copyright 2005-2014 Gentoo Foundation
> +# Copyright 2005-2019 Gentoo Authors
>  # Distributed under the terms of the GNU General Public License v2
>
>  import errno
> @@ -27,7 +27,6 @@ class BinhostHandler(object):
>                 eroot = portage.settings['EROOT']
>                 self._bintree = portage.db[eroot]["bintree"]
>                 self._bintree.populate()
> -               self._pkgindex_file = self._bintree._pkgindex_file
>                 self._pkgindex = self._bintree._load_pkgindex()
>
>         def _need_update(self, cpv, data):
> @@ -118,9 +117,7 @@ class BinhostHandler(object):
>                                 missing.append(cpv)
>
>                 if missing or stale:
> -                       from portage import locks
> -                       pkgindex_lock = locks.lockfile(
> -                               self._pkgindex_file, wantnewlockfile=1)
> +                       bintree.dbapi.lock()
>                         try:
>                                 # Repopulate with lock held. If
> _populate_local returns
>                                 # data then use that, since _load_pkgindex
> would return
> @@ -174,7 +171,7 @@ class BinhostHandler(object):
>                                 bintree._pkgindex_write(self._pkgindex)
>
>                         finally:
> -                               locks.unlockfile(pkgindex_lock)
> +                               bintree.dbapi.unlock()
>
>                 if onProgress:
>                         if maxval == 0:
> --
> 2.21.0
>
>
>

[-- Attachment #2: Type: text/html, Size: 10544 bytes --]

  reply	other threads:[~2019-05-07 14:55 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-05-07  0:14 [gentoo-portage-dev] [PATCH] bindbapi: add reentrant lock method (bug 685236) Zac Medico
2019-05-07 14:55 ` Alec Warner [this message]
2019-05-07 20:01   ` Zac Medico
2019-05-08 18:05     ` Zac Medico
2019-05-08 18:09       ` Alec Warner
2019-05-10  9:05       ` Zac Medico

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAAr7Pr9t2wYzfYbu6gsqsCsNUxm0irr-i_4uYDsN+XEJOtxeuw@mail.gmail.com \
    --to=antarus@gentoo.org \
    --cc=gentoo-portage-dev@lists.gentoo.org \
    --cc=zmedico@gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox