watchdog icon indicating copy to clipboard operation
watchdog copied to clipboard

Missing events on Windows 10 (Performance issues)

Open BoshenGuan opened this issue 1 year ago • 9 comments

ISSUE-1: event_buffer = ctypes.create_string_buffer(BUFFER_SIZE) creates a big buffer every time in the loop. That is a bad idea. It would be better to create one and re-use on the following calls.

BoshenGuan avatar Oct 24 '23 06:10 BoshenGuan

Other performance/code issues: ISSUE-2: ctypes.byref(nbytes) and return event_buffer.raw, int(nbytes.value) is ignored when nbytes is 0. When nbytes is 0, it means buffer-overflow. The code does not deal with 0 nbytes properly, which can lead to missing events.

BoshenGuan avatar Oct 24 '23 06:10 BoshenGuan

Other performance/code issues: ISSUE-3: Although not in use, the OVERLAPPED structure is wrong. There is a union in C/C++ header for OVERLAPPED structure. Therefore,

class OVERLAPPED(ctypes.Structure):
    _fields_ = [
        ("Internal", LPVOID),
        ("InternalHigh", LPVOID),
        ("Offset", ctypes.wintypes.DWORD),
        ("OffsetHigh", ctypes.wintypes.DWORD),
        ("Pointer", LPVOID),
        ("hEvent", ctypes.wintypes.HANDLE),
    ]

shoud be

class OVERLAPPED(ctypes.Structure):
    _fields_ = [
        ("Internal", ctypes.wintypes.LPVOID),
        ("InternalHigh", ctypes.wintypes.LPVOID),
        ("Offset", ctypes.wintypes.DWORD),
        ("OffsetHigh", ctypes.wintypes.DWORD),
        ("hEvent", ctypes.wintypes.HANDLE),
    ]

. Because Pointer is in the union.

BoshenGuan avatar Oct 24 '23 06:10 BoshenGuan

Wow, you seem to have a good knowledge of the win API. Would you mind creating PRs for each issue 🙏🏻 (if you can add tests too to prevent regressions, that would be awesome too)?

BoboTiG avatar Oct 24 '23 07:10 BoboTiG

ISSUE-3: it is used here: https://github.com/gorakhargosh/watchdog/blob/6cdf07efa04d2651a632e963fb464c2265c91b9c/src/watchdog/observers/winapi.py#L140

BoboTiG avatar Oct 24 '23 07:10 BoboTiG

@BoshenGuan Can you give more details on how to fix ISSUE-2? I'm getting lots of missing events with moving large numbers of items into a directory.

P.S.: Found it: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw#remarks

matkuki avatar Nov 25 '23 17:11 matkuki

@BoshenGuan I still don't know how to handle a nbytes == 0 situation. I have tried increasing the BUFFER_SIZE, it makes it a bit more consistant, but still there are missed events! I'm not sure what the documentation means in the last sentence:

Upon successful synchronous completion, the lpBuffer parameter is a formatted buffer and the number of bytes written to the buffer is available in lpBytesReturned. If the number of bytes transferred is zero, the buffer was either too large for the system to allocate or too small to provide detailed information on all the changes that occurred in the directory or subtree. In this case, you should compute the changes by enumerating the directory or subtree.

In this case, you should compute the changes by enumerating the directory or subtree.: Does that mean a manual walk throught the directory is needed in this case?

matkuki avatar Nov 25 '23 23:11 matkuki

Here is a very detailed answer of why there is no escaping missing events: https://stackoverflow.com/a/49888600/2603918

matkuki avatar Nov 27 '23 08:11 matkuki

@BoboTiG @BoshenGuan @tommorris @pilt There seems to be no way to ensure catching all the events, especially not in Python. In the case that the ReadDirectoryChangesW returns nbytes == 0, how can we signal this to the user that a rescan is necessary?

matkuki avatar Nov 28 '23 16:11 matkuki

Python 3.12 solves this issue on my machine without any changes in the code! No idea why currently.

matkuki avatar Dec 26 '23 11:12 matkuki