1 |
On Sat, Nov 19, 2005 at 01:12:46PM +0900, Jason Stubbs wrote: |
2 |
> > > +++ ./pym/portage_locks.py 2005-11-16 01:56:25.152161768 -0500 |
3 |
> > > @@ -358,3 +359,91 @@ |
4 |
> > > +# should the fd be left open indefinitely? |
5 |
> > > +# IMO, it shouldn't, but opening/closing everytime around is expensive |
6 |
> > > |
7 |
> > > It should be closed when locks are released, I think. Otherwise the file may |
8 |
> > > be deleted and recreated while one FsLock instance has it open which could |
9 |
> > > create situations where two FsLocks are trying to lock the same path but |
10 |
> > > using different fds. |
11 |
> > Leave it open, on locking do a inode verification of the existing |
12 |
> > handle- if it's behind, close the fd, open, resume locking (offhand). |
13 |
> > |
14 |
> > Race potential, but that can be addressed by getting the read lock, |
15 |
> > then doing a second (or first, if it's not re-upping a lock) inode |
16 |
> > verification |
17 |
> |
18 |
> Umm.. getting complicated. How often are locks actually obtained, released |
19 |
> and then obtained a second time? Sacrificing readability for performance in |
20 |
> corner cases doesn't seem quite right. |
21 |
It's not really that complicated, and (imo) is a requisite if the |
22 |
locking obj is used for synchronizing during deletions; |
23 |
|
24 |
lock_obtained = False |
25 |
while not lock_obtained: |
26 |
if self.fd: |
27 |
self.read_lock() |
28 |
# check that the file didn't change under us |
29 |
if os.fstat(self.fd).st_ino != os.stat(self.path).st_ino): |
30 |
self.release_lock() |
31 |
else: |
32 |
lock_obtained = True |
33 |
|
34 |
if not self.fd: |
35 |
self._acquire_fd() |
36 |
|
37 |
Would be a rough example of it, bordering pseudocode (pardon bugs, |
38 |
5:48am my time right now). :) |
39 |
|
40 |
EIther way, yes, it's a bit complicated, but locking isn't simple; as |
41 |
long as the api exported by the object is simple, handling the nasty |
42 |
crap internally shouldn't be an issue. |
43 |
|
44 |
|
45 |
> Also, with a long running portage |
46 |
> process and badly written code, it'd be quite easy to run out of fds... |
47 |
All locking code (stable included) has this potential though- it's not |
48 |
something limited to this implementation. |
49 |
|
50 |
> > > + def __del__(self): |
51 |
> > > + # alright, it's 5:45am, yes this is weird code. |
52 |
> > > + try: |
53 |
> > > + if self.fd != None: |
54 |
> > > + self.release_read_lock() |
55 |
> > > + finally: |
56 |
> > > + if self.fd != None: |
57 |
> > > + os.close(self.fd) |
58 |
> > > |
59 |
> > > Allowing an exception to be raised during __del__ is not a good thing... |
60 |
> > |
61 |
> No traceback though... Something like the following (on one line): |
62 |
> |
63 |
> Exception exceptions.Exception: |
64 |
> <exceptions.Exception instance at 0x2aaaaab2db48> in |
65 |
> <bound method foo.__del__ of <__main__.foo instance at 0x2aaaaab2d5f0>> |
66 |
> ignored |
67 |
> |
68 |
> I guess it's enough to point out where debugging needs to start... |
69 |
Exactly. Misbehaving __del__ code can be fun to track, usually prefer |
70 |
the ugly complaints dumped to term to silence. |
71 |
|
72 |
> > > +++ ./pym/portage_modules.py 2005-11-16 01:56:28.269687832 -0500 |
73 |
> > > + # * ferringb|afk hit it at some point, sure of that |
74 |
> > > + # but no idea how, so commenting out to see if things break... |
75 |
> > > + # except AttributeError, e: |
76 |
> > > + # raise FailedImport(name, e) |
77 |
> > > |
78 |
> > > I can't see how either, but comments like these don't really help. Please |
79 |
> > > leave them out unless they describe how the code works. |
80 |
> > Leaving comments regarding "weirdness occured via here, but went away" |
81 |
> > isn't a bad thing- if it reappears, you know that at least it was hit |
82 |
> > once prior. |
83 |
> > |
84 |
> > I know how it was hit also (just dawned on me how it would happen)- |
85 |
> > echo $'import os;print os.dar' > test.py |
86 |
> > python -c'import test' |
87 |
> > |
88 |
> > AttributeError... |
89 |
> > |
90 |
> > Should nuke the module imo in that case still, since it's a failed |
91 |
> > import. |
92 |
> |
93 |
> Yep, so it shouldn't be commented then - which is my point. If code is to be |
94 |
> going into stable, things like this should be resolved prior to. |
95 |
|
96 |
Code needs further work if it's going into stable imo. |
97 |
|
98 |
Reason I wanted the code split off from 3.0 and backported was to show |
99 |
a file based approach rather then trying to do scans at load up. |
100 |
|
101 |
|
102 |
> > > + def query_plugins(self, plugin_type=None, locking=True, raw=False): |
103 |
> > > |
104 |
> > > Not sure about allowing the external callers to disable locking... That |
105 |
> > > should only be used by register and deregister, right? Perhaps breaking |
106 |
> > > the unlocked version into a "protected" method? |
107 |
> > |
108 |
> > Debatable; any protection used people can stomp right past. Not much |
109 |
> > for the "sitting on the porch with the shotgun" approach. |
110 |
> > |
111 |
> > Not really seeing the gain in hiding the locking tbh; shoving it into |
112 |
> > the instance namespace would require locking the attribute to maintain |
113 |
> > thread safety; the only gain is just an extra hoop to jump through if |
114 |
> > I'm after doing something stupid. :) |
115 |
> |
116 |
> Should query_plugins be called without no locking in place? In the code so |
117 |
> far it is only called when a lock is being maintained outside of the method |
118 |
> call. I'm not sure what you are referring to with regard to instance |
119 |
> namespace. To be clear, I'm suggesting: |
120 |
> |
121 |
> def register_plugins(...): |
122 |
> lock_stuff() |
123 |
> do_stuff() |
124 |
> internal_query_plugins() |
125 |
> unlock_stuff() |
126 |
> |
127 |
> def query_plugins(...): |
128 |
> lock_stuff() |
129 |
> internal_query_plugins() |
130 |
> unlock_stuff() |
131 |
> |
132 |
> def internal_query_plugins(...): |
133 |
> do_stuff() |
134 |
> |
135 |
> There's no namespace stuff there... |
136 |
Doable in that route; was assuming you meant to shove the lock into |
137 |
the instance. |
138 |
|
139 |
> > > + d[x[:-len_exten]] = dict([(y, dict(c.items(y))) for y in c.sections()]) |
140 |
> > > |
141 |
> > > Same here on the 2.2 bit... Although that's pretty hard to read either way. |
142 |
> > Prefer a map call? :) |
143 |
> |
144 |
> Whatever is most readable (and supported). |
145 |
Bit from above is supported in 2.2 and up. Probably early, offhand. |
146 |
The example is a bit out of context, since the len_exten is defined |
147 |
above, and clear what it's doing- the dict arg instantiation might be |
148 |
a bit weird if people don't know that you can (essentially) pass a |
149 |
zipped set of arrays to dict, which it'll convert into key->vals... |
150 |
that said, chunking out a replacement of 3 lines seems a bit pointless |
151 |
imo. |
152 |
|
153 |
Dunno. for stable, can go either way, for 3.0, questionable since |
154 |
it's a common enough trick. |
155 |
|
156 |
> > > + finally: |
157 |
> > > + if locking: |
158 |
> > > + plug_lock.release_read_lock() |
159 |
> > > + if plugin_type != None: |
160 |
> > > + return d[plugin_type] |
161 |
> > > + return d |
162 |
> > > |
163 |
> > > Not sure about having different return types... |
164 |
> > Agreed. |
165 |
> |
166 |
> So one method for querying what plugin types are available and another for |
167 |
> querying plugins of a given type? |
168 |
Or one for querying the full tree, one for pulling a specific |
169 |
branch... |
170 |
|
171 |
> > > +registry = None |
172 |
> > > +from portage.util.currying import pre_curry, pretty_docs |
173 |
> > > + |
174 |
> > > +def proxy_it(method, *a, **kw): |
175 |
> > > + global registry |
176 |
> > > + if registry == None: |
177 |
> > > + registry = GlobalPluginRegistry() |
178 |
> > > + return getattr(registry, method)(*a, **kw) |
179 |
> > > + |
180 |
> > > +for x in ["register", "deregister", "query_plugins", "get_plugin"]: |
181 |
> > > + v = pre_curry(proxy_it, x) |
182 |
> > > + doc = getattr(GlobalPluginRegistry, x).__doc__ |
183 |
> > > + if doc == None: |
184 |
> > > + doc = '' |
185 |
> > > + else: |
186 |
> > > + # do this so indentation on pydoc __doc__ is sane |
187 |
> > > + doc = "\n".join(map(lambda x:x.lstrip(), doc.split("\n"))) +"\n" |
188 |
> > > + doc += "proxied call to module level registry instances %s method" % x |
189 |
> > > + globals()[x] = pretty_docs(v, doc) |
190 |
> > > + |
191 |
> > > +del x, v, proxy_it, doc |
192 |
> > > |
193 |
> > > What's this currying and pretty_docs stuff? |
194 |
> > currying == arg binding to a func- in this case, proxy_it gets first |
195 |
> > arg of the attr requested; the proxy_it func instantiates a |
196 |
> > GlobalPluginRegistry instance if it's missing. |
197 |
> |
198 |
> This stuff seems to be missing from the patch then. |
199 |
Offhand, looking over the split patch, this won't even work. |
200 |
The from portage.util.currying import ... is still savior namespace. |
201 |
|
202 |
Curious, qualms about using a massively cleaned up version of this? |
203 |
The reason I wanted this seperated out and backported was to go with |
204 |
an actual registry, and abuse the existing code- specific complaints |
205 |
with that route, beyond patch sucking? |
206 |
~harring |