/** * The main thread tells the run loop thread that a new command is ready by * writing to a pipe. This function tells the run loop to call a callback * whenever that fd is ready to be read */ static void listen_for_command_fd(struct thread_env *thread_env) { int fd = thread_env->read_command_fd; CFFileDescriptorContext fd_context = {}; fd_context.info = thread_env; CFFileDescriptorRef fdref = CFFileDescriptorCreate( kCFAllocatorDefault, fd, true, command_callback, &fd_context ); CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack); CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource( kCFAllocatorDefault, fdref, 0 ); CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); CFRelease(source); }
FskErr FskFSDirectoryChangeNotifierNew(const char *path, UInt32 flags, FskDirectoryChangeNotifierCallbackProc callback, void *refCon, FskDirectoryChangeNotifier *dirChangeNotifier) { FskErr err = kFskErrNone; FskFSDirectoryChangeNotifier directoryChangeNotifier; struct kevent eventToAdd; int errNum; CFFileDescriptorContext context = {0, NULL, NULL, NULL, NULL}; CFRunLoopSourceRef rls; err = FskMemPtrNewClear(sizeof(FskFSDirectoryChangeNotifierRecord), (FskMemPtr *)(&directoryChangeNotifier)); BAIL_IF_ERR(err); directoryChangeNotifier->base.dispatch.dispatch = &gFSDispatch; directoryChangeNotifier->callback = callback; directoryChangeNotifier->refCon = refCon; directoryChangeNotifier->path = FskStrDoCopy(path); directoryChangeNotifier->dirFD = -1; directoryChangeNotifier->kq = -1; directoryChangeNotifier->dirKQRef = NULL; directoryChangeNotifier->dirFD = open(path, O_EVTONLY); if (directoryChangeNotifier->dirFD < 0) { BAIL(kFskErrFileNotFound); } directoryChangeNotifier->kq = kqueue(); if (directoryChangeNotifier->kq < 0) { BAIL(kFskErrOperationFailed); } eventToAdd.ident = directoryChangeNotifier->dirFD; eventToAdd.filter = EVFILT_VNODE; eventToAdd.flags = EV_ADD | EV_CLEAR; eventToAdd.fflags = NOTE_WRITE; eventToAdd.data = 0; eventToAdd.udata = NULL; errNum = kevent(directoryChangeNotifier->kq, &eventToAdd, 1, NULL, 0, NULL); if (errNum != 0) { BAIL(kFskErrOperationFailed); } context.info = directoryChangeNotifier; directoryChangeNotifier->dirKQRef = CFFileDescriptorCreate(NULL, directoryChangeNotifier->kq, true, KQCallback, &context); if (directoryChangeNotifier->dirKQRef == NULL) { BAIL(kFskErrOperationFailed); } rls = CFFileDescriptorCreateRunLoopSource(NULL, directoryChangeNotifier->dirKQRef, 0); if (rls == NULL) { BAIL(kFskErrOperationFailed); } CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); CFRelease(rls); CFFileDescriptorEnableCallBacks(directoryChangeNotifier->dirKQRef, kCFFileDescriptorReadCallBack); bail: if (err) { FskFSDirectoryChangeNotifierDispose((FskDirectoryChangeNotifier)directoryChangeNotifier); directoryChangeNotifier = NULL; } *dirChangeNotifier = (FskDirectoryChangeNotifier)directoryChangeNotifier; return err; }
void FSEventsWatcher::FSEventsThread(const std::shared_ptr<w_root_t>& root) { CFFileDescriptorRef fdref; CFFileDescriptorContext fdctx; w_set_thread_name("fsevents %s", root->root_path.c_str()); { // Block until fsevents_root_start is waiting for our initialization auto wlock = items_.wlock(); attempt_resync_on_drop = root->config.getBool("fsevents_try_resync", true); memset(&fdctx, 0, sizeof(fdctx)); fdctx.info = root.get(); fdref = CFFileDescriptorCreate( nullptr, fse_pipe.read.fd(), true, fse_pipe_callback, &fdctx); CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack); { CFRunLoopSourceRef fdsrc; fdsrc = CFFileDescriptorCreateRunLoopSource(nullptr, fdref, 0); if (!fdsrc) { root->failure_reason = w_string_new_typed( "CFFileDescriptorCreateRunLoopSource failed", W_STRING_UNICODE); goto done; } CFRunLoopAddSource(CFRunLoopGetCurrent(), fdsrc, kCFRunLoopDefaultMode); CFRelease(fdsrc); } stream = fse_stream_make( root, kFSEventStreamEventIdSinceNow, root->failure_reason); if (!stream) { goto done; } if (!FSEventStreamStart(stream->stream)) { root->failure_reason = w_string::printf( "FSEventStreamStart failed, look at your log file %s for " "lines mentioning FSEvents and see %s#fsevents for more information\n", log_name, cfg_get_trouble_url()); goto done; } // Signal to fsevents_root_start that we're done initializing fse_cond.notify_one(); } // Process the events stream until we get signalled to quit CFRunLoopRun(); done: if (stream) { delete stream; } if (fdref) { CFRelease(fdref); } w_log(W_LOG_DBG, "fse_thread done\n"); }
static void *fsevents_thread(void *arg) { w_root_t *root = arg; FSEventStreamContext ctx; CFMutableArrayRef parray; CFStringRef cpath; FSEventStreamRef fs_stream = NULL; CFFileDescriptorContext fdctx; CFFileDescriptorRef fdref; struct fsevents_root_state *state = root->watch; double latency; w_set_thread_name("fsevents %.*s", root->root_path->len, root->root_path->buf); // Block until fsevents_root_start is waiting for our initialization pthread_mutex_lock(&state->fse_mtx); memset(&ctx, 0, sizeof(ctx)); ctx.info = root; memset(&fdctx, 0, sizeof(fdctx)); fdctx.info = root; fdref = CFFileDescriptorCreate(NULL, state->fse_pipe[0], true, fse_pipe_callback, &fdctx); CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack); { CFRunLoopSourceRef fdsrc; fdsrc = CFFileDescriptorCreateRunLoopSource(NULL, fdref, 0); if (!fdsrc) { root->failure_reason = w_string_new( "CFFileDescriptorCreateRunLoopSource failed"); goto done; } CFRunLoopAddSource(CFRunLoopGetCurrent(), fdsrc, kCFRunLoopDefaultMode); CFRelease(fdsrc); } parray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); if (!parray) { root->failure_reason = w_string_new("CFArrayCreateMutable failed"); goto done; } cpath = CFStringCreateWithBytes(NULL, (const UInt8*)root->root_path->buf, root->root_path->len, kCFStringEncodingUTF8, false); if (!cpath) { root->failure_reason = w_string_new("CFStringCreateWithBytes failed"); goto done; } CFArrayAppendValue(parray, cpath); CFRelease(cpath); latency = cfg_get_double(root, "fsevents_latency", 0.01), w_log(W_LOG_DBG, "FSEventStreamCreate for path %.*s with latency %f seconds\n", root->root_path->len, root->root_path->buf, latency); fs_stream = FSEventStreamCreate(NULL, fse_callback, &ctx, parray, kFSEventStreamEventIdSinceNow, latency, kFSEventStreamCreateFlagNoDefer| kFSEventStreamCreateFlagWatchRoot| kFSEventStreamCreateFlagFileEvents); if (!fs_stream) { root->failure_reason = w_string_new("FSEventStreamCreate failed"); goto done; } FSEventStreamScheduleWithRunLoop(fs_stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); if (!FSEventStreamStart(fs_stream)) { root->failure_reason = w_string_make_printf( "FSEventStreamStart failed, look at your log file %s for " "lines mentioning FSEvents and see " "https://facebook.github.io/watchman/docs/troubleshooting.html#" "fsevents for more information\n", log_name); goto done; } // Signal to fsevents_root_start that we're done initializing pthread_cond_signal(&state->fse_cond); pthread_mutex_unlock(&state->fse_mtx); // Process the events stream until we get signalled to quit CFRunLoopRun(); // Since the goto's above hold fse_mtx, we should grab it here pthread_mutex_lock(&state->fse_mtx); done: if (fs_stream) { FSEventStreamStop(fs_stream); FSEventStreamInvalidate(fs_stream); FSEventStreamRelease(fs_stream); } if (fdref) { CFRelease(fdref); } // Signal to fsevents_root_start that we're done initializing in // the failure path. We'll also do this after we've completed // the run loop in the success path; it's a spurious wakeup but // harmless and saves us from adding and setting a control flag // in each of the failure `goto` statements. fsevents_root_dtor // will `pthread_join` us before `state` is freed. pthread_cond_signal(&state->fse_cond); pthread_mutex_unlock(&state->fse_mtx); w_log(W_LOG_DBG, "fse_thread done\n"); w_root_delref(root); return NULL; }