-
Notifications
You must be signed in to change notification settings - Fork 415
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds native polling mode support on Windows #6087
Conversation
Just to add a little background information: @yams-yams worked on this project as part of an internship at LexiFi, and we have already discussed the general appraoch internally. I am planning to do a review of the PR, but of course additional reviewers are welcome! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a first pass over the C code; I will do another pass later over the Caml code.
src/csexp_rpc/csexp_rpc.ml
Outdated
| [], [], [] -> | ||
(* Timeout *) | ||
if peek_named_pipe t.r_interrupt_accept then ( | ||
print_endline "data available!"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debug left-over, can be removed.
src/winwatch/winwatch_stubs.c
Outdated
@@ -0,0 +1,207 @@ | |||
#include <stdio.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think stdio.h
can be removed.
#include <stdio.h> |
#include <caml/threads.h> | ||
#include <caml/unixsupport.h> | ||
#include <caml/custom.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can these three be removed?
#include <caml/threads.h> | |
#include <caml/unixsupport.h> | |
#include <caml/custom.h> |
src/winwatch/winwatch_stubs.c
Outdated
if (event->NextEntryOffset) | ||
{ | ||
*((char**)&event) += event->NextEntryOffset; | ||
} | ||
else | ||
{ | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be simplified a bit:
if (event->NextEntryOffset) | |
{ | |
*((char**)&event) += event->NextEntryOffset; | |
} | |
else | |
{ | |
break; | |
} | |
if (event->NextEntryOffset == 0) break; | |
*((char**)&event) += event->NextEntryOffset; |
src/winwatch/winwatch_stubs.c
Outdated
|
||
for (;;) | ||
{ | ||
name_len = event->FileNameLength / sizeof(WCHAR); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move this to below the switch
so that it is closer to where it is used.
winwatch_watch(t); | ||
} | ||
|
||
CAMLreturn(Val_unit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function signature specifies that it returns (unit, exn) result
, so this is wrong here. It should either return Ok ()
or Error exn
(if the callback raises an exception). Currently it doesn't matter because we never exit the main loop.
src/winwatch/winwatch_stubs.c
Outdated
file_name = malloc(sizeof(WCHAR) * (name_len + 1)); | ||
memcpy(file_name, event->FileName, sizeof(WCHAR) * name_len); | ||
file_name[name_len] = 0; | ||
v_file_name = caml_copy_string_of_utf16(file_name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use WideCharToMultiByte
directly here to convert from UTF-16 to UTF-8 to avoid the extra copy.
https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree, you can allocate and copy directly into the ocaml value (if that's what you meant @nojb ?). no need to nul-terminate this way.
src/winwatch/winwatch_stubs.c
Outdated
if (ok == FALSE) | ||
/* Ignore errors */ | ||
continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cosmetic:
if (ok == FALSE) | |
/* Ignore errors */ | |
continue; | |
if (ok == FALSE) /* Ignore errors */ | |
continue; |
src/winwatch/winwatch.mli
Outdated
(** return None if the file does not exit *) | ||
val create : string -> f:(Event.t -> string -> unit) -> t option |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(** return None if the file does not exit *) | |
val create : string -> f:(Event.t -> string -> unit) -> t option | |
val create : string -> f:(Event.t -> string -> unit) -> t option | |
(** Returns [None] if the file does not exit. *) |
src/csexp_rpc/csexp_rpc.ml
Outdated
| [], [], [] -> | ||
(* Timeout *) | ||
if peek_named_pipe t.r_interrupt_accept then ( | ||
print_endline "data available!"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left-over debug code.
print_endline "data available!"; |
The DCO check is failing because your git "Author" name (
|
@@ -575,6 +587,88 @@ let create_fsevents ?(latency = 0.2) ~(scheduler : Scheduler.t) () = | |||
; sync_table | |||
} | |||
|
|||
let winwatch_patterns = | |||
List.rev_map ~f:Re.Str.regexp_case_fold | |||
[ {|^_opam|} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of this list is already present in exclude_patterns
in this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moreover, there's duplication within this list. I'd like to see this regex defined in a non redundant way.
scheduler.spawn_thread (fun () -> | ||
match Winwatch.Iocp.run iocp with | ||
| Ok () -> () | ||
| Error _ -> (* Winwatch.Iocp.run never returns *) assert false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are exactly the semantics of run
? It looks like we could make it return _
, or some kind of empty type.
@@ -590,11 +684,12 @@ let create_default ?fsevents_debounce ~scheduler () = | |||
~debounce_interval:(Some 0.5 (* seconds *)) ~backend | |||
| `Fsevents -> create_fsevents ?latency:fsevents_debounce ~scheduler () | |||
| `Inotify_lib -> create_inotifylib ~scheduler | |||
| `Winwatch -> create_winwatch ~scheduler ~debounce_interval:0.5 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's group this debounce_interval
constant with the fswatch one in a module global.
src/winwatch/winwatch_stubs.c
Outdated
break; | ||
} | ||
|
||
file_name = malloc(sizeof(WCHAR) * (name_len + 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
file_name
is not freed (it can be right after it's been copied in v_file_name
)
src/winwatch/winwatch_stubs.c
Outdated
file_name = malloc(sizeof(WCHAR) * (name_len + 1)); | ||
memcpy(file_name, event->FileName, sizeof(WCHAR) * name_len); | ||
file_name[name_len] = 0; | ||
v_file_name = caml_copy_string_of_utf16(file_name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree, you can allocate and copy directly into the ocaml value (if that's what you meant @nojb ?). no need to nul-terminate this way.
t->overlapped = caml_stat_alloc(sizeof(OVERLAPPED)); | ||
t->handle = handle; | ||
|
||
caml_register_generational_global_root(&t->func); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this necessary? this is in t
which is returned through v_res
.
Signed-off-by: Uma Kothuri <[email protected]> Signed-off-by: Nicolás Ojeda Bär <[email protected]>
Signed-off-by: Nicolás Ojeda Bär <[email protected]>
Signed-off-by: Nicolás Ojeda Bär <[email protected]>
Signed-off-by: Nicolás Ojeda Bär <[email protected]>
Signed-off-by: Nicolás Ojeda Bär <[email protected]>
@nojb could you move the RPC changes to a separate PR? |
@@ -0,0 +1,25 @@ | |||
module Iocp : sig |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add some docs to this module?
@@ -583,6 +595,88 @@ let create_fsevents ?(latency = 0.2) ~(scheduler : Scheduler.t) () = | |||
; sync_table | |||
} | |||
|
|||
let winwatch_patterns = | |||
List.rev_map ~f:Re.Str.regexp_case_fold |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you switch from Re.Str
to Re
combinators?
Thanks for the review @rgrinberg. I am in the process of rebasing it (I started a few days ago, but didn't get enough time to finish). Will amend according to your suggestions. |
Superceded by #7010 |
Adds support for polling mode on Windows using a new internal library
winwatch
, which takes inspiration for its structure fromfsevents
.winwatch
uses the Windows ReadDirectoryChangesW API to watch for file changes and an I/O Completion Port to report notifications in a callback.dune_file_watcher
now useswinwatch
instead offswatch
on native Windows, and notifications received indune_file_watcher
are filtered for relevance before they are passed toscheduler.thread_safe_send_emit_events_job
.One additional change was made for compatibility on Windows:
The rpc server typically uses
Unix.pipe
in non-blocking mode, but non-blocking mode is not supported on Windows. The proposed solution instead uses a blocking pipe that times out every 0.1 second and reblocks.Potential areas for improvement:
The proposed solution does not prevent the same path from being watched multiple times. A potential solution would be a trie that stores paths, like
Watch_trie
used by forfsevents
.Signed-off-by: Uma Kothuri [email protected]