remove loadedFoldersMutex; bugfixes

This commit is contained in:
nakst 2021-08-19 17:22:38 +01:00
parent 9ad6930ac7
commit db3f454bba
7 changed files with 127 additions and 160 deletions

View File

@ -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");

View File

@ -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<Folder *> 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<Folder *> 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<Instance *> 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();
}

View File

@ -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<Instance *> attachedInstances;
Array<Instance *> 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<Folder *> 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);
}
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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) {

View File

@ -349,7 +349,7 @@ bool HashStoreDelete(HashTable *table, HashStoreOptions *options, const void *ke
//////////////////////////////////////////
template <class K /* set to char * for long keys */, class V>
template <class K /* set to char for long keys */, class V>
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);
}
};