| 83 | |
| 84 | template <bool writing> |
| 85 | Future<Void> AsyncFileCached::read_write_impl(AsyncFileCached* self, |
| 86 | typename std::conditional_t<writing, const uint8_t*, uint8_t*> data, |
| 87 | int length, |
| 88 | int64_t offset) { |
| 89 | if constexpr (writing) { |
| 90 | if (offset + length > self->length) |
| 91 | self->length = offset + length; |
| 92 | } |
| 93 | |
| 94 | std::vector<Future<Void>> actors; |
| 95 | |
| 96 | int offsetInPage = offset % self->pageCache->pageSize; |
| 97 | int64_t pageOffset = offset - offsetInPage; |
| 98 | |
| 99 | int remaining = length; |
| 100 | |
| 101 | while (remaining) { |
| 102 | ++self->countFileCacheFinds; |
| 103 | ++self->countCacheFinds; |
| 104 | auto p = self->pages.find(pageOffset); |
| 105 | if (p == self->pages.end()) { |
| 106 | AFCPage* page = new AFCPage(self, pageOffset); |
| 107 | p = self->pages.insert(std::make_pair(pageOffset, page)).first; |
| 108 | } else { |
| 109 | self->pageCache->updateHit(p->second); |
| 110 | } |
| 111 | |
| 112 | int bytesInPage = std::min(self->pageCache->pageSize - offsetInPage, remaining); |
| 113 | |
| 114 | Future<Void> w; |
| 115 | if constexpr (writing) { |
| 116 | w = p->second->write(data, bytesInPage, offsetInPage); |
| 117 | } else { |
| 118 | w = p->second->read(data, bytesInPage, offsetInPage); |
| 119 | } |
| 120 | if (!w.isReady() || w.isError()) |
| 121 | actors.push_back(w); |
| 122 | |
| 123 | data += bytesInPage; |
| 124 | pageOffset += self->pageCache->pageSize; |
| 125 | offsetInPage = 0; |
| 126 | |
| 127 | remaining -= bytesInPage; |
| 128 | } |
| 129 | |
| 130 | // This is susceptible to the introduction of waits on the read/write path: no wait can occur prior to |
| 131 | // AFCPage::readThrough or prevLength will be set prematurely |
| 132 | self->prevLength = self->length; |
| 133 | |
| 134 | return waitForAll(actors); |
| 135 | } |
| 136 | |
| 137 | Future<Void> AsyncFileCached::readZeroCopy(void** data, int* length, int64_t offset) { |
| 138 | ++countFileCacheReads; |