Alexandria 2.31.0
SDC-CH common library for the Euclid project
Loading...
Searching...
No Matches
FileHandler.icpp
Go to the documentation of this file.
1/**
2 * Copyright (C) 2012-2022 Euclid Science Ground Segment
3 *
4 * This library is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License as published by the Free
6 * Software Foundation; either version 3.0 of the License, or (at your option)
7 * any later version.
8 *
9 * This library is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12 * details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#ifndef FILEHANDLER_IMPL
20#error "This file should not be included directly! Use FileHandler.h instead"
21#else
22
23namespace Euclid {
24namespace FilePool {
25
26template <typename TFD>
27auto FileHandler::getWriteAccessor(bool try_lock) -> std::unique_ptr<FileAccessor<TFD>> {
28 auto manager = m_file_manager.lock();
29 assert(manager);
30
31 UniqueLock unique_lock(m_file_mutex, boost::defer_lock);
32 if (try_lock && !unique_lock.try_lock()) {
33 return nullptr;
34 } else {
35 unique_lock.lock();
36 }
37
38 std::unique_ptr<FdWrapper> fd_ptr;
39 {
40 std::unique_lock<std::mutex> this_lock(m_handler_mutex);
41
42 // If we have changed mode, we need to close all existing fd
43 if (m_is_readonly) {
44 for (auto& fd : m_available_fd) {
45 fd.second->close();
46 }
47 m_available_fd.clear();
48 m_is_readonly = false;
49 }
50
51 assert(m_available_fd.size() <= 1);
52
53 // If there is one, but of a different type, close it
54 if (!m_available_fd.empty()) {
55 auto typed_ptr = dynamic_cast<TypedFdWrapper<TFD>*>(m_available_fd.begin()->second.get());
56 if (!typed_ptr) {
57 m_available_fd.clear();
58 }
59 }
60
61 // Open one file if we need
62 if (m_available_fd.empty()) {
63 this_lock.unlock();
64 auto fd = manager->open<TFD>(m_path, true, [this](FileManager::FileId id) { return this->close(id); });
65 this_lock.lock();
66 m_available_fd[fd.first].reset(new TypedFdWrapper<TFD>(fd.first, std::move(fd.second), manager.get()));
67 }
68
69 assert(m_available_fd.size() == 1);
70
71 // Build and return accessor
72 fd_ptr = std::move(m_available_fd.begin()->second);
73 m_available_fd.clear();
74 }
75
76 auto typed_ptr = dynamic_cast<TypedFdWrapper<TFD>*>(fd_ptr.get());
77 if (typed_ptr == nullptr) {
78 throw std::logic_error("Failed to cast type-erased file descriptor wrapper");
79 }
80 auto fd = std::move(typed_ptr->m_fd);
81 auto id = typed_ptr->m_id;
82
83 auto return_callback = [this, id, manager](TFD&& returned_fd) {
84 std::lock_guard<std::mutex> lambda_this_lock(m_handler_mutex);
85 m_available_fd[id] =
86 std::unique_ptr<TypedFdWrapper<TFD>>(new TypedFdWrapper<TFD>(id, std::move(returned_fd), manager.get()));
87 };
88
89 manager->notifyUsed(id);
90 return std::unique_ptr<FileWriteAccessor<TFD>>(
91 new FileWriteAccessor<TFD>(std::move(fd), return_callback, std::move(unique_lock)));
92}
93
94template <typename TFD>
95auto FileHandler::getReadAccessor(bool try_lock) -> std::unique_ptr<FileAccessor<TFD>> {
96 auto manager = m_file_manager.lock();
97 assert(manager);
98
99 SharedLock shared_lock(m_file_mutex, boost::defer_lock);
100 if (try_lock && !shared_lock.try_lock()) {
101 return nullptr;
102 } else {
103 shared_lock.lock();
104 }
105
106 std::unique_ptr<FdWrapper> fd_ptr = nullptr;
107 {
108 std::unique_lock<std::mutex> this_lock(m_handler_mutex);
109
110 // If we have changed mode, we need to close all existing fd
111 if (!m_is_readonly) {
112 for (auto& fd : m_available_fd) {
113 fd.second->close();
114 }
115 m_available_fd.clear();
116 m_is_readonly = true;
117 }
118
119 // Find the first with a matching type
120 auto avail_i = m_available_fd.begin();
121 TypedFdWrapper<TFD>* typed_ptr = nullptr;
122 while (typed_ptr == nullptr && avail_i != m_available_fd.end()) {
123 if ((typed_ptr = dynamic_cast<TypedFdWrapper<TFD>*>(avail_i->second.get())) == nullptr)
124 ++avail_i;
125 }
126
127 // Open one file if we need
128 if (!typed_ptr) {
129 this_lock.unlock();
130 auto fd = manager->open<TFD>(m_path, false, [this](FileManager::FileId id) { return this->close(id); });
131 typed_ptr = new TypedFdWrapper<TFD>(fd.first, std::move(fd.second), manager.get());
132 this_lock.lock();
133 avail_i = m_available_fd.emplace(fd.first, std::unique_ptr<TypedFdWrapper<TFD>>(typed_ptr)).first;
134 }
135
136 assert(typed_ptr && avail_i != m_available_fd.end());
137
138 fd_ptr = std::move(avail_i->second);
139 m_available_fd.erase(avail_i);
140 }
141
142 // Build and return accessor
143 TypedFdWrapper<TFD>* typed_ptr = dynamic_cast<TypedFdWrapper<TFD>*>(fd_ptr.get());
144 if (typed_ptr == nullptr) {
145 throw std::logic_error("Failed to cast type-erased file descriptor wrapper");
146 }
147 auto fd = std::move(typed_ptr->m_fd);
148 auto id = typed_ptr->m_id;
149
150 auto return_callback = [this, id, manager](TFD&& returned_fd) {
151 std::lock_guard<std::mutex> lambda_this_lock(m_handler_mutex);
152 m_available_fd[id] =
153 std::unique_ptr<TypedFdWrapper<TFD>>(new TypedFdWrapper<TFD>(id, std::move(returned_fd), manager.get()));
154 };
155
156 manager->notifyUsed(id);
157 return std::unique_ptr<FileReadAccessor<TFD>>(
158 new FileReadAccessor<TFD>(std::move(fd), return_callback, std::move(shared_lock)));
159}
160
161template <typename TFD>
162auto FileHandler::getAccessor(Mode mode) -> std::unique_ptr<FileAccessor<TFD>> {
163 bool write_bool = mode & kWrite;
164 bool try_bool = mode & kTry;
165
166 if (write_bool) {
167 return getWriteAccessor<TFD>(try_bool);
168 }
169 return getReadAccessor<TFD>(try_bool);
170}
171
172} // namespace FilePool
173} // namespace Euclid
174
175#endif