Gentoo Logo
Gentoo Spaceship




Note: Due to technical difficulties, the Archives are currently not up to date. GMANE provides an alternative service for most mailing lists.
c.f. bug 424647
List Archive: gentoo-portage-dev
Navigation:
Lists: gentoo-portage-dev: < Prev By Thread Next > < Prev By Date Next >
Headers:
To: gentoo-portage-dev@g.o
From: Jason Stubbs <jstubbs@g.o>
Subject: Re: Plugin backport PATCH (1/2)/(2/2)
Date: Sat, 19 Nov 2005 13:12:46 +0900
On Saturday 19 November 2005 08:47, Brian Harring wrote:
> On Sat, Nov 19, 2005 at 12:43:43AM +0900, Jason Stubbs wrote:
> > +++ ./pym/portage_file.py       2005-11-16 01:56:19.157073160 -0500
> > 
> > +                       # if the dir perms would lack +wx, we have to force it
> > +                       force_temp_perms = ((mode & 0300) != 0300)
> > +                       resets = []
> > +                       apath = normpath(os.path.abspath(path))
> > +                       sticky_parent = False
> > +                       creating = False
> > +                       
> > +                       for dir in apath.split(os.path.sep):
> > +                               base = os.path.join(base,dir)
> > +                               try:
> > +                                       st = os.stat(base)
> > +                                       # something exists.  why are we doing isdir?
> > +                                       if not stat.S_ISDIR(st.st_mode):
> > +                                               return False
> > 
> > What's with the comment here?
> I blame crack.
> Rhetorical question in the code, which isn't very clear in hindsight

Dunno.. The code makes complete sense to me by itself. The comment only
confuses.

> > 
> > +                                       # if it's a subdir, we need +wx at least
> > +                                       if apath != base:
> > +                                               if ((st.st_mode & 0300) != 0300):
> > +                                                       try:                    os.chmod(base, (st.st_mode | 0300))
> > +                                                       except OSError: return False
> > +                                                       resets.append((base, st.st_mode))
> > +                                               sticky_parent = (st.st_gid & stat.S_ISGID)
> > 
> > <snip>
> > 
> > +                       try:
> > +                               for base, m in reversed(resets):
> > +                                       os.chmod(base, m)
> > +                               if uid != -1 or gid != -1:
> > +                                       os.chown(base, uid, gid)
> > +                       except OSError:
> > +                               return False
> > 
> > If for some reason the parent dir originally has perms 0000, this code will
> > incorrectly change its uid and gid.
> That's buggy anyways, since it won't play well with /blah

Yep...

> > +++ ./pym/portage_locks.py      2005-11-16 01:56:25.152161768 -0500
> > @@ -358,3 +359,91 @@
> > +# should the fd be left open indefinitely?
> > +# IMO, it shouldn't, but opening/closing everytime around is expensive
> > 
> > It should be closed when locks are released, I think. Otherwise the file may
> > be deleted and recreated while one FsLock instance has it open which could
> > create situations where two FsLocks are trying to lock the same path but
> > using different fds.
> Leave it open, on locking do a inode verification of the existing 
> handle- if it's behind, close the fd, open, resume locking (offhand).
> 
> Race potential, but that can be addressed by getting the read lock, 
> then doing a second (or first, if it's not re-upping a lock) inode 
> verification

Umm.. getting complicated. How often are locks actually obtained, released
and then obtained a second time? Sacrificing readability for performance in
corner cases doesn't seem quite right. Also, with a long running portage
process and badly written code, it'd be quite easy to run out of fds...

> > 
> > +               if not blocking:
> > +                       try:    fcntl.flock(self.fd, flags|fcntl.LOCK_NB)
> > +                       except IOError, ie:
> > +                               if ie.errno == 11:
> > 
> > 11 ?
> 42 is better? ;)
> 
> Should be using errno.EAGAIN instead of hardcoding though...

Yep.

> > +       def __del__(self):
> > +               # alright, it's 5:45am, yes this is weird code.
> > +               try:
> > +                       if self.fd != None:
> > +                               self.release_read_lock()
> > +               finally:
> > +                       if self.fd != None:
> > +                               os.close(self.fd)
> > 
> > Allowing an exception to be raised during __del__ is not a good thing...
> 
> The reason it's left in is that-
> A) try/finally ensures despite whatever crazyness might occur with 
> locking, the fd gets waxed
> B) blow ups in the code are visible, rather then silently swallowed
> C) exceptions in __del__ python complains about, but swallows.
> 
> So... rather then silently ignoring any crazyness, it allows some 
> noise to be made if the locking code does something weird, and also 
> ensures the fd is closed.

No traceback though... Something like the following (on one line):

Exception exceptions.Exception:
<exceptions.Exception instance at 0x2aaaaab2db48> in
<bound method foo.__del__ of <__main__.foo instance at 0x2aaaaab2d5f0>>
ignored

I guess it's enough to point out where debugging needs to start...

> Should check self.fd.closed on a sidenote.
> 
> > Any held lock should be cleaned up too otherwise misbehaving callers could
> > deadlock.
> 
> Closing the fd == release of any locks that are bound to that fd.
> Hence the anal measures to make damn sure the fd gets closed :)

Ok. That works then.

> > +++ ./pym/portage_modules.py    2005-11-16 01:56:28.269687832 -0500

> > +               # * ferringb|afk hit it at some point, sure of that
> > +               # but no idea how, so commenting out to see if things break...
> > +               # except AttributeError, e:
> > +               #       raise FailedImport(name, e)
> > 
> > I can't see how either, but comments like these don't really help. Please
> > leave them out unless they describe how the code works.
> Leaving comments regarding "weirdness occured via here, but went away" 
> isn't a bad thing- if it reappears, you know that at least it was hit 
> once prior.
> 
> I know how it was hit also (just dawned on me how it would happen)- 
> echo $'import os;print os.dar' > test.py
> python -c'import test'
> 
> AttributeError...
> 
> Should nuke the module imo in that case still, since it's a failed 
> import.

Yep, so it shouldn't be commented then - which is my point. If code is to be
going into stable, things like this should be resolved prior to.

> > +++ ./pym/portage_plugins.py    2005-11-16 01:56:51.419168576 -0500
> > @@ -0,0 +1,174 @@
> > +# Copyright: 2005 Gentoo Foundation
> > +# License: GPL2
> > +# $Id:$
> > +
> > +# I don't like this.
> > +# doesn't seem clean/right.
> > 
> > ???
> Someone didn't clean up the files prior to posting this...

Still not understanding, but as long as whatever it is it's addressed and
the comment removed...

> > +               # this could be fine grained down to per plugin_type
> > +               plug_lock = FsLock(plugins_dir)
> > 
> > Then it probably should be. ;)
> It's not needed though.  Need a write lock on the plugins dir for when 
> new plugin types are added; the only gain from it is when read locks 
> are held long term, and parallel registration is occuring (for savior, 
> it's possible, for stable, it isn't, not within the portage process at 
> least).

Hrmm.. Then there should be a comment why the top-level plugins dir is
locked instead. Or perhaps there should be a global lock only while creating
the plugin_type subdir and then just locking the subdir afterward...

> > 
> > +       def query_plugins(self, plugin_type=None, locking=True, raw=False):
> > 
> > Not sure about allowing the external callers to disable locking... That
> > should only be used by register and deregister, right? Perhaps breaking
> > the unlocked version into a "protected" method?
> 
> Debatable; any protection used people can stomp right past.  Not much 
> for the "sitting on the porch with the shotgun" approach.
> 
> Not really seeing the gain in hiding the locking tbh; shoving it into 
> the instance namespace would require locking the attribute to maintain 
> thread safety; the only gain is just an extra hoop to jump through if 
> I'm after doing something stupid. :)

Should query_plugins be called without no locking in place? In the code so
far it is only called when a lock is being maintained outside of the method
call. I'm not sure what you are referring to with regard to instance
namespace. To be clear, I'm suggesting:

def register_plugins(...):
	lock_stuff()
	do_stuff()
	internal_query_plugins()
	unlock_stuff()

def query_plugins(...):
	lock_stuff()
	internal_query_plugins()
	unlock_stuff()

def internal_query_plugins(...):
	do_stuff()

There's no namespace stuff there...

> > +                                       d[x[:-len_exten]] = dict([(y, dict(c.items(y))) for y in c.sections()])
> > 
> > Same here on the 2.2 bit... Although that's pretty hard to read either way.
> Prefer a map call? :)

Whatever is most readable (and supported).

> > 
> > +               finally:
> > +                       if locking:
> > +                               plug_lock.release_read_lock()
> > +               if plugin_type != None:
> > +                       return d[plugin_type]
> > +               return d
> > 
> > Not sure about having different return types...
> Agreed.

So one method for querying what plugin types are available and another for
querying plugins of a given type?

> > +registry = None
> > +from portage.util.currying import pre_curry, pretty_docs
> > +
> > +def proxy_it(method, *a, **kw):
> > +       global registry
> > +       if registry == None:
> > +               registry = GlobalPluginRegistry()
> > +       return getattr(registry, method)(*a, **kw)
> > +
> > +for x in ["register", "deregister", "query_plugins", "get_plugin"]:
> > +       v = pre_curry(proxy_it, x)
> > +       doc = getattr(GlobalPluginRegistry, x).__doc__
> > +       if doc == None:
> > +               doc = ''
> > +       else:
> > +               # do this so indentation on pydoc __doc__ is sane
> > +               doc = "\n".join(map(lambda x:x.lstrip(), doc.split("\n"))) +"\n"
> > +       doc += "proxied call to module level registry instances %s method" % x
> > +       globals()[x] = pretty_docs(v, doc)
> > +
> > +del x, v, proxy_it, doc
> > 
> > What's this currying and pretty_docs stuff?
> currying == arg binding to a func- in this case, proxy_it gets first 
> arg of the attr requested; the proxy_it func instantiates a 
> GlobalPluginRegistry instance if it's missing.

This stuff seems to be missing from the patch then.

--
Jason Stubbs

-- 
gentoo-portage-dev@g.o mailing list

Replies:
Re: Plugin backport PATCH (1/2)/(2/2)
-- Brian Harring
Re: Plugin backport PATCH (1/2)/(2/2)
-- Alec Warner
References:
Plugin backport PATCH (1/2)/(2/2)
-- Alec Warner
Re: Plugin backport PATCH (1/2)/(2/2)
-- Jason Stubbs
Re: Plugin backport PATCH (1/2)/(2/2)
-- Brian Harring
Navigation:
Lists: gentoo-portage-dev: < Prev By Thread Next > < Prev By Date Next >
Previous by thread:
Re: Plugin backport PATCH (1/2)/(2/2)
Next by thread:
Re: Plugin backport PATCH (1/2)/(2/2)
Previous by date:
Re: Re: Bugzilla Bug 112779: New and Improved Way to Handle /etc/portage
Next by date:
Re: Plugin backport PATCH (1/2)/(2/2)


Updated Jun 17, 2009

Summary: Archive of the gentoo-portage-dev mailing list.

Donate to support our development efforts.

Copyright 2001-2013 Gentoo Foundation, Inc. Questions, Comments? Contact us.