diff --git a/apps/file_manager/commands.cpp b/apps/file_manager/commands.cpp index 556d17d..8d22c06 100644 --- a/apps/file_manager/commands.cpp +++ b/apps/file_manager/commands.cpp @@ -64,8 +64,11 @@ void CommandRename(Instance *instance, EsElement *, EsCommand *) { EsDirectoryChild information = {}; EsPathQueryInformation(newPath, newPathBytes, &information); + EsMutexAcquire(&folder->modifyEntriesMutex); + EsAssert(folder->doneInitialEnumeration); uint64_t id = FolderRemoveEntryAndUpdateInstances(folder, STRING(task->string2)); - FolderAddEntryAndUpdateInstances(folder, STRING(task->string), &information, instance, false, id); + FolderAddEntryAndUpdateInstances(folder, STRING(task->string), &information, instance, id); + EsMutexRelease(&folder->modifyEntriesMutex); EsHeapFree(oldPath); EsHeapFree(newPath); @@ -102,7 +105,10 @@ void CommandNewFolder(Instance *instance, EsElement *, EsCommand *) { Folder *folder = instance->folder; EsDirectoryChild information = {}; information.type = ES_NODE_DIRECTORY; - FolderAddEntryAndUpdateInstances(folder, STRING(task->string), &information, instance, false); + EsMutexAcquire(&folder->modifyEntriesMutex); + EsAssert(folder->doneInitialEnumeration); + FolderAddEntryAndUpdateInstances(folder, STRING(task->string), &information, instance); + EsMutexRelease(&folder->modifyEntriesMutex); CommandRename(instance, nullptr, nullptr); } @@ -136,9 +142,7 @@ void InstanceRegisterCommands(Instance *instance) { }, stableCommandID++, "Alt+Up"); EsCommandRegister(&instance->commandRefresh, instance, [] (Instance *instance, EsElement *, EsCommand *) { - EsMutexAcquire(&loadedFoldersMutex); FolderRefresh(instance->folder); - EsMutexRelease(&loadedFoldersMutex); }, stableCommandID++, "F5"); EsCommandRegister(&instance->commandNewFolder, instance, CommandNewFolder, stableCommandID++, "Ctrl+Shift+N"); diff --git a/apps/file_manager/folder.cpp b/apps/file_manager/folder.cpp index c1385ea..2f129f1 100644 --- a/apps/file_manager/folder.cpp +++ b/apps/file_manager/folder.cpp @@ -3,10 +3,11 @@ #define NAMESPACE_HANDLER_ROOT (3) // Acts as a container handler where needed. #define NAMESPACE_HANDLER_INVALID (4) // For when a folder does not exist. -EsMutex loadedFoldersMutex; Array loadedFolders; -#define MAXIMUM_FOLDERS_WITH_NO_ATTACHED_INSTANCES (20) +// #define MAXIMUM_FOLDERS_WITH_NO_ATTACHED_INSTANCES (20) +// TODO Temporary. +#define MAXIMUM_FOLDERS_WITH_NO_ATTACHED_INSTANCES (0) Array foldersWithNoAttachedInstances; ///////////////////////////////// @@ -68,15 +69,16 @@ EsError FSDirEnumerate(Folder *folder) { EsDirectoryChild *buffer = nullptr; ptrdiff_t _entryCount = EsDirectoryEnumerateChildren(STRING(folder->path), &buffer); - if (ES_CHECK_ERROR(_entryCount)) { - EsHeapFree(buffer); - return (EsError) _entryCount; + if (!ES_CHECK_ERROR(_entryCount)) { + for (intptr_t i = 0; i < _entryCount; i++) { + FolderAddEntry(folder, buffer[i].name, buffer[i].nameBytes, &buffer[i]); + } + + _entryCount = ES_SUCCESS; } - FolderAddEntries(folder, buffer, _entryCount); EsHeapFree(buffer); - - return ES_SUCCESS; + return (EsError) _entryCount; } void FSDirGetTotalSize(Folder *folder) { @@ -252,6 +254,8 @@ void NamespaceFindHandlersForPath(NamespaceHandler **itemHandler, NamespaceHandl } } } + + EsAssert(*itemHandler && *containerHandler); // NAMESPACE_HANDLER_INVALID should handle failure cases. } uint32_t NamespaceGetIcon(String path) { @@ -327,6 +331,10 @@ void FolderDestroy(Folder *folder) { } } + StringDestroy(&folder->path); + folder->attachedInstances.Free(); + HashTableFree(&folder->entries, false); + EsMutexDestroy(&folder->modifyEntriesMutex); EsHeapFree(folder); } @@ -335,8 +343,10 @@ void FolderDetachInstance(Instance *instance) { if (!folder) return; instance->folder = nullptr; folder->attachedInstances.FindAndDeleteSwap(instance, true); + folder->referenceCount--; - if (!folder->attachedInstances.Length() && !folder->attachingInstances.Length()) { + if (!folder->referenceCount) { + EsAssert(!folder->attachedInstances.Length()); foldersWithNoAttachedInstances.Add(folder); if (foldersWithNoAttachedInstances.Length() > MAXIMUM_FOLDERS_WITH_NO_ATTACHED_INSTANCES) { @@ -391,16 +401,8 @@ FolderEntry *FolderAddEntry(Folder *folder, const char *_name, size_t nameBytes, return entry; } -void FolderAddEntries(Folder *folder, EsDirectoryChild *buffer, size_t entryCount) { - for (uintptr_t i = 0; i < entryCount; i++) { - FolderAddEntry(folder, buffer[i].name, buffer[i].nameBytes, &buffer[i]); - } -} - void FolderAddEntryAndUpdateInstances(Folder *folder, const char *name, size_t nameBytes, - EsDirectoryChild *information, Instance *selectItem, bool mutexAlreadyAcquired, uint64_t id = 0) { - if (!mutexAlreadyAcquired) EsMutexAcquire(&loadedFoldersMutex); - + EsDirectoryChild *information, Instance *selectItem, uint64_t id = 0) { if (folder->containerHandler->getTotalSize) { folder->containerHandler->getTotalSize(folder); } @@ -414,13 +416,9 @@ void FolderAddEntryAndUpdateInstances(Folder *folder, const char *name, size_t n InstanceAddSingle(instance, listEntry); InstanceUpdateStatusString(instance); } - - if (!mutexAlreadyAcquired) EsMutexRelease(&loadedFoldersMutex); } uint64_t FolderRemoveEntryAndUpdateInstances(Folder *folder, const char *name, size_t nameBytes) { - EsMutexAcquire(&loadedFoldersMutex); - FolderEntry *entry = (FolderEntry *) HashTableGetLong(&folder->entries, name, nameBytes); uint64_t id = 0; @@ -434,15 +432,12 @@ uint64_t FolderRemoveEntryAndUpdateInstances(Folder *folder, const char *name, s FolderEntryCloseHandle(folder, entry); } - EsMutexRelease(&loadedFoldersMutex); return id; } void FolderPathMoved(Instance *instance, String oldPath, String newPath) { _EsPathAnnouncePathMoved(instance, STRING(oldPath), STRING(newPath)); - EsMutexAcquire(&loadedFoldersMutex); - for (uintptr_t i = 0; i < loadedFolders.Length(); i++) { Folder *folder = loadedFolders[i]; @@ -453,8 +448,6 @@ void FolderPathMoved(Instance *instance, String oldPath, String newPath) { } } - EsMutexRelease(&loadedFoldersMutex); - for (uintptr_t i = 0; i < bookmarks.Length(); i++) { PathReplacePrefix(&bookmarks[i], oldPath, newPath); } @@ -502,21 +495,12 @@ void FolderPathMoved(Instance *instance, String oldPath, String newPath) { ConfigurationSave(); } -EsError FolderAttachInstance(Instance *instance, String path, bool recurse, Folder **newFolder) { - // TODO Don't modify attachedInstances/attachingInstances/loadedFolders without the message mutex! - // And then we can remove loadedFoldersMutex. - - // (Called on the blocking task thread.) - +void FolderAttachInstance(Instance *instance, String path, bool recurse) { Folder *folder = nullptr; - NamespaceHandler *itemHandler = nullptr, *containerHandler = nullptr; - EsError error; bool driveRemoved = false; // Check if we've already loaded the folder. - EsMutexAcquire(&loadedFoldersMutex); - for (uintptr_t i = 0; i < loadedFolders.Length(); i++) { if (StringEquals(loadedFolders[i]->path, path) && loadedFolders[i]->recurse == recurse) { if (!loadedFolders[i]->refreshing) { @@ -528,70 +512,39 @@ EsError FolderAttachInstance(Instance *instance, String path, bool recurse, Fold } } - EsMutexRelease(&loadedFoldersMutex); - - // Find the handler for the path. - - NamespaceFindHandlersForPath(&itemHandler, &containerHandler, path); - - if (!itemHandler || !containerHandler) { - return ES_ERROR_FILE_DOES_NOT_EXIST; - } - - // Load a new folder. - folder = (Folder *) EsHeapAllocate(sizeof(Folder), true); - folder->path = StringDuplicate(path); folder->recurse = recurse; - folder->itemHandler = itemHandler; - folder->containerHandler = containerHandler; + NamespaceFindHandlersForPath(&folder->itemHandler, &folder->containerHandler, path); folder->cEmptyMessage = interfaceString_FileManagerEmptyFolderView; folder->driveRemoved = driveRemoved; - EsArenaInitialise(&folder->entryArena, 1048576, sizeof(FolderEntry)); - - // TODO Make this asynchronous for some folder providers, or recursive requests - // (that is, immediately present to the user while streaming data in). - error = itemHandler->enumerate(folder); - - folder->driveRemoved = false; - folder->refreshing = false; - - if (error != ES_SUCCESS) { - StringDestroy(&folder->path); - EsHeapFree(folder); - return error; - } - - if (containerHandler->getTotalSize) { - containerHandler->getTotalSize(folder); - } - - EsMutexAcquire(&loadedFoldersMutex); loadedFolders.Add(folder); success:; foldersWithNoAttachedInstances.FindAndDeleteSwap(folder, false); - folder->attachingInstances.Add(instance); - - EsMutexRelease(&loadedFoldersMutex); - - *newFolder = folder; - return ES_SUCCESS; + folder->referenceCount++; + FolderDetachInstance(instance); + instance->folder = folder; } void FolderRefresh(Folder *folder) { - // EsMutexAssertLocked(&loadedFoldersMutex); - if (folder->refreshing) { return; } folder->refreshing = true; + Array instancesToRefresh = {}; + for (uintptr_t i = 0; i < folder->attachedInstances.Length(); i++) { - InstanceLoadFolder(folder->attachedInstances[i], StringDuplicate(folder->path), LOAD_FOLDER_REFRESH); + instancesToRefresh.Add(folder->attachedInstances[i]); } + + for (uintptr_t i = 0; i < instancesToRefresh.Length(); i++) { + InstanceLoadFolder(instancesToRefresh[i], StringDuplicate(folder->path), LOAD_FOLDER_REFRESH); + } + + instancesToRefresh.Free(); } diff --git a/apps/file_manager/main.cpp b/apps/file_manager/main.cpp index ebefd4d..1120184 100644 --- a/apps/file_manager/main.cpp +++ b/apps/file_manager/main.cpp @@ -101,7 +101,7 @@ struct HistoryEntry { }; struct Drive { - const char *prefix; + char *prefix; size_t prefixBytes; EsVolumeInformation information; }; @@ -201,18 +201,20 @@ struct Folder { EsArena entryArena; Array attachedInstances; - Array attachingInstances; + uintptr_t referenceCount; String path; bool recurse; bool refreshing; bool driveRemoved; + bool doneInitialEnumeration; + EsMutex modifyEntriesMutex; + EsFileOffset spaceTotal; EsFileOffset spaceUsed; NamespaceHandler *itemHandler, *containerHandler; - const char *cEmptyMessage; }; @@ -409,6 +411,25 @@ void ConfigurationSave() { #include "commands.cpp" #include "ui.cpp" +void DriveRefreshFolders(bool removed, const char *prefix, size_t prefixBytes) { + Array foldersToRefresh = {}; + + for (uintptr_t i = 0; i < loadedFolders.Length(); i++) { + Folder *folder = loadedFolders[i]; + + if (folder->itemHandler->type == NAMESPACE_HANDLER_DRIVES_PAGE || StringStartsWith(folder->path, StringFromLiteralWithSize(prefix, prefixBytes))) { + foldersToRefresh.Add(folder); + } + } + + for (uintptr_t i = 0; i < foldersToRefresh.Length(); i++) { + foldersToRefresh[i]->driveRemoved = removed; + FolderRefresh(foldersToRefresh[i]); + } + + foldersToRefresh.Free(); +} + void DriveRemove(const char *prefix, size_t prefixBytes) { if (!prefixBytes || prefix[0] == '|') return; EsMutexAcquire(&drivesMutex); @@ -416,6 +437,7 @@ void DriveRemove(const char *prefix, size_t prefixBytes) { for (uintptr_t index = 0; index < drives.Length(); index++) { if (0 == EsStringCompareRaw(prefix, prefixBytes, drives[index].prefix, drives[index].prefixBytes)) { + EsHeapFree(drives[index].prefix); drives.Delete(index); found = true; @@ -429,20 +451,7 @@ void DriveRemove(const char *prefix, size_t prefixBytes) { EsAssert(found); EsMutexRelease(&drivesMutex); - - EsMutexAcquire(&loadedFoldersMutex); - - for (uintptr_t i = 0; i < loadedFolders.Length(); i++) { - Folder *folder = loadedFolders[i]; - - if (folder->itemHandler->type == NAMESPACE_HANDLER_DRIVES_PAGE - || StringStartsWith(folder->path, StringFromLiteralWithSize(prefix, prefixBytes))) { - folder->driveRemoved = true; - FolderRefresh(folder); - } - } - - EsMutexRelease(&loadedFoldersMutex); + DriveRefreshFolders(true, prefix, prefixBytes); } void DriveAdd(const char *prefix, size_t prefixBytes) { @@ -451,7 +460,8 @@ void DriveAdd(const char *prefix, size_t prefixBytes) { EsMutexAcquire(&drivesMutex); Drive drive = {}; - drive.prefix = prefix; + drive.prefix = (char *) EsHeapAllocate(prefixBytes, false); + EsMemoryCopy(drive.prefix, prefix, prefixBytes); drive.prefixBytes = prefixBytes; EsMountPointGetVolumeInformation(prefix, prefixBytes, &drive.information); drives.Add(drive); @@ -461,13 +471,7 @@ void DriveAdd(const char *prefix, size_t prefixBytes) { } EsMutexRelease(&drivesMutex); - - for (uintptr_t i = 0; i < instances.Length(); i++) { - if (instances[i]->folder->itemHandler->type == NAMESPACE_HANDLER_DRIVES_PAGE - || StringStartsWith(instances[i]->folder->path, StringFromLiteralWithSize(prefix, prefixBytes))) { - FolderRefresh(instances[i]->folder); - } - } + DriveRefreshFolders(false, prefix, prefixBytes); } void LoadSettings() { @@ -528,7 +532,6 @@ void _start() { InstanceCreateUI(instance); } else if (message->type == ES_MSG_INSTANCE_DESTROY) { // TODO Cleanup/cancel any unfinished non-blocking tasks before we get here! - Instance *instance = message->instanceDestroy.instance; InstanceDestroy(instance); instances.FindAndDeleteSwap(instance, true); @@ -549,15 +552,18 @@ void _start() { EsDirectoryChild information = {}; if (EsPathQueryInformation(STRING(path), &information)) { - EsMutexAcquire(&loadedFoldersMutex); - for (uintptr_t i = 0; i < loadedFolders.Length(); i++) { if (loadedFolders[i]->itemHandler->type != NAMESPACE_HANDLER_FILE_SYSTEM) continue; if (EsStringCompareRaw(STRING(loadedFolders[i]->path), STRING(folder))) continue; - FolderAddEntryAndUpdateInstances(loadedFolders[i], file.text, file.bytes, &information, nullptr, true); - } - EsMutexRelease(&loadedFoldersMutex); + EsMutexAcquire(&loadedFolders[i]->modifyEntriesMutex); + + if (loadedFolders[i]->doneInitialEnumeration) { + FolderAddEntryAndUpdateInstances(loadedFolders[i], file.text, file.bytes, &information, nullptr); + } + + EsMutexRelease(&loadedFolders[i]->modifyEntriesMutex); + } } } diff --git a/apps/file_manager/ui.cpp b/apps/file_manager/ui.cpp index 8006453..2022d08 100644 --- a/apps/file_manager/ui.cpp +++ b/apps/file_manager/ui.cpp @@ -23,37 +23,46 @@ void InstanceFolderPathChanged(Instance *instance, bool fromLoadFolder) { } bool InstanceLoadFolder(Instance *instance, String path /* takes ownership */, int historyMode) { - // Check if the path hasn't changed. - if (instance->folder && !instance->folder->refreshing && StringEquals(path, instance->folder->path)) { + // The path hasn't changed; ignore. StringDestroy(&path); return true; } + InstanceRemoveContents(instance); + FolderAttachInstance(instance, path, false); + StringDestroy(&path); + Task task = {}; - task.context = historyMode; - task.string = path; task.cDescription = interfaceString_FileManagerOpenFolderTask; - task.callback = [] (Instance *instance, Task *task) { - Folder *newFolder = nullptr; - task->result = FolderAttachInstance(instance, task->string, false, &newFolder); - task->context2 = newFolder; - StringDestroy(&task->string); + task.callback = [] (Instance *instance, Task *) { + Folder *folder = instance->folder; + EsMutexAcquire(&folder->modifyEntriesMutex); + + if (!folder->doneInitialEnumeration) { + folder->itemHandler->enumerate(folder); + + if (folder->containerHandler->getTotalSize) { + folder->containerHandler->getTotalSize(folder); + } + + folder->driveRemoved = false; + folder->refreshing = false; + folder->doneInitialEnumeration = true; + } + + EsMutexRelease(&folder->modifyEntriesMutex); }; task.then = [] (Instance *instance, Task *task) { - if (ES_CHECK_ERROR(task->result)) { - EsTextboxSelectAll(instance->breadcrumbBar); - EsTextboxInsert(instance->breadcrumbBar, STRING(instance->path)); - InstanceReportError(instance, ERROR_LOAD_FOLDER, task->result); - return; - } + // TODO Check if folder was marked for refresh. int historyMode = task->context.i & 0xFF; int flags = task->context.i; - Folder *folder = (Folder *) task->context2.p; + Folder *folder = instance->folder; + folder->attachedInstances.Add(instance); // Add the path to the history array. @@ -92,13 +101,6 @@ bool InstanceLoadFolder(Instance *instance, String path /* takes ownership */, i EsCommandSetDisabled(&instance->commandRename, true); EsCommandSetDisabled(&instance->commandRefresh, false); - // Detach from the old folder. - - EsMutexAcquire(&loadedFoldersMutex); - - InstanceRemoveContents(instance); - FolderDetachInstance(instance); - // Load the view settings for the folder. bool foundViewSettings = false; @@ -129,24 +131,13 @@ bool InstanceLoadFolder(Instance *instance, String path /* takes ownership */, i InstanceRefreshViewType(instance); - // Attach to the new folder. - - folder->attachingInstances.FindAndDeleteSwap(instance, true); - instance->folder = folder; - folder->attachedInstances.Add(instance); + // Update the user interface. EsListViewSetEmptyMessage(instance->list, folder->cEmptyMessage); InstanceAddContents(instance, &folder->entries); - - EsMutexRelease(&loadedFoldersMutex); - InstanceFolderPathChanged(instance, true); - - if (~flags & LOAD_FOLDER_NO_FOCUS) { - EsElementFocus(instance->list); - } - EsListViewInvalidateAll(instance->placesView); + if (~flags & LOAD_FOLDER_NO_FOCUS) EsElementFocus(instance->list); }; BlockingTaskQueue(instance, task); @@ -277,7 +268,7 @@ void InstanceUpdateStatusString(Instance *instance) { } void InstanceAddSingle(Instance *instance, ListEntry entry) { - // Call with loadedFoldersMutex and message mutex. + // Call with the message mutex acquired. if (!InstanceAddInternal(instance, &entry)) { return; // Filtered out. @@ -326,7 +317,7 @@ ES_MACRO_SORT(InstanceSortListContents, ListEntry, { }, uint16_t); void InstanceAddContents(Instance *instance, HashTable *newEntries) { - // Call with loadedFoldersMutex and message mutex. + // Call with the message mutex acquired. size_t oldListEntryCount = instance->listContents.Length(); diff --git a/desktop/api.cpp b/desktop/api.cpp index fe0b1d2..8183e50 100644 --- a/desktop/api.cpp +++ b/desktop/api.cpp @@ -742,7 +742,7 @@ EsMessage *EsMessageReceive() { elements.Free(); } - instance->commands.Free(false); + instance->commands.Free(); if (instance->fileStore) FileStoreCloseHandle(instance->fileStore); EsHeapFree(instance); EsHeapFree(message.message.instanceDestroy.instance); diff --git a/desktop/desktop.cpp b/desktop/desktop.cpp index c332b2e..2e8ee76 100644 --- a/desktop/desktop.cpp +++ b/desktop/desktop.cpp @@ -116,6 +116,7 @@ struct InstalledApplication { uint64_t permissions; size_t openInstanceCount; // Only used if useSingleProcess is true. EsHandle singleProcessHandle; + bool notified; // Temporary flag. }; struct CrashedTabInstance : EsInstance { @@ -2010,19 +2011,31 @@ void DesktopMessage(EsMessage *message) { for (uintptr_t i = 0; i < desktop.allApplicationInstances.Length(); i++) { ApplicationInstance *instance = desktop.allApplicationInstances[i]; - if (instance->application && (instance->application->permissions & APPLICATION_PERMISSION_ALL_FILES) && instance->processHandle) { + if (instance->application && (instance->application->permissions & APPLICATION_PERMISSION_ALL_FILES) + && instance->processHandle && !instance->application->notified) { message->registerFileSystem.rootDirectory = EsSyscall(ES_SYSCALL_NODE_SHARE, rootDirectory, instance->processHandle, 0, 0); EsMessagePostRemote(instance->processHandle, message); + if (instance->application->useSingleProcess) instance->application->notified = true; } } + + for (uintptr_t i = 0; i < desktop.installedApplications.Length(); i++) { + desktop.installedApplications[i]->notified = false; + } } else if (message->type == ES_MSG_UNREGISTER_FILE_SYSTEM) { for (uintptr_t i = 0; i < desktop.allApplicationInstances.Length(); i++) { ApplicationInstance *instance = desktop.allApplicationInstances[i]; - if (instance->application && (instance->application->permissions & APPLICATION_PERMISSION_ALL_FILES) && instance->processHandle) { + if (instance->application && (instance->application->permissions & APPLICATION_PERMISSION_ALL_FILES) + && instance->processHandle && !instance->application->notified) { EsMessagePostRemote(instance->processHandle, message); + if (instance->application->useSingleProcess) instance->application->notified = true; } } + + for (uintptr_t i = 0; i < desktop.installedApplications.Length(); i++) { + desktop.installedApplications[i]->notified = false; + } } else if (message->type == ES_MSG_DEVICE_CONNECTED) { desktop.connectedDevices.Add(message->device); } else if (message->type == ES_MSG_DEVICE_DISCONNECTED) { diff --git a/shared/hash_table.cpp b/shared/hash_table.cpp index 5a08f80..f776c91 100644 --- a/shared/hash_table.cpp +++ b/shared/hash_table.cpp @@ -349,7 +349,7 @@ bool HashStoreDelete(HashTable *table, HashStoreOptions *options, const void *ke ////////////////////////////////////////// -template +template struct HashStore { HashTable table; @@ -393,8 +393,8 @@ struct HashStore { return HashStoreDelete(&table, &options, k, bytes); } - inline void Free(bool variableLengthKeys) { - HashTableFree(&table, variableLengthKeys); + inline void Free() { + HashTableFree(&table, sizeof(K) == 1); } };