Commit 39dd5f0f authored by Jens Gustedt's avatar Jens Gustedt

work on the introduction and add a figure for the back to front jumping

parent f691df9e
*.a
*.bak
*.mk
*.o
*.so
......@@ -10,3 +11,5 @@ defer3
defer4
test_lang
test_lang++
README.pdf
README.tex
......@@ -18,7 +18,7 @@ pages:
stage: build
script:
- mkdir .public
- cp -r *.html *.css .public
- cp -r *.html *.css *.png .public
- mv .public/README.html .public/index.html
- mv .public public
artifacts:
......
This diff is collapsed.
......@@ -44,75 +44,183 @@
* Description
This is a reference implementation of a [[defer][=defer=]] feature for =C=. This
is a tool that is designed with golang's =defer/panic/recover= in mind,
adapting it to the specificities of =C=.
This is a reference implementation of a [[defer][=defer=]] feature for
=C=. This is a tool that is designed with golang's
=defer/panic/recover= in mind, adapting it to the specificities of
=C=. It aims to provide four properties to the handling of
circumstantial cleanup or error handling code:
#+BEGIN_CENTER
*proximity -- visibility -- stability -- robustness*
#+END_CENTER
The intent is to integrate the basic tools that are provided here into
the =C= standard by providing a new library clause for a new header with
the proposed name =<stddefer.h>=.
** An example for static cleanup handling
In the following code snippet we
have one /guarded block/ and three /deferred statements/.
#+BEGIN_SRC C
guard {
void*const p = malloc(25);
defer {
free(p);
defer free(p);
if (something_bad) {
break;
}
void*const q = malloc(25);
defer {
free(q);
}
defer free(q);
if (something_bad) {
break;
}
...
mtx_lock(&mut);
defer mtx_unlock(&mut);
... do your thing ...
}
#+END_SRC
The idea is that we indicate with a =defer= keyword that the
statement, e.g a call to =free=, is only to be executed at the end of
the guarded block, and, that we want this action to happen
unconditionally in which way ever the guarded block is left. For the
three deferred statements together it says that they should be
executed in the inverse order than they were encountered. So the
control flow for this code example can be visualized as follows:
[[file:defer-image.png]]
Here, the dashed lines represent circumstancial control flow that
might arise when some resources are not available or when the
execution is interrupted by a signal.
This new technique has at least two advantages to commonly used =C= or
=C++= techniques:
- proximity :: The cleanup code (=free= or =mtx_unlock=) is coded
close to the place where its need arises.
- visibility :: The cleanup code is not hidden in some previously
defined function (such as for =atexit= handlers) or
constructor/destructor pairs (=C++=)
For normal control flow (without intermediate =return=, =exit=, ...)
this is equivalent to something like the following
code with similar properties can be coded with existing tools. The
above is equivalent to something like the following
#+BEGIN_SRC C
{
void*const p = malloc(25);
if (false) {
DEFER0_START:;
free(p);
goto GUARDED_END;
DEFER0_START:
free(p);
goto GUARDED_END;
}
if (something_bad) {
goto DEFER0_START;
}
void*const q = malloc(25);
if (false) {
DEFER1_START:;
free(q);
goto DEFER0_START;
DEFER1_START:
free(q);
goto DEFER0_START;
}
if (something_bad) {
goto DEFER1_START;
goto DEFER1_START;
}
mtx_lock(&mut);
if (false) {
DEFER2_START:
mtx_unlock(&mut);
goto DEFER1_START;
}
...
goto DEFER1_START;
... do your thing ...
goto DEFER2_START;
GUARDED_END:;
}
#+END_SRC
That is, defer clauses inside a guarded block are not executed
directly, but only later ("deferred") when the execution of the whole
block ends, either "naturally" or either because its execution is
interrupted by a =break=, =return=, =exit= or [[panic][=panic=]]. That is, unless
there are nasal deamons flying around,
Here, the =if(false)= clauses guarantee that the deferred statements
are jumped over when they are first met, and the labels and =goto=
statements implement the hops from back to front to execute the
deferred statements eventually.
Obviously, most =C= programmers would not code like this but they
would prefer to write down a /linearization/ of the above, which is a
quite common idiom for cleanup handling in =C=:
#+BEGIN_SRC C
{
void*const p = malloc(25);
if (something_bad) {
goto DEFER0_START;
}
void*const q = malloc(25);
if (something_bad) {
goto DEFER1_START;
}
mtx_lock(&mut);
... do your thing ...
mtx_unlock(&mut);
DEFER1_START:
free(q);
DEFER0_START:
free(p);
}
#+END_SRC
This has the advantage of only making the circumstantial control flow
explicit (with two =goto=) but it does that for the price of
proximity; the cleanup code is far from the place where its need
arises.
Nevertheless, even this linearized form needs some form of naming
convention for the labels. For more complicated code the maintenance
of these jumps can be tricky and prone to errors. This shows another
advantage of the [[defer][=defer=]] approach:
- stability :: The cleanup specification is not dependent on
arbitrary naming such as labels (=C=) or RAII classes
(=C++=) and does not change when [[defer][=defer=]] or =break=
statements are added or removed.
Another important property that is much more difficult to implement in
=C= (and that needs =try/catch= blocks in =C++=) is that *all* exits
from the guarded block are detected and acted upon: =break=, =return=,
[[thrd_exit][=thrd_exit=]], [[exit][=exit=]], [[panic][=panic=]], or
an interruption by a signal. That is, unless there are nasal deamons
flying around, we have a forth important property
- *Any deferred statement is guaranteed to be executed eventually.*
- robustness :: Any deferred statement is guaranteed to be executed
eventually.
This is different from =C++='s handling of destructors, which are only
guaranteed to be executed if there is a =try/catch= block underneath.
......
#FIG 3.2 Produced by xfig version 3.2.7b
Portrait
Center
Metric
A4
100.00
Single
-2
1200 2
5 1 0 3 0 7 50 -1 -1 0.000 0 1 1 0 11182.500 7814.423 12690 7290 12330 6705 9675 7290
1 1 3.00 60.00 120.00
5 1 0 3 0 7 50 -1 -1 0.000 0 0 1 0 11066.935 9344.674 7380 7290 13680 6030 15255 8820
1 1 4.00 90.00 150.00
5 1 0 3 0 7 50 -1 -1 0.000 0 1 1 0 13432.783 7727.779 14400 8415 14580 7425 12330 7290
1 1 4.00 90.00 150.00
5 1 1 3 0 7 50 -1 -1 8.000 0 1 1 0 10777.783 7727.779 11745 8415 11925 7425 9675 7290
1 1 4.00 90.00 150.00
5 1 0 3 0 7 50 -1 -1 0.000 0 1 1 0 8527.500 7814.423 10035 7290 9675 6705 7020 7290
1 1 4.00 90.00 150.00
5 1 1 3 0 7 50 -1 -1 8.000 0 1 1 0 8122.783 7727.779 9090 8415 9270 7425 7020 7290
1 1 3.00 60.00 120.00
2 2 0 1 0 0 50 -1 20 0.000 0 0 -1 0 0 5
12330 7290 12690 7290 12690 7920 12330 7920 12330 7290
2 2 0 1 0 0 50 -1 20 0.000 0 0 -1 0 0 5
9675 7290 10035 7290 10035 7920 9675 7920 9675 7290
2 2 0 1 0 0 50 -1 20 0.000 0 0 -1 0 0 5
7020 7290 7380 7290 7380 7920 7020 7920 7020 7290
2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2
1 1 4.00 90.00 150.00
13320 8415 14445 8415
2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2
1 1 4.00 90.00 150.00
10665 8415 11790 8415
2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2
1 1 4.00 90.00 150.00
8010 8415 9135 8415
2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2
1 1 4.00 90.00 150.00
5355 8415 6480 8415
2 3 1 1 0 7 60 -1 17 4.000 0 0 7 0 0 4
6435 8415 8010 8415 7200 7740 6435 8415
2 3 1 1 0 7 60 -1 17 4.000 0 0 7 0 0 4
9090 8415 10665 8415 9855 7740 9090 8415
2 3 1 1 0 7 60 -1 17 4.000 0 0 7 0 0 4
11745 8415 13320 8415 12510 7740 11745 8415
2 3 0 1 0 7 70 -1 14 0.000 0 0 -1 0 0 5
15255 8820 4725 8820 5310 8415 14400 8415 15255 8820
4 2 0 50 -1 0 17 0.0000 2 195 120 9090 8325 ?\001
4 2 0 50 -1 0 17 0.0000 2 195 120 11745 8325 ?\001
4 1 0 50 -1 2 17 0.0000 2 195 600 7200 8370 defer\001
4 1 0 50 -1 2 17 0.0000 2 195 600 9855 8370 defer\001
4 1 0 50 -1 2 17 0.0000 2 195 600 12510 8370 defer\001
4 1 0 50 -1 2 17 0.0000 2 255 705 9855 8730 guard\001
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment