// Schedules the Stream on a new thread // and starts delivering events on the channel. // // https://developer.apple.com/library/mac/documentation/Darwin/Reference/FSEvents_Ref/Reference/reference.html#jumpTo_20 // https://developer.apple.com/library/mac/documentation/Darwin/Reference/FSEvents_Ref/Reference/reference.html#jumpTo_17 func (s *Stream) Start() bool { type watchSuccessData struct { runloop C.CFRunLoopRef stream Stream } successChan := make(chan C.CFRunLoopRef) go func() { C.FSEventStreamScheduleWithRunLoop(s.cstream, C.CFRunLoopGetCurrent(), C.kCFRunLoopCommonModes) ok := C.FSEventStreamStart(s.cstream) != C.FALSE if ok { successChan <- C.CFRunLoopGetCurrent() C.CFRunLoopRun() } else { successChan <- nil } }() runloop := <-successChan if runloop == nil { return false } s.runloop = runloop return true }
func startScanner(dir string) { lock.Lock() chans[dir] = make(chan string) lock.Unlock() cpaths := C.fswatch_make_mutable_array() defer C.free(unsafe.Pointer(cpaths)) path := C.CString(dir) str := C.CFStringCreateWithCString(nil, path, C.kCFStringEncodingUTF8) defer C.free(unsafe.Pointer(path)) defer C.free(unsafe.Pointer(str)) C.CFArrayAppendValue(cpaths, unsafe.Pointer(str)) ctx := C.FSEventStreamContext{info: unsafe.Pointer(C.CString(dir))} stream := C.fswatch_create(&ctx, cpaths, now, C.CFTimeInterval(interval/time.Second), cflags) go func() { C.FSEventStreamScheduleWithRunLoop(stream, C.CFRunLoopGetCurrent(), C.kCFRunLoopCommonModes) C.FSEventStreamStart(stream) C.CFRunLoopRun() }() }
// initializes the global runloop and ensures any created stream awaits its // readiness. func init() { wg.Add(1) go func() { runloop = C.CFRunLoopGetCurrent() C.CFRunLoopAddSource(runloop, source, C.kCFRunLoopDefaultMode) C.CFRunLoopRun() panic("runloop has just unexpectedly stopped") }() C.CFRunLoopSourceSignal(source) }
// Start listening to an event stream. func (es *EventStream) Start() { cPaths := C.ArrayCreateMutable(C.int(len(es.Paths))) defer C.CFRelease(C.CFTypeRef(cPaths)) for _, p := range es.Paths { p, _ = filepath.Abs(p) cpath := C.CString(p) defer C.free(unsafe.Pointer(cpath)) str := C.CFStringCreateWithCString(nil, cpath, C.kCFStringEncodingUTF8) C.CFArrayAppendValue(cPaths, unsafe.Pointer(str)) } since := C.FSEventStreamEventId(EventIDSinceNow) if es.Resume { since = C.FSEventStreamEventId(es.EventID) } if es.Events == nil { es.Events = make(chan []Event) } es.registryID = registry.Add(es) context := C.FSEventStreamContext{} info := C.uintptr_t(es.registryID) latency := C.CFTimeInterval(float64(es.Latency) / float64(time.Second)) if es.Device != 0 { es.stream = C.EventStreamCreateRelativeToDevice(&context, info, C.dev_t(es.Device), cPaths, since, latency, C.FSEventStreamCreateFlags(es.Flags)) } else { es.stream = C.EventStreamCreate(&context, info, cPaths, since, latency, C.FSEventStreamCreateFlags(es.Flags)) } started := make(chan struct{}) go func() { runtime.LockOSThread() es.rlref = C.CFRunLoopGetCurrent() C.FSEventStreamScheduleWithRunLoop(es.stream, es.rlref, C.kCFRunLoopDefaultMode) C.FSEventStreamStart(es.stream) close(started) C.CFRunLoopRun() }() if !es.hasFinalizer { runtime.SetFinalizer(es, finalizer) es.hasFinalizer = true } <-started }
func NewWatcher() (*Watcher, error) { w := &Watcher{ watches: make(map[string]C.FSEventStreamRef), fsnFlags: make(map[string]uint32), enFlags: make(map[string]uint32), paths: make(map[int]string), finfo: make(map[int]os.FileInfo), fileExists: make(map[string]bool), internalEvent: make(chan *FileEvent), Event: make(chan *FileEvent), Error: make(chan error), done: make(chan bool, 1), } w.rlref = C.CFRunLoopGetCurrent() // XXX: accessing the 'current' runloop. This will cause problems with multiple watchers in one process // Start the runloop. This exists for the duration of the watcher go func() { C.CFRunLoopRun() }() return w, nil }