cs引擎中的错误处理

typedef struct _neo_err 
{
int error;//用于记录错误类型
int err_stack;//记录错误层次,可以进行错误链跟踪
int flags;//此结构体是否在使用,当释放时并非真正释放时使用
char desc[256];//错误描述
const char *file;//出错的文件
const char *func;//出错的函数
int lineno;//出错的行数
/* internal use only */
struct _neo_err *next;//当此单元不再使用时,放到缓存队列,起到资源池的作用,
//也可以通过把错误结构链接起来起到错误链跟踪作用
} NEOERR;

注:以上代码定义了错误信息结构体,涵盖了一个可能错误的基本信息。

//初始化错误号,并且和相应的错误名字关联起来 
NEOERR *nerr_init (void)
{
NEOERR *err;
if (Inited == 0)
{
#ifdef HAVE_PTHREADS
/* In threaded environments, we have to mutex lock to do this init, but
* we don't want to use a mutex every time to check that it was Inited.
* So, we only lock if our first test of Inited was false
*/
err = mLock(&InitLock);
if (err != STATUS_OK) return nerr_pass(err);
if (Inited == 0) {
#endif
err = uListInit (&Errors, 10, 0);
if (err != STATUS_OK) return nerr_pass(err);
err = nerr_register (&NERR_PASS, "InternalPass");
if (err != STATUS_OK) return nerr_pass(err);
err = nerr_register (&NERR_ASSERT, "AssertError");
if (err != STATUS_OK) return nerr_pass(err);
err = nerr_register (&NERR_NOT_FOUND, "NotFoundError");
if (err != STATUS_OK) return nerr_pass(err);
err = nerr_register (&NERR_DUPLICATE, "DuplicateError");
if (err != STATUS_OK) return nerr_pass(err);
err = nerr_register (&NERR_NOMEM, "MemoryError");
if (err != STATUS_OK) return nerr_pass(err);
err = nerr_register (&NERR_PARSE, "ParseError");
if (err != STATUS_OK) return nerr_pass(err);
err = nerr_register (&NERR_OUTOFRANGE, "RangeError");
if (err != STATUS_OK) return nerr_pass(err);
err = nerr_register (&NERR_SYSTEM, "SystemError");
if (err != STATUS_OK) return nerr_pass(err);
err = nerr_register (&NERR_IO, "IOError");
if (err != STATUS_OK) return nerr_pass(err);
err = nerr_register (&NERR_LOCK, "LockError");
if (err != STATUS_OK) return nerr_pass(err);
err = nerr_register (&NERR_DB, "DBError");
if (err != STATUS_OK) return nerr_pass(err);
err = nerr_register (&NERR_EXISTS, "ExistsError");
if (err != STATUS_OK) return nerr_pass(err);
Inited = 1;
#ifdef HAVE_PTHREADS
}
err = mUnlock(&InitLock);
if (err != STATUS_OK) return nerr_pass(err);
#endif
}
return STATUS_OK;
}

注: 以上代码由于多线程的需要,进行了资源加锁操作,并尽量减少加锁的代价。

表示管理列表实体的结构体 
typedef struct _ulist
{
int flags;
void **items;
int num;
int max;
} ULIST;

下面的代码进行管理列表的初始化

 

NEOERR *uListInit(ULIST **ul, int size, int flags) 
{
ULIST *r_ul;
*ul = NULL;
if (size == 0)
{
size = ULIST_DEFAULT_SIZE;
}
r_ul = (ULIST *) calloc (1, sizeof (ULIST));
if (r_ul == NULL)
{
return nerr_raise(NERR_NOMEM, "Unable to create ULIST: Out of memory");
}
r_ul->items = (void **) calloc (size, sizeof(void *));
if (r_ul->items == NULL)
{
free (r_ul);
return nerr_raise(NERR_NOMEM, "Unable to create ULIST: Out of memory");
}
r_ul->num = 0;
r_ul->max = size;
r_ul->flags = flags;
*ul = r_ul;
return STATUS_OK;
}

基本的操作是分配一块(max*sizeof(void*))内存,然后每一个指针具体指向什么 
根据需要而定。 
在我们这里指向错误的名字,相应指针的索引是错误的代号。 
现在我们已经完成这样的功能:给每个全局的错误编号设定相应的值(int),然后 
以其值为索引,列表的相应的指针指向相应的名字。 

这样当给定一个错误编号,我们在文本化(显示给用户时)就可以显示相应的文字描述了。 
这样的好处可以在引擎实现时简化错误的处理以及一致化,免去不必要的烦恼。 

如果说在出错时,我们每次都需要进行文件名字,函数名字,出错的行数等进行手动设定 
的话,那么一定会很痛苦,而且代码会很臃肿,不利于重用,所以实现了一个宏来填充 
错误结构。 

#define nerr_pass(e) \ 
   nerr_passf(__PRETTY_FUNCTION__,__FILE__,__LINE__,e) 

__PRETTY_FUNCTION__编译器在预处理时会把其替换成相应的函数名,没有运行时开销 
__FILE__相应的文件名字 
__LINE__相应的出错行数 
我们传进的错误信息存储结构NEOERR 

具体过程如下所示. 

 

NEOERR *nerr_passf (const char *func, const char *file, int lineno, NEOERR *err) 
{
NEOERR *nerr;
if (err == STATUS_OK)
return err;
nerr = _err_alloc();
if (nerr == INTERNAL_ERR)
return err;
nerr->error = NERR_PASS;
nerr->func = func;
nerr->file = file;
nerr->lineno = lineno;
nerr->next = err;
return nerr;
}

在进行函数调用的过程中,为了更好的定位错误,我们会以栈的方式打印错误 
那么我们的错误存储单元也会以这种方式存储,当打印的方式我们只需要遍历这个 
列表即可。具体处理在此的体现是nerr->next = err;我们把当前的错误放到队列中去。 

 

 


下面是用于生成相应的错误存储单元的函数.

 

#if defined(USE_C99_VARARG_MACROS) 
#define nerr_raise(e,f,...) \
nerr_raisef(__PRETTY_FUNCTION__,__FILE__,__LINE__,e,f,__VA_ARGS__)
#elif defined(USE_GNUC_VARARG_MACROS)
#define nerr_raise(e,f,a...) \
nerr_raisef(__PRETTY_FUNCTION__,__FILE__,__LINE__,e,f,##a)
#endif

NEOERR *nerr_raisef (const char *func, const char *file, int lineno, int error,
const char *fmt, ...)
{
NEOERR *err;
va_list ap;
err = _err_alloc();
if (err == INTERNAL_ERR)
return err;
va_start(ap, fmt);
vsnprintf (err->desc, sizeof(err->desc), fmt, ap);
va_end(ap);
err->error = error;
err->func = func;
err->file = file;
err->lineno = lineno;
return err;
}

存储单元的分配_err_alloc() 函数的实现如下

先判断是否使用空闲链表以及空闲链表是否为空 
根据相应的情况进行接下来的工作.
static int UseFreeList = 0;
static NEOERR *_err_alloc(void)
{
NEOERR *err;
if (!UseFreeList || FreeList == NULL)
{
err = (NEOERR *)calloc (1, sizeof (NEOERR));
if (err == NULL)
{
ne_warn ("INTERNAL ERROR: Unable to allocate memory for NEOERR");
return INTERNAL_ERR;
}
return err;
}
else
{
err = FreeList;
FreeList = FreeList->next;
}
err->flags |= NE_IN_USE;
err->next = NULL;
return err;
}
FreeList在第一次时为空,那么如果我们希望使用这项功能的时候 
FreeList什么时候会被赋值呢?
答案是在进行错误存储单元进行释放的时候.代码如下所示:

static int _err_free (NEOERR *err)
{
if (err == NULL || err == INTERNAL_ERR)
return 0;
if (err->next != NULL)
_err_free(err->next);
if (UseFreeList)
{
err->next = FreeList;
FreeList = err;
err->flags = 0;
err->desc[0] = '\0';
}
else
{
free(err);
}
return 0;
}

至此完成了以下功能

1,给每种错误定义一个错误编号以及相应的字符串描述 
2,初始化一个链表结构用来把这两项关联起来,他们在引擎整个生存过程中是一直存在的. 
3,定义了工具函数,根据具体策略分配相应的错误存储单元,并且设定相应的用于定位 
一个错误的基本的信息(包括文件名,函数名,错误编号,时间等信息) 
4,为了给出用户友好的出错信息提示,我们把每个出错存储单元根据出错点按照栈的方式 
链接起来,这样一旦上层建筑需要这些信息,就可以很好的跟踪到问题的所在了。 




 

 

posted @ 2012-02-29 00:07  Lesterwang  阅读(551)  评论(0编辑  收藏  举报