Skip to content

Commit

Permalink
Fixed #100 (some pesky FS drivers can send paths with and without tra…
Browse files Browse the repository at this point in the history
…iling slashes, now the code accounts for that). Improved the datastructures in FSEventsDirUpdateImpl - less mallocs, less caches trashing. Added logging as well. (#108)
  • Loading branch information
mikekazakov committed Jan 27, 2024
1 parent 31ce792 commit bbe0d31
Show file tree
Hide file tree
Showing 6 changed files with 385 additions and 299 deletions.
6 changes: 6 additions & 0 deletions Source/Utility/Utility.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
CF52C39B22B974280043E825 /* UTIImpl_UT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CF52C39922B974210043E825 /* UTIImpl_UT.cpp */; };
CF5D1DC72B52B9E900750174 /* Tags.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CF5D1DC62B52B9E900750174 /* Tags.cpp */; };
CF5D1DC92B52B9FA00750174 /* Tags_UT.mm in Sources */ = {isa = PBXBuildFile; fileRef = CF5D1DC82B52B9FA00750174 /* Tags_UT.mm */; };
CF5D1E442B652AA900750174 /* FSEventsDirUpdateImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CF5D1E432B652AA900750174 /* FSEventsDirUpdateImpl.cpp */; };
CF61F305264048F6009FF900 /* FSEventsFileUpdate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CF61F304264048F6009FF900 /* FSEventsFileUpdate.cpp */; };
CF61F30B26404962009FF900 /* FSEventsFileUpdateImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = CF61F30A26404962009FF900 /* FSEventsFileUpdateImpl.h */; };
CF61F30F26404971009FF900 /* FSEventsFileUpdateImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CF61F30E26404971009FF900 /* FSEventsFileUpdateImpl.cpp */; };
Expand Down Expand Up @@ -159,6 +160,8 @@
CF5D1DC52B52B9D900750174 /* Tags.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Tags.h; path = include/Utility/Tags.h; sourceTree = "<group>"; };
CF5D1DC62B52B9E900750174 /* Tags.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tags.cpp; path = source/Tags.cpp; sourceTree = "<group>"; };
CF5D1DC82B52B9FA00750174 /* Tags_UT.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Tags_UT.mm; path = tests/Tags_UT.mm; sourceTree = "<group>"; };
CF5D1E422B652A9A00750174 /* FSEventsDirUpdateImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FSEventsDirUpdateImpl.h; path = include/Utility/FSEventsDirUpdateImpl.h; sourceTree = "<group>"; };
CF5D1E432B652AA900750174 /* FSEventsDirUpdateImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FSEventsDirUpdateImpl.cpp; path = source/FSEventsDirUpdateImpl.cpp; sourceTree = "<group>"; };
CF614AA81F9D871D0005F2DB /* ByteCountFormatter_UT.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ByteCountFormatter_UT.mm; path = tests/ByteCountFormatter_UT.mm; sourceTree = SOURCE_ROOT; };
CF614AA91F9D871D0005F2DB /* Encodings_UT.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Encodings_UT.mm; path = tests/Encodings_UT.mm; sourceTree = SOURCE_ROOT; };
CF614AAA1F9D871D0005F2DB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = tests/Info.plist; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -361,6 +364,7 @@
CFD62BD91C9A6BE30021EE7F /* FontExtras.h */,
CFD62BE71C9A709C0021EE7F /* FPSLimitedDrawer.h */,
CFD62C111C9AB47F0021EE7F /* FSEventsDirUpdate.h */,
CF5D1E422B652A9A00750174 /* FSEventsDirUpdateImpl.h */,
CF61F303264048EA009FF900 /* FSEventsFileUpdate.h */,
CF61F30A26404962009FF900 /* FSEventsFileUpdateImpl.h */,
CF09392F1D10214200D67AC6 /* FunctionKeysPass.h */,
Expand Down Expand Up @@ -425,6 +429,7 @@
CFD62C001C9A87170021EE7F /* FontExtras.mm */,
CFD62BE91C9A70A40021EE7F /* FPSLimitedDrawer.mm */,
CFD62C0F1C9AB4770021EE7F /* FSEventsDirUpdate.cpp */,
CF5D1E432B652AA900750174 /* FSEventsDirUpdateImpl.cpp */,
CF61F304264048F6009FF900 /* FSEventsFileUpdate.cpp */,
CF61F30E26404971009FF900 /* FSEventsFileUpdateImpl.cpp */,
CF0939311D10214D00D67AC6 /* FunctionKeysPass.mm */,
Expand Down Expand Up @@ -611,6 +616,7 @@
CF71BCE32932389100997E0F /* SpdLogWindow.mm in Sources */,
CF46015C256125E50095FC73 /* NativeFSManager.mm in Sources */,
CF460149256125E50095FC73 /* FPSLimitedDrawer.mm in Sources */,
CF5D1E442B652AA900750174 /* FSEventsDirUpdateImpl.cpp in Sources */,
CF460143256125E50095FC73 /* NativeFSManagerVolumeLookup.cpp in Sources */,
CF460158256125E50095FC73 /* VerticallyCenteredTextFieldCell.mm in Sources */,
CF460164256125E50095FC73 /* SystemInformation.mm in Sources */,
Expand Down
35 changes: 17 additions & 18 deletions Source/Utility/include/Utility/FSEventsDirUpdate.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// Copyright (C) 2013-2020 Michael Kazakov. Subject to GNU General Public License version 3.
// Copyright (C) 2013-2024 Michael Kazakov. Subject to GNU General Public License version 3.
#pragma once

#include <stdint.h>
#include <string>
#include <functional>
#include <memory>

namespace nc::utility {

Expand All @@ -13,25 +12,25 @@ class NativeFSManagerImpl;
class FSEventsDirUpdate
{
public:
static FSEventsDirUpdate &Instance();

// zero returned value means error. any others - valid observation tickets
uint64_t AddWatchPath(const char *_path, std::function<void()> _handler);

// it's better to use this method
void RemoveWatchPathWithTicket(uint64_t _ticket);
virtual ~FSEventsDirUpdate() = default;

// Registers _handler as a watch callback for any changes of the directory '_path' (but not its children)
// Zero will be returned to indicate an error.
// Any other values represent observation tickets.
virtual uint64_t AddWatchPath(const char *_path, std::function<void()> _handler) = 0;

// Deregisters the watcher identified by _ticket.
virtual void RemoveWatchPathWithTicket(uint64_t _ticket) = 0;

static FSEventsDirUpdate &Instance() noexcept;

static inline const uint64_t no_ticket = 0;

private:
friend class nc::utility::NativeFSManagerImpl;

// called exclusevily by NativeFSManager
void OnVolumeDidUnmount(const std::string &_on_path);

FSEventsDirUpdate();
struct Impl;
std::unique_ptr<Impl> me;

// called exclusively by NativeFSManager
virtual void OnVolumeDidUnmount(const std::string &_on_path) = 0;
};

}
} // namespace nc::utility
51 changes: 51 additions & 0 deletions Source/Utility/include/Utility/FSEventsDirUpdateImpl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (C) 2013-2024 Michael Kazakov. Subject to GNU General Public License version 3.
#pragma once
#include "FSEventsDirUpdate.h"
#include <DiskArbitration/DiskArbitration.h>
#include <CoreServices/CoreServices.h>
#include <Base/spinlock.h>
#include <filesystem>
#include <Base/RobinHoodUtil.h>

namespace nc::utility {

class FSEventsDirUpdateImpl : public FSEventsDirUpdate
{
public:
uint64_t AddWatchPath(const char *_path, std::function<void()> _handler) override;

void RemoveWatchPathWithTicket(uint64_t _ticket) override;

// Implementation detail exposed for testability
static bool ShouldFire(std::string_view _watched_path,
size_t _num_events,
const char *_event_paths[],
const FSEventStreamEventFlags _event_flags[]) noexcept;

private:
struct WatchData {
std::string_view path; // canonical fs representation, should include a trailing slash. points into hashmap
FSEventStreamRef stream = nullptr;
std::vector<std::pair<uint64_t, std::function<void()>>> handlers;
};

using WatchesT = robin_hood::
unordered_node_map<std::string, WatchData, RHTransparentStringHashEqual, RHTransparentStringHashEqual>;

void OnVolumeDidUnmount(const std::string &_on_path) override;

static void DiskDisappeared(DADiskRef disk, void *context);
static void FSEventsDirUpdateCallback(ConstFSEventStreamRef streamRef,
void *userData,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]);
static FSEventStreamRef CreateEventStream(const std::string &path, void *context);

spinlock m_Lock;
WatchesT m_Watches; // path -> watch data;
std::atomic_ulong m_LastTicket{1}; // no #0 ticket, it's an error code
};

} // namespace nc::utility
Loading

0 comments on commit bbe0d31

Please sign in to comment.