| 116 | } |
| 117 | |
| 118 | void WriterThread::inputPwrite(int tid, string* data) { |
| 119 | size_t bound = libdeflate_gzip_compress_bound(mCompressors[tid], data->size()); |
| 120 | // Grow per-worker buffer if needed |
| 121 | if (bound > mCompBufSizes[tid]) { |
| 122 | delete[] mCompBufs[tid]; |
| 123 | mCompBufs[tid] = new char[bound]; |
| 124 | mCompBufSizes[tid] = bound; |
| 125 | } |
| 126 | size_t outsize = libdeflate_gzip_compress(mCompressors[tid], data->data(), data->size(), |
| 127 | mCompBufs[tid], bound); |
| 128 | if (outsize == 0) |
| 129 | error_exit("libdeflate gzip compression failed"); |
| 130 | delete data; |
| 131 | const char* writeData = mCompBufs[tid]; |
| 132 | size_t wsize = outsize; |
| 133 | |
| 134 | size_t seq = mNextSeq[tid]; |
| 135 | |
| 136 | // Wait for previous batch's cumulative offset. |
| 137 | // Sleep yields CPU to prevent livelock under contention. |
| 138 | size_t offset = 0; |
| 139 | if (seq > 0) { |
| 140 | size_t prevSlot = (seq - 1) & (OFFSET_RING_SIZE - 1); |
| 141 | while (mOffsetRing[prevSlot].published_seq.load(std::memory_order_acquire) != seq - 1) { |
| 142 | std::this_thread::sleep_for(std::chrono::microseconds(1)); |
| 143 | } |
| 144 | offset = mOffsetRing[prevSlot].cumulative_offset.load(std::memory_order_relaxed); |
| 145 | } |
| 146 | |
| 147 | // Publish offset BEFORE pwrite — next worker starts immediately |
| 148 | size_t mySlot = seq & (OFFSET_RING_SIZE - 1); |
| 149 | mOffsetRing[mySlot].cumulative_offset.store(offset + wsize, std::memory_order_relaxed); |
| 150 | mOffsetRing[mySlot].published_seq.store(seq, std::memory_order_release); |
| 151 | |
| 152 | // pwrite (concurrent with other workers on non-overlapping regions) |
| 153 | if (wsize > 0) { |
| 154 | size_t written = 0; |
| 155 | while (written < wsize) { |
| 156 | ssize_t ret = pwrite(mFd, writeData + written, wsize - written, offset + written); |
| 157 | if (ret < 0) { |
| 158 | if (errno == EINTR) continue; |
| 159 | error_exit("pwrite failed: " + string(strerror(errno))); |
| 160 | } |
| 161 | if (ret == 0) |
| 162 | error_exit("pwrite returned 0 (disk full?)"); |
| 163 | written += ret; |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | mNextSeq[tid] += mOptions->thread; |
| 168 | } |
| 169 | |
| 170 | void WriterThread::cleanup() { |
| 171 | if (mPwriteMode) { |
nothing calls this directly
no test coverage detected