Skip to content

Commit 975e922

Browse files
committed
Update PR
Update PR
1 parent fe366fd commit 975e922

3 files changed

Lines changed: 118 additions & 125 deletions

File tree

src/main.d

Lines changed: 38 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module main;
33

44
// What does this module require to function?
55
import core.memory;
6-
import core.stdc.errno : ENOENT, EINTR, EBUSY, EXDEV, EAGAIN, EPERM, EACCES, EROFS;
6+
import core.stdc.errno : EINTR, EBUSY, EXDEV, EAGAIN, EPERM, EACCES, EROFS;
77
import core.stdc.stdlib: EXIT_SUCCESS, EXIT_FAILURE, exit;
88
import core.sys.posix.signal;
99
import core.sys.posix.unistd : write, _exit, STDERR_FILENO;
@@ -1217,7 +1217,7 @@ int main(string[] cliArgs) {
12171217
// If we are in a --download-only method of operation, there is no filesystem monitoring, so no inotify events to check
12181218
if (!appConfig.getValueBool("download_only")) {
12191219
// Process any inotify events queued before this loop iteration
1220-
processInotifyEvents(true);
1220+
processInotifyEvents();
12211221
}
12221222

12231223
// WebSocket and Webhook Notification Handling
@@ -1358,10 +1358,10 @@ int main(string[] cliArgs) {
13581358
performStandardSyncProcess(localPath, filesystemMonitor);
13591359
}
13601360

1361-
// Handle any local filesystem events that occurred while the sync was running.
1362-
// Remote-generated and genuine concurrent local events are both processed at
1363-
// each reconciliation boundary; database/hash checks suppress harmless echoes.
1364-
processInotifyEvents(true);
1361+
// Handle every local filesystem event queued while the sync was running.
1362+
// Event-origin and reconciliation safeguards are handled by the sync engine;
1363+
// the monitor layer must not consume queued events selectively.
1364+
processInotifyEvents();
13651365

13661366
// Detail the outcome of the sync process
13671367
displaySyncOutcome();
@@ -1661,7 +1661,7 @@ void oneDriveOnlineCallback() {
16611661
// If we are in a --download-only method of operation, there is no filesystem monitoring, so no inotify events to check
16621662
if (!appConfig.getValueBool("download_only")) {
16631663
// Handle inotify events queued before online reconciliation
1664-
processInotifyEvents(true);
1664+
processInotifyEvents();
16651665
}
16661666

16671667
// Sync any online change down to the local disk.
@@ -1673,19 +1673,17 @@ void oneDriveOnlineCallback() {
16731673
appConfig.monitorSyncTriggeredByApiSignal = false;
16741674
}
16751675

1676-
syncEngineInstance.syncOneDriveAccountToLocalDisk();
1676+
syncEngineInstance.syncOneDriveAccountToLocalDisk(delegate() { processInotifyEvents(); });
16771677

16781678
if (syncEngineInstance.authoritativeCleanupPassUsedInLastSync) {
16791679
syncEngineInstance.performDatabaseConsistencyAndIntegrityCheck();
16801680
}
16811681
}
16821682
if (appConfig.getValueBool("monitor")) {
16831683
// Process every queued inotify event after online -> local reconciliation.
1684-
// The database and local filesystem have already been updated by the remote
1685-
// reconciliation, so self-generated events are naturally filtered by the
1686-
// existing database/hash checks. Genuine concurrent local events must not be
1687-
// consumed and discarded here.
1688-
processInotifyEvents(true);
1684+
// Online-originated events still require explicit origin and deletion-precedence
1685+
// handling; the monitor layer must not discard events based on presumed origin.
1686+
processInotifyEvents();
16891687
}
16901688
}
16911689

@@ -1695,14 +1693,14 @@ void performUploadOnlySyncProcess(string localPath, Monitor filesystemMonitor =
16951693
syncEngineInstance.performDatabaseConsistencyAndIntegrityCheck();
16961694
if (appConfig.getValueBool("monitor")) {
16971695
// Handle any inotify events whilst the DB was being scanned
1698-
processInotifyEvents(true);
1696+
processInotifyEvents();
16991697
}
17001698

17011699
// Scan the configured 'sync_dir' for new data to upload
17021700
syncEngineInstance.scanLocalFilesystemPathForNewData(localPath);
17031701
if (appConfig.getValueBool("monitor")) {
17041702
// Handle any new inotify events whilst the local filesystem was being scanned
1705-
processInotifyEvents(true);
1703+
processInotifyEvents();
17061704
}
17071705
}
17081706

@@ -1726,23 +1724,23 @@ void performStandardSyncProcess(string localPath, Monitor filesystemMonitor = nu
17261724
syncEngineInstance.performDatabaseConsistencyAndIntegrityCheck();
17271725
if (appConfig.getValueBool("monitor")) {
17281726
// Handle any inotify events whilst the DB was being scanned
1729-
processInotifyEvents(true);
1727+
processInotifyEvents();
17301728
}
17311729

17321730
// Scan the configured 'sync_dir' for new data to upload to OneDrive
17331731
syncEngineInstance.scanLocalFilesystemPathForNewData(localPath);
17341732
if (appConfig.getValueBool("monitor")) {
17351733
// Handle any new inotify events whilst the local filesystem was being scanned
1736-
processInotifyEvents(true);
1734+
processInotifyEvents();
17371735
}
17381736

17391737
// Download data from OneDrive last
1740-
syncEngineInstance.syncOneDriveAccountToLocalDisk();
1738+
syncEngineInstance.syncOneDriveAccountToLocalDisk(delegate() { processInotifyEvents(); });
17411739
if (appConfig.getValueBool("monitor")) {
1742-
// Process all queued inotify events. Events caused by the completed remote
1743-
// reconciliation are filtered by current database/hash state; concurrent
1744-
// local activity must remain actionable.
1745-
processInotifyEvents(true);
1740+
// Process all queued inotify events. Concurrent local activity must remain
1741+
// actionable; online-originated events are handled by the active-pass
1742+
// deletion-precedence protection where applicable.
1743+
processInotifyEvents();
17461744
}
17471745

17481746
// At this point, we have done a sync from:
@@ -1758,19 +1756,19 @@ void performStandardSyncProcess(string localPath, Monitor filesystemMonitor = nu
17581756
} else {
17591757
// Normal sync process
17601758
// Download data from OneDrive first
1761-
syncEngineInstance.syncOneDriveAccountToLocalDisk();
1759+
syncEngineInstance.syncOneDriveAccountToLocalDisk(delegate() { processInotifyEvents(); });
17621760
if (appConfig.getValueBool("monitor")) {
1763-
// Process all queued inotify events. Events caused by the completed remote
1764-
// reconciliation are filtered by current database/hash state; concurrent
1765-
// local activity must remain actionable.
1766-
processInotifyEvents(true);
1761+
// Process all queued inotify events. Concurrent local activity must remain
1762+
// actionable; online-originated events are handled by the active-pass
1763+
// deletion-precedence protection where applicable.
1764+
processInotifyEvents();
17671765
}
17681766

17691767
// Perform the local database consistency check, picking up locally modified data and uploading this to OneDrive
17701768
syncEngineInstance.performDatabaseConsistencyAndIntegrityCheck();
17711769
if (appConfig.getValueBool("monitor")) {
17721770
// Handle any inotify events whilst the DB was being scanned
1773-
processInotifyEvents(true);
1771+
processInotifyEvents();
17741772
}
17751773

17761774
// Is --download-only NOT configured?
@@ -1780,7 +1778,7 @@ void performStandardSyncProcess(string localPath, Monitor filesystemMonitor = nu
17801778
syncEngineInstance.scanLocalFilesystemPathForNewData(localPath);
17811779
if (appConfig.getValueBool("monitor")) {
17821780
// Handle any new inotify events whilst the local filesystem was being scanned
1783-
processInotifyEvents(true);
1781+
processInotifyEvents();
17841782
}
17851783

17861784
// If we are not doing a 'force_children_scan' perform a true-up
@@ -1794,12 +1792,11 @@ void performStandardSyncProcess(string localPath, Monitor filesystemMonitor = nu
17941792
}
17951793
// We pass in the 'appConfig.fullScanTrueUpRequired' value which then flags do we use the configured 'deltaLink'
17961794
// If 'appConfig.fullScanTrueUpRequired' is true, we do not use the 'deltaLink' if we are in --monitor mode, thus forcing a full scan true up
1797-
syncEngineInstance.syncOneDriveAccountToLocalDisk();
1795+
syncEngineInstance.syncOneDriveAccountToLocalDisk(delegate() { processInotifyEvents(); });
17981796
if (appConfig.getValueBool("monitor")) {
1799-
// Process all queued inotify events after the final online true-up.
1800-
// Never consume concurrent local changes merely because remote work
1801-
// also generated filesystem notifications.
1802-
processInotifyEvents(true);
1797+
// Process every queued inotify event after the final online true-up.
1798+
// Event-origin and reconciliation safeguards are handled by the sync engine.
1799+
processInotifyEvents();
18031800
}
18041801
} else {
18051802
// exitHandlerTriggered triggered
@@ -1823,34 +1820,22 @@ void performStandardSyncProcess(string localPath, Monitor filesystemMonitor = nu
18231820
}
18241821
}
18251822

1826-
// Process any inotify events
1827-
void processInotifyEvents(bool updateFlag, bool processDeletesWhenDraining = false) {
1823+
// Process and dispatch every queued inotify event. There is intentionally no
1824+
// cancellation or selective-drain mode because consuming an event without
1825+
// dispatching it can lose genuine local activity.
1826+
void processInotifyEvents() {
18281827
if ((filesystemMonitor is null) || (!filesystemMonitor.initialised)) {
18291828
return;
18301829
}
18311830

1832-
// Attempt to process or cancel inotify events
1833-
// filesystemMonitor.update will throw this, thus needs to be caught
1834-
// monitor.MonitorException@src/monitor.d(549): inotify queue overflow: some events may be lost (Interrupted system call)
18351831
try {
1836-
// Process any inotify events or cancel events based on flag value
1837-
// True = process
1838-
// False = cancel
1839-
filesystemMonitor.update(updateFlag, processDeletesWhenDraining);
1832+
filesystemMonitor.update();
18401833
} catch (MonitorException exception) {
18411834
// Catch any exceptions thrown by inotify / monitor engine
18421835
addLogEntry("ERROR: The following inotify error was generated: " ~ exception.msg);
18431836
} catch (FileException exception) {
1844-
// A local path can legitimately disappear while queued inotify events are
1845-
// being cancelled / drained, especially under monitor stress testing.
1846-
// Treat ENOENT during event cancellation as non-fatal.
1847-
if ((!updateFlag) && (exception.errno == ENOENT)) {
1848-
if (debugLogging) {
1849-
addLogEntry("Ignoring stale inotify cancellation event for path that no longer exists: " ~ exception.msg, ["debug"]);
1850-
}
1851-
return;
1852-
}
1853-
// Log error message
1837+
// Preserve the existing full-processing behaviour: filesystem errors are
1838+
// reported rather than silently converting them into discarded events.
18541839
addLogEntry("ERROR: The following filesystem error was generated while processing inotify events: " ~ exception.msg);
18551840
}
18561841
}

src/monitor.d

Lines changed: 10 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -970,58 +970,8 @@ final class Monitor {
970970
return true;
971971
}
972972

973-
private void drainPendingEventsOnly(ref pollfd fds) {
974-
size_t drainedEvents = 0;
975-
976-
while (true) {
977-
bool hasNotification = false;
978-
int sleep_counter = 0;
979-
980-
// Preserve the existing short batching window, but do not resolve paths,
981-
// update watches, evaluate filters, or invoke callbacks. This path is
982-
// used when callers explicitly want queued local events cancelled.
983-
while (sleep_counter < 5) {
984-
int ret = poll(&fds, 1, 0);
985-
if (ret == -1) throw new MonitorException("poll failed");
986-
else if (ret == 0) break;
987-
988-
hasNotification = true;
989-
size_t length = read(worker.fd, buffer.ptr, buffer.length);
990-
if (length == -1) throw new MonitorException("read failed");
991-
992-
int i = 0;
993-
while (i < length) {
994-
inotify_event *event = cast(inotify_event*) &buffer[i];
995-
996-
if (event.mask & IN_IGNORED) {
997-
string ignoredPath;
998-
unregisterWatchDescriptor(event.wd, ignoredPath);
999-
} else if (event.mask & IN_Q_OVERFLOW) {
1000-
monitorStateDirty = true;
1001-
clearTransientEventState();
1002-
throw new MonitorException("inotify queue overflow: some events may be lost");
1003-
}
1004-
1005-
drainedEvents++;
1006-
i += inotify_event.sizeof + event.len;
1007-
}
1008-
1009-
if (poll(&fds, 1, 0) == 0) {
1010-
sleep_counter += 1;
1011-
Thread.sleep(dur!"seconds"(1));
1012-
}
1013-
}
1014-
1015-
if (!hasNotification) break;
1016-
}
1017-
1018-
if ((drainedEvents > 0) && debugLogging) {
1019-
addLogEntry("Drained " ~ drainedEvents.to!string ~ " stale local filesystem monitor event(s) without processing", ["debug"]);
1020-
}
1021-
}
1022-
1023-
// Update
1024-
void update(bool useCallbacks = true, bool processDeletesWhenDraining = false) {
973+
// Update and dispatch every queued event. There is no discard-only mode.
974+
void update() {
1025975
if(!initialised)
1026976
return;
1027977

@@ -1030,11 +980,6 @@ final class Monitor {
1030980
events: POLLIN
1031981
};
1032982

1033-
if (!useCallbacks && !processDeletesWhenDraining) {
1034-
clearTransientEventState();
1035-
drainPendingEventsOnly(fds);
1036-
return;
1037-
}
1038983

1039984
while (true) {
1040985
bool hasNotification = false;
@@ -1152,15 +1097,15 @@ final class Monitor {
11521097
// recursive watch tree for the same directory hierarchy.
11531098
if (event.mask & IN_ISDIR) rebaseWatchTree(*from, path);
11541099
cookieToPath.remove(event.cookie);
1155-
if (useCallbacks) actionHolder.append(ActionType.moved, *from, path);
1100+
actionHolder.append(ActionType.moved, *from, path);
11561101
movedNotDeleted.remove(*from); // Clear moved status
11571102
} else {
11581103
// Handle item moved in from outside the watched tree.
11591104
if (event.mask & IN_ISDIR) {
11601105
addRecursive(path);
1161-
if (useCallbacks) actionHolder.append(ActionType.createDir, path);
1106+
actionHolder.append(ActionType.createDir, path);
11621107
} else {
1163-
if (useCallbacks) actionHolder.append(ActionType.changed, path);
1108+
actionHolder.append(ActionType.changed, path);
11641109
}
11651110
}
11661111
} else if (event.mask & IN_CREATE) {
@@ -1174,12 +1119,12 @@ final class Monitor {
11741119
}
11751120
}
11761121
addRecursive(path);
1177-
if (useCallbacks) actionHolder.append(ActionType.createDir, path);
1122+
actionHolder.append(ActionType.createDir, path);
11781123
}
11791124
} else if (event.mask & IN_DELETE_SELF) {
11801125
if (debugLogging) {addLogEntry("event IN_DELETE_SELF: " ~ path, ["debug"]);}
11811126
removeWatchTree(path);
1182-
if (useCallbacks || processDeletesWhenDraining) actionHolder.append(ActionType.deleted, path);
1127+
actionHolder.append(ActionType.deleted, path);
11831128
} else if (event.mask & IN_MOVE_SELF) {
11841129
// Do not remove the watch here. Directory moves inside the watched tree
11851130
// are reconciled via the matching parent IN_MOVED_FROM/IN_MOVED_TO
@@ -1191,7 +1136,7 @@ final class Monitor {
11911136
} else {
11921137
if (debugLogging) {addLogEntry("event IN_DELETE: " ~ path, ["debug"]);}
11931138
if (event.mask & IN_ISDIR) removeWatchTree(path);
1194-
if (useCallbacks || processDeletesWhenDraining) actionHolder.append(ActionType.deleted, path);
1139+
actionHolder.append(ActionType.deleted, path);
11951140
}
11961141
} else if ((event.mask & IN_CLOSE_WRITE) && !(event.mask & IN_ISDIR)) {
11971142
if (debugLogging) {addLogEntry("event IN_CLOSE_WRITE and not IN_ISDIR: " ~ path, ["debug"]);}
@@ -1202,7 +1147,7 @@ final class Monitor {
12021147
cookieToPath.remove(cookie);
12031148
}
12041149
}
1205-
if (useCallbacks) actionHolder.append(ActionType.changed, path);
1150+
actionHolder.append(ActionType.changed, path);
12061151
} else {
12071152
if (debugLogging) {addLogEntry("Ignoring unhandled inotify event for path: " ~ path ~ ", mask=" ~ event.mask.to!string, ["debug"]);}
12081153
}
@@ -1225,7 +1170,7 @@ final class Monitor {
12251170
auto cookieToPathCopy = cookieToPath.dup;
12261171
foreach (cookie, path; cookieToPathCopy) {
12271172
if (debugLogging) {addLogEntry("Deleting cookie|watch (post loop): " ~ path, ["debug"]);}
1228-
if (useCallbacks || processDeletesWhenDraining) onDelete(path);
1173+
onDelete(path);
12291174
removeWatchTree(path);
12301175
cookieToPath.remove(cookie);
12311176
movedNotDeleted.remove(path);

0 commit comments

Comments
 (0)