func (s *resultSrv) Run() { var o *syscall.Overlapped var key uint32 var r ioResult for { r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, 0) if r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil { runtime_blockingSyscallHint() r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE) } switch { case r.err == nil: // Dequeued successfully completed IO packet. case r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil: // Wait has timed out (should not happen now, but might be used in the future). panic("GetQueuedCompletionStatus timed out") case o == nil: // Failed to dequeue anything -> report the error. panic("GetQueuedCompletionStatus failed " + r.err.Error()) default: // Dequeued failed IO packet. } (*anOp)(unsafe.Pointer(o)).resultc <- r } }
// TODO(pknap) : doc func (r *readdcw) loop() { var n, key uint32 var overlapped *syscall.Overlapped for { err := syscall.GetQueuedCompletionStatus(r.cph, &n, &key, &overlapped, syscall.INFINITE) if key == stateCPClose { r.Lock() handle := r.cph r.cph = syscall.InvalidHandle r.Unlock() syscall.CloseHandle(handle) r.wg.Done() return } if overlapped == nil { // TODO: check key == rewatch delete or 0(panic) continue } overEx := (*overlappedEx)(unsafe.Pointer(overlapped)) if n == 0 { r.loopstate(overEx) } else { r.loopevent(n, overEx) if err = overEx.parent.readDirChanges(); err != nil { // TODO: error handling } } } }
func initJob() { // Create Job object and assign current process to it. jobObject, err := createJobObject(nil, nil) if err != nil { log.Printf("CreateJobObject failed: %v", err) return } if err = assignProcessToJobObject(jobObject, currentProcess); err != nil { log.Printf("AssignProcessToJobObject failed: %v", err) syscall.Close(jobObject) return } iocp, err := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1) if err != nil { log.Printf("CreateIoCompletionPort failed: %v", err) syscall.Close(jobObject) return } port := JOBOBJECT_ASSOCIATE_COMPLETION_PORT{ CompletionKey: uintptr(jobObject), CompletionPort: iocp, } err = setInformationJobObject(jobObject, JobObjectAssociateCompletionPortInformation, uintptr(unsafe.Pointer(&port)), uint32(unsafe.Sizeof(port))) if err != nil { log.Printf("SetInformationJobObject failed: %v", err) syscall.Close(jobObject) syscall.Close(iocp) return } // Read Job notifications about new "child" processes and collect them in childProcesses. go func() { var code, key uint32 var o *syscall.Overlapped for { err := syscall.GetQueuedCompletionStatus(iocp, &code, &key, &o, syscall.INFINITE) if err != nil { log.Printf("GetQueuedCompletionStatus failed: %v", err) return } if key != uint32(jobObject) { panic("Invalid GetQueuedCompletionStatus key parameter") } if code == JOB_OBJECT_MSG_NEW_PROCESS { pid := int(uintptr(unsafe.Pointer(o))) if pid == syscall.Getpid() { continue } c, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid)) if err != nil { log.Printf("OpenProcess failed: %v", err) continue } childMu.Lock() childProcesses = append(childProcesses, c) childMu.Unlock() } } }() }
func (s *pollServer) getCompletedIO() (ov *syscall.Overlapped, result *ioResult, err os.Error) { var r ioResult var o *syscall.Overlapped _, e := syscall.GetQueuedCompletionStatus(s.iocp, &r.qty, &r.key, &o, syscall.INFINITE) switch { case e == 0: // Dequeued successfully completed io packet. return o, &r, nil case e == syscall.WAIT_TIMEOUT && o == nil: // Wait has timed out (should not happen now, but might be used in the future). return nil, &r, os.NewSyscallError("GetQueuedCompletionStatus", e) case o == nil: // Failed to dequeue anything -> report the error. return nil, &r, os.NewSyscallError("GetQueuedCompletionStatus", e) default: // Dequeued failed io packet. r.errno = e return o, &r, nil } return }
// readEvents reads from the I/O completion port, converts the // received events into Event objects and sends them via the Event channel. // Entry point to the I/O thread. func (w *Watcher) readEvents() { var ( n, key uint32 ov *syscall.Overlapped ) runtime.LockOSThread() for { e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE) watch := (*watch)(unsafe.Pointer(ov)) if watch == nil { select { case ch := <-w.quit: w.mu.Lock() indexes := make([]indexMap, 0) for _, index := range w.watches { indexes = append(indexes, index) } w.mu.Unlock() for _, index := range indexes { for _, watch := range index { w.deleteWatch(watch) w.startRead(watch) } } var err error if e := syscall.CloseHandle(w.port); e != nil { err = os.NewSyscallError("CloseHandle", e) } close(w.internalEvent) close(w.Error) ch <- err return case in := <-w.input: switch in.op { case opAddWatch: in.reply <- w.addWatch(in.path, uint64(in.flags)) case opRemoveWatch: in.reply <- w.remWatch(in.path) } default: } continue } switch e { case sys_ERROR_MORE_DATA: if watch == nil { w.Error <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer") } else { //The i/o succeeded but buffer is full //in theory we should be building up a full packet //in practice we can get away with just carrying on n = uint32(unsafe.Sizeof(watch.buf)) } case syscall.ERROR_ACCESS_DENIED: // Watched directory was probably removed w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) w.deleteWatch(watch) w.startRead(watch) continue case syscall.ERROR_OPERATION_ABORTED: // CancelIo was called on this handle continue default: w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e) continue case nil: } var offset uint32 for { if n == 0 { w.internalEvent <- &FileEvent{mask: sys_FS_Q_OVERFLOW} w.Error <- errors.New("short read in readEvents()") break } // Point "raw" to the event in the buffer raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) fullname := watch.path + "\\" + name var mask uint64 switch raw.Action { case syscall.FILE_ACTION_REMOVED: mask = sys_FS_DELETE_SELF case syscall.FILE_ACTION_MODIFIED: mask = sys_FS_MODIFY case syscall.FILE_ACTION_RENAMED_OLD_NAME: watch.rename = name case syscall.FILE_ACTION_RENAMED_NEW_NAME: if watch.names[watch.rename] != 0 { watch.names[name] |= watch.names[watch.rename] delete(watch.names, watch.rename) mask = sys_FS_MOVE_SELF } } sendNameEvent := func() { if w.sendEvent(fullname, watch.names[name]&mask) { if watch.names[name]&sys_FS_ONESHOT != 0 { delete(watch.names, name) } } } if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME { sendNameEvent() } if raw.Action == syscall.FILE_ACTION_REMOVED { w.sendEvent(fullname, watch.names[name]&sys_FS_IGNORED) delete(watch.names, name) } if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { if watch.mask&sys_FS_ONESHOT != 0 { watch.mask = 0 } } if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { fullname = watch.path + "\\" + watch.rename sendNameEvent() } // Move to the next event in the buffer if raw.NextEntryOffset == 0 { break } offset += raw.NextEntryOffset // Error! if offset >= n { w.Error <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.") break } } if err := w.startRead(watch); err != nil { w.Error <- err } } }
// readEvents reads from the I/O completion port, converts the // received events into Event objects and sends them via the Event channel. // Entry point to the I/O thread. func (w *Watcher) readEvents() { var ( n, key uint32 ov *syscall.Overlapped ) runtime.LockOSThread() for { e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE) watch := (*watch)(unsafe.Pointer(ov)) if watch == nil { select { case ch := <-w.quit: for _, index := range w.watches { for _, watch := range index { w.deleteWatch(watch) w.startRead(watch) } } var err error if e := syscall.CloseHandle(w.port); e != nil { err = os.NewSyscallError("CloseHandle", e) } close(w.Event) close(w.Error) ch <- err return case in := <-w.input: switch in.op { case opAddWatch: in.reply <- w.addWatch(in.path, uint64(in.flags)) case opRemoveWatch: in.reply <- w.removeWatch(in.path) } default: } continue } switch e { case syscall.ERROR_ACCESS_DENIED: // Watched directory was probably removed w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF) w.deleteWatch(watch) w.startRead(watch) continue case syscall.ERROR_OPERATION_ABORTED: // CancelIo was called on this handle continue default: w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e) continue case nil: } var offset uint32 for { if n == 0 { w.Event <- &FileEvent{mask: FS_Q_OVERFLOW} w.Error <- errors.New("short read in readEvents()") break } // Point "raw" to the event in the buffer raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) fullname := watch.path + "/" + name var mask uint64 switch raw.Action { case syscall.FILE_ACTION_REMOVED: mask = FS_DELETE_SELF case syscall.FILE_ACTION_MODIFIED: mask = FS_MODIFY case syscall.FILE_ACTION_RENAMED_OLD_NAME: watch.rename = name case syscall.FILE_ACTION_RENAMED_NEW_NAME: if watch.names[watch.rename] != 0 { watch.names[name] |= watch.names[watch.rename] delete(watch.names, watch.rename) mask = FS_MOVE_SELF } } sendNameEvent := func() { if w.sendEvent(fullname, watch.names[name]&mask) { if watch.names[name]&FS_ONESHOT != 0 { delete(watch.names, name) } } } if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME { sendNameEvent() } if raw.Action == syscall.FILE_ACTION_REMOVED { w.sendEvent(fullname, watch.names[name]&FS_IGNORED) delete(watch.names, name) } if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { if watch.mask&FS_ONESHOT != 0 { watch.mask = 0 } } if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { fullname = watch.path + "/" + watch.rename sendNameEvent() } // Move to the next event in the buffer if raw.NextEntryOffset == 0 { break } offset += raw.NextEntryOffset } if err := w.startRead(watch); err != nil { w.Error <- err } } }