new cache write back algorithm; fix single key press with key repeat

This commit is contained in:
nakst 2021-10-24 10:31:20 +01:00
parent 2e457eb792
commit 411ff15698
5 changed files with 113 additions and 70 deletions

View File

@ -475,6 +475,12 @@ ptrdiff_t EsDirectoryEnumerateChildren(const char *path, ptrdiff_t pathBytes, Es
node.directoryChildren = 4194304 / sizeof(EsDirectoryChild); // TODO Grow the buffer until all entries fit. node.directoryChildren = 4194304 / sizeof(EsDirectoryChild); // TODO Grow the buffer until all entries fit.
} }
if (node.directoryChildren == 0) {
// Empty directory.
*buffer = nullptr;
return 0;
}
*buffer = (EsDirectoryChild *) EsHeapAllocate(sizeof(EsDirectoryChild) * node.directoryChildren, true); *buffer = (EsDirectoryChild *) EsHeapAllocate(sizeof(EsDirectoryChild) * node.directoryChildren, true);
ptrdiff_t result; ptrdiff_t result;

View File

@ -43,7 +43,7 @@ struct MMActiveSectionManager {
KMutex mutex; KMutex mutex;
LinkedList<CCActiveSection> lruList; LinkedList<CCActiveSection> lruList;
LinkedList<CCActiveSection> modifiedList; LinkedList<CCActiveSection> modifiedList;
KEvent modifiedNonEmpty, modifiedNonFull; KEvent modifiedNonEmpty, modifiedNonFull, modifiedGettingFull;
Thread *writeBackThread; Thread *writeBackThread;
}; };
@ -245,6 +245,9 @@ void CCWriteSectionPrepare(CCActiveSection *section) {
section->flush = false; section->flush = false;
KEventReset(&section->writeCompleteEvent); KEventReset(&section->writeCompleteEvent);
section->accessors = 1; section->accessors = 1;
if (!activeSectionManager.modifiedList.count) KEventReset(&activeSectionManager.modifiedNonEmpty);
if (activeSectionManager.modifiedList.count < CC_MODIFIED_GETTING_FULL) KEventReset(&activeSectionManager.modifiedGettingFull);
KEventSet(&activeSectionManager.modifiedNonFull, false, true);
} }
void CCWriteSection(CCActiveSection *section) { void CCWriteSection(CCActiveSection *section) {
@ -368,12 +371,19 @@ void CCActiveSectionReturnToLists(CCActiveSection *section, bool writeBack) {
if (activeSectionManager.modifiedList.count > CC_MAX_MODIFIED) { if (activeSectionManager.modifiedList.count > CC_MAX_MODIFIED) {
waitNonFull = true; waitNonFull = true;
continue; continue;
} else if (activeSectionManager.modifiedList.count == CC_MAX_MODIFIED) { }
if (activeSectionManager.modifiedList.count == CC_MAX_MODIFIED) {
KEventReset(&activeSectionManager.modifiedNonFull); KEventReset(&activeSectionManager.modifiedNonFull);
} }
activeSectionManager.modifiedList.InsertEnd(&section->listItem); if (activeSectionManager.modifiedList.count >= CC_MODIFIED_GETTING_FULL) {
KEventSet(&activeSectionManager.modifiedGettingFull, false, true);
}
KEventSet(&activeSectionManager.modifiedNonEmpty, false, true); KEventSet(&activeSectionManager.modifiedNonEmpty, false, true);
activeSectionManager.modifiedList.InsertEnd(&section->listItem);
} else { } else {
activeSectionManager.lruList.InsertEnd(&section->listItem); activeSectionManager.lruList.InsertEnd(&section->listItem);
} }
@ -1156,10 +1166,30 @@ EsError CCSpaceAccess(CCSpace *cache, K_USER_BUFFER void *_buffer, EsFileOffset
return ES_SUCCESS; return ES_SUCCESS;
} }
void CCWriteBehindThread() { bool CCWriteBehindSection() {
while (true) { CCActiveSection *section = nullptr;
// Wait for an active section to be modified, and have no accessors. KMutexAcquire(&activeSectionManager.mutex);
if (activeSectionManager.modifiedList.count) {
section = activeSectionManager.modifiedList.firstItem->thisItem;
CCWriteSectionPrepare(section);
}
KMutexRelease(&activeSectionManager.mutex);
if (section) {
CCWriteSection(section);
return true;
} else {
return false;
}
}
void CCWriteBehindThread() {
uintptr_t lastWriteMs = 0;
while (true) {
#if 0
KEventWait(&activeSectionManager.modifiedNonEmpty); KEventWait(&activeSectionManager.modifiedNonEmpty);
if (MM_AVAILABLE_PAGES() > MM_LOW_AVAILABLE_PAGES_THRESHOLD && !scheduler.shutdown) { if (MM_AVAILABLE_PAGES() > MM_LOW_AVAILABLE_PAGES_THRESHOLD && !scheduler.shutdown) {
@ -1167,26 +1197,32 @@ void CCWriteBehindThread() {
KEventWait(&pmm.availableLow, CC_WAIT_FOR_WRITE_BEHIND); KEventWait(&pmm.availableLow, CC_WAIT_FOR_WRITE_BEHIND);
} }
while (true) { while (CCWriteBehindSection());
// Take a section, and mark it as being written. #else
// Wait until the modified list is non-empty.
KEventWait(&activeSectionManager.modifiedNonEmpty);
CCActiveSection *section = nullptr; if (lastWriteMs < CC_WAIT_FOR_WRITE_BEHIND) {
KMutexAcquire(&activeSectionManager.mutex); // Wait for a reason to want to write behind.
// - The CC_WAIT_FOR_WRITE_BEHIND timer expires.
if (activeSectionManager.modifiedList.count) { // - The number of available page frames is low (pmm.availableLow).
section = activeSectionManager.modifiedList.firstItem->thisItem; // - The system is shutting down and so the cache must be flushed (scheduler.killedEvent).
CCWriteSectionPrepare(section); // - The modified list is getting full (activeSectionManager.modifiedGettingFull).
} KTimer timer = {};
KTimerSet(&timer, CC_WAIT_FOR_WRITE_BEHIND - lastWriteMs);
KEventSet(&activeSectionManager.modifiedNonFull, false, true); KEvent *events[] = { &timer.event, &pmm.availableLow, &scheduler.killedEvent, &activeSectionManager.modifiedGettingFull };
KMutexRelease(&activeSectionManager.mutex); scheduler.WaitEvents(events, sizeof(events) / sizeof(events[0]));
KTimerRemove(&timer);
if (!section) {
break;
} else {
CCWriteSection(section);
}
} }
// Write back 1/CC_WRITE_BACK_DIVISORth of the modified list.
lastWriteMs = scheduler.timeMs;
KMutexAcquire(&activeSectionManager.mutex);
uintptr_t writeCount = (activeSectionManager.modifiedList.count + CC_WRITE_BACK_DIVISOR - 1) / CC_WRITE_BACK_DIVISOR;
KMutexRelease(&activeSectionManager.mutex);
while (writeCount && CCWriteBehindSection()) writeCount--;
lastWriteMs = scheduler.timeMs - lastWriteMs;
#endif
} }
} }
@ -1206,7 +1242,6 @@ void CCInitialise() {
KernelLog(LOG_INFO, "Memory", "cache initialised", "MMInitialise - Active section manager initialised with a maximum of %d of entries.\n", KernelLog(LOG_INFO, "Memory", "cache initialised", "MMInitialise - Active section manager initialised with a maximum of %d of entries.\n",
activeSectionManager.sectionCount); activeSectionManager.sectionCount);
activeSectionManager.modifiedNonEmpty.autoReset = true;
KEventSet(&activeSectionManager.modifiedNonFull); KEventSet(&activeSectionManager.modifiedNonFull);
activeSectionManager.writeBackThread = scheduler.SpawnThread("CCWriteBehind", (uintptr_t) CCWriteBehindThread, 0, ES_FLAGS_DEFAULT); activeSectionManager.writeBackThread = scheduler.SpawnThread("CCWriteBehind", (uintptr_t) CCWriteBehindThread, 0, ES_FLAGS_DEFAULT);
activeSectionManager.writeBackThread->isPageGenerator = true; activeSectionManager.writeBackThread->isPageGenerator = true;

View File

@ -8,8 +8,12 @@
// TODO Determine the best values for these constants. // TODO Determine the best values for these constants.
// Wait 1 second before running the write behind thread. (Ignored if MM_AVAILABLE_PAGES() is below the low threshold.) // Interval between write behinds. (Assuming no low memory conditions are in effect.)
#define CC_WAIT_FOR_WRITE_BEHIND (1000) #define CC_WAIT_FOR_WRITE_BEHIND (1000)
// Divisor of the modified list size for each write behind batch.
// That is, every CC_WAIT_FOR_WRITE_BEHIND ms, 1/CC_WRITE_BACK_DIVISORth of the modified list is written back.
#define CC_WRITE_BACK_DIVISOR (8)
// Describes the virtual memory covering a section of a file. // Describes the virtual memory covering a section of a file.
#define CC_ACTIVE_SECTION_SIZE ((EsFileOffset) 262144) #define CC_ACTIVE_SECTION_SIZE ((EsFileOffset) 262144)
@ -17,6 +21,10 @@
// Maximum number of active sections on the modified list. If exceeded, writers will wait for it to drop before retrying. // Maximum number of active sections on the modified list. If exceeded, writers will wait for it to drop before retrying.
// TODO This should based off the amount of physical memory. // TODO This should based off the amount of physical memory.
#define CC_MAX_MODIFIED (67108864 / CC_ACTIVE_SECTION_SIZE) #define CC_MAX_MODIFIED (67108864 / CC_ACTIVE_SECTION_SIZE)
// The size at which the modified list is determined to be getting worryingly full;
// passing this threshold causes the write back thread to immediately start working.
#define CC_MODIFIED_GETTING_FULL (CC_MAX_MODIFIED * 2 / 3)
// The size of the kernel's address space used for mapping active sections. // The size of the kernel's address space used for mapping active sections.
#if defined(ARCH_32) #if defined(ARCH_32)

View File

@ -319,26 +319,20 @@ void WindowManager::PressKey(unsigned scancode) {
return; return;
} }
bool moveCursorNone = false;
KMutexAcquire(&mutex); KMutexAcquire(&mutex);
if (scancode == ES_SCANCODE_NUM_DIVIDE) { if (scancode == ES_SCANCODE_NUM_DIVIDE) {
KernelPanic("WindowManager::PressKey - Panic key pressed.\n"); KernelPanic("WindowManager::PressKey - Panic key pressed.\n");
} }
bool single = (scancode & K_SCANCODE_KEY_RELEASED) && maximumKeysHeld == 1;
keysHeld += (scancode & K_SCANCODE_KEY_RELEASED) ? -1 : 1;
keysHeld = MaximumInteger(keysHeld, 0); // Prevent negative keys held count.
maximumKeysHeld = (!keysHeld || keysHeld > maximumKeysHeld) ? keysHeld : maximumKeysHeld;
if (eyedropping) { if (eyedropping) {
if (scancode == (ES_SCANCODE_ESCAPE | K_SCANCODE_KEY_RELEASED)) { if (scancode == (ES_SCANCODE_ESCAPE | K_SCANCODE_KEY_RELEASED)) {
EndEyedrop(true); EndEyedrop(true);
moveCursorNone = true;
} }
goto done; MoveCursor(0, 0);
KMutexRelease(&mutex);
return;
} }
// TODO Caps lock. // TODO Caps lock.
@ -372,43 +366,43 @@ void WindowManager::PressKey(unsigned scancode) {
| ((shift | shift2) ? ES_MODIFIER_SHIFT : 0) | ((shift | shift2) ? ES_MODIFIER_SHIFT : 0)
| ((flag | flag2) ? ES_MODIFIER_FLAG : 0); | ((flag | flag2) ? ES_MODIFIER_FLAG : 0);
KernelLog(LOG_VERBOSE, "WM", "press key", "WindowManager::PressKey - Received key press %x. Modifiers are %X. Keys held: %d/%d%z.\n", EsMessage message;
scancode, modifiers, keysHeld, maximumKeysHeld, single ? " (single)" : ""); EsMemoryZero(&message, sizeof(EsMessage));
message.type = (scancode & K_SCANCODE_KEY_RELEASED) ? ES_MSG_KEY_UP : ES_MSG_KEY_DOWN;
message.keyboard.modifiers = modifiers;
message.keyboard.scancode = scancode & ~K_SCANCODE_KEY_RELEASED;
message.keyboard.numlock = numlock;
{ if (message.keyboard.scancode >= 512) {
EsMessage message; KernelPanic("WindowManager::PressKey - Scancode outside valid range.\n");
EsMemoryZero(&message, sizeof(EsMessage)); }
message.type = (scancode & K_SCANCODE_KEY_RELEASED) ? ES_MSG_KEY_UP : ES_MSG_KEY_DOWN;
message.keyboard.modifiers = modifiers; if (message.type == ES_MSG_KEY_DOWN && (keysHeldBitSet[message.keyboard.scancode / 8] & (1 << (message.keyboard.scancode % 8)))) {
message.keyboard.scancode = scancode & ~K_SCANCODE_KEY_RELEASED; message.keyboard.repeat = true;
message.keyboard.numlock = numlock; }
message.keyboard.single = single;
if (message.type == ES_MSG_KEY_DOWN) {
if (message.keyboard.scancode >= 512) { keysHeldBitSet[message.keyboard.scancode / 8] |= (1 << (message.keyboard.scancode % 8));
KernelPanic("WindowManager::PressKey - Scancode outside valid range.\n"); } else {
} keysHeldBitSet[message.keyboard.scancode / 8] &= ~(1 << (message.keyboard.scancode % 8));
}
if (message.type == ES_MSG_KEY_DOWN && (keysHeldBitSet[message.keyboard.scancode / 8] & (1 << (message.keyboard.scancode % 8)))) {
message.keyboard.repeat = true; message.keyboard.single = (scancode & K_SCANCODE_KEY_RELEASED) && maximumKeysHeld == 1;
} if (!message.keyboard.repeat) keysHeld += (scancode & K_SCANCODE_KEY_RELEASED) ? -1 : 1;
keysHeld = MaximumInteger(keysHeld, 0); // Prevent negative keys held count.
if (message.type == ES_MSG_KEY_DOWN) { maximumKeysHeld = (!keysHeld || keysHeld > maximumKeysHeld) ? keysHeld : maximumKeysHeld;
keysHeldBitSet[message.keyboard.scancode / 8] |= (1 << (message.keyboard.scancode % 8));
} else { KernelLog(LOG_VERBOSE, "WM", "press key", "WindowManager::PressKey - Received key press %x. Modifiers are %X. Keys held: %d/%d%z.\n",
keysHeldBitSet[message.keyboard.scancode / 8] &= ~(1 << (message.keyboard.scancode % 8)); scancode, modifiers, keysHeld, maximumKeysHeld, message.keyboard.single ? " (single)" : "");
}
if ((modifiers & ES_MODIFIER_CTRL) && (modifiers & ES_MODIFIER_FLAG)) {
if ((modifiers & ES_MODIFIER_CTRL) && (modifiers & ES_MODIFIER_FLAG)) { desktopProcess->messageQueue.SendMessage(nullptr, &message);
desktopProcess->messageQueue.SendMessage(nullptr, &message); } else if (activeWindow) {
} else if (activeWindow) { SendMessageToWindow(activeWindow, &message);
SendMessageToWindow(activeWindow, &message); } else {
} else { desktopProcess->messageQueue.SendMessage(nullptr, &message);
desktopProcess->messageQueue.SendMessage(nullptr, &message);
}
} }
done:;
if (moveCursorNone) MoveCursor(0, 0);
KMutexRelease(&mutex); KMutexRelease(&mutex);
} }

View File

@ -286,7 +286,7 @@ void Compile(uint32_t flags, int partitionSize, const char *volumeLabel) {
fileIndex++; fileIndex++;
} }
if (noRequiredFiles) { if (requiredFontsOnly && noRequiredFiles) {
continue; continue;
} }