static void InstallHandleSIGTERMFromRunLoop(void) // This routine installs HandleSIGTERMFromRunLoop as a SIGTERM handler. // The wrinkle is, HandleSIGTERMFromRunLoop is called from the runloop rather // than as a signal handler. This means that HandleSIGTERMFromRunLoop is not // limited to calling just the miniscule set of system calls that are safe // from a signal handler. // // This routine leaks lots of stuff. You're only expected to call it once, // from the main thread of your program. { static const CFFileDescriptorContext kContext = { 0, NULL, NULL, NULL, NULL }; sig_t sigErr; int kq; CFFileDescriptorRef kqRef; CFRunLoopSourceRef kqSource; struct kevent changes; int changeCount; // Ignore SIGTERM. Even though we've ignored the signal, the kqueue will // still see it. sigErr = signal(SIGTERM, SIG_IGN); assert(sigErr != SIG_ERR); // Create a kqueue and configure it to listen for the SIGTERM signal. kq = kqueue(); assert(kq >= 0); // Use the new-in-10.5 EV_RECEIPT flag to ensure that we get what we expect. EV_SET(&changes, SIGTERM, EVFILT_SIGNAL, EV_ADD | EV_RECEIPT, 0, 0, NULL); changeCount = kevent(kq, &changes, 1, &changes, 1, NULL); assert(changeCount == 1); // check that we get an event back assert(changes.flags & EV_ERROR); // and that it contains error information assert(changes.data == 0); // with no error // Wrap the kqueue in a CFFileDescriptor (new in Mac OS X 10.5!). Then // create a run-loop source from the CFFileDescriptor and add that to the // runloop. kqRef = CFFileDescriptorCreate(NULL, kq, true, HandleSIGTERMFromRunLoop, &kContext); assert(kqRef != NULL); kqSource = CFFileDescriptorCreateRunLoopSource(NULL, kqRef, 0); assert(kqSource != NULL); CFRunLoopAddSource(CFRunLoopGetCurrent(), kqSource, kCFRunLoopDefaultMode); CFFileDescriptorEnableCallBacks(kqRef, kCFFileDescriptorReadCallBack); // Clean up. We can release kqSource and kqRef because they're all being // kept live by the fact that the kqSource is added to the runloop. We // must not close kq because file descriptors are not reference counted // and kqRef now 'owns' this descriptor. CFRelease(kqSource); CFRelease(kqRef); }
static int add_command_pipe (SeafWTMonitorPriv *priv) { CFFileDescriptorContext ctx = {0, priv, NULL, NULL, NULL}; CFFileDescriptorRef fdref = CFFileDescriptorCreate(NULL, priv->cmd_pipe[0], true, command_read_cb, &ctx); if (fdref == NULL) { return -1; } CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack); CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0); CFRunLoopAddSource (CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); CFRelease(source); return 0; }
static void constructCFFD(_CFFileStreamContext *fileStream, Boolean forRead, struct _CFStream *stream) { CFFileDescriptorContext context = {0, stream, NULL, NULL, (void *)CFCopyDescription}; CFFileDescriptorRef cffd = CFFileDescriptorCreate(CFGetAllocator(stream), fileStream->fd, false, fileCallBack, &context); CFFileDescriptorEnableCallBacks(cffd, forRead ? kCFFileDescriptorReadCallBack : kCFFileDescriptorWriteCallBack); if (fileStream->rlInfo.rlArray) { CFIndex i, c = CFArrayGetCount(fileStream->rlInfo.rlArray); CFRunLoopSourceRef src = CFFileDescriptorCreateRunLoopSource(CFGetAllocator(stream), cffd, 0); for (i = 0; i+1 < c; i += 2) { CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i); CFStringRef mode = CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i+1); CFRunLoopAddSource(rl, src, mode); } CFRelease(fileStream->rlInfo.rlArray); CFRelease(src); } fileStream->rlInfo.cffd = cffd; }
/** * 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); }
wxEventLoopSource * wxCFEventLoop::AddSourceForFD(int fd, wxEventLoopSourceHandler *handler, int flags) { wxCHECK_MSG( fd != -1, NULL, "can't monitor invalid fd" ); wxScopedPtr<wxCFEventLoopSource> source(new wxCFEventLoopSource(handler, flags)); CFFileDescriptorContext ctx = { 0, source.get(), NULL, NULL, NULL }; wxCFRef<CFFileDescriptorRef> cffd(CFFileDescriptorCreate ( kCFAllocatorDefault, fd, true, // close on invalidate wx_cffiledescriptor_callback, &ctx )); if ( !cffd ) return NULL; wxCFRef<CFRunLoopSourceRef> cfsrc(CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, cffd, 0)); if ( !cfsrc ) return NULL; CFRunLoopRef cfloop = CFGetCurrentRunLoop(); CFRunLoopAddSource(cfloop, cfsrc, kCFRunLoopDefaultMode); // Enable the callbacks initially. EnableDescriptorCallBacks(cffd, source->GetFlags()); source->SetFileDescriptor(cffd.release()); return source.release(); }
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; }