Alexandria  2.25.0
SDC-CH common library for the Euclid project
FileHandler.icpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012-2021 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 
23 namespace Euclid {
24 namespace FilePool {
25 
26 template <typename TFD>
27 auto 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  assert(typed_ptr != nullptr);
78  auto fd = std::move(typed_ptr->m_fd);
79  auto id = typed_ptr->m_id;
80 
81  auto return_callback = [this, id, manager](TFD&& returned_fd) {
82  std::lock_guard<std::mutex> lambda_this_lock(m_handler_mutex);
83  m_available_fd[id] =
84  std::unique_ptr<TypedFdWrapper<TFD>>(new TypedFdWrapper<TFD>(id, std::move(returned_fd), manager.get()));
85  };
86 
87  manager->notifyUsed(id);
88  return std::unique_ptr<FileWriteAccessor<TFD>>(
89  new FileWriteAccessor<TFD>(std::move(fd), return_callback, std::move(unique_lock)));
90 }
91 
92 template <typename TFD>
93 auto FileHandler::getReadAccessor(bool try_lock) -> std::unique_ptr<FileAccessor<TFD>> {
94  auto manager = m_file_manager.lock();
95  assert(manager);
96 
97  SharedLock shared_lock(m_file_mutex, boost::defer_lock);
98  if (try_lock && !shared_lock.try_lock()) {
99  return nullptr;
100  } else {
101  shared_lock.lock();
102  }
103 
104  std::unique_ptr<FdWrapper> fd_ptr = nullptr;
105  {
106  std::unique_lock<std::mutex> this_lock(m_handler_mutex);
107 
108  // If we have changed mode, we need to close all existing fd
109  if (!m_is_readonly) {
110  for (auto& fd : m_available_fd) {
111  fd.second->close();
112  }
113  m_available_fd.clear();
114  m_is_readonly = true;
115  }
116 
117  // Find the first with a matching type
118  auto avail_i = m_available_fd.begin();
119  TypedFdWrapper<TFD>* typed_ptr = nullptr;
120  while (typed_ptr == nullptr && avail_i != m_available_fd.end()) {
121  if ((typed_ptr = dynamic_cast<TypedFdWrapper<TFD>*>(avail_i->second.get())) == nullptr)
122  ++avail_i;
123  }
124 
125  // Open one file if we need
126  if (!typed_ptr) {
127  this_lock.unlock();
128  auto fd = manager->open<TFD>(m_path, false, [this](FileManager::FileId id) { return this->close(id); });
129  typed_ptr = new TypedFdWrapper<TFD>(fd.first, std::move(fd.second), manager.get());
130  this_lock.lock();
131  avail_i = m_available_fd.emplace(fd.first, std::unique_ptr<TypedFdWrapper<TFD>>(typed_ptr)).first;
132  }
133 
134  assert(typed_ptr && avail_i != m_available_fd.end());
135 
136  fd_ptr = std::move(avail_i->second);
137  m_available_fd.erase(avail_i);
138  }
139 
140  // Build and return accessor
141  TypedFdWrapper<TFD>* typed_ptr = dynamic_cast<TypedFdWrapper<TFD>*>(fd_ptr.get());
142  assert(typed_ptr != nullptr);
143  auto fd = std::move(typed_ptr->m_fd);
144  auto id = typed_ptr->m_id;
145 
146  auto return_callback = [this, id, manager](TFD&& returned_fd) {
147  std::lock_guard<std::mutex> lambda_this_lock(m_handler_mutex);
148  m_available_fd[id] =
149  std::unique_ptr<TypedFdWrapper<TFD>>(new TypedFdWrapper<TFD>(id, std::move(returned_fd), manager.get()));
150  };
151 
152  manager->notifyUsed(id);
153  return std::unique_ptr<FileReadAccessor<TFD>>(
154  new FileReadAccessor<TFD>(std::move(fd), return_callback, std::move(shared_lock)));
155 }
156 
157 template <typename TFD>
158 auto FileHandler::getAccessor(Mode mode) -> std::unique_ptr<FileAccessor<TFD>> {
159  bool write_bool = mode & kWrite;
160  bool try_bool = mode & kTry;
161 
162  if (write_bool) {
163  return getWriteAccessor<TFD>(try_bool);
164  }
165  return getReadAccessor<TFD>(try_bool);
166 }
167 
168 } // namespace FilePool
169 } // namespace Euclid
170 
171 #endif