1 /*
2 * Distributed under the OSI-approved Apache License, Version 2.0. See
3 * accompanying file Copyright.txt for details.
4 *
5 * FileIME.cpp file I/O using IME Native library
6 *
7 * Created on: Dec 1, 2019
8 * Author: Keichi Takahashi keichi@is.naist.jp
9 */
10 #include "FileIME.h"
11
12 #include "adios2/helper/adiosFunctions.h"
13
14 #include <iostream>
15
16 #include <fcntl.h> // O_* flags
17 #include <linux/limits.h> // PATH_MAX
18 #include <unistd.h> // getcwd
19
20 extern "C" {
21 #include <im_client_native2.h>
22 }
23
24 /// \cond EXCLUDE_FROM_DOXYGEN
25 #include <ios> //std::ios_base::failure
26 /// \endcond
27
28 namespace adios2
29 {
30 namespace transport
31 {
32
33 std::atomic_uint FileIME::client_refcount = 0;
34
FileIME(helper::Comm const & comm)35 FileIME::FileIME(helper::Comm const &comm) : Transport("File", "IME", comm)
36 {
37 /** Initialize the IME client if there are no existing FileIME instances.
38 * We need to check if the IME client has been already initialized because
39 * initializing the IME multiple times leaves the client in a weired
40 * state. */
41 if (!client_refcount)
42 {
43 ime_client_native2_init();
44 }
45
46 client_refcount++;
47 }
48
~FileIME()49 FileIME::~FileIME()
50 {
51 if (m_IsOpen)
52 {
53 /** Trigger a flush from IME to PFS. Note that fsync needs to be
54 * called before bfs_sync. */
55 if (m_SyncToPFS)
56 {
57 ime_client_native2_fsync(m_FileDescriptor);
58 ime_client_native2_bfs_sync(m_FileDescriptor, true);
59 }
60 ime_client_native2_close(m_FileDescriptor);
61 }
62
63 client_refcount--;
64
65 /** Finalize the IME client if there are no existing FileIME instances. */
66 if (client_refcount)
67 {
68 ime_client_native2_finalize();
69 }
70 }
71
72 /** Note that async mode is unsupported in FileIME. */
Open(const std::string & name,const Mode openMode,const bool async)73 void FileIME::Open(const std::string &name, const Mode openMode,
74 const bool async)
75 {
76 /** DEFAULT_IME_FILE_PREFIX is "ime://" */
77 m_Name = DEFAULT_IME_FILE_PREFIX;
78
79 /** Convert relative path to absolute path. The IME API only supports
80 * absolute path. */
81 if (name[0] != '/')
82 {
83 char tmp[PATH_MAX];
84 m_Name += std::string(getcwd(tmp, PATH_MAX)) + "/";
85 }
86 m_Name += name;
87
88 CheckName();
89 m_OpenMode = openMode;
90 switch (m_OpenMode)
91 {
92 case (Mode::Write):
93 ProfilerStart("open");
94 m_FileDescriptor = ime_client_native2_open(
95 m_Name.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
96 ProfilerStop("open");
97 break;
98
99 case (Mode::Append):
100 ProfilerStart("open");
101 m_FileDescriptor =
102 ime_client_native2_open(m_Name.c_str(), O_RDWR | O_CREAT, 0777);
103 lseek(m_FileDescriptor, 0, SEEK_END);
104 ProfilerStop("open");
105 break;
106
107 case (Mode::Read):
108 ProfilerStart("open");
109 m_FileDescriptor =
110 ime_client_native2_open(m_Name.c_str(), O_RDONLY, 0000);
111 ProfilerStop("open");
112 break;
113
114 default:
115 CheckFile("unknown open mode for file " + m_Name +
116 ", in call to IME open");
117 }
118
119 CheckFile("couldn't open file " + m_Name +
120 ", check permissions or path existence, in call to IME open");
121
122 m_IsOpen = true;
123 }
124
SetParameters(const Params & parameters)125 void FileIME::SetParameters(const Params ¶meters)
126 {
127 for (const auto &pair : parameters)
128 {
129 const std::string key = helper::LowerCase(pair.first);
130 const std::string value = helper::LowerCase(pair.second);
131
132 if (key == "synctopfs")
133 {
134 m_SyncToPFS =
135 helper::StringTo<bool>(value, " in Parameter key=SyncToPFS");
136 }
137 }
138 }
139
Write(const char * buffer,size_t size,size_t start)140 void FileIME::Write(const char *buffer, size_t size, size_t start)
141 {
142 auto lf_Write = [&](const char *buffer, size_t size) {
143 while (size > 0)
144 {
145 ProfilerStart("write");
146 const auto writtenSize =
147 ime_client_native2_write(m_FileDescriptor, buffer, size);
148 ProfilerStop("write");
149
150 if (writtenSize == -1)
151 {
152 if (errno == EINTR)
153 {
154 continue;
155 }
156
157 throw std::ios_base::failure(
158 "ERROR: couldn't write to file " + m_Name +
159 ", in call to FileDescriptor Write\n");
160 }
161
162 buffer += writtenSize;
163 size -= writtenSize;
164 }
165 };
166
167 if (start != MaxSizeT)
168 {
169 const auto newPosition =
170 ime_client_native2_lseek(m_FileDescriptor, start, SEEK_SET);
171
172 if (static_cast<size_t>(newPosition) != start)
173 {
174 throw std::ios_base::failure(
175 "ERROR: couldn't move to start position " +
176 std::to_string(start) + " in file " + m_Name +
177 ", in call to IME lseek\n");
178 }
179 }
180
181 if (size > DefaultMaxFileBatchSize)
182 {
183 const size_t batches = size / DefaultMaxFileBatchSize;
184 const size_t remainder = size % DefaultMaxFileBatchSize;
185
186 size_t position = 0;
187 for (size_t b = 0; b < batches; ++b)
188 {
189 lf_Write(&buffer[position], DefaultMaxFileBatchSize);
190 position += DefaultMaxFileBatchSize;
191 }
192 lf_Write(&buffer[position], remainder);
193 }
194 else
195 {
196 lf_Write(buffer, size);
197 }
198 }
199
Read(char * buffer,size_t size,size_t start)200 void FileIME::Read(char *buffer, size_t size, size_t start)
201 {
202 auto lf_Read = [&](char *buffer, size_t size) {
203 while (size > 0)
204 {
205 ProfilerStart("read");
206 const auto readSize =
207 ime_client_native2_read(m_FileDescriptor, buffer, size);
208 ProfilerStop("read");
209
210 if (readSize == -1)
211 {
212 if (errno == EINTR)
213 {
214 continue;
215 }
216
217 throw std::ios_base::failure("ERROR: couldn't read from file " +
218 m_Name +
219 ", in call to IME IO read\n");
220 }
221
222 buffer += readSize;
223 size -= readSize;
224 }
225 };
226
227 if (start != MaxSizeT)
228 {
229 const auto newPosition =
230 ime_client_native2_lseek(m_FileDescriptor, start, SEEK_SET);
231
232 if (static_cast<size_t>(newPosition) != start)
233 {
234 throw std::ios_base::failure(
235 "ERROR: couldn't move to start position " +
236 std::to_string(start) + " in file " + m_Name +
237 ", in call to IME lseek errno " + std::to_string(errno) + "\n");
238 }
239 }
240
241 if (size > DefaultMaxFileBatchSize)
242 {
243 const size_t batches = size / DefaultMaxFileBatchSize;
244 const size_t remainder = size % DefaultMaxFileBatchSize;
245
246 size_t position = 0;
247 for (size_t b = 0; b < batches; ++b)
248 {
249 lf_Read(&buffer[position], DefaultMaxFileBatchSize);
250 position += DefaultMaxFileBatchSize;
251 }
252 lf_Read(&buffer[position], remainder);
253 }
254 else
255 {
256 lf_Read(buffer, size);
257 }
258 }
259
GetSize()260 size_t FileIME::GetSize()
261 {
262 struct stat fileStat;
263 if (fstat(m_FileDescriptor, &fileStat) == -1)
264 {
265 throw std::ios_base::failure("ERROR: couldn't get size of file " +
266 m_Name + "\n");
267 }
268 return static_cast<size_t>(fileStat.st_size);
269 }
270
Flush()271 void FileIME::Flush()
272 {
273 if (m_SyncToPFS)
274 {
275 ime_client_native2_fsync(m_FileDescriptor);
276 ime_client_native2_bfs_sync(m_FileDescriptor, true);
277 }
278 }
279
Close()280 void FileIME::Close()
281 {
282 ProfilerStart("close");
283 if (m_SyncToPFS)
284 {
285 ime_client_native2_fsync(m_FileDescriptor);
286 ime_client_native2_bfs_sync(m_FileDescriptor, true);
287 }
288 const int status = ime_client_native2_close(m_FileDescriptor);
289 ProfilerStop("close");
290
291 if (status == -1)
292 {
293 throw std::ios_base::failure("ERROR: couldn't close file " + m_Name +
294 ", in call to IME IO close\n");
295 }
296
297 m_IsOpen = false;
298 }
299
Delete()300 void FileIME::Delete()
301 {
302 if (m_IsOpen)
303 {
304 Close();
305 }
306 ime_client_native2_unlink(m_Name.c_str());
307 }
308
CheckFile(const std::string hint) const309 void FileIME::CheckFile(const std::string hint) const
310 {
311 if (m_FileDescriptor == -1)
312 {
313 throw std::ios_base::failure("ERROR: " + hint + "\n");
314 }
315 }
316
SeekToEnd()317 void FileIME::SeekToEnd()
318 {
319 const int status = ime_client_native2_lseek(m_FileDescriptor, 0, SEEK_END);
320 if (status == -1)
321 {
322 throw std::ios_base::failure(
323 "ERROR: couldn't seek to the end of file " + m_Name +
324 ", in call to IME IO lseek\n");
325 }
326 }
327
SeekToBegin()328 void FileIME::SeekToBegin()
329 {
330 const int status = ime_client_native2_lseek(m_FileDescriptor, 0, SEEK_SET);
331 if (status == -1)
332 {
333 throw std::ios_base::failure(
334 "ERROR: couldn't seek to the begin of file " + m_Name +
335 ", in call to IME IO lseek\n");
336 }
337 }
338
339 } // end namespace transport
340 } // end namespace adios2
341