File indexing completed on 2024-04-28 04:58:02
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0003 SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org> 0004 */ 0005 0006 #ifndef TRANSFER_H 0007 #define TRANSFER_H 0008 0009 #include <QVarLengthArray> 0010 #include <QtGlobal> 0011 0012 #include <array> 0013 #include <condition_variable> 0014 #include <mutex> 0015 0016 constexpr off_t c_minSegmentSize = 64 * 1024; // minimal size on stack 0017 constexpr off_t c_maxSegmentSize = 4L * 1024 * 1024; // 4MiB is the largest request we make 0018 0019 struct TransferSegment { 0020 explicit TransferSegment(const off_t fileSize); 0021 0022 ssize_t size = 0; // current size (i.e. the size that was put into buf) 0023 QVarLengthArray<char, c_minSegmentSize> buf; // data buffer, only filled up to size! 0024 0025 private: 0026 static off_t segmentSizeForFileSize(const off_t fileSize_); 0027 }; 0028 0029 // Naive ring buffer. 0030 // Segment instances are held in the buffer, i.e. only alloc'd once at 0031 // beginning of the operation. Kind of a mix between ring and pool. 0032 // 0033 // The popping thread cannot pop while the pushing thread is still on 0034 // an element. As such we need at least 3 elements to prevent dead locks. 0035 class TransferRingBuffer 0036 { 0037 public: 0038 // fileSize is the stat'd file size of the source file. 0039 explicit TransferRingBuffer(const off_t fileSize_); 0040 ~TransferRingBuffer() = default; 0041 0042 // Pops an item into the pull thread. This blocks 0043 // when the push thread is also currently on that index. 0044 // This can return nullptr if the push thread set the done state. 0045 // @note once done unpop() needs calling 0046 TransferSegment *pop(); 0047 0048 // Frees the item used by the pull thread. So it may be used by the 0049 // push thread. 0050 void unpop(); 0051 0052 // Simply returns a ptr to the item the current push thread marker is 0053 // at. i.e. the item "locked" for reading. 0054 // @note once done push() needs calling 0055 TransferSegment *nextFree(); 0056 0057 // Pushes ahead from the item obtained by nextFree. 0058 // This effectively allows the pull thread to pop() this item again. 0059 void push(); 0060 0061 // Only called by push thread to mark the buffer done and wake waiting 0062 // threads. 0063 void done(); 0064 0065 private: 0066 bool m_done = false; 0067 std::mutex m_mutex; 0068 std::condition_variable m_cond; 0069 static const size_t m_capacity = 4; 0070 std::array<std::unique_ptr<TransferSegment>, m_capacity> m_buffer; 0071 size_t head = 0; // index of push thread (prevents pop() from pull thread) 0072 size_t tail = 0; // index of pull thread (prevents push() from push thread) 0073 }; 0074 0075 #endif // TRANSFER_H