2005-02-14 02:49:59 +00:00
|
|
|
#include <pthread.h>
|
2005-02-16 02:13:54 +00:00
|
|
|
#include <lladd/common.h>
|
|
|
|
#ifndef __COMPENSATIONS_H
|
|
|
|
#define __COMPENSATIONS_H
|
|
|
|
|
|
|
|
BEGIN_C_DECLS
|
2005-02-14 02:49:59 +00:00
|
|
|
|
|
|
|
/** Rants about cpp:
|
|
|
|
|
|
|
|
There seems to be no way to add this syntax:
|
|
|
|
|
|
|
|
foo() {
|
|
|
|
lock * l = hashLookup(foo);
|
|
|
|
|
|
|
|
// stuff
|
|
|
|
|
2005-02-16 02:13:54 +00:00
|
|
|
compensate_(l) {
|
2005-02-14 02:49:59 +00:00
|
|
|
lock(l);
|
|
|
|
// blah blah
|
|
|
|
} with {
|
|
|
|
unlock(l);
|
|
|
|
}
|
|
|
|
|
|
|
|
// more stuff
|
|
|
|
}
|
|
|
|
|
|
|
|
=>
|
|
|
|
|
|
|
|
foo() {
|
|
|
|
push_compensation_stack(lock_c_line_1231, l);
|
|
|
|
|
|
|
|
lock(l);
|
|
|
|
|
|
|
|
pop_compensation_stack(); // remove top stack entry and execute it.
|
|
|
|
}
|
|
|
|
|
|
|
|
void lock_c_line_1231(lock * l) {
|
|
|
|
unlock(l);
|
|
|
|
}
|
|
|
|
|
|
|
|
(note that this syntax doesn't require closures!)
|
|
|
|
|
|
|
|
There are a few problems:
|
|
|
|
|
|
|
|
1: 'compensate' and 'with' need to know the name of the
|
|
|
|
compensation's implementation function.
|
|
|
|
|
|
|
|
2: the 'with' block needs to move its code to the outside of the
|
|
|
|
enclosing function's scope, since nested functions cannot be called
|
|
|
|
after the function they are declared in returns.
|
|
|
|
|
|
|
|
You could try #defining a temporary variable, and reading from it in
|
|
|
|
the 'with' macro, but you seem to need a stack in order to support
|
|
|
|
that.
|
|
|
|
|
|
|
|
Here is the syntax that I've settled on:
|
|
|
|
|
|
|
|
lock_t * l = foo();
|
|
|
|
|
|
|
|
begin_action(unlock, l) {
|
|
|
|
lock(l);
|
|
|
|
// ...
|
|
|
|
} compensate;
|
|
|
|
|
|
|
|
// Or: (not recommended)
|
|
|
|
|
|
|
|
begin_action(unlock, l) {
|
|
|
|
lock(l);
|
|
|
|
// ...
|
|
|
|
unlock(l);
|
|
|
|
} end_action;
|
|
|
|
|
|
|
|
// This is a useful variant, however:
|
|
|
|
|
|
|
|
lock(l);
|
|
|
|
// while loop , complicated stuff, etc
|
|
|
|
{
|
|
|
|
begin_action(unlock, l) {
|
|
|
|
// something that can cause an error.
|
|
|
|
} end_action;
|
|
|
|
|
|
|
|
}
|
|
|
|
unlock(l);
|
|
|
|
|
|
|
|
In all cases, an error can be raised using:
|
|
|
|
|
|
|
|
compensation_set_error(int i);
|
|
|
|
|
|
|
|
If an error is set, then each instance of begin_action and
|
|
|
|
end_action will cause the function to return, as appropriate.
|
|
|
|
|
|
|
|
Currently, nesting begin_actions within each other in the same
|
|
|
|
function will not work. This could probably be partially fixed by
|
|
|
|
replacing return statements with 'break' statements, or a GOTO to
|
|
|
|
the proper enclosing end_action/compensate. There may be a way to
|
|
|
|
#define/#undefine a variable in a way that would handle this
|
|
|
|
properly.
|
|
|
|
|
|
|
|
Also, begin_action(NULL, NULL) is supported, and is useful for
|
2005-02-16 04:11:14 +00:00
|
|
|
checking the return value of a called function, but, for
|
|
|
|
efficiency, try{ } end; is recommended
|
2005-02-14 02:49:59 +00:00
|
|
|
*/
|
2005-02-16 02:13:54 +00:00
|
|
|
|
2005-02-14 02:49:59 +00:00
|
|
|
void compensations_init();
|
|
|
|
void compensations_deinit();
|
|
|
|
int compensation_error();
|
|
|
|
void compensation_clear_error();
|
|
|
|
void compensation_set_error(int code);
|
|
|
|
|
2005-02-16 04:11:14 +00:00
|
|
|
#define try do { if(compensation_error()) return; do
|
|
|
|
#define try_ret(x) do { if(compensation_error()) return (x); do
|
|
|
|
|
|
|
|
#define end while(0); if(compensation_error()) return; }while(0)
|
|
|
|
#define end_ret(x) while(0); if(compensation_error()) return (x); }while(0)
|
|
|
|
|
2005-03-10 03:19:04 +00:00
|
|
|
extern int ___compensation_count___;
|
|
|
|
|
2005-02-14 02:49:59 +00:00
|
|
|
#define begin_action(func, var) \
|
|
|
|
if(compensation_error()) return; \
|
|
|
|
do{ \
|
|
|
|
void (*_func_)(void*); \
|
2005-03-10 03:19:04 +00:00
|
|
|
assert(func); \
|
2005-02-14 02:49:59 +00:00
|
|
|
pthread_cleanup_push(_func_=(void(*)(void*))(func), (void*)(var));\
|
2005-03-10 03:19:04 +00:00
|
|
|
assert(_func_); \
|
2005-02-14 02:49:59 +00:00
|
|
|
do
|
2005-02-16 04:11:14 +00:00
|
|
|
/** @todo compensation variables don't need _func_ anymore. */
|
2005-02-14 02:49:59 +00:00
|
|
|
#define end_action \
|
|
|
|
while(0); \
|
2005-03-10 03:19:04 +00:00
|
|
|
pthread_cleanup_pop(/*_func_ &&*/compensation_error()); \
|
2005-02-14 02:49:59 +00:00
|
|
|
if(compensation_error()) return; \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
#define compensate \
|
|
|
|
while(0); \
|
2005-03-10 03:19:04 +00:00
|
|
|
pthread_cleanup_pop(1/*(int)_func_*/); \
|
2005-02-14 02:49:59 +00:00
|
|
|
if(compensation_error()) return; \
|
|
|
|
} while(0)
|
|
|
|
|
2005-02-16 02:13:54 +00:00
|
|
|
#define begin_action_ret(func, var, ret) \
|
|
|
|
if(compensation_error()) return (ret); \
|
|
|
|
do{ \
|
|
|
|
void (*_func_)(void*); \
|
|
|
|
pthread_cleanup_push(_func_=(void(*)(void*))(func), (void*)(var));\
|
|
|
|
do
|
|
|
|
|
|
|
|
#define end_action_ret(ret) \
|
|
|
|
while(0); \
|
2005-03-10 03:19:04 +00:00
|
|
|
pthread_cleanup_pop(/*_func_ &&*/compensation_error()); \
|
2005-02-16 02:13:54 +00:00
|
|
|
if(compensation_error()) return (ret); \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
#define compensate_ret(ret) \
|
|
|
|
while(0); \
|
2005-03-10 03:19:04 +00:00
|
|
|
pthread_cleanup_pop(1/*(int)_func*/); \
|
2005-02-16 02:13:54 +00:00
|
|
|
if(compensation_error()) return (ret); \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
#define compensated_function
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
END_C_DECLS
|
|
|
|
|