Skip to content

Commit bbe0d31

Browse files
authored
Fixed #100 (some pesky FS drivers can send paths with and without trailing slashes, now the code accounts for that). Improved the datastructures in FSEventsDirUpdateImpl - less mallocs, less caches trashing. Added logging as well. (#108)
1 parent 31ce792 commit bbe0d31

File tree

6 files changed

+385
-299
lines changed

6 files changed

+385
-299
lines changed

Source/Utility/Utility.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
CF52C39B22B974280043E825 /* UTIImpl_UT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CF52C39922B974210043E825 /* UTIImpl_UT.cpp */; };
6868
CF5D1DC72B52B9E900750174 /* Tags.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CF5D1DC62B52B9E900750174 /* Tags.cpp */; };
6969
CF5D1DC92B52B9FA00750174 /* Tags_UT.mm in Sources */ = {isa = PBXBuildFile; fileRef = CF5D1DC82B52B9FA00750174 /* Tags_UT.mm */; };
70+
CF5D1E442B652AA900750174 /* FSEventsDirUpdateImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CF5D1E432B652AA900750174 /* FSEventsDirUpdateImpl.cpp */; };
7071
CF61F305264048F6009FF900 /* FSEventsFileUpdate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CF61F304264048F6009FF900 /* FSEventsFileUpdate.cpp */; };
7172
CF61F30B26404962009FF900 /* FSEventsFileUpdateImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = CF61F30A26404962009FF900 /* FSEventsFileUpdateImpl.h */; };
7273
CF61F30F26404971009FF900 /* FSEventsFileUpdateImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CF61F30E26404971009FF900 /* FSEventsFileUpdateImpl.cpp */; };
@@ -159,6 +160,8 @@
159160
CF5D1DC52B52B9D900750174 /* Tags.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Tags.h; path = include/Utility/Tags.h; sourceTree = "<group>"; };
160161
CF5D1DC62B52B9E900750174 /* Tags.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tags.cpp; path = source/Tags.cpp; sourceTree = "<group>"; };
161162
CF5D1DC82B52B9FA00750174 /* Tags_UT.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Tags_UT.mm; path = tests/Tags_UT.mm; sourceTree = "<group>"; };
163+
CF5D1E422B652A9A00750174 /* FSEventsDirUpdateImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FSEventsDirUpdateImpl.h; path = include/Utility/FSEventsDirUpdateImpl.h; sourceTree = "<group>"; };
164+
CF5D1E432B652AA900750174 /* FSEventsDirUpdateImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FSEventsDirUpdateImpl.cpp; path = source/FSEventsDirUpdateImpl.cpp; sourceTree = "<group>"; };
162165
CF614AA81F9D871D0005F2DB /* ByteCountFormatter_UT.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ByteCountFormatter_UT.mm; path = tests/ByteCountFormatter_UT.mm; sourceTree = SOURCE_ROOT; };
163166
CF614AA91F9D871D0005F2DB /* Encodings_UT.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Encodings_UT.mm; path = tests/Encodings_UT.mm; sourceTree = SOURCE_ROOT; };
164167
CF614AAA1F9D871D0005F2DB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = tests/Info.plist; sourceTree = SOURCE_ROOT; };
@@ -361,6 +364,7 @@
361364
CFD62BD91C9A6BE30021EE7F /* FontExtras.h */,
362365
CFD62BE71C9A709C0021EE7F /* FPSLimitedDrawer.h */,
363366
CFD62C111C9AB47F0021EE7F /* FSEventsDirUpdate.h */,
367+
CF5D1E422B652A9A00750174 /* FSEventsDirUpdateImpl.h */,
364368
CF61F303264048EA009FF900 /* FSEventsFileUpdate.h */,
365369
CF61F30A26404962009FF900 /* FSEventsFileUpdateImpl.h */,
366370
CF09392F1D10214200D67AC6 /* FunctionKeysPass.h */,
@@ -425,6 +429,7 @@
425429
CFD62C001C9A87170021EE7F /* FontExtras.mm */,
426430
CFD62BE91C9A70A40021EE7F /* FPSLimitedDrawer.mm */,
427431
CFD62C0F1C9AB4770021EE7F /* FSEventsDirUpdate.cpp */,
432+
CF5D1E432B652AA900750174 /* FSEventsDirUpdateImpl.cpp */,
428433
CF61F304264048F6009FF900 /* FSEventsFileUpdate.cpp */,
429434
CF61F30E26404971009FF900 /* FSEventsFileUpdateImpl.cpp */,
430435
CF0939311D10214D00D67AC6 /* FunctionKeysPass.mm */,
@@ -611,6 +616,7 @@
611616
CF71BCE32932389100997E0F /* SpdLogWindow.mm in Sources */,
612617
CF46015C256125E50095FC73 /* NativeFSManager.mm in Sources */,
613618
CF460149256125E50095FC73 /* FPSLimitedDrawer.mm in Sources */,
619+
CF5D1E442B652AA900750174 /* FSEventsDirUpdateImpl.cpp in Sources */,
614620
CF460143256125E50095FC73 /* NativeFSManagerVolumeLookup.cpp in Sources */,
615621
CF460158256125E50095FC73 /* VerticallyCenteredTextFieldCell.mm in Sources */,
616622
CF460164256125E50095FC73 /* SystemInformation.mm in Sources */,
Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
// Copyright (C) 2013-2020 Michael Kazakov. Subject to GNU General Public License version 3.
1+
// Copyright (C) 2013-2024 Michael Kazakov. Subject to GNU General Public License version 3.
22
#pragma once
33

44
#include <stdint.h>
55
#include <string>
66
#include <functional>
7-
#include <memory>
87

98
namespace nc::utility {
109

@@ -13,25 +12,25 @@ class NativeFSManagerImpl;
1312
class FSEventsDirUpdate
1413
{
1514
public:
16-
static FSEventsDirUpdate &Instance();
17-
18-
// zero returned value means error. any others - valid observation tickets
19-
uint64_t AddWatchPath(const char *_path, std::function<void()> _handler);
20-
21-
// it's better to use this method
22-
void RemoveWatchPathWithTicket(uint64_t _ticket);
15+
virtual ~FSEventsDirUpdate() = default;
16+
17+
// Registers _handler as a watch callback for any changes of the directory '_path' (but not its children)
18+
// Zero will be returned to indicate an error.
19+
// Any other values represent observation tickets.
20+
virtual uint64_t AddWatchPath(const char *_path, std::function<void()> _handler) = 0;
21+
22+
// Deregisters the watcher identified by _ticket.
23+
virtual void RemoveWatchPathWithTicket(uint64_t _ticket) = 0;
24+
25+
static FSEventsDirUpdate &Instance() noexcept;
2326

2427
static inline const uint64_t no_ticket = 0;
25-
28+
2629
private:
2730
friend class nc::utility::NativeFSManagerImpl;
28-
29-
// called exclusevily by NativeFSManager
30-
void OnVolumeDidUnmount(const std::string &_on_path);
31-
32-
FSEventsDirUpdate();
33-
struct Impl;
34-
std::unique_ptr<Impl> me;
31+
32+
// called exclusively by NativeFSManager
33+
virtual void OnVolumeDidUnmount(const std::string &_on_path) = 0;
3534
};
3635

37-
}
36+
} // namespace nc::utility
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (C) 2013-2024 Michael Kazakov. Subject to GNU General Public License version 3.
2+
#pragma once
3+
#include "FSEventsDirUpdate.h"
4+
#include <DiskArbitration/DiskArbitration.h>
5+
#include <CoreServices/CoreServices.h>
6+
#include <Base/spinlock.h>
7+
#include <filesystem>
8+
#include <Base/RobinHoodUtil.h>
9+
10+
namespace nc::utility {
11+
12+
class FSEventsDirUpdateImpl : public FSEventsDirUpdate
13+
{
14+
public:
15+
uint64_t AddWatchPath(const char *_path, std::function<void()> _handler) override;
16+
17+
void RemoveWatchPathWithTicket(uint64_t _ticket) override;
18+
19+
// Implementation detail exposed for testability
20+
static bool ShouldFire(std::string_view _watched_path,
21+
size_t _num_events,
22+
const char *_event_paths[],
23+
const FSEventStreamEventFlags _event_flags[]) noexcept;
24+
25+
private:
26+
struct WatchData {
27+
std::string_view path; // canonical fs representation, should include a trailing slash. points into hashmap
28+
FSEventStreamRef stream = nullptr;
29+
std::vector<std::pair<uint64_t, std::function<void()>>> handlers;
30+
};
31+
32+
using WatchesT = robin_hood::
33+
unordered_node_map<std::string, WatchData, RHTransparentStringHashEqual, RHTransparentStringHashEqual>;
34+
35+
void OnVolumeDidUnmount(const std::string &_on_path) override;
36+
37+
static void DiskDisappeared(DADiskRef disk, void *context);
38+
static void FSEventsDirUpdateCallback(ConstFSEventStreamRef streamRef,
39+
void *userData,
40+
size_t numEvents,
41+
void *eventPaths,
42+
const FSEventStreamEventFlags eventFlags[],
43+
const FSEventStreamEventId eventIds[]);
44+
static FSEventStreamRef CreateEventStream(const std::string &path, void *context);
45+
46+
spinlock m_Lock;
47+
WatchesT m_Watches; // path -> watch data;
48+
std::atomic_ulong m_LastTicket{1}; // no #0 ticket, it's an error code
49+
};
50+
51+
} // namespace nc::utility

0 commit comments

Comments
 (0)