28 : m_TerminationDeferral(), m_Cache(cache), m_Entries(), m_Count(0), m_Committed(false) {}
48 if (m_Committed || !callback)
52 for (
size_t i = 0; i < m_Count; ++i) {
53 CachePage* page = m_Entries.get()[i].page;
54 if (m_Entries.get()[i].references || page->
refcnt != 1 || page->writebackPins ||
55 page->callbackActive || page->evictionState != CachePage::EvictionState::Draining)
59 bool succeeded =
true;
60 for (
size_t i = 0; i < m_Count; ++i) {
61 CachePage* page = m_Entries.get()[i].page;
64 page->callbackActive =
true;
70 m_Cache.recordMutation(page);
72 const bool written = callback(page->
key, page->
location, context);
77 page->writebackFailed = page->writebackFailed || !written;
78 page->callbackActive =
false;
80 page->callbackOwner =
nullptr;
82 m_Cache.updateWritebackIndex(page);
113 if ((count && !references) || cutoff % CachePageSize) {
114 return DiscardStatus::Invalid;
116 for (
size_t i = 0; i < count; ++i) {
117 if (references[i].key % CachePageSize || references[i].references == ~
size_t(0) ||
118 (i && references[i - 1].key >= references[i].key)) {
119 return DiscardStatus::Invalid;
127 return DiscardStatus::NoMemory;
131 return DiscardStatus::Closed;
134 constexpr size_t MaximumPages = 65536;
139 return DiscardStatus::Closed;
143 uintptr_t cursor = cutoff;
144 while (
m_Pages.lowerBound(cursor, key, page)) {
145 if (key == ~uintptr_t(0) || ++pages > MaximumPages) {
146 return DiscardStatus::NoMemory;
153 if (!plan.get()->m_Entries) {
154 return DiscardStatus::NoMemory;
158 DiscardStatus status = DiscardStatus::Ready;
161 uintptr_t cursor = cutoff;
164 size_t referenceIndex = 0;
165 while (referenceIndex < count && references[referenceIndex].key < cutoff) {
168 while (
m_Pages.lowerBound(cursor, key, page)) {
169 if (plan.get()->m_Count == pages || key == ~uintptr_t(0)) {
170 status = DiscardStatus::Busy;
174 while (referenceIndex < count && references[referenceIndex].key < key) {
175 if (references[referenceIndex++].references) {
176 status = DiscardStatus::Invalid;
180 if (status != DiscardStatus::Ready) {
184 if (referenceIndex < count && references[referenceIndex].key == key) {
185 expected = references[referenceIndex++].references;
187 if (page->evictionState != CachePage::EvictionState::None ||
188 page->status == CachePage::Editing || page->writebackPins > page->
refcnt ||
189 page->
refcnt - page->writebackPins != 1 + expected) {
190 status = DiscardStatus::Busy;
193 page->evictionState = CachePage::EvictionState::Draining;
194 plan.get()->m_Entries.get()[plan.get()->m_Count++] = {page, expected};
196 while (status == DiscardStatus::Ready && referenceIndex < count) {
197 if (references[referenceIndex++].references) {
198 status = DiscardStatus::Invalid;
202 if (status != DiscardStatus::Ready) {
206 for (
size_t i = 0; i < plan.get()->m_Count; ++i) {
207 const auto& entry = plan.get()->m_Entries.get()[i];
213 if (!entry.page->writebackPins) {
214 assert(entry.page->refcnt == 1 + entry.references);
219 Thread::CallbackDrain, entry.page->key);
220 if (reason == WaitQueue::WakeReason::Unwinding ||
221 reason == WaitQueue::WakeReason::Terminating) {
222 FATAL(
"Prepared cache discard interrupted during callback drain");
226 if (entry.page->writebackPins) {
227 return DiscardStatus::Busy;
231 result = pedigree_std::move(plan);
232 return DiscardStatus::Ready;
uint64_t addCacheRequest(Cache *cache, bool asynchronous, CacheConstants::CallbackCause cause, uintptr_t key, uintptr_t location=0, bool transferredPin=false, bool onlyIfDirty=false, bool batch=false)
uintptr_t location
The location of this page in memory.