|
4 | 4 |
|
5 | 5 | #include "../fs_interface/File.h" |
6 | 6 | #include "../fs_interface/OpenFile.h" |
| 7 | +#include "../fs_interface/FuseErrnoException.h" |
7 | 8 | #include <cpp-utils/macros.h> |
| 9 | +#include <cpp-utils/assert/assert.h> |
8 | 10 | #include "IdList.h" |
| 11 | +#include <condition_variable> |
9 | 12 |
|
10 | 13 | 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 | +} |
11 | 28 |
|
12 | 29 | class FuseOpenFileList final { |
13 | 30 | public: |
14 | 31 | FuseOpenFileList(); |
15 | 32 | ~FuseOpenFileList(); |
16 | 33 |
|
17 | 34 | int open(cpputils::unique_ref<OpenFile> file); |
18 | | - OpenFile *get(int descriptor); |
| 35 | + template<class Func> |
| 36 | + auto load(int descriptor, Func&& callback); |
19 | 37 | void close(int descriptor); |
20 | 38 |
|
21 | 39 | private: |
22 | 40 | IdList<OpenFile> _open_files; |
23 | 41 |
|
| 42 | + std::unordered_map<int, size_t> _refcounts; |
| 43 | + std::mutex _mutex; |
| 44 | + |
| 45 | + std::condition_variable _refcount_zero_cv; |
| 46 | + |
24 | 47 | DISALLOW_COPY_AND_ASSIGN(FuseOpenFileList); |
25 | 48 | }; |
26 | 49 |
|
27 | 50 | inline FuseOpenFileList::FuseOpenFileList() |
28 | | - :_open_files() { |
| 51 | + :_open_files(), _refcounts(), _mutex(), _refcount_zero_cv() { |
29 | 52 | } |
30 | 53 |
|
31 | 54 | 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"); |
32 | 71 | } |
33 | 72 |
|
34 | 73 | 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; |
36 | 79 | } |
37 | 80 |
|
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 | + } |
40 | 101 | } |
41 | 102 |
|
42 | 103 | 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 | + |
43 | 110 | //The destructor of the stored FuseOpenFile closes the file |
44 | 111 | _open_files.remove(descriptor); |
| 112 | + _refcounts.erase(descriptor); |
45 | 113 | } |
46 | 114 |
|
47 | 115 | } |
|
0 commit comments