git_wrapper_x_api_learn
The Git source file wrapper.c wraps many commonly used functions; I’m just taking notes for myself.
xstrbup: strbup + die
xmalloc: calls do_xmalloc; first uses memory_limit_check to check whether the requested allocation size exceeds the environment variable GIT_ALLOC_LIMIT, and dies if so; then malloc; if malloc fails it tries to malloc 1 byte, and dies if that fails too.
xmallocz/xmallocz_gently provide graceful (return on failure) and non-graceful (die on failure) malloc respectively.
xmemdupz calls memcpy(xmallocz(len)) — a very neat API.
xstrndup calls xmemdupz, copying up to ‘\0’ or the specified length.
xstrncmpz: strncmp that also guarantees the first argument string is ‘\0’-terminated.
xrealloc: also handles some overflow cases, and when realloc to 0 is needed it frees first then xmalloc(0). I hadn’t thought about the behavior of malloc(0) before: According to the specification, malloc(0) returns "a null pointer or a unique pointer that can be successfully passed to free()." (quoted from Stack Overflow).
xcalloc: similarly handles overflow like xmalloc.
xopen: if open fails and the failure is an interrupt EINTR, retry open; otherwise die with mode and path.
ssize_t xread(int fd, void *buf, size_t len)
{
ssize_t nr;
if (len > MAX_IO_SIZE)
len = MAX_IO_SIZE;
while (1) {
nr = read(fd, buf, len);
if (nr < 0) {
if (errno == EINTR)
continue;
if (handle_nonblock(fd, POLLIN, errno))
continue;
}
return nr;
}
}
xread first limits the read size to MAX_IO_SIZE. Then if read returns -1 and the interrupt is EINTR, it continues to read. We see that handle_nonblock looks like this:
static int handle_nonblock(int fd, short poll_events, int err)
{
struct pollfd pfd;
if (err != EAGAIN && err != EWOULDBLOCK)
return 0;
pfd.fd = fd;
pfd.events = poll_events;
/*
* no need to check for errors, here;
* ra subsequent read/write will detect unrecoverable erors
*/
poll(&pfd, 1, -1);
return 1;
}
This means if err==EAGAIN || err==EWOULDBLOCK, we use poll to wait for a read event on fd, and then re-read when it occurs.
This is for non-blocking I/O.
So xread is read + auto-retry.
xwrite: same idea, but the retry waits for a writable event (write buffer not full).
xpread: pread with EAGAIN and EINTR retry handling.
xdup: dup + die_errno
xfopen: fopen + EINTR retry + die_errno
xfdopen: fdopen + die_errno
xmkstemp: mkstemp + die_errno
xmkstemp_mode: same as xmkstemp.
xgetcwd: strbuf_getcwd + strbuf_detach
xgethostname: gethostname + ‘\0’-terminate the end.
xsnprintf: snprintf + BUG error handling.
Summary: the wrappers are well done; callers don’t need too much extra code to handle errors.
git源码wrapper.c对很多常用的函数做了一层包装, 我这里仅仅是为自己做做笔记。
xstrbup:strbup+die
xmalloc:调用do_xmalloc,先用memory_limit_check检查需要分配的内存大小是否超出环境变量GIT_ALLOC_LIMIT,超过则die,接着再malloc,malloc失败再malloc1个字节,如果分配失败再die
xmallocz/xmallocz_gently分别以优雅方式mallloc(失败return)/非优雅方式malloc(失败die)
xmemdupz 调用memcpy(xmallocz(len)) 很妙的api
xstrndup 调用xmemdupz,复制到’\0’或者指定长度
xstrncmpz strncmp 并保证第一个参数字符串是以’\0’结尾
xrealloc 还是一些溢出的处理 并且在需要realloc到0的情况下先free再xmalloc(0),之前没思考过malloc(0)的效果,根据规范,malloc (0)将返回“一个空指针或一个可以成功传递给 free ()的唯一指针”。(引用自stackoverflow)
xcalloc 同理还是一些溢出的处理和xmalloc类似
xopen 如果open失败并且是中断产生EINTR那就继续open,否则die with mode and path.
ssize_t xread(int fd, void *buf, size_t len)
{
ssize_t nr;
if (len > MAX_IO_SIZE)
len = MAX_IO_SIZE;
while (1) {
nr = read(fd, buf, len);
if (nr < 0) {
if (errno == EINTR)
continue;
if (handle_nonblock(fd, POLLIN, errno))
continue;
}
return nr;
}
}
xread 首先读取的大小限制在MAX_IO_SIZE以下,接着如果read返回-1,并且是中断EINTR,那么就continue read.我们看到这个 handle_nonblock是这样的:
static int handle_nonblock(int fd, short poll_events, int err)
{
struct pollfd pfd;
if (err != EAGAIN && err != EWOULDBLOCK)
return 0;
pfd.fd = fd;
pfd.events = poll_events;
/*
* no need to check for errors, here;
* ra subsequent read/write will detect unrecoverable erors
*/
poll(&pfd, 1, -1);
return 1;
}
这意味着如果err==EAGAIN||err==EWOULDBLOCK,我们就使用poll去等待fd上的读事件发生,发生了再重新read。
是为非阻塞IO使用。
所以xread就是read+自重启。
xwrite: 同理也是一样的操作,不过重启等待的是可写时间(写缓冲不满)
xpread: pread+EAGAIN和EINTR的重启处理
xdup: dup+die_errno
xfopen: fopen+EINTR重启+die_errno
xfdopen: fdopen +die_errno
xmkstemp: mkstemp +die_errno
xmkstemp_mode: 同理xmkstemp
xgetcwd: strbuf_getcwd+strbuf_detach
xgethostname: gethostname + 末尾置’\0’
xsnprintf: snprintf + BUG错误处理
总结:包装包的好,外界调用不用过多代码去处理错误。