Skip to content

Commit

Permalink
[fs] Make the POSIX implementation if directory_iterator threadsafe.
Browse files Browse the repository at this point in the history
  • Loading branch information
dscharrer committed Nov 10, 2011
1 parent f69c60c commit 57a38b8
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ else(MSVC)
check_symbol_exists(readlink "unistd.h" HAVE_READLINK)
check_symbol_exists(dup2 "unistd.h" HAVE_DUP2)
check_symbol_exists(execlp "unistd.h" HAVE_EXECLP)
check_symbol_exists(fpathconf "unistd.h" HAVE_FPATHCONF)
check_symbol_exists(dirfd "dirent.h" HAVE_DIRFD)
if(NOT WIN32)
check_symbol_exists(isatty "unistd.h" HAVE_ISATTY)
endif()
Expand Down
59 changes: 50 additions & 9 deletions src/io/FilesystemPOSIX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
#include <dirent.h>
#include <cstdio>
#include <cstring>
#include <cstdlib>

#include "io/FilePath.h"
#include "io/FileStream.h"

using std::string;
using std::malloc;
using std::free;

namespace fs {

Expand Down Expand Up @@ -146,31 +149,69 @@ bool rename(const path & old_p, const path & new_p) {
return !::rename(old_p.string().c_str(), new_p.string().c_str());
}

static void readdir(void * _handle, void * & _buf) {

DIR * handle = reinterpret_cast<DIR *>(_handle);

dirent * buf = reinterpret_cast<dirent *>(_buf);

do {

dirent * entry;
if(readdir_r(handle, buf, &entry) || !entry) {
free(_buf), _buf = NULL;
return;
}

} while(!strcmp(buf->d_name, ".") || !strcmp(buf->d_name, ".."));

}

directory_iterator::directory_iterator(const fs::path & p) : buf(NULL) {

handle = opendir(p.empty() ? "./" : p.string().c_str());

if(handle) {
dirent * d;
do {
buf = d = readdir(reinterpret_cast<DIR *>(handle));
} while(d && (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")));

// Allocate a large enough buffer for readdir_r.
long name_max;
#if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
if(name_max == -1) {
# if defined(NAME_MAX)
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
# else
arx_assert_msg(false, "cannot determine maximum dirname size");
# endif
}
#elif defined(NAME_MAX)
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
#else
# error "buffer size for readdir_r cannot be determined"
#endif
size_t size = (size_t)offsetof(dirent, d_name) + name_max + 1;
if(size < sizeof(dirent)) {
size = sizeof(dirent);
}
buf = malloc(size);

readdir(handle, buf);
}
};

directory_iterator::~directory_iterator() {
if(handle) {
closedir(reinterpret_cast<DIR*>(handle));
closedir(reinterpret_cast<DIR *>(handle));
if(buf) {
free(buf);
}
}
}

directory_iterator & directory_iterator::operator++() {
arx_assert(buf != NULL);

dirent * d;
do {
buf = d = readdir(reinterpret_cast<DIR *>(handle));
} while(d && (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")));
readdir(handle, buf);

return *this;
}
Expand Down

0 comments on commit 57a38b8

Please sign in to comment.