1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
.TH ERROR 9
.SH NAME
error, nexterror, poperror, waserror \- error handling functions
.SH SYNOPSIS
.ta \w'\fL#define 'u
.B
void error(char*)
.PP
.B
void nexterror(void)
.sp 0.1
.PP
.B
#define poperror() (up->nerrlab--)
.PP
.B
#define waserror() (setlabel(&up->errlab[up->nerrlab++]))
.SH DESCRIPTION
The kernel handles error conditions using non-local gotos,
similar to
.IR setjmp (2),
but using a stack of error labels to implement nested exception handling.
This simplifies many of the internal interfaces by eliminating the need
for returning and checking error codes at every level of the call stack,
at the cost of requiring kernel routines to adhere to a strict discipline.
.PP
Each process has in its defining kernel
.B Proc
structure a stack of labels,
.B NERR
(currently 64) elements deep.
A kernel function that must perform a clean up or recovery action on an error
makes a stylised call to
.IR waserror ,
.IR nexterror
and
.IR poperror :
.IP
.EX
.DT
if(waserror()){
/* recovery action */
nexterror();
}
/* normal action */
poperror();
.EE
.PP
When called in the normal course of events,
.I waserror
registers an error handling block by pushing its label onto the stack,
and returns zero.
The return value of
.I waserror
should be tested as shown above.
If non-zero (true), the calling function should perform the needed
error recovery, ended by a call to
.I nexterror
to transfer control to the next location on the error stack.
Typical recovery actions include deallocating memory, unlocking resources, and
resetting state variables.
.PP
Within the recovery block,
after handling an error condition, there must normally
be a call to
.I nexterror
to transfer control to any error recovery lower down in the stack.
The main exception is in the outermost function in a process,
which must not call
.I nexterror
(there being nothing further on the stack), but calls
.I pexit
(see
.IR kproc (9))
instead,
to terminate the process.
.PP
When the need to recover a particular resource has passed,
a function that has called
.I waserror
must
remove the corresponding label from the stack by calling
.IR poperror .
This
must
be done before returning from the function; otherwise, a subsequent call to
.I error
will return to an obsolete activation record, with unpredictable but unpleasant consequences.
.PP
.I Error
copies the given error message, which is limited to
.B ERRMAX
bytes, into the
.B Proc.errstr
of the current process,
enables interrupts by calling
.I spllo
.RI ( native
only),
and finally calls
.I nexterror
to start invoking the recovery procedures currently stacked by
.IR waserror .
The file
.B /sys/src/9/port/error.h
offers a wide selection of predefined error messages, suitable for almost any occasion.
The message set by the most recent call to
.I error
can be obtained within the kernel by examining
.B up->error
and in an application, by using the
.L %r
directive of
.IR print (2).
.PP
A complex function can have nested error handlers.
A
.I waserror
block will follow the acquisition of a resource, releasing it
on error before calling
.I nexterror,
and a
.I poperror
will precede its release in the normal case.
For example:
.IP
.EX
.DT
void
outer(Thing *t)
{
qlock(t);
if(waserror()){ /* A */
qunlock(t);
nexterror();
}
m = mallocz(READSTR, 0);
if(m == nil)
error(Enomem); /* returns to A */
if(waserror()){ /* B */
free(m);
nexterror(); /* invokes A */
}
inner(t);
poperror(); /* pops B */
free(m);
poperror(); /* pops A */
qunlock(t);
}
.sp 1v
void
inner(Thing *t)
{
if(t->bad)
error(Egreg); /* returns to B */
t->valid++;
}
.EE
.SH SOURCE
.B /sys/src/9/port/proc.c
.SH CAVEATS
The description above has many instances of
.IR should ,
.IR will ,
.I must
and
.IR "must not" .
.SH SEE ALSO
.IR panic (9),
.IR kproc (9),
.IR splhi (9)
|