GlusterFS 编码规范
1. 每个结构成员需要有一条能够说明其用途的注释
Bad:
gf_lock_t lock; /* lock */
Good:
DBTYPE access_mode; /* access mode for accessing
*the databases, can be
*DB_HASH, DB_BTREE
*(option access-mode <mode>)
*/
2. 在函数的开始声明所有变量
所有函数中的局部变量必须在"{ "之后立刻声明,这样可以帮助跟踪在退出时需要free的内存,同时还有助于debug,因为gdb不能处理在循环或者类似的块中声明的变量
3. 永远要初始化变量
每个局部变量需要在声明的时候就被初始化到一个合理的初值,所有的指针需要初始化为NULL,所有的整数需要初始化为零,或者一个错误代码。
Good:
int ret = 0;
char *databuf = NULL;
int _fd = -1;
4. 永远用常量来初始化
不要用一个表达式来初始化变量
Bad:
pid_t pid = frame->root->pid;
char *databuf = malloc (1024);
5. 验证函数的所有参数
需要检查所有指针类型的参数是否为NULL.
在common-utils.h中定义的宏 VALIDATE,可以验证一个参数是否为NULL,如果是,则写一条log然后跳到err标志处
Good:
VALIDATE(frame);
VALIDATE(this);
VALIDATE(inode);
6. 不要依赖于操作符优先级(健忘症者的福音)
这样的代码很难读,也许其它人没有你对优先级了解的那么多
Bad:
if (op_ret == -1 && errno != ENOENT)
Good:
if ((op_ret == -1) && (errno != ENOENT))
7. 使用精确的数据类型
使用库函数在manual中声明的返回类型,不要用他们的"等价类型"
Bad:
int len = strlen (path);
Good:
size_t len = strlen (path);
8. 不要写类似foo->bar->baz的 指针链
在使用之前需要检查所有指针是否为NULL
9. 检查所有的系统调用或者API函数的返回值(success|failure.
Bad:
close (fd);
Good:
op_ret = close (_fd);
if (op_ret == -1) {
gf_log (this->name, GF_LOG_ERROR,
"close on file %s failed (%s)", real_path,
strerror (errno));
op_errno = errno;
goto out;
}
10.优雅的处理malloc错误
GlusterFS不应该因为缺少内存crash或者退出,如果内存分配失败,调用应该被unwound,错误应该返回给用户
11. 使用返回参数,并且保留函数返回值,来说明函数的成功和失败
如果一个函数需要返回更多的数据,使用指针,返回值一般用来标识函数是否成功。
Bad:
int32_t dict_get_int32 (dict_t *this, char *key);
Good:
int dict_get_int32 (dict_t *this, char *key, int32_t *val);
12. 使用带“n”的字符串函数
Bad:
strcpy (entry_path, real_path);
Good:
strncpy (entry_path, real_path, entry_path_len);
13. 不应该有从来不被执行的代码,或者被注释掉得代码
14. 每个函数中只有一个地方可以UNWIND或者返回
15. 控制每个函数的规模
每个函数控制在2到3页(80行),超过这个规模可以写多个helper函数
helper函数的例子
static int
same_owner (posix_lock_t *l1, posix_lock_t *l2)
{
return ((l1->client_pid == l2->client_pid) &&
(l1->transport == l2->transport));
}
代码风格
括号的位置
使用K&R/Linux 风格
int some_function (...)
{
if (...) {
/* ... */
} else if (...) {
/* ... */
} else {
/* ... */
}
do {
/* ... */
} while (cond);
}
缩进
使用8个空格,并且保证代码中只有空格没有tab
注释
在每个函数之前写一个注释描述他的功能,参数和返回值,说明它是 一个内部函数还是函数
在每个结构之前写一个注释,对每个结构成员写注释
例子:
/**
* hash_name -hash function for filenames
* @par: parent inode number
* @name: basename of inode
* @mod: number of buckets in the hashtable
*
* @return: success: bucket number
* failure: -1
*
* Not for external use.
*/
突出显示竞争区域
pthread_mutex_lock (&mutex);
{
/* code */
}
pthread_mutex_unlock (&mutex);
一个fop的代码框架
首先是初始化,然后是参数检查,所有的错误都会goto 到唯一点"out",在这个地方,代码将检查错误的类型,然后做相应的清理。
int32_t
sample_fop (call_frame_t *frame,
xlator_t *this,
...)
{
char * var1 = NULL;
int32_t op_ret = -1;
int32_t op_errno = 0;
DIR * dir = NULL;
struct posix_fd * pfd =NULL;
VALIDATE_OR_GOTO (frame,out);
VALIDATE_OR_GOTO (this,out);
/* other validations */
dir = opendir (...);
if (dir == NULL) {
op_errno = errno;
gf_log (this->name,GF_LOG_ERROR,
"opendirfailed on %s (%s)", loc->path,
strerror(op_errno));
goto out;
}
/* another system call */
if (...) {
op_errno = ENOMEM;
gf_log(this->name, GF_LOG_ERROR,
"out ofmemory :(");
goto out;
}
/* ... */
out:
if (op_ret == -1) {
/* check for all the cleanupthat needs to be
done */
if (dir) {
closedir (dir);
dir = NULL;
}
if (pfd) {
if (pfd->path)
FREE(pfd->path);
FREE (pfd);
pfd = NULL;
}
}
STACK_UNWIND (frame, op_ret,op_errno, fd);
return 0;
}