fsmonitor: eliminate call to deprecated FSEventStream function

Replace the call to `FSEventStreamScheduleWithRunLoop()` function with the suggested `FSEventStreamSetDispatchQueue()` function. The MacOS version of the builtin FSMonitor feature uses the `FSEventStreamScheduleWithRunLoop()` function to drive the event loop and process FSEvents from the system. This routine has now been deprecated by Apple. The MacOS 13 (Ventura) compiler tool chain now generates a warning when compiling calls to this function. In DEVELOPER=1 mode, this now causes a compile error. The `FSEventStreamSetDispatchQueue()` function is conceptually similar and is the suggested replacement. However, there are some subtle thread-related differences. Previously, the event stream would be processed by the `fsm_listen__loop()` thread while it was in the `CFRunLoopRun()` method. (Conceptually, this was a blocking call on the lifetime of the event stream where our thread drove the event loop and individual events were handled by the `fsevent_callback()`.) With the change, a "dispatch queue" is created and FSEvents will be processed by a hidden queue-related thread (that calls the `fsevent_callback()` on our behalf). Our `fsm_listen__loop()` thread maintains the original blocking model by waiting on a mutex/condition variable pair while the hidden thread does all of the work. While the deprecated API used by the original were introduced in macOS 10.5 (Oct 2007), the API used by the updated code were introduced back in macOS 10.6 (Aug 2009) and has been available since then. So this change _could_ break those who have happily been using 10.5 (if there were such people), but these two dates both predate the oldest versions of macOS Apple seems to support anyway, so we should be safe. Signed-off-by: Jeff Hostetler <jeffhostetler@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff Hostetler committed Dec 14, 2022 at 19:12 UTC b0226007f0aaf448dec1defe3e44c4e3d7513aa8
2 files changed +25 -14
compat/fsmonitor/fsm-darwin-gcc.h
+1 -3
@@ -80,9 +80,7 @@ void CFRunLoopRun(void);
80 void CFRunLoopStop(CFRunLoopRef run_loop);
81 CFRunLoopRef CFRunLoopGetCurrent(void);
82 extern CFStringRef kCFRunLoopDefaultMode;
83 -void FSEventStreamScheduleWithRunLoop(FSEventStreamRef stream,
84 - CFRunLoopRef run_loop,
85 - CFStringRef run_loop_mode);
83 +void FSEventStreamSetDispatchQueue(FSEventStreamRef stream, dispatch_queue_t q);
84 unsigned char FSEventStreamStart(FSEventStreamRef stream);
85 void FSEventStreamStop(FSEventStreamRef stream);
86 void FSEventStreamInvalidate(FSEventStreamRef stream);
compat/fsmonitor/fsm-listen-darwin.c
+24 -11
@@ -1,4 +1,5 @@
1 #ifndef __clang__
2 +#include <dispatch/dispatch.h>
3 #include "fsm-darwin-gcc.h"
4 #else
5 #include <CoreFoundation/CoreFoundation.h>
@@ -37,7 +38,9 @@ struct fsm_listen_data
38
39 FSEventStreamRef stream;
40
40 - CFRunLoopRef rl;
41 + dispatch_queue_t dq;
42 + pthread_cond_t dq_finished;
43 + pthread_mutex_t dq_lock;
44
45 enum shutdown_style {
46 SHUTDOWN_EVENT = 0,
@@ -369,8 +372,11 @@ force_shutdown:
372 fsmonitor_batch__free_list(batch);
373 string_list_clear(&cookie_list, 0);
374
375 + pthread_mutex_lock(&data->dq_lock);
376 data->shutdown_style = FORCE_SHUTDOWN;
373 - CFRunLoopStop(data->rl);
377 + pthread_cond_broadcast(&data->dq_finished);
378 + pthread_mutex_unlock(&data->dq_lock);
379 +
380 strbuf_release(&tmp);
381 return;
382 }
@@ -431,10 +437,6 @@ int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
437 if (!data->stream)
438 goto failed;
439
434 - /*
435 - * `data->rl` needs to be set inside the listener thread.
436 - */
437 -
440 return 0;
441
442 failed:
@@ -461,6 +463,11 @@ void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
463 FSEventStreamRelease(data->stream);
464 }
465
466 + if (data->dq)
467 + dispatch_release(data->dq);
468 + pthread_cond_destroy(&data->dq_finished);
469 + pthread_mutex_destroy(&data->dq_lock);
470 +
471 FREE_AND_NULL(state->listen_data);
472 }
473
@@ -469,9 +476,11 @@ void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
476 struct fsm_listen_data *data;
477
478 data = state->listen_data;
472 - data->shutdown_style = SHUTDOWN_EVENT;
479
474 - CFRunLoopStop(data->rl);
480 + pthread_mutex_lock(&data->dq_lock);
481 + data->shutdown_style = SHUTDOWN_EVENT;
482 + pthread_cond_broadcast(&data->dq_finished);
483 + pthread_mutex_unlock(&data->dq_lock);
484 }
485
486 void fsm_listen__loop(struct fsmonitor_daemon_state *state)
@@ -480,9 +489,11 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
489
490 data = state->listen_data;
491
483 - data->rl = CFRunLoopGetCurrent();
492 + pthread_mutex_init(&data->dq_lock, NULL);
493 + pthread_cond_init(&data->dq_finished, NULL);
494 + data->dq = dispatch_queue_create("FSMonitor", NULL);
495
485 - FSEventStreamScheduleWithRunLoop(data->stream, data->rl, kCFRunLoopDefaultMode);
496 + FSEventStreamSetDispatchQueue(data->stream, data->dq);
497 data->stream_scheduled = 1;
498
499 if (!FSEventStreamStart(data->stream)) {
@@ -491,7 +502,9 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
502 }
503 data->stream_started = 1;
504
494 - CFRunLoopRun();
505 + pthread_mutex_lock(&data->dq_lock);
506 + pthread_cond_wait(&data->dq_finished, &data->dq_lock);
507 + pthread_mutex_unlock(&data->dq_lock);
508
509 switch (data->shutdown_style) {
510 case FORCE_ERROR_STOP: