Commit 02107493 authored by Jens Gustedt's avatar Jens Gustedt

mark keywords

parent bcd6d47f
This diff is collapsed.
......@@ -87,8 +87,8 @@ guard {
#+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 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
......@@ -113,7 +113,7 @@ This new technique has at least two advantages to commonly used =C= or
constructor/destructor pairs (=C++=)
For normal control flow (without intermediate =return=, =exit=, ...)
For normal control flow (without intermediate *=return=*, =exit=, ...)
code with similar properties can be coded with existing tools. The
above is equivalent to something like the following
......@@ -153,8 +153,8 @@ above is equivalent to something like the following
#+END_SRC
Here, the =if(false)= clauses guarantee that the deferred statements
are jumped over when they are first met, and the labels and =goto=
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.
......@@ -187,7 +187,7 @@ quite common idiom for cleanup handling in =C=:
#+END_SRC
This has the advantage of only making the circumstantial control flow
explicit (with three =goto=) but it does that for the price of
explicit (with three *=goto=) but it does that for the price of
proximity; the cleanup code is far from the place where its need
arises.
......@@ -198,12 +198,12 @@ advantage of the [[defer][=defer=]] approach:
- maintainability :: 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=
(=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=,
=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
......@@ -213,7 +213,7 @@ flying around, we have a forth important property
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.
guaranteed to be executed if there is a *=try/catch=* block underneath.
This principle of deferred execution extends to nested
guarded blocks in a natural way, even if they are stacked in
......@@ -222,9 +222,9 @@ different function calls.
** Dynamic versus static control flow for deferred statements
In the example above the control flow that was the result of deferring
statements was /static/, that is the =defer= statements themselves
statements was /static/, that is the *=defer=* statements themselves
were not placed into conditionals or loops. Being able to place them
inside an =if=, for example, gives the programmer more flexibility, in
inside an *=if=*, for example, gives the programmer more flexibility, in
particular it allows to only defer a statement if the corresponding
resource has really been needed.
......@@ -241,7 +241,7 @@ resource has really been needed.
#+END_SRC
Here, the deferred statement will only be evaluated at the termination
of the guarded block only in cases where the buffer referenced by p=
of the guarded block only in cases where the buffer referenced by =p=
is dynamically allocated. To support such constructs, our
implementation determines the order and number of times each deferred
statement is executed at run-time. This approach requires allocating
......@@ -254,7 +254,7 @@ deferred statement is executed to release the resource.
If the call succeeds (so a resource is allocated) but the *=defer=*
mechanism fails, the call to *=free=* is evaluated immediately and the
execution of the enclosing guarded block is terminated by a panic with
an error code of =DEFER_ENOMEM=.
an error code of *=DEFER_ENOMEM=*.
** Guarded blocks for function bodies
......@@ -274,7 +274,7 @@ the function body acts as such.
** Triggering the execution of deferred statements
There are three "severity" levels for termination of a guarded block,
=break= or similar just terminates the guarded block, =return=
*=break=* or similar just terminates the guarded block, *=return=*
terminates all guarded blocks of the same function and
[[exit][=exit=]] or [[panic][=panic=]] of the same thread across all
function calls.
......@@ -282,7 +282,7 @@ function calls.
A deferred statement can be triggered in two ways.
- explicitly: by leaving a guarded block when coming to the end of
it or by using =break=, =defer_break=, =exit=
it or by using *=break=*, *=defer_break=*, *=exit=*
... from within.
- implicitly: from a signal handler. Two signal handlers are
......@@ -304,7 +304,7 @@ Once execution starts inside a deferred statement, the condition that
lead there can be investigated by means of [[recover][=recover=]]. This returns
an integer error code that indicates what happened. If it is =0=,
this execution of the defer clause is just "normal" retreat as of
=break=, =return= or =exit=. If it is any other value
*=break=*, *=return=* or =exit=. If it is any other value
something "remarkable" might have happened, generally a
[[panic][=panic=]] is on its way down in the call stack.
......@@ -350,10 +350,10 @@ The important interfaces of this tool are:
- [[guard][=guard=]] prefixes a guarded block
- [[defer][=defer=]] prefixes a defer clause
- =break= ends a guarded block and executes all its defer clauses
- =return= unwinds all guarded blocks of the current function and returns to the caller
- *=break=* ends a guarded block and executes all its defer clauses
- *=return=* unwinds all guarded blocks of the current function and returns to the caller
- =exit= unwinds all defer clauses of all active function calls of the thread and exits normally
- [[panic][=panic=]] starts global unwinding of all guarded blocks
- [[panic][*=panic=*]] starts global unwinding of all guarded blocks
- [[recover][=recover=]] inside a defer clause stops a panic and provides an error code
The features =exit=, =quick_exit= and =thrd_exit= are overloaded to do
......@@ -412,7 +412,7 @@ recover just signals and do nothing if there was none.
** Interoperability with C++
C++ has similar features for out-of-order code execution, namely
destructors and =catch= clauses. The [[defer][=defer=]] statement combines those
destructors and *=catch=* clauses. The [[defer][=defer=]] statement combines those
two features into one.
This implementation provides features to translate between these
......@@ -429,7 +429,7 @@ Nevertheless, to really ensure that resources are released by such a
C++ programm it is a good idea to have all exceptions caught. This
will ensure that all destructors down to and including in =main= are
called when unwound. An easy way to achieve this is to establish
=main= as a =try-catch= block:
=main= as a *=try-catch=* block:
#+BEGIN_SRC C++
int main(int argc, char* argv[]) try {
......@@ -470,10 +470,10 @@ That is, we need
- a local variable of type =std::defer_boundary= for which
the constructor memorizes the state prior to the call,
- a =try/catch= clause that guarantees that exceptions are caught
- a *=try/catch=* clause that guarantees that exceptions are caught
and that all the destructors are called when unwound
- a call to the method =panic()= that either propagates any
- a call to the method *=panic()=* that either propagates any
exception that was caught (if the caller is also =C++=) or that
translates an exception to a panic (if the caller is =C=).
......@@ -507,34 +507,34 @@ stack.
We distinguishes "jumps" that are known to target
the same function (=_Defer_shrtjmp=) and those that are known to
jump to another function on the call stack (=_Defer_longjmp=). The
unwind for a =return= will usually be all short jumps, whereas
unwind for a *=return=* will usually be all short jumps, whereas
=exit= and other unwinding of the call stack always initiates a long
jump.
This implementation is quite hackerish because it uses nested =for=
This implementation is quite hackerish because it uses nested *=for=*
loops to implement the principal macros [[defer][=defer=]] and [[guard][=guard=]]. It does
so to ensure a mostly library only iplementation through macros, that
could in principle work with any C compiler. This technique leads to
code that is difficult to read and can therefore not be recommended.
Also, we use a intermediate processor for the production of the C (and
C++) code called [[https://gustedt.gitlabpages.inria.fr/shnell/][shnell]]. This uses =#pragma= annotations for the
C++) code called [[https://gustedt.gitlabpages.inria.fr/shnell/][shnell]]. This uses *=#pragma=* annotations for the
definition of complicated macros and for "code unrolling". So if you'd
want to modify this implementation, you'd have to download shnell and
modify the "real" sources.
*** Extension: using computed =goto=
*** Extension: using computed *=goto=*
The implementation can distinguish cases where basically all jumps
are finally implemented as being long, or platforms where some
shortcut for a short jump can be taken. Currently this is only
implemented for gcc and friends that implement so-called "computed
=goto=", that is labels for which addresses can be taken, and
where these addresses then can be used in an extended =goto=
where these addresses then can be used in an extended *=goto=*
feature. Such a specialized implementation can gain a lot on the
unwind side (these then are mostly simple jumps), but they still
have to keep track of all the guarded blocks and defer clauses
with =setjmp= because these could be jumped to from other
with *=setjmp=* because these could be jumped to from other
functions or from signal handlers.
*** Extension: Captured values for [[defer][=defer=]]
......@@ -610,7 +610,7 @@ implementation will still work, but precautions as described for
*** <<caveats>> Caveats
This implementation is special, because it is almost a "header only"
implementation that overloads =return= (plus some others) with a
implementation that overloads *=return=* (plus some others) with a
macro. This is not ideal and can have some performance issues or even
compilation failures with system-provided inline functions. As a rule
of thumb, try to include the =<stddefer.h>= header as late as
......@@ -619,7 +619,7 @@ possible, such that there are as few interactions as possible.
* Reference manual
<<defer>>
** =defer=: ensure the execution of the deferred statement at the end of the guarded block
** *=defer=: ensure the execution of the deferred statement at the end of the guarded block
#+BEGIN_SRC C
......@@ -633,7 +633,7 @@ such a call to [[panic][=panic=]] must only occur *after* the current panic stat
had been tested with [[recover][=recover=]].
The deferred statement may use any local variable that is visible
where the =defer= is placed and that is still alive when the deferred
where the *=defer=* is placed and that is still alive when the deferred
statement is executed, that is at the end of the surrounding [[guard][=guard=]]
or function body. This property is checkable at compile time, and a
violation usually results in an abortion of the compilation. This
......@@ -643,12 +643,12 @@ success of that depends on the presence of some synchronization
feautures.
If such synchronization features are not available, local variables
that may change between the =defer= itself and the execution of the
that may change between the *=defer=* itself and the execution of the
deferred statement and that are used in the deferred statement must be
declared =volatile= such that the latest value is taken into
declared *=volatile=* such that the latest value is taken into
account. So as a rule of thumb, variables that you use inside a
deferred statement should be qualified: =const= qualified for those
where the defer clause should use the original value, and =volatile=
deferred statement should be qualified: *=const=* qualified for those
where the defer clause should use the original value, and *=volatile=*
qualified for those that should be used with their latest changes.
The implementation uses allocation as of =calloc= and =free= to
......@@ -673,23 +673,23 @@ executed, address-less local variables with the same name and type
are initialized with these frozen values.
<<guard>>
** =guard=: mark a whole block as "guarded" and as using the defer mechanism.
** *=guard=: mark a whole block as "guarded" and as using the defer mechanism.
#+BEGIN_SRC C
guard compound-statement
#+END_SRC
If such a block is terminated normally or with a =break= or =continue=
If such a block is terminated normally or with a *=break=* or *=continue=*
statement, all deferred statements that have been registered a [[defer][=defer=]]
statement are executed in reverse order of their registration. There
is also a macro =defer_break= that can be used in contexts where
=break= or =continue= statements would refer to an inner loop or
=switch= statement. Also, =return=, =exit=, =quick_exit= and
is also a macro *=defer_break=* that can be used in contexts where
*=break=* or *=continue=* statements would refer to an inner loop or
*=switch=* statement. Also, *=return=*, =exit=, =quick_exit= and
=thrd_exit= all trigger the execution of deferred statements, up to
their respective levels of nestedness of guarded blocks.
Other standard means of non-linear control flow out of or into the
block (=goto=, =longjmp=, =_Exit=, =abort=), do not invoke that
block (*=goto=*, =longjmp=, =_Exit=, =abort=), do not invoke that
mechanism and may result in memory leaks or other damage when used
within such a guarded block.
......@@ -698,7 +698,7 @@ For some of these constructs, there are replacements =defer_goto=,
<<panic>>
** =panic=: Unwind the whole call stack and execute the deferred statements on the way down
** *=panic=: Unwind the whole call stack and execute the deferred statements on the way down
#+BEGIN_SRC C
noreturn void panic(int C);
......@@ -796,7 +796,7 @@ above. The use of this macro is not recommended.
<<recover>>
** =recover=: stop a panic and/or return such the error code
** *=recover=: stop a panic and/or return such the error code
#+BEGIN_SRC C
int recover(void);
......@@ -805,13 +805,13 @@ int recover(void);
A call to this function must reside within a deferred statement.
This will only stop unwinding of the panic if the error code had not
been =0=. Normal =break=, =return=, =thrd_exit=, =exit= etc will not
been =0=. Normal *=break=*, *=return=*, *=thrd_exit=*, =exit= etc will not
be be stopped and the guarded block, function, thread or program will
finish when all the associated deferred statements have been executed.
If the error code is non-zero, execution of the defer clause
then continues as if the nearest guarded block had been broken by
=break=. That is, the execution of the defer clauses of
*=break=*. That is, the execution of the defer clauses of
that particular guarded block are continued and then execution
resumes at the end of that guarded block.
......@@ -843,15 +843,15 @@ defer {
#+END_SRC
This stops an unwind originating from a =panic= call or from a signal
This stops an unwind originating from a *=panic=* call or from a signal
with non-zero error code (or similar), returns the error code in a
local =int= variable named =err=, and executes the depending statement
if the value is non-zero, or an alternative =else= clause, if any, if
the value is zero. The =else= clause is optional.
local *=int=* variable named =err=, and executes the depending statement
if the value is non-zero, or an alternative *=else=* clause, if any, if
the value is zero. The *=else=* clause is optional.
the variable =err= can then be used inside the =if= or =else= clauses,
the variable =err= can then be used inside the *=if=* or *=else=* clauses,
but it is not mutable and its address cannot be taken. And it is a bit
useless within the =else= clause, because we know that it is zero,
useless within the *=else=* clause, because we know that it is zero,
there.
<<defer_show>>
......@@ -873,7 +873,7 @@ int recover_signal(void);
This differs from [[recover][=recover=]] because it only reacts on signals that are
caught by the provided signal handlers (=defer_sig_flag= and
=defer_sig_jump=), not on other =break= or [[panic][=panic=]] events. It can be
=defer_sig_jump=), not on other *=break=* or [[panic][=panic=]] events. It can be
used in any context and does not need a surrounding guarded block or
[[defer][=defer=]] clause. Its cost is the lookup of a thread local variable and
a memory synchronization with that variable. So it should probably not
......@@ -905,8 +905,8 @@ int main(void) {
#+END_SRC
Here, an =INT= event can occur at any point of the computation before
or inside the =while= loop. At the first detection of a signal, the
=while= loop then is broken, and execution continues after it. This
or inside the *=while=* loop. At the first detection of a signal, the
*=while=* loop then is broken, and execution continues after it. This
guarantees that the whole loop body is always executed until its end.
** Signal handlers
......
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