Gentoo Archives: gentoo-portage-dev

From: Jason Stubbs <jstubbs@g.o>
To: gentoo-portage-dev@l.g.o
Subject: Re: [gentoo-portage-dev] Plugin backport PATCH (1/2)/(2/2)
Date: Sat, 19 Nov 2005 15:49:48
Message-Id: 200511200050.25776.jstubbs@gentoo.org
In Reply to: Re: [gentoo-portage-dev] Plugin backport PATCH (1/2)/(2/2) by Brian Harring
1 On Saturday 19 November 2005 20:48, Brian Harring wrote:
2 > On Sat, Nov 19, 2005 at 01:12:46PM +0900, Jason Stubbs wrote:
3 > > > > +++ ./pym/portage_locks.py      2005-11-16 01:56:25.152161768 -0500
4 > > > > @@ -358,3 +359,91 @@
5 > > > > +# should the fd be left open indefinitely?
6 > > > > +# IMO, it shouldn't, but opening/closing everytime around is
7 > > > > expensive
8 > > > >
9 > > > > It should be closed when locks are released, I think. Otherwise the
10 > > > > file may be deleted and recreated while one FsLock instance has it
11 > > > > open which could create situations where two FsLocks are trying to
12 > > > > lock the same path but using different fds.
13 > > >
14 > > > Leave it open, on locking do a inode verification of the existing
15 > > > handle- if it's behind, close the fd, open, resume locking (offhand).
16 > > >
17 > > > Race potential, but that can be addressed by getting the read lock,
18 > > > then doing a second (or first, if it's not re-upping a lock) inode
19 > > > verification
20 > >
21 > > Umm.. getting complicated. How often are locks actually obtained,
22 > > released and then obtained a second time? Sacrificing readability for
23 > > performance in corner cases doesn't seem quite right.
24 >
25 > It's not really that complicated, and (imo) is a requisite if the
26 > locking obj is used for synchronizing during deletions;
27 >
28 > lock_obtained = False
29 > while not lock_obtained:
30 > if self.fd:
31 > self.read_lock()
32 > # check that the file didn't change under us
33 > if os.fstat(self.fd).st_ino != os.stat(self.path).st_ino):
34 > self.release_lock()
35 > else:
36 > lock_obtained = True
37 >
38 > if not self.fd:
39 > self._acquire_fd()
40 >
41 > Would be a rough example of it, bordering pseudocode (pardon bugs,
42 > 5:48am my time right now). :)
43 >
44 > EIther way, yes, it's a bit complicated, but locking isn't simple; as
45 > long as the api exported by the object is simple, handling the nasty
46 > crap internally shouldn't be an issue.
47
48 I'm not sure what use case you're talking about here... Hmm, actually yes
49 I do. So, you're right in that the above code is needed regardless, but...
50
51 > > Also, with a long running portage
52 > > process and badly written code, it'd be quite easy to run out of fds...
53 >
54 > All locking code (stable included) has this potential though- it's not
55 > something limited to this implementation.
56
57 I still don't see why fds should remain open after all locks are released.
58 What my point above is about is that if some external process creates a bunch
59 of FsLock objects that it keeps around to obtain future locks (bad design,
60 but) fds will quickly become used up when they are not actually being used
61 for anything.
62
63 > > > > +++ ./pym/portage_modules.py    2005-11-16 01:56:28.269687832 -0500
64 > > > > +               # * ferringb|afk hit it at some point, sure of that
65 > > > > +               # but no idea how, so commenting out to see if things
66 > > > > break... +               # except AttributeError, e:
67 > > > > +               #       raise FailedImport(name, e)
68 > > > >
69 > > > > I can't see how either, but comments like these don't really help.
70 > > > > Please leave them out unless they describe how the code works.
71 > > >
72 > > > Leaving comments regarding "weirdness occured via here, but went away"
73 > > > isn't a bad thing- if it reappears, you know that at least it was hit
74 > > > once prior.
75 > > >
76 > > > I know how it was hit also (just dawned on me how it would happen)-
77 > > > echo $'import os;print os.dar' > test.py
78 > > > python -c'import test'
79 > > >
80 > > > AttributeError...
81 > > >
82 > > > Should nuke the module imo in that case still, since it's a failed
83 > > > import.
84 > >
85 > > Yep, so it shouldn't be commented then - which is my point. If code is to
86 > > be going into stable, things like this should be resolved prior to.
87 >
88 > Code needs further work if it's going into stable imo.
89 >
90 > Reason I wanted the code split off from 3.0 and backported was to show
91 > a file based approach rather then trying to do scans at load up.
92
93 So, off topic then, eh? ;) I should actually take the code for a spin then to
94 see how it works.
95
96 > > > > +                                       d[x[:-len_exten]] = dict([(y,
97 > > > > dict(c.items(y))) for y in c.sections()])
98 > > > >
99 > > > > Same here on the 2.2 bit... Although that's pretty hard to read
100 > > > > either way.
101 > > >
102 > > > Prefer a map call? :)
103 > >
104 > > Whatever is most readable (and supported).
105 >
106 > Bit from above is supported in 2.2 and up. Probably early, offhand.
107 > The example is a bit out of context, since the len_exten is defined
108 > above, and clear what it's doing- the dict arg instantiation might be
109 > a bit weird if people don't know that you can (essentially) pass a
110 > zipped set of arrays to dict, which it'll convert into key->vals...
111 > that said, chunking out a replacement of 3 lines seems a bit pointless
112 > imo.
113
114 Hmm.. I know the dict zipping bit, but no I can't see what it's doing. "x" is
115 defined 6 lines above and seems to be a ptype. From what I know so far, I
116 guess an example would be "cache"? "c" is defined 5 lines above and is an
117 instance of RawConfigParser that has had read(os.path.join(plugins_dir,
118 x.lstrip(os.path.sep))) called on it.
119
120 After reading over the patch in it's (incomplete ;) entirety, I'm guessing
121 that "c" represents a file that defines what plugins have been registered for
122 the current ptype. However, in this context the only indication of what that
123 file contains is:
124
125 + if raw:
126 +   d[x[:-len_exten]] = c
127 + else:
128 +  d[x[:-len_exten]] = dict([(y, dict(c.items(y))) for y in c.sections()])
129
130 raw is a parameter/undocumented and d is the return type. This is what I mean
131 by not readable. (Sorry to be harsh/dumb)
132
133 > > > > +               finally:
134 > > > > +                       if locking:
135 > > > > +                               plug_lock.release_read_lock()
136 > > > > +               if plugin_type != None:
137 > > > > +                       return d[plugin_type]
138 > > > > +               return d
139 > > > >
140 > > > > Not sure about having different return types...
141 > > >
142 > > > Agreed.
143 > >
144 > > So one method for querying what plugin types are available and another
145 > > for querying plugins of a given type?
146 >
147 > Or one for querying the full tree, one for pulling a specific
148 > branch...
149
150 Or all three? ;)
151
152 This is a difference in style, I guess. A list of plugin types plus a method
153 to enumerate registered plugins for each type allows one to create a tree of
154 plugins on one's own. Providing the tree of plugins means one has to extract
155 information from a return and then throw away the rest. Performance-wise
156 they're probably both about the same when talking the non-catered case...
157
158 > > > > +registry = None
159 > > > > +from portage.util.currying import pre_curry, pretty_docs
160 > > > > +
161 > > > > +def proxy_it(method, *a, **kw):
162 > > > > +       global registry
163 > > > > +       if registry == None:
164 > > > > +               registry = GlobalPluginRegistry()
165 > > > > +       return getattr(registry, method)(*a, **kw)
166 > > > > +
167 > > > > +for x in ["register", "deregister", "query_plugins", "get_plugin"]:
168 > > > > +       v = pre_curry(proxy_it, x)
169 > > > > +       doc = getattr(GlobalPluginRegistry, x).__doc__
170 > > > > +       if doc == None:
171 > > > > +               doc = ''
172 > > > > +       else:
173 > > > > +               # do this so indentation on pydoc __doc__ is sane
174 > > > > +               doc = "\n".join(map(lambda x:x.lstrip(),
175 > > > > doc.split("\n"))) +"\n" +       doc += "proxied call to module level
176 > > > > registry instances %s method" % x +       globals()[x] =
177 > > > > pretty_docs(v, doc)
178 > > > > +
179 > > > > +del x, v, proxy_it, doc
180 > > > >
181 > > > > What's this currying and pretty_docs stuff?
182 > > >
183 > > > currying == arg binding to a func- in this case, proxy_it gets first
184 > > > arg of the attr requested; the proxy_it func instantiates a
185 > > > GlobalPluginRegistry instance if it's missing.
186 > >
187 > > This stuff seems to be missing from the patch then.
188 >
189 > Offhand, looking over the split patch, this won't even work.
190 > The from portage.util.currying import ... is still savior namespace.
191 >
192 > Curious, qualms about using a massively cleaned up version of this?
193 > The reason I wanted this seperated out and backported was to go with
194 > an actual registry, and abuse the existing code- specific complaints
195 > with that route, beyond patch sucking?
196
197 None here. For what it's worth at this late stage, this patch is much better
198 than my attempt. :)
199
200 --
201 Jason Stubbs
202
203 --
204 gentoo-portage-dev@g.o mailing list

Replies

Subject Author
Re: [gentoo-portage-dev] Plugin backport PATCH (1/2)/(2/2) Brian Harring <ferringb@g.o>