VRS
A file format for sensor data.
Loading...
Searching...
No Matches
AsyncDiskFileChunk.h
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <vrs/DiskFile.h>
20
21#if VRS_ASYNC_DISKFILE_SUPPORTED()
22
23#include <cassert>
24#include <cstdio>
25
26// POSIX AIO (aio_write/aio_error/aio_return) is available on Linux/Mac but not Android NDK.
27// When AIO is not available, fallback to PSync (O_DIRECT + pwrite) which bypasses page cache
28// without needing POSIX AIO.
29#define POSIX_AIO_SUPPORTED() (IS_MAC_PLATFORM() || IS_LINUX_PLATFORM())
30
31#if IS_WINDOWS_PLATFORM()
32#define VC_EXTRALEAN
33#include <Windows.h>
34#elif POSIX_AIO_SUPPORTED()
35#include <aio.h>
36#include <fcntl.h>
37#include <unistd.h>
38#else
39#include <fcntl.h>
40#include <unistd.h>
41#endif
42
43#include <atomic>
44#include <condition_variable>
45#include <deque>
46#include <functional>
47#include <map>
48#include <memory>
49#include <string>
50
51#include <vrs/ErrorCode.h>
52#include <vrs/VrsExport.h>
53#include <vrs/os/Platform.h>
54
55#define VRS_DISKFILECHUNK "AsyncDiskFileChunk"
56
57namespace vrs {
58
59#if IS_WINDOWS_PLATFORM()
60// Windows doesn't normally define these.
61using ssize_t = int64_t;
62#define O_DIRECT 0x80000000U
63
64struct VRS_API AsyncWindowsHandle {
65 AsyncWindowsHandle() : h_(INVALID_HANDLE_VALUE) {}
66 AsyncWindowsHandle(HANDLE h) : h_(h) {}
67 AsyncWindowsHandle(AsyncWindowsHandle&& rhs) : h_(rhs.h_) {
68 rhs.h_ = INVALID_HANDLE_VALUE;
69 }
70 AsyncWindowsHandle(AsyncWindowsHandle& rhs) : h_(rhs.h_) {}
71 AsyncWindowsHandle& operator=(AsyncWindowsHandle&& rhs) {
72 h_ = rhs.h_;
73 rhs.h_ = INVALID_HANDLE_VALUE;
74 return *this;
75 }
76
77 bool isOpened() const;
78 int open(const std::string& path, const char* modes, int flags);
79 int close();
80 int pwrite(const void* buf, size_t count, int64_t offset, size_t& outWriteSize);
81 int read(void* buf, size_t count, int64_t offset, size_t& outReadSize);
82 int truncate(int64_t newSize);
83 int seek(int64_t pos, int origin, int64_t& outFilepos);
84
85 private:
86 int _readwrite(bool readNotWrite, void* buf, size_t count, int64_t offset, size_t& outSize);
87
88 public:
89 HANDLE h_ = INVALID_HANDLE_VALUE;
90 std::mutex mtx_;
91};
92using AsyncHandle = AsyncWindowsHandle;
93#else
94struct VRS_API AsyncFileDescriptor {
95 static constexpr int INVALID_FILE_DESCRIPTOR = -1;
96
97 AsyncFileDescriptor() = default;
98 explicit AsyncFileDescriptor(int fd) : fd_(fd) {}
99 AsyncFileDescriptor(AsyncFileDescriptor&& rhs) noexcept : fd_(rhs.fd_) {
100 rhs.fd_ = INVALID_FILE_DESCRIPTOR;
101 }
102 AsyncFileDescriptor(const AsyncFileDescriptor& rhs) noexcept = delete;
103 AsyncFileDescriptor& operator=(AsyncFileDescriptor&& rhs) noexcept {
104 fd_ = rhs.fd_;
105 rhs.fd_ = INVALID_FILE_DESCRIPTOR;
106 return *this;
107 }
108 AsyncFileDescriptor& operator=(const AsyncFileDescriptor& rhs) = delete;
109
110 bool operator==(int fd) const {
111 return fd_ == fd;
112 }
113
114 int open(const std::string& path, const char* modes, int flags);
115 [[nodiscard]] bool isOpened() const;
116 int read(void* ptr, size_t bufferSize, size_t offset, size_t& outReadSize);
117 int truncate(int64_t newSize);
118 int seek(int64_t pos, int origin, int64_t& outFilepos);
119 int pwrite(const void* buf, size_t count, off_t offset, size_t& written);
120 int close();
121
122 int fd_ = INVALID_FILE_DESCRIPTOR;
123};
124using AsyncHandle = AsyncFileDescriptor;
125#endif
126
127class VRS_API AlignedBuffer {
128 private:
129 void* aligned_buffer_ = nullptr;
130 size_t capacity_ = 0;
131 size_t size_ = 0;
132
133 protected:
135 AlignedBuffer(size_t size, size_t memalign, size_t lenalign);
136
137 [[nodiscard]] inline bool isValid() const {
138 return aligned_buffer_ != nullptr;
139 }
140
141 public:
144 static std::unique_ptr<AlignedBuffer> make(size_t size, size_t memalign, size_t lenalign);
145
146 virtual ~AlignedBuffer();
147
148 [[nodiscard]] inline size_t size() const {
149 return size_;
150 }
151 [[nodiscard]] inline size_t capacity() const {
152 return capacity_;
153 }
154 [[nodiscard]] inline bool empty() const {
155 return !size();
156 }
157 [[nodiscard]] inline bool full() const {
158 return size() == capacity();
159 }
160
161 void free();
162 void clear();
163 [[nodiscard]] inline void* data() const {
164 return aligned_buffer_;
165 }
166 [[nodiscard]] inline char* bdata() const {
167 return reinterpret_cast<char*>(aligned_buffer_);
168 }
173 [[nodiscard]] bool add(const void* buffer, size_t size, size_t& outCopiedSize);
174};
175
176class AsyncBuffer;
177#if IS_WINDOWS_PLATFORM()
178struct VRS_API AsyncOVERLAPPED {
179 OVERLAPPED ov;
180 // Allows the completion routine to recover a pointer to the containing AsyncBuffer
181 AsyncBuffer* self;
182};
183#endif
184
185class VRS_API AsyncBuffer : public AlignedBuffer {
186 public:
187 using complete_write_callback = std::function<void(ssize_t io_return, int io_errno)>;
188
191 static std::unique_ptr<AsyncBuffer> make(size_t size, size_t memalign, size_t lenalign);
192
193 ~AsyncBuffer() override = default;
194
195 void complete_write(ssize_t io_return, int io_errno);
196 [[nodiscard]] int
197 start_write(const AsyncHandle& file, int64_t offset, complete_write_callback on_complete);
198
199 protected:
200 AsyncBuffer(size_t size, size_t memalign, size_t lenalign)
201 : AlignedBuffer(size, memalign, lenalign) {}
202
203 private:
204#if IS_WINDOWS_PLATFORM()
205 AsyncOVERLAPPED ov_;
206 static void CompletedWriteRoutine(DWORD dwErr, DWORD cbBytesWritten, LPOVERLAPPED lpOverlapped);
207#elif POSIX_AIO_SUPPORTED()
208 struct aiocb aiocb_{};
209 static void SigEvNotifyFunction(union sigval val);
210#endif
211 complete_write_callback on_complete_ = nullptr;
212};
213
214class VRS_API AsyncDiskFileChunk {
215 public:
216 AsyncDiskFileChunk() = default;
217 AsyncDiskFileChunk(std::string path, int64_t offset, int64_t size)
218 : path_{std::move(path)}, offset_{offset}, size_{size} {}
219 AsyncDiskFileChunk(AsyncDiskFileChunk&& other) noexcept;
220
221 // Prevent copying
222 AsyncDiskFileChunk(const AsyncDiskFileChunk& other) noexcept = delete;
223 AsyncDiskFileChunk& operator=(const AsyncDiskFileChunk& other) noexcept = delete;
224 AsyncDiskFileChunk& operator=(AsyncDiskFileChunk&& rhs) noexcept = delete;
225
226 ~AsyncDiskFileChunk();
227
228 int create(const std::string& newpath, const FileSpec::Extras& options);
229 int open(bool readOnly, const FileSpec::Extras& options);
230 int close();
231 int rewind();
232 [[nodiscard]] bool eof() const;
233 bool isOpened();
234 int write(const void* buffer, size_t count, size_t& outWrittenSize);
235 void setSize(int64_t newSize);
236 int flush();
237 int truncate(int64_t newSize);
238 int read(void* buffer, size_t count, size_t& outReadSize);
239 [[nodiscard]] int64_t getSize() const;
240 [[nodiscard]] bool contains(int64_t fileOffset) const;
241 int tell(int64_t& outFilepos) const;
242 int seek(int64_t pos, int origin);
243 [[nodiscard]] const std::string& getPath() const;
244 void setOffset(int64_t newOffset);
245 [[nodiscard]] int64_t getOffset() const;
246
247 enum class IoEngine {
248 Sync,
249 AIO,
250 PSync,
251 };
252
253 private:
254 struct QueuedWrite {
255 AsyncBuffer* buffer_;
256 // N.B. QueuedWrite's are guaranteed to be flushed before the associated file descriptor is
257 // close, so storing this via reference is safe.
258 const AsyncHandle& file_;
259 off_t offset_;
260 AsyncBuffer::complete_write_callback callback_;
261 QueuedWrite(
262 AsyncBuffer* buffer,
263 AsyncHandle& file,
264 off_t offset,
265 AsyncBuffer::complete_write_callback callback)
266 : buffer_(buffer), file_(file), offset_(offset), callback_(std::move(callback)) {}
267 };
268
269 int flushWriteBuffer();
270 int ensureOpenNonDirect();
271 int ensureOpenDirect();
272 int ensureOpen_(int requested_flags);
273 void complete_write(AsyncBuffer* buffer, ssize_t io_return, int io_errno);
274 AsyncBuffer* get_free_buffer_locked(std::unique_lock<std::mutex>& lock);
275 AsyncBuffer* get_free_buffer();
276 void free_buffer(AsyncBuffer*& buffer);
277 void free_buffer_locked(std::unique_lock<std::mutex>& lock, AsyncBuffer*& buffer);
278 void pump_buffers();
279 void pump_buffers_locked();
280 int alloc_write_buffers();
281 int free_write_buffers();
282 int init_parameters(const FileSpec::Extras& options);
283
284 AsyncHandle file_{};
285 std::string path_; // path of this chunk
286 int64_t offset_{}; // offset of this chunk in the file
287 int64_t size_{}; // size of the chunk
288
289 // Keeps track of the current read/write position in the file of the current buffer.
290 int64_t file_position_ = 0;
291
292 const char* file_mode_ = nullptr;
293 // Keeps track of the flags currently in force for the opened fd_. Typically a subset of the
294 // supported_flags_
295 int current_flags_ = 0;
296 // The flags supported by the underlying path_ file
297 int supported_flags_ = 0;
298
299 // Protects the following members from the writing thread as well as the asyncio callback
300 // thread(s). Note that this lock is not really required on Windows, as the callbacks are
301 // delivered on the dispatching thread when it's in an alertable state.
302 std::mutex buffers_mutex_;
303 // Used to notify a waiting writing thread that a buffer was freed.
304 std::condition_variable buffer_freed_cv_;
305 // The list of free buffers
306 std::vector<AsyncBuffer*> buffers_free_;
307 // The list of buffers to be written. Drained by pump_buffers()
308 std::deque<QueuedWrite> buffers_queued_;
309 // A count of the number of buffers waiting on async completions
310 //
311 // This could be a std::atomic<size_t>, but the current implementation has to take the lock
312 // anyway to manage the list of buffers_free_, so don't bother.
313 size_t buffers_writing_ = 0;
314 // A list of all the buffers to keep them alive when they are being written (no longer in any
315 // other queue)
316 std::vector<std::unique_ptr<AsyncBuffer>> buffers_;
317 // The current buffer (if any) being filled by calls to `write()`. It will either be queued
318 // for async write by `write()`, or written out by `flushWriteBuffer()`
319 AsyncBuffer* current_buffer_ = nullptr;
320 // If != SUCCESS, represents errors that were signaled by async writes completing. Typically
321 // returned to the caller as the result of another, later operation (e.g. another write after
322 // the failure, or a call to flushWriteBuffer(), etc)
323 std::atomic<int> async_error_ = SUCCESS;
324
325 // Operational parameters initialized from the FileSpec extra params/options at create/open
326 // time. These can be tuned by the user via uri parameters.
327 IoEngine ioengine_ = IoEngine::AIO;
328 bool use_directio_ = true;
329 // How many asyncio buffers to allocate and fill
330 size_t num_buffers_ = 0;
331 // The size of each individual buffer
332 size_t buffer_size_ = 0;
333 // The maximum number of simultaneous async_write operations allowed
334 size_t iodepth_ = 4;
335 // The requested alignment of buffer lengths and file offsets
336 size_t offset_align_ = 0;
337 // The requested length of memory alignment
338 size_t mem_align_ = 0;
339};
340
341} // namespace vrs
342
343#endif
int write(const string &cacheFile, const set< StreamId > &streamIds, const map< string, string > &fileTags, const map< StreamId, StreamTags > &streamTags, const vector< IndexRecord::RecordInfo > &recordIndex, bool fileHasIndex)
Definition FileDetailsCache.cpp:225
int read(const string &cacheFile, set< StreamId > &outStreamIds, map< string, string > &outFileTags, map< StreamId, StreamTags > &outStreamTags, vector< IndexRecord::RecordInfo > &outRecordIndex, bool &outFileHasIndex)
Definition FileDetailsCache.cpp:266
Definition Compressor.cpp:113