diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2023-03-23 13:59:48 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2023-06-13 11:06:47 -0400 |
commit | 6719107e95a41b9ef241bdaa272eed505865710c (patch) | |
tree | 0d44c4640ef123ee8f74e051b6fe5c628d62de95 | |
parent | 9dca9f2bcd06f921312762e0b07254f5f8f51fc2 (diff) | |
download | bfs-6719107e95a41b9ef241bdaa272eed505865710c.tar.xz |
dir: New bfs_polldir() function for directory readahead
-rw-r--r-- | src/dir.c | 88 | ||||
-rw-r--r-- | src/dir.h | 10 | ||||
-rw-r--r-- | src/ioq.c | 4 |
3 files changed, 80 insertions, 22 deletions
@@ -107,7 +107,10 @@ struct bfs_dir { // sys_dirent buf[]; #else DIR *dir; + struct dirent *de; #endif + + bool eof; }; #if BFS_GETDENTS @@ -152,8 +155,10 @@ struct bfs_dir *bfs_opendir(int at_fd, const char *at_path) { free(dir); return NULL; } + dir->de = NULL; #endif + dir->eof = false; return dir; } @@ -165,38 +170,52 @@ int bfs_dirfd(const struct bfs_dir *dir) { #endif } -/** Convert de->d_type to a bfs_type, if it exists. */ -static enum bfs_type bfs_d_type(const sys_dirent *de) { -#ifdef DTTOIF - return bfs_mode_to_type(DTTOIF(de->d_type)); -#else - return BFS_UNKNOWN; -#endif -} - -/** Read a single directory entry. */ -static int bfs_getdent(struct bfs_dir *dir, const sys_dirent **de) { +int bfs_polldir(struct bfs_dir *dir) { #if BFS_GETDENTS + if (dir->pos < dir->size) { + return 1; + } else if (dir->eof) { + return 0; + } + char *buf = (char *)(dir + 1); + ssize_t size = bfs_getdents(dir->fd, buf, BUF_SIZE); + if (size == 0) { + dir->eof = true; + return 0; + } else if (size < 0) { + return -1; + } - if (dir->pos >= dir->size) { - ssize_t ret = bfs_getdents(dir->fd, buf, BUF_SIZE); - if (ret <= 0) { - return ret; + dir->pos = 0; + dir->size = size; + + // Like read(), getdents() doesn't indicate EOF until another call returns zero. + // Check that eagerly here to hopefully avoid a syscall in the last bfs_readdir(). + size_t rest = BUF_SIZE - size; + if (rest >= sizeof(sys_dirent)) { + size = bfs_getdents(dir->fd, buf + size, rest); + if (size > 0) { + dir->size += size; + } else if (size == 0) { + dir->eof = true; } - dir->pos = 0; - dir->size = ret; } - *de = (void *)(buf + dir->pos); - dir->pos += (*de)->d_reclen; return 1; -#else +#else // !BFS_GETDENTS + if (dir->de) { + return 1; + } else if (dir->eof) { + return 0; + } + errno = 0; - *de = readdir(dir->dir); - if (*de) { + dir->de = readdir(dir->dir); + if (dir->de) { return 1; } else if (errno == 0) { + dir->eof = true; return 0; } else { return -1; @@ -204,6 +223,22 @@ static int bfs_getdent(struct bfs_dir *dir, const sys_dirent **de) { #endif } +/** Read a single directory entry. */ +static int bfs_getdent(struct bfs_dir *dir, const sys_dirent **de) { + int ret = bfs_polldir(dir); + if (ret > 0) { +#if BFS_GETDENTS + char *buf = (char *)(dir + 1); + *de = (const sys_dirent *)(buf + dir->pos); + dir->pos += (*de)->d_reclen; +#else + *de = dir->de; + dir->de = NULL; +#endif + } + return ret; +} + /** Skip ".", "..", and deleted/empty dirents. */ static bool skip_dirent(const sys_dirent *de) { #if __FreeBSD__ @@ -217,6 +252,15 @@ static bool skip_dirent(const sys_dirent *de) { return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } +/** Convert de->d_type to a bfs_type, if it exists. */ +static enum bfs_type bfs_d_type(const sys_dirent *de) { +#ifdef DTTOIF + return bfs_mode_to_type(DTTOIF(de->d_type)); +#else + return BFS_UNKNOWN; +#endif +} + int bfs_readdir(struct bfs_dir *dir, struct bfs_dirent *de) { while (true) { const sys_dirent *sysde; @@ -80,6 +80,16 @@ struct bfs_dir *bfs_opendir(int at_fd, const char *at_path); int bfs_dirfd(const struct bfs_dir *dir); /** + * Performs any I/O necessary for the next bfs_readdir() call. + * + * @param dir + * The directory to poll. + * @return + * 1 on success, 0 on EOF, or -1 on failure. + */ +int bfs_polldir(struct bfs_dir *dir); + +/** * Read a directory entry. * * @param dir @@ -158,6 +158,10 @@ static void *ioq_work(void *ptr) { res->ptr = req.ptr; res->dir = bfs_opendir(req.dfd, req.path); res->error = errno; + if (res->dir) { + bfs_polldir(res->dir); + } + ioqq_push(ioq->ready, cmd); } |