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 |