Rewrote error & log functions to be more general, use longjmp to get out of
trouble and into predictable cleanup functions (one for each of serve, client & control contexts). We use 'fatal' to mean 'kill the thread' and 'error' to mean 'don't kill the thread', assuming some recovery action, except I don't use error anywhere yet.
This commit is contained in:
108
src/util.h
108
src/util.h
@@ -3,36 +3,106 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
|
||||
void error_init();
|
||||
|
||||
void error(int consult_errno, int fatal, int close_socket, pthread_mutex_t* unlock, const char* format, ...);
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
void* xrealloc(void* ptr, size_t size);
|
||||
|
||||
void* xmalloc(size_t size);
|
||||
|
||||
void set_debug(int value);
|
||||
typedef void (cleanup_handler)(void* /* context */, int /* is fatal? */);
|
||||
|
||||
/* set from 0 - 5 depending on what level of verbosity you want */
|
||||
extern int log_level;
|
||||
|
||||
/* set up the error globals */
|
||||
void error_init();
|
||||
|
||||
/* error_set_handler must be a macro not a function due to setjmp stack rules */
|
||||
#include <setjmp.h>
|
||||
|
||||
struct error_handler_context {
|
||||
jmp_buf jmp;
|
||||
cleanup_handler* handler;
|
||||
void* data;
|
||||
};
|
||||
|
||||
#define DECLARE_ERROR_CONTEXT(name) \
|
||||
struct error_handler_context *name = (struct error_handler_context*) \
|
||||
pthread_getspecific(cleanup_handler_key)
|
||||
|
||||
/* clean up with the given function & data when error_handler() is invoked,
|
||||
* non-fatal errors will also return here (if that's dangerous, use fatal()
|
||||
* instead of error()).
|
||||
*
|
||||
* error handlers are thread-local, so you need to call this when starting a
|
||||
* new thread.
|
||||
*/
|
||||
extern pthread_key_t cleanup_handler_key;
|
||||
#define error_set_handler(cleanfn, cleandata) \
|
||||
{ \
|
||||
DECLARE_ERROR_CONTEXT(old); \
|
||||
struct error_handler_context *context = \
|
||||
xmalloc(sizeof(struct error_handler_context)); \
|
||||
context->handler = (cleanfn); \
|
||||
context->data = (cleandata); \
|
||||
\
|
||||
switch (setjmp(context->jmp)) \
|
||||
{ \
|
||||
case 0: /* setup time */ \
|
||||
if (old) \
|
||||
free(old); \
|
||||
pthread_setspecific(cleanup_handler_key, context); \
|
||||
break; \
|
||||
case 1: /* fatal error, terminate thread */ \
|
||||
context->handler(context->data, 1); \
|
||||
pthread_exit((void*) 1); \
|
||||
abort(); \
|
||||
case 2: /* non-fatal error, return to context of error handler setup */ \
|
||||
context->handler(context->data, 0); \
|
||||
default: \
|
||||
abort(); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
/* invoke the error handler - longjmps away, don't use directly */
|
||||
void error_handler(int fatal);
|
||||
|
||||
/* mylog a line at the given level (0 being most verbose) */
|
||||
void mylog(int line_level, const char* format, ...);
|
||||
|
||||
#ifdef DEBUG
|
||||
void debug(const char*msg, ...);
|
||||
# define debug(msg, ...) mylog(0, "%s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
/* no-op */
|
||||
# define debug( msg, ...)
|
||||
# define debug(msg, ...) /* no-op */
|
||||
#endif
|
||||
|
||||
#define CLIENT_ERROR(msg, ...) \
|
||||
error(0, 0, client->socket, &client->serve->l_io, msg, ##__VA_ARGS__)
|
||||
#define CLIENT_ERROR_ON_FAILURE(test, msg, ...) \
|
||||
if (test < 0) { error(1, 0, client->socket, &client->serve->l_io, msg, ##__VA_ARGS__); }
|
||||
/* informational message, not expected to be compiled out */
|
||||
#define info(msg, ...) mylog(1, "%s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
#define SERVER_ERROR(msg, ...) \
|
||||
error(0, 1, 0, NULL, msg, ##__VA_ARGS__)
|
||||
#define SERVER_ERROR_ON_FAILURE(test, msg, ...) \
|
||||
if (test < 0) { error(1, 1, 0, NULL, msg, ##__VA_ARGS__); }
|
||||
/* messages that might indicate a problem */
|
||||
#define warn(msg, ...) mylog(2, "%s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
/* mylog a message and invoke the error handler to recover */
|
||||
#define error(msg, ...) { \
|
||||
mylog(3, "*** %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||
error_handler(0); \
|
||||
}
|
||||
|
||||
#define NULLCHECK(x) \
|
||||
do { if ( NULL == (x) ) { SERVER_ERROR( "Null " #x "." ); } } while(0)
|
||||
/* mylog a message and invoke the error handler to kill the current thread */
|
||||
#define fatal(msg, ...) { \
|
||||
mylog(4, "*** %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||
error_handler(1); \
|
||||
}
|
||||
|
||||
#define ERROR_IF_NULL(value, msg, ...) if (NULL == value) error(msg " (errno=%d, %s)", ##__VA_ARGS__, errno, strerror(errno))
|
||||
#define ERROR_IF_NEGATIVE(value, msg, ...) if (value < 0) error(msg, ##__VA_ARGS__)
|
||||
#define ERROR_IF_ZERO(value, msg, ...) if (0 == value) error(msg, ##__VA_ARGS__)
|
||||
#define FATAL_IF_NULL(value, msg, ...) if (NULL == value) fatal(msg, ##__VA_ARGS__)
|
||||
#define FATAL_IF_NEGATIVE(value, msg, ...) if (value < 0) fatal(msg " (errno=%d, %s)", ##__VA_ARGS__, errno, strerror(errno))
|
||||
#define FATAL_IF_ZERO(value, msg, ...) if (0 == value) fatal(msg, ##__VA_ARGS__)
|
||||
|
||||
#define NULLCHECK(value) FATAL_IF_NULL(value, "BUG: " #value " is null")
|
||||
|
||||
#endif
|
||||
|
||||
|
Reference in New Issue
Block a user