Skip to content

Commit e26c76d

Browse files
committed
Merge branch 'release/0.10' into develop
2 parents 0aa0b64 + 251272b commit e26c76d

5 files changed

Lines changed: 120 additions & 32 deletions

File tree

ChangeLog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ Version 0.10.2 (unreleased)
22
---------------
33
Fixed bugs:
44
* Fix occasional crash in mkdir() on Windows
5+
* Fix a race condition when a file descriptor is closed while there's read/write requests for that file being processed.
56

67
Improvements:
78
* Better logging when local state can't be loaded

src/fspp/impl/FilesystemImpl.cpp

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ int FilesystemImpl::openFile(File *file, int flags) {
130130

131131
void FilesystemImpl::flush(int descriptor) {
132132
PROFILE(_flushNanosec);
133-
_open_files.get(descriptor)->flush();
133+
_open_files.load(descriptor, [](OpenFile* openFile) {
134+
openFile->flush();
135+
});
134136
}
135137

136138
void FilesystemImpl::closeFile(int descriptor) {
@@ -164,9 +166,11 @@ void FilesystemImpl::lstat(const bf::path &path, fspp::fuse::STAT *stbuf) {
164166
}
165167

166168
void FilesystemImpl::fstat(int descriptor, fspp::fuse::STAT *stbuf) {
167-
PROFILE(_fstatNanosec);
168-
auto stat_info = _open_files.get(descriptor)->stat();
169-
convert_stat_info_(stat_info, stbuf);
169+
PROFILE(_fstatNanosec);
170+
auto stat_info = _open_files.load(descriptor, [] (OpenFile* openFile) {
171+
return openFile->stat();
172+
});
173+
convert_stat_info_(stat_info, stbuf);
170174
}
171175

172176
void FilesystemImpl::chmod(const boost::filesystem::path &path, ::mode_t mode) {
@@ -196,27 +200,37 @@ void FilesystemImpl::truncate(const bf::path &path, fspp::num_bytes_t size) {
196200

197201
void FilesystemImpl::ftruncate(int descriptor, fspp::num_bytes_t size) {
198202
PROFILE(_ftruncateNanosec);
199-
_open_files.get(descriptor)->truncate(size);
203+
_open_files.load(descriptor, [size] (OpenFile* openFile) {
204+
openFile->truncate(size);
205+
});
200206
}
201207

202208
fspp::num_bytes_t FilesystemImpl::read(int descriptor, void *buf, fspp::num_bytes_t count, fspp::num_bytes_t offset) {
203209
PROFILE(_readNanosec);
204-
return _open_files.get(descriptor)->read(buf, count, offset);
210+
return _open_files.load(descriptor, [buf, count, offset] (OpenFile* openFile) {
211+
return openFile->read(buf, count, offset);
212+
});
205213
}
206214

207215
void FilesystemImpl::write(int descriptor, const void *buf, fspp::num_bytes_t count, fspp::num_bytes_t offset) {
208216
PROFILE(_writeNanosec);
209-
_open_files.get(descriptor)->write(buf, count, offset);
217+
return _open_files.load(descriptor, [buf, count, offset] (OpenFile* openFile) {
218+
return openFile->write(buf, count, offset);
219+
});
210220
}
211221

212222
void FilesystemImpl::fsync(int descriptor) {
213223
PROFILE(_fsyncNanosec);
214-
_open_files.get(descriptor)->fsync();
224+
_open_files.load(descriptor, [] (OpenFile* openFile) {
225+
openFile->fsync();
226+
});
215227
}
216228

217229
void FilesystemImpl::fdatasync(int descriptor) {
218230
PROFILE(_fdatasyncNanosec);
219-
_open_files.get(descriptor)->fdatasync();
231+
_open_files.load(descriptor, [] (OpenFile* openFile) {
232+
openFile->fdatasync();
233+
});
220234
}
221235

222236
void FilesystemImpl::access(const bf::path &path, int mask) {

src/fspp/impl/FuseOpenFileList.h

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,112 @@
44

55
#include "../fs_interface/File.h"
66
#include "../fs_interface/OpenFile.h"
7+
#include "../fs_interface/FuseErrnoException.h"
78
#include <cpp-utils/macros.h>
9+
#include <cpp-utils/assert/assert.h>
810
#include "IdList.h"
11+
#include <condition_variable>
912

1013
namespace fspp {
14+
namespace detail {
15+
class OnScopeExit final {
16+
public:
17+
explicit OnScopeExit(std::function<void()> handler)
18+
: _handler(std::move(handler)) {}
19+
20+
~OnScopeExit() {
21+
_handler();
22+
}
23+
24+
private:
25+
std::function<void()> _handler;
26+
};
27+
}
1128

1229
class FuseOpenFileList final {
1330
public:
1431
FuseOpenFileList();
1532
~FuseOpenFileList();
1633

1734
int open(cpputils::unique_ref<OpenFile> file);
18-
OpenFile *get(int descriptor);
35+
template<class Func>
36+
auto load(int descriptor, Func&& callback);
1937
void close(int descriptor);
2038

2139
private:
2240
IdList<OpenFile> _open_files;
2341

42+
std::unordered_map<int, size_t> _refcounts;
43+
std::mutex _mutex;
44+
45+
std::condition_variable _refcount_zero_cv;
46+
2447
DISALLOW_COPY_AND_ASSIGN(FuseOpenFileList);
2548
};
2649

2750
inline FuseOpenFileList::FuseOpenFileList()
28-
:_open_files() {
51+
:_open_files(), _refcounts(), _mutex(), _refcount_zero_cv() {
2952
}
3053

3154
inline FuseOpenFileList::~FuseOpenFileList() {
55+
std::unique_lock<std::mutex> lock(_mutex);
56+
57+
// Wait until all pending requests are done
58+
_refcount_zero_cv.wait(lock, [&] {
59+
for (const auto& refcount : _refcounts) {
60+
if (0 != refcount.second) {
61+
return false;
62+
}
63+
}
64+
return true;
65+
});
66+
67+
// There might still be open files when the file system is shutdown, so we can't assert it's empty.
68+
// But to check that _refcounts has been updated correctly, we can assert the invariant that we have as many
69+
// refcounts as open files.
70+
ASSERT(_refcounts.size() == _refcounts.size(), "Didn't clean up refcounts properly");
3271
}
3372

3473
inline int FuseOpenFileList::open(cpputils::unique_ref<OpenFile> file) {
35-
return _open_files.add(std::move(file));
74+
std::lock_guard<std::mutex> lock(_mutex);
75+
76+
int descriptor = _open_files.add(std::move(file));
77+
_refcounts.emplace(descriptor, 0);
78+
return descriptor;
3679
}
3780

38-
inline OpenFile *FuseOpenFileList::get(int descriptor) {
39-
return _open_files.get(descriptor);
81+
template<class Func>
82+
inline auto FuseOpenFileList::load(int descriptor, Func&& callback) {
83+
try {
84+
std::unique_lock<std::mutex> lock(_mutex);
85+
_refcounts.at(descriptor) += 1;
86+
detail::OnScopeExit _([&] {
87+
if (!lock.owns_lock()) { // own_lock can be true when _open_files.get() below fails before the lock is unlocked
88+
lock.lock();
89+
}
90+
_refcounts.at(descriptor) -= 1;
91+
_refcount_zero_cv.notify_all();
92+
});
93+
94+
OpenFile* loaded = _open_files.get(descriptor);
95+
lock.unlock();
96+
97+
return std::forward<Func>(callback)(loaded);
98+
} catch (const std::out_of_range& e) {
99+
throw fspp::fuse::FuseErrnoException(EBADF);
100+
}
40101
}
41102

42103
inline void FuseOpenFileList::close(int descriptor) {
104+
std::unique_lock<std::mutex> lock(_mutex);
105+
106+
_refcount_zero_cv.wait(lock, [&] {
107+
return 0 == _refcounts.at(descriptor);
108+
});
109+
43110
//The destructor of the stored FuseOpenFile closes the file
44111
_open_files.remove(descriptor);
112+
_refcounts.erase(descriptor);
45113
}
46114

47115
}

src/fspp/impl/IdList.h

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#ifndef MESSMER_FSPP_IMPL_IDLIST_H_
33
#define MESSMER_FSPP_IMPL_IDLIST_H_
44

5-
#include <map>
5+
#include <unordered_map>
66
#include <mutex>
77
#include <stdexcept>
88
#include <cpp-utils/pointer/unique_ref.h>
@@ -19,17 +19,18 @@ class IdList final {
1919
Entry *get(int id);
2020
const Entry *get(int id) const;
2121
void remove(int id);
22+
size_t size() const;
23+
2224
private:
23-
std::map<int, cpputils::unique_ref<Entry>> _entries;
25+
std::unordered_map<int, cpputils::unique_ref<Entry>> _entries;
2426
int _id_counter;
25-
mutable std::mutex _mutex;
2627

2728
DISALLOW_COPY_AND_ASSIGN(IdList<Entry>);
2829
};
2930

3031
template<class Entry>
3132
IdList<Entry>::IdList()
32-
: _entries(), _id_counter(0), _mutex() {
33+
: _entries(), _id_counter(0) {
3334
}
3435

3536
template<class Entry>
@@ -38,10 +39,9 @@ IdList<Entry>::~IdList() {
3839

3940
template<class Entry>
4041
int IdList<Entry>::add(cpputils::unique_ref<Entry> entry) {
41-
std::lock_guard<std::mutex> lock(_mutex);
4242
//TODO Reuse IDs (ids = descriptors)
4343
int new_id = ++_id_counter;
44-
_entries.insert(std::make_pair(new_id, std::move(entry)));
44+
_entries.emplace(new_id, std::move(entry));
4545
return new_id;
4646
}
4747

@@ -52,21 +52,24 @@ Entry *IdList<Entry>::get(int id) {
5252

5353
template<class Entry>
5454
const Entry *IdList<Entry>::get(int id) const {
55-
std::lock_guard<std::mutex> lock(_mutex);
5655
const Entry *result = _entries.at(id).get();
5756
return result;
5857
}
5958

6059
template<class Entry>
6160
void IdList<Entry>::remove(int id) {
62-
std::lock_guard<std::mutex> lock(_mutex);
6361
auto found_iter = _entries.find(id);
6462
if (found_iter == _entries.end()) {
6563
throw std::out_of_range("Called IdList::remove() with an invalid ID");
6664
}
6765
_entries.erase(found_iter);
6866
}
6967

68+
template<class Entry>
69+
size_t IdList<Entry>::size() const {
70+
return _entries.size();
71+
}
72+
7073
}
7174

7275
#endif

test/fspp/impl/FuseOpenFileListTest.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,26 @@ struct FuseOpenFileListTest: public ::testing::Test {
4545
return open(FILEID1, FILEID2);
4646
}
4747
void check(int id, int fileid, int flags) {
48-
MockOpenFile *openFile = dynamic_cast<MockOpenFile*>(list.get(id));
49-
EXPECT_EQ(fileid, openFile->fileid);
50-
EXPECT_EQ(flags, openFile->flags);
48+
list.load(id, [=](OpenFile* _openFile) {
49+
MockOpenFile *openFile = dynamic_cast<MockOpenFile*>(_openFile);
50+
EXPECT_EQ(fileid, openFile->fileid);
51+
EXPECT_EQ(flags, openFile->flags);
52+
});
5153
}
5254
};
5355

5456
TEST_F(FuseOpenFileListTest, EmptyList1) {
55-
ASSERT_THROW(list.get(0), std::out_of_range);
57+
ASSERT_THROW(list.load(0, [](OpenFile*) {}), fspp::fuse::FuseErrnoException);
5658
}
5759

5860
TEST_F(FuseOpenFileListTest, EmptyList2) {
59-
ASSERT_THROW(list.get(3), std::out_of_range);
61+
ASSERT_THROW(list.load(3, [](OpenFile*) {}), fspp::fuse::FuseErrnoException);
6062
}
6163

6264
TEST_F(FuseOpenFileListTest, InvalidId) {
6365
int valid_id = open();
6466
int invalid_id = valid_id + 1;
65-
ASSERT_THROW(list.get(invalid_id), std::out_of_range);
67+
ASSERT_THROW(list.load(invalid_id, [](OpenFile*) {}), fspp::fuse::FuseErrnoException);
6668
}
6769

6870
TEST_F(FuseOpenFileListTest, Open1AndGet) {
@@ -102,18 +104,18 @@ TEST_F(FuseOpenFileListTest, Open3AndGet) {
102104
TEST_F(FuseOpenFileListTest, GetClosedItemOnEmptyList) {
103105
int id = open();
104106

105-
ASSERT_NO_THROW(list.get(id));
107+
ASSERT_NO_THROW(list.load(id, [](OpenFile*) {}));
106108
list.close(id);
107-
ASSERT_THROW(list.get(id), std::out_of_range);
109+
ASSERT_THROW(list.load(id, [](OpenFile*) {}), fspp::fuse::FuseErrnoException);
108110
}
109111

110112
TEST_F(FuseOpenFileListTest, GetClosedItemOnNonEmptyList) {
111113
int id = open();
112114
open();
113115

114-
ASSERT_NO_THROW(list.get(id));
116+
ASSERT_NO_THROW(list.load(id, [](OpenFile*) {}));
115117
list.close(id);
116-
ASSERT_THROW(list.get(id), std::out_of_range);
118+
ASSERT_THROW(list.load(id, [](OpenFile*) {}), fspp::fuse::FuseErrnoException);
117119
}
118120

119121
TEST_F(FuseOpenFileListTest, CloseOnEmptyList1) {

0 commit comments

Comments
 (0)