On 2015-10-19 at 05:37 ron minnich <
rmin...@gmail.com> wrote:
> I got curious and asked a buddy of mine who used to be at the Labs
> who's been in the community a bit longer than me :-)
>
>
> His comment:
> ----
> I don't have easy access to the Plan 9 codebase (a neverending story,
> mostly my fault), but yes, that looks wrong.
>
> The usual sequence would be
>
> do thing
> if(waserror()){
> undo thing
> nexterror() or return as appropriate
> }
> ...
> poperror();
> undo thing
This seems to be the more correct way. Doing the mallocs and qlocks
later assumes they do not throw.
It is true, however, that those do not throw - yet! Eventually, I want
to allow any function that blocks to throw. That would allow syscall
aborts in more places. Right now, you can only abort when in a
rendez. I looked into this very thing a year or so ago when I did the
syscall aborting, and I saw that qlocks are not allowed to throw (based
on their current usage).
There are some places where the waserror handling gets tricky. Check
out devwalk:
volatile int alloc; /* to keep waserror from optimizing this out */
struct walkqid *wq;
[...]
alloc = 0;
wq = kzmalloc(sizeof(struct walkqid) + nname * sizeof(struct qid),
KMALLOC_WAIT);
if (waserror()) {
if (alloc && wq->clone != NULL)
cclose(wq->clone);
kfree(wq);
poperror();
return NULL;
}
Later on, alloc gets set, so we know that wq exists. But the compiler
is smart, and it saw that alloc == 0, and it optimized out the cclose()
(until I made alloc a volatile).
> There's also an idiom (not used by me)
>
> say, allocate something
> if(!waserror()){
> do something that might make an error return
> poperror();
> }
> say, free the allocated thing
>
> where you only care about catching the error, not about returning that
> to a higher level.
Yeah, I call those the 'discard' style.
/* "discard the error" style (we run the conditional code) */
if (!waserror()) {
rendez_init(&rv);
rendez_sleep_timeout(&rv, ret_zero, 0, usec);
}
Barret