OSDN Git Service

Thanos did someting
[bytom/vapor.git] / vendor / github.com / rjeczalik / notify / watcher_readdcw.go
diff --git a/vendor/github.com/rjeczalik/notify/watcher_readdcw.go b/vendor/github.com/rjeczalik/notify/watcher_readdcw.go
deleted file mode 100644 (file)
index 5923bfd..0000000
+++ /dev/null
@@ -1,574 +0,0 @@
-// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-// +build windows
-
-package notify
-
-import (
-       "errors"
-       "runtime"
-       "sync"
-       "sync/atomic"
-       "syscall"
-       "unsafe"
-)
-
-// readBufferSize defines the size of an array in which read statuses are stored.
-// The buffer have to be DWORD-aligned and, if notify is used in monitoring a
-// directory over the network, its size must not be greater than 64KB. Each of
-// watched directories uses its own buffer for storing events.
-const readBufferSize = 4096
-
-// Since all operations which go through the Windows completion routine are done
-// asynchronously, filter may set one of the constants belor. They were defined
-// in order to distinguish whether current folder should be re-registered in
-// ReadDirectoryChangesW function or some control operations need to be executed.
-const (
-       stateRewatch uint32 = 1 << (28 + iota)
-       stateUnwatch
-       stateCPClose
-)
-
-// Filter used in current implementation was split into four segments:
-//  - bits  0-11 store ReadDirectoryChangesW filters,
-//  - bits 12-19 store File notify actions,
-//  - bits 20-27 store notify specific events and flags,
-//  - bits 28-31 store states which are used in loop's FSM.
-// Constants below are used as masks to retrieve only specific filter parts.
-const (
-       onlyNotifyChanges uint32 = 0x00000FFF
-       onlyNGlobalEvents uint32 = 0x0FF00000
-       onlyMachineStates uint32 = 0xF0000000
-)
-
-// grip represents a single watched directory. It stores the data required by
-// ReadDirectoryChangesW function. Only the filter, recursive, and handle members
-// may by modified by watcher implementation. Rest of the them have to remain
-// constant since they are used by Windows completion routine. This indicates that
-// grip can be removed only when all operations on the file handle are finished.
-type grip struct {
-       handle    syscall.Handle
-       filter    uint32
-       recursive bool
-       pathw     []uint16
-       buffer    [readBufferSize]byte
-       parent    *watched
-       ovlapped  *overlappedEx
-}
-
-// overlappedEx stores information used in asynchronous input and output.
-// Additionally, overlappedEx contains a pointer to 'grip' item which is used in
-// order to gather the structure in which the overlappedEx object was created.
-type overlappedEx struct {
-       syscall.Overlapped
-       parent *grip
-}
-
-// newGrip creates a new file handle that can be used in overlapped operations.
-// Then, the handle is associated with I/O completion port 'cph' and its value
-// is stored in newly created 'grip' object.
-func newGrip(cph syscall.Handle, parent *watched, filter uint32) (*grip, error) {
-       g := &grip{
-               handle:    syscall.InvalidHandle,
-               filter:    filter,
-               recursive: parent.recursive,
-               pathw:     parent.pathw,
-               parent:    parent,
-               ovlapped:  &overlappedEx{},
-       }
-       if err := g.register(cph); err != nil {
-               return nil, err
-       }
-       g.ovlapped.parent = g
-       return g, nil
-}
-
-// NOTE : Thread safe
-func (g *grip) register(cph syscall.Handle) (err error) {
-       if g.handle, err = syscall.CreateFile(
-               &g.pathw[0],
-               syscall.FILE_LIST_DIRECTORY,
-               syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
-               nil,
-               syscall.OPEN_EXISTING,
-               syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED,
-               0,
-       ); err != nil {
-               return
-       }
-       if _, err = syscall.CreateIoCompletionPort(g.handle, cph, 0, 0); err != nil {
-               syscall.CloseHandle(g.handle)
-               return
-       }
-       return g.readDirChanges()
-}
-
-// readDirChanges tells the system to store file change information in grip's
-// buffer. Directory changes that occur between calls to this function are added
-// to the buffer and then, returned with the next call.
-func (g *grip) readDirChanges() error {
-       return syscall.ReadDirectoryChanges(
-               g.handle,
-               &g.buffer[0],
-               uint32(unsafe.Sizeof(g.buffer)),
-               g.recursive,
-               encode(g.filter),
-               nil,
-               (*syscall.Overlapped)(unsafe.Pointer(g.ovlapped)),
-               0,
-       )
-}
-
-// encode transforms a generic filter, which contains platform independent and
-// implementation specific bit fields, to value that can be used as NotifyFilter
-// parameter in ReadDirectoryChangesW function.
-func encode(filter uint32) uint32 {
-       e := Event(filter & (onlyNGlobalEvents | onlyNotifyChanges))
-       if e&dirmarker != 0 {
-               return uint32(FileNotifyChangeDirName)
-       }
-       if e&Create != 0 {
-               e = (e ^ Create) | FileNotifyChangeFileName
-       }
-       if e&Remove != 0 {
-               e = (e ^ Remove) | FileNotifyChangeFileName
-       }
-       if e&Write != 0 {
-               e = (e ^ Write) | FileNotifyChangeAttributes | FileNotifyChangeSize |
-                       FileNotifyChangeCreation | FileNotifyChangeSecurity
-       }
-       if e&Rename != 0 {
-               e = (e ^ Rename) | FileNotifyChangeFileName
-       }
-       return uint32(e)
-}
-
-// watched is made in order to check whether an action comes from a directory or
-// file. This approach requires two file handlers per single monitored folder. The
-// second grip handles actions which include creating or deleting a directory. If
-// these processes are not monitored, only the first grip is created.
-type watched struct {
-       filter    uint32
-       recursive bool
-       count     uint8
-       pathw     []uint16
-       digrip    [2]*grip
-}
-
-// newWatched creates a new watched instance. It splits the filter variable into
-// two parts. The first part is responsible for watching all events which can be
-// created for a file in watched directory structure and the second one watches
-// only directory Create/Remove actions. If all operations succeed, the Create
-// message is sent to I/O completion port queue for further processing.
-func newWatched(cph syscall.Handle, filter uint32, recursive bool,
-       path string) (wd *watched, err error) {
-       wd = &watched{
-               filter:    filter,
-               recursive: recursive,
-       }
-       if wd.pathw, err = syscall.UTF16FromString(path); err != nil {
-               return
-       }
-       if err = wd.recreate(cph); err != nil {
-               return
-       }
-       return wd, nil
-}
-
-// TODO : doc
-func (wd *watched) recreate(cph syscall.Handle) (err error) {
-       filefilter := wd.filter &^ uint32(FileNotifyChangeDirName)
-       if err = wd.updateGrip(0, cph, filefilter == 0, filefilter); err != nil {
-               return
-       }
-       dirfilter := wd.filter & uint32(FileNotifyChangeDirName|Create|Remove)
-       if err = wd.updateGrip(1, cph, dirfilter == 0, wd.filter|uint32(dirmarker)); err != nil {
-               return
-       }
-       wd.filter &^= onlyMachineStates
-       return
-}
-
-// TODO : doc
-func (wd *watched) updateGrip(idx int, cph syscall.Handle, reset bool,
-       newflag uint32) (err error) {
-       if reset {
-               wd.digrip[idx] = nil
-       } else {
-               if wd.digrip[idx] == nil {
-                       if wd.digrip[idx], err = newGrip(cph, wd, newflag); err != nil {
-                               wd.closeHandle()
-                               return
-                       }
-               } else {
-                       wd.digrip[idx].filter = newflag
-                       wd.digrip[idx].recursive = wd.recursive
-                       if err = wd.digrip[idx].register(cph); err != nil {
-                               wd.closeHandle()
-                               return
-                       }
-               }
-               wd.count++
-       }
-       return
-}
-
-// closeHandle closes handles that are stored in digrip array. Function always
-// tries to close all of the handlers before it exits, even when there are errors
-// returned from the operating system kernel.
-func (wd *watched) closeHandle() (err error) {
-       for _, g := range wd.digrip {
-               if g != nil && g.handle != syscall.InvalidHandle {
-                       switch suberr := syscall.CloseHandle(g.handle); {
-                       case suberr == nil:
-                               g.handle = syscall.InvalidHandle
-                       case err == nil:
-                               err = suberr
-                       }
-               }
-       }
-       return
-}
-
-// watcher implements Watcher interface. It stores a set of watched directories.
-// All operations which remove watched objects from map `m` must be performed in
-// loop goroutine since these structures are used internally by operating system.
-type readdcw struct {
-       sync.Mutex
-       m     map[string]*watched
-       cph   syscall.Handle
-       start bool
-       wg    sync.WaitGroup
-       c     chan<- EventInfo
-}
-
-// NewWatcher creates new non-recursive watcher backed by ReadDirectoryChangesW.
-func newWatcher(c chan<- EventInfo) watcher {
-       r := &readdcw{
-               m:   make(map[string]*watched),
-               cph: syscall.InvalidHandle,
-               c:   c,
-       }
-       runtime.SetFinalizer(r, func(r *readdcw) {
-               if r.cph != syscall.InvalidHandle {
-                       syscall.CloseHandle(r.cph)
-               }
-       })
-       return r
-}
-
-// Watch implements notify.Watcher interface.
-func (r *readdcw) Watch(path string, event Event) error {
-       return r.watch(path, event, false)
-}
-
-// RecursiveWatch implements notify.RecursiveWatcher interface.
-func (r *readdcw) RecursiveWatch(path string, event Event) error {
-       return r.watch(path, event, true)
-}
-
-// watch inserts a directory to the group of watched folders. If watched folder
-// already exists, function tries to rewatch it with new filters(NOT VALID). Moreover,
-// watch starts the main event loop goroutine when called for the first time.
-func (r *readdcw) watch(path string, event Event, recursive bool) (err error) {
-       if event&^(All|fileNotifyChangeAll) != 0 {
-               return errors.New("notify: unknown event")
-       }
-       r.Lock()
-       wd, ok := r.m[path]
-       r.Unlock()
-       if !ok {
-               if err = r.lazyinit(); err != nil {
-                       return
-               }
-               r.Lock()
-               if wd, ok = r.m[path]; ok {
-                       r.Unlock()
-                       return
-               }
-               if wd, err = newWatched(r.cph, uint32(event), recursive, path); err != nil {
-                       r.Unlock()
-                       return
-               }
-               r.m[path] = wd
-               r.Unlock()
-       }
-       return nil
-}
-
-// lazyinit creates an I/O completion port and starts the main event processing
-// loop. This method uses Double-Checked Locking optimization.
-func (r *readdcw) lazyinit() (err error) {
-       invalid := uintptr(syscall.InvalidHandle)
-       if atomic.LoadUintptr((*uintptr)(&r.cph)) == invalid {
-               r.Lock()
-               defer r.Unlock()
-               if atomic.LoadUintptr((*uintptr)(&r.cph)) == invalid {
-                       cph := syscall.InvalidHandle
-                       if cph, err = syscall.CreateIoCompletionPort(cph, 0, 0, 0); err != nil {
-                               return
-                       }
-                       r.cph, r.start = cph, true
-                       go r.loop()
-               }
-       }
-       return
-}
-
-// 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
-                       }
-               }
-       }
-}
-
-// TODO(pknap) : doc
-func (r *readdcw) loopstate(overEx *overlappedEx) {
-       filter := atomic.LoadUint32(&overEx.parent.parent.filter)
-       if filter&onlyMachineStates == 0 {
-               return
-       }
-       if overEx.parent.parent.count--; overEx.parent.parent.count == 0 {
-               switch filter & onlyMachineStates {
-               case stateRewatch:
-                       r.Lock()
-                       overEx.parent.parent.recreate(r.cph)
-                       r.Unlock()
-               case stateUnwatch:
-                       r.Lock()
-                       delete(r.m, syscall.UTF16ToString(overEx.parent.pathw))
-                       r.Unlock()
-               case stateCPClose:
-               default:
-                       panic(`notify: windows loopstate logic error`)
-               }
-       }
-}
-
-// TODO(pknap) : doc
-func (r *readdcw) loopevent(n uint32, overEx *overlappedEx) {
-       events := []*event{}
-       var currOffset uint32
-       for {
-               raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&overEx.parent.buffer[currOffset]))
-               name := syscall.UTF16ToString((*[syscall.MAX_LONG_PATH]uint16)(unsafe.Pointer(&raw.FileName))[:raw.FileNameLength>>1])
-               events = append(events, &event{
-                       pathw:  overEx.parent.pathw,
-                       filter: overEx.parent.filter,
-                       action: raw.Action,
-                       name:   name,
-               })
-               if raw.NextEntryOffset == 0 {
-                       break
-               }
-               if currOffset += raw.NextEntryOffset; currOffset >= n {
-                       break
-               }
-       }
-       r.send(events)
-}
-
-// TODO(pknap) : doc
-func (r *readdcw) send(es []*event) {
-       for _, e := range es {
-               var syse Event
-               if e.e, syse = decode(e.filter, e.action); e.e == 0 && syse == 0 {
-                       continue
-               }
-               switch {
-               case e.action == syscall.FILE_ACTION_MODIFIED:
-                       e.ftype = fTypeUnknown
-               case e.filter&uint32(dirmarker) != 0:
-                       e.ftype = fTypeDirectory
-               default:
-                       e.ftype = fTypeFile
-               }
-               switch {
-               case e.e == 0:
-                       e.e = syse
-               case syse != 0:
-                       r.c <- &event{
-                               pathw:  e.pathw,
-                               name:   e.name,
-                               ftype:  e.ftype,
-                               action: e.action,
-                               filter: e.filter,
-                               e:      syse,
-                       }
-               }
-               r.c <- e
-       }
-}
-
-// Rewatch implements notify.Rewatcher interface.
-func (r *readdcw) Rewatch(path string, oldevent, newevent Event) error {
-       return r.rewatch(path, uint32(oldevent), uint32(newevent), false)
-}
-
-// RecursiveRewatch implements notify.RecursiveRewatcher interface.
-func (r *readdcw) RecursiveRewatch(oldpath, newpath string, oldevent,
-       newevent Event) error {
-       if oldpath != newpath {
-               if err := r.unwatch(oldpath); err != nil {
-                       return err
-               }
-               return r.watch(newpath, newevent, true)
-       }
-       return r.rewatch(newpath, uint32(oldevent), uint32(newevent), true)
-}
-
-// TODO : (pknap) doc.
-func (r *readdcw) rewatch(path string, oldevent, newevent uint32, recursive bool) (err error) {
-       if Event(newevent)&^(All|fileNotifyChangeAll) != 0 {
-               return errors.New("notify: unknown event")
-       }
-       var wd *watched
-       r.Lock()
-       if wd, err = r.nonStateWatched(path); err != nil {
-               r.Unlock()
-               return
-       }
-       if wd.filter&(onlyNotifyChanges|onlyNGlobalEvents) != oldevent {
-               panic(`notify: windows re-watcher logic error`)
-       }
-       wd.filter = stateRewatch | newevent
-       wd.recursive, recursive = recursive, wd.recursive
-       if err = wd.closeHandle(); err != nil {
-               wd.filter = oldevent
-               wd.recursive = recursive
-               r.Unlock()
-               return
-       }
-       r.Unlock()
-       return
-}
-
-// TODO : pknap
-func (r *readdcw) nonStateWatched(path string) (wd *watched, err error) {
-       wd, ok := r.m[path]
-       if !ok || wd == nil {
-               err = errors.New(`notify: ` + path + ` path is unwatched`)
-               return
-       }
-       if filter := atomic.LoadUint32(&wd.filter); filter&onlyMachineStates != 0 {
-               err = errors.New(`notify: another re/unwatching operation in progress`)
-               return
-       }
-       return
-}
-
-// Unwatch implements notify.Watcher interface.
-func (r *readdcw) Unwatch(path string) error {
-       return r.unwatch(path)
-}
-
-// RecursiveUnwatch implements notify.RecursiveWatcher interface.
-func (r *readdcw) RecursiveUnwatch(path string) error {
-       return r.unwatch(path)
-}
-
-// TODO : pknap
-func (r *readdcw) unwatch(path string) (err error) {
-       var wd *watched
-       r.Lock()
-       if wd, err = r.nonStateWatched(path); err != nil {
-               r.Unlock()
-               return
-       }
-       wd.filter |= stateUnwatch
-       if err = wd.closeHandle(); err != nil {
-               wd.filter &^= stateUnwatch
-               r.Unlock()
-               return
-       }
-       r.Unlock()
-       return
-}
-
-// Close resets the whole watcher object, closes all existing file descriptors,
-// and sends stateCPClose state as completion key to the main watcher's loop.
-func (r *readdcw) Close() (err error) {
-       r.Lock()
-       if !r.start {
-               r.Unlock()
-               return nil
-       }
-       for _, wd := range r.m {
-               wd.filter &^= onlyMachineStates
-               wd.filter |= stateCPClose
-               if e := wd.closeHandle(); e != nil && err == nil {
-                       err = e
-               }
-       }
-       r.start = false
-       r.Unlock()
-       r.wg.Add(1)
-       if e := syscall.PostQueuedCompletionStatus(r.cph, 0, stateCPClose, nil); e != nil && err == nil {
-               return e
-       }
-       r.wg.Wait()
-       return
-}
-
-// decode creates a notify event from both non-raw filter and action which was
-// returned from completion routine. Function may return Event(0) in case when
-// filter was replaced by a new value which does not contain fields that are
-// valid with passed action.
-func decode(filter, action uint32) (Event, Event) {
-       switch action {
-       case syscall.FILE_ACTION_ADDED:
-               return gensys(filter, Create, FileActionAdded)
-       case syscall.FILE_ACTION_REMOVED:
-               return gensys(filter, Remove, FileActionRemoved)
-       case syscall.FILE_ACTION_MODIFIED:
-               return gensys(filter, Write, FileActionModified)
-       case syscall.FILE_ACTION_RENAMED_OLD_NAME:
-               return gensys(filter, Rename, FileActionRenamedOldName)
-       case syscall.FILE_ACTION_RENAMED_NEW_NAME:
-               return gensys(filter, Rename, FileActionRenamedNewName)
-       }
-       panic(`notify: cannot decode internal mask`)
-}
-
-// gensys decides whether the Windows action, system-independent event or both
-// of them should be returned. Since the grip's filter may be atomically changed
-// during watcher lifetime, it is possible that neither Windows nor notify masks
-// are watched by the user when this function is called.
-func gensys(filter uint32, ge, se Event) (gene, syse Event) {
-       isdir := filter&uint32(dirmarker) != 0
-       if isdir && filter&uint32(FileNotifyChangeDirName) != 0 ||
-               !isdir && filter&uint32(FileNotifyChangeFileName) != 0 ||
-               filter&uint32(fileNotifyChangeModified) != 0 {
-               syse = se
-       }
-       if filter&uint32(ge) != 0 {
-               gene = ge
-       }
-       return
-}