A lightweight mechanism to provide an *instant kickstart* to a Go web server instance upon changing any Go source files under the project directory (and its subdirectories).

windows.go 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build windows
  5. package fsnotify
  6. import (
  7. "errors"
  8. "fmt"
  9. "os"
  10. "path/filepath"
  11. "runtime"
  12. "sync"
  13. "syscall"
  14. "unsafe"
  15. )
  16. // Watcher watches a set of files, delivering events to a channel.
  17. type Watcher struct {
  18. Events chan Event
  19. Errors chan error
  20. isClosed bool // Set to true when Close() is first called
  21. mu sync.Mutex // Map access
  22. port syscall.Handle // Handle to completion port
  23. watches watchMap // Map of watches (key: i-number)
  24. input chan *input // Inputs to the reader are sent on this channel
  25. quit chan chan<- error
  26. }
  27. // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
  28. func NewWatcher() (*Watcher, error) {
  29. port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
  30. if e != nil {
  31. return nil, os.NewSyscallError("CreateIoCompletionPort", e)
  32. }
  33. w := &Watcher{
  34. port: port,
  35. watches: make(watchMap),
  36. input: make(chan *input, 1),
  37. Events: make(chan Event, 50),
  38. Errors: make(chan error),
  39. quit: make(chan chan<- error, 1),
  40. }
  41. go w.readEvents()
  42. return w, nil
  43. }
  44. // Close removes all watches and closes the events channel.
  45. func (w *Watcher) Close() error {
  46. if w.isClosed {
  47. return nil
  48. }
  49. w.isClosed = true
  50. // Send "quit" message to the reader goroutine
  51. ch := make(chan error)
  52. w.quit <- ch
  53. if err := w.wakeupReader(); err != nil {
  54. return err
  55. }
  56. return <-ch
  57. }
  58. // Add starts watching the named file or directory (non-recursively).
  59. func (w *Watcher) Add(name string) error {
  60. if w.isClosed {
  61. return errors.New("watcher already closed")
  62. }
  63. in := &input{
  64. op: opAddWatch,
  65. path: filepath.Clean(name),
  66. flags: sysFSALLEVENTS,
  67. reply: make(chan error),
  68. }
  69. w.input <- in
  70. if err := w.wakeupReader(); err != nil {
  71. return err
  72. }
  73. return <-in.reply
  74. }
  75. // Remove stops watching the the named file or directory (non-recursively).
  76. func (w *Watcher) Remove(name string) error {
  77. in := &input{
  78. op: opRemoveWatch,
  79. path: filepath.Clean(name),
  80. reply: make(chan error),
  81. }
  82. w.input <- in
  83. if err := w.wakeupReader(); err != nil {
  84. return err
  85. }
  86. return <-in.reply
  87. }
  88. const (
  89. // Options for AddWatch
  90. sysFSONESHOT = 0x80000000
  91. sysFSONLYDIR = 0x1000000
  92. // Events
  93. sysFSACCESS = 0x1
  94. sysFSALLEVENTS = 0xfff
  95. sysFSATTRIB = 0x4
  96. sysFSCLOSE = 0x18
  97. sysFSCREATE = 0x100
  98. sysFSDELETE = 0x200
  99. sysFSDELETESELF = 0x400
  100. sysFSMODIFY = 0x2
  101. sysFSMOVE = 0xc0
  102. sysFSMOVEDFROM = 0x40
  103. sysFSMOVEDTO = 0x80
  104. sysFSMOVESELF = 0x800
  105. // Special events
  106. sysFSIGNORED = 0x8000
  107. sysFSQOVERFLOW = 0x4000
  108. )
  109. func newEvent(name string, mask uint32) Event {
  110. e := Event{Name: name}
  111. if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO {
  112. e.Op |= Create
  113. }
  114. if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF {
  115. e.Op |= Remove
  116. }
  117. if mask&sysFSMODIFY == sysFSMODIFY {
  118. e.Op |= Write
  119. }
  120. if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
  121. e.Op |= Rename
  122. }
  123. if mask&sysFSATTRIB == sysFSATTRIB {
  124. e.Op |= Chmod
  125. }
  126. return e
  127. }
  128. const (
  129. opAddWatch = iota
  130. opRemoveWatch
  131. )
  132. const (
  133. provisional uint64 = 1 << (32 + iota)
  134. )
  135. type input struct {
  136. op int
  137. path string
  138. flags uint32
  139. reply chan error
  140. }
  141. type inode struct {
  142. handle syscall.Handle
  143. volume uint32
  144. index uint64
  145. }
  146. type watch struct {
  147. ov syscall.Overlapped
  148. ino *inode // i-number
  149. path string // Directory path
  150. mask uint64 // Directory itself is being watched with these notify flags
  151. names map[string]uint64 // Map of names being watched and their notify flags
  152. rename string // Remembers the old name while renaming a file
  153. buf [4096]byte
  154. }
  155. type indexMap map[uint64]*watch
  156. type watchMap map[uint32]indexMap
  157. func (w *Watcher) wakeupReader() error {
  158. e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
  159. if e != nil {
  160. return os.NewSyscallError("PostQueuedCompletionStatus", e)
  161. }
  162. return nil
  163. }
  164. func getDir(pathname string) (dir string, err error) {
  165. attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
  166. if e != nil {
  167. return "", os.NewSyscallError("GetFileAttributes", e)
  168. }
  169. if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
  170. dir = pathname
  171. } else {
  172. dir, _ = filepath.Split(pathname)
  173. dir = filepath.Clean(dir)
  174. }
  175. return
  176. }
  177. func getIno(path string) (ino *inode, err error) {
  178. h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
  179. syscall.FILE_LIST_DIRECTORY,
  180. syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
  181. nil, syscall.OPEN_EXISTING,
  182. syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
  183. if e != nil {
  184. return nil, os.NewSyscallError("CreateFile", e)
  185. }
  186. var fi syscall.ByHandleFileInformation
  187. if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
  188. syscall.CloseHandle(h)
  189. return nil, os.NewSyscallError("GetFileInformationByHandle", e)
  190. }
  191. ino = &inode{
  192. handle: h,
  193. volume: fi.VolumeSerialNumber,
  194. index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
  195. }
  196. return ino, nil
  197. }
  198. // Must run within the I/O thread.
  199. func (m watchMap) get(ino *inode) *watch {
  200. if i := m[ino.volume]; i != nil {
  201. return i[ino.index]
  202. }
  203. return nil
  204. }
  205. // Must run within the I/O thread.
  206. func (m watchMap) set(ino *inode, watch *watch) {
  207. i := m[ino.volume]
  208. if i == nil {
  209. i = make(indexMap)
  210. m[ino.volume] = i
  211. }
  212. i[ino.index] = watch
  213. }
  214. // Must run within the I/O thread.
  215. func (w *Watcher) addWatch(pathname string, flags uint64) error {
  216. dir, err := getDir(pathname)
  217. if err != nil {
  218. return err
  219. }
  220. if flags&sysFSONLYDIR != 0 && pathname != dir {
  221. return nil
  222. }
  223. ino, err := getIno(dir)
  224. if err != nil {
  225. return err
  226. }
  227. w.mu.Lock()
  228. watchEntry := w.watches.get(ino)
  229. w.mu.Unlock()
  230. if watchEntry == nil {
  231. if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
  232. syscall.CloseHandle(ino.handle)
  233. return os.NewSyscallError("CreateIoCompletionPort", e)
  234. }
  235. watchEntry = &watch{
  236. ino: ino,
  237. path: dir,
  238. names: make(map[string]uint64),
  239. }
  240. w.mu.Lock()
  241. w.watches.set(ino, watchEntry)
  242. w.mu.Unlock()
  243. flags |= provisional
  244. } else {
  245. syscall.CloseHandle(ino.handle)
  246. }
  247. if pathname == dir {
  248. watchEntry.mask |= flags
  249. } else {
  250. watchEntry.names[filepath.Base(pathname)] |= flags
  251. }
  252. if err = w.startRead(watchEntry); err != nil {
  253. return err
  254. }
  255. if pathname == dir {
  256. watchEntry.mask &= ^provisional
  257. } else {
  258. watchEntry.names[filepath.Base(pathname)] &= ^provisional
  259. }
  260. return nil
  261. }
  262. // Must run within the I/O thread.
  263. func (w *Watcher) remWatch(pathname string) error {
  264. dir, err := getDir(pathname)
  265. if err != nil {
  266. return err
  267. }
  268. ino, err := getIno(dir)
  269. if err != nil {
  270. return err
  271. }
  272. w.mu.Lock()
  273. watch := w.watches.get(ino)
  274. w.mu.Unlock()
  275. if watch == nil {
  276. return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
  277. }
  278. if pathname == dir {
  279. w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
  280. watch.mask = 0
  281. } else {
  282. name := filepath.Base(pathname)
  283. w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED)
  284. delete(watch.names, name)
  285. }
  286. return w.startRead(watch)
  287. }
  288. // Must run within the I/O thread.
  289. func (w *Watcher) deleteWatch(watch *watch) {
  290. for name, mask := range watch.names {
  291. if mask&provisional == 0 {
  292. w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
  293. }
  294. delete(watch.names, name)
  295. }
  296. if watch.mask != 0 {
  297. if watch.mask&provisional == 0 {
  298. w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
  299. }
  300. watch.mask = 0
  301. }
  302. }
  303. // Must run within the I/O thread.
  304. func (w *Watcher) startRead(watch *watch) error {
  305. if e := syscall.CancelIo(watch.ino.handle); e != nil {
  306. w.Errors <- os.NewSyscallError("CancelIo", e)
  307. w.deleteWatch(watch)
  308. }
  309. mask := toWindowsFlags(watch.mask)
  310. for _, m := range watch.names {
  311. mask |= toWindowsFlags(m)
  312. }
  313. if mask == 0 {
  314. if e := syscall.CloseHandle(watch.ino.handle); e != nil {
  315. w.Errors <- os.NewSyscallError("CloseHandle", e)
  316. }
  317. w.mu.Lock()
  318. delete(w.watches[watch.ino.volume], watch.ino.index)
  319. w.mu.Unlock()
  320. return nil
  321. }
  322. e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
  323. uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
  324. if e != nil {
  325. err := os.NewSyscallError("ReadDirectoryChanges", e)
  326. if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
  327. // Watched directory was probably removed
  328. if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) {
  329. if watch.mask&sysFSONESHOT != 0 {
  330. watch.mask = 0
  331. }
  332. }
  333. err = nil
  334. }
  335. w.deleteWatch(watch)
  336. w.startRead(watch)
  337. return err
  338. }
  339. return nil
  340. }
  341. // readEvents reads from the I/O completion port, converts the
  342. // received events into Event objects and sends them via the Events channel.
  343. // Entry point to the I/O thread.
  344. func (w *Watcher) readEvents() {
  345. var (
  346. n, key uint32
  347. ov *syscall.Overlapped
  348. )
  349. runtime.LockOSThread()
  350. for {
  351. e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
  352. watch := (*watch)(unsafe.Pointer(ov))
  353. if watch == nil {
  354. select {
  355. case ch := <-w.quit:
  356. w.mu.Lock()
  357. var indexes []indexMap
  358. for _, index := range w.watches {
  359. indexes = append(indexes, index)
  360. }
  361. w.mu.Unlock()
  362. for _, index := range indexes {
  363. for _, watch := range index {
  364. w.deleteWatch(watch)
  365. w.startRead(watch)
  366. }
  367. }
  368. var err error
  369. if e := syscall.CloseHandle(w.port); e != nil {
  370. err = os.NewSyscallError("CloseHandle", e)
  371. }
  372. close(w.Events)
  373. close(w.Errors)
  374. ch <- err
  375. return
  376. case in := <-w.input:
  377. switch in.op {
  378. case opAddWatch:
  379. in.reply <- w.addWatch(in.path, uint64(in.flags))
  380. case opRemoveWatch:
  381. in.reply <- w.remWatch(in.path)
  382. }
  383. default:
  384. }
  385. continue
  386. }
  387. switch e {
  388. case syscall.ERROR_MORE_DATA:
  389. if watch == nil {
  390. w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")
  391. } else {
  392. // The i/o succeeded but the buffer is full.
  393. // In theory we should be building up a full packet.
  394. // In practice we can get away with just carrying on.
  395. n = uint32(unsafe.Sizeof(watch.buf))
  396. }
  397. case syscall.ERROR_ACCESS_DENIED:
  398. // Watched directory was probably removed
  399. w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
  400. w.deleteWatch(watch)
  401. w.startRead(watch)
  402. continue
  403. case syscall.ERROR_OPERATION_ABORTED:
  404. // CancelIo was called on this handle
  405. continue
  406. default:
  407. w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e)
  408. continue
  409. case nil:
  410. }
  411. var offset uint32
  412. for {
  413. if n == 0 {
  414. w.Events <- newEvent("", sysFSQOVERFLOW)
  415. w.Errors <- errors.New("short read in readEvents()")
  416. break
  417. }
  418. // Point "raw" to the event in the buffer
  419. raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
  420. buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
  421. name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
  422. fullname := filepath.Join(watch.path, name)
  423. var mask uint64
  424. switch raw.Action {
  425. case syscall.FILE_ACTION_REMOVED:
  426. mask = sysFSDELETESELF
  427. case syscall.FILE_ACTION_MODIFIED:
  428. mask = sysFSMODIFY
  429. case syscall.FILE_ACTION_RENAMED_OLD_NAME:
  430. watch.rename = name
  431. case syscall.FILE_ACTION_RENAMED_NEW_NAME:
  432. if watch.names[watch.rename] != 0 {
  433. watch.names[name] |= watch.names[watch.rename]
  434. delete(watch.names, watch.rename)
  435. mask = sysFSMOVESELF
  436. }
  437. }
  438. sendNameEvent := func() {
  439. if w.sendEvent(fullname, watch.names[name]&mask) {
  440. if watch.names[name]&sysFSONESHOT != 0 {
  441. delete(watch.names, name)
  442. }
  443. }
  444. }
  445. if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
  446. sendNameEvent()
  447. }
  448. if raw.Action == syscall.FILE_ACTION_REMOVED {
  449. w.sendEvent(fullname, watch.names[name]&sysFSIGNORED)
  450. delete(watch.names, name)
  451. }
  452. if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
  453. if watch.mask&sysFSONESHOT != 0 {
  454. watch.mask = 0
  455. }
  456. }
  457. if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
  458. fullname = filepath.Join(watch.path, watch.rename)
  459. sendNameEvent()
  460. }
  461. // Move to the next event in the buffer
  462. if raw.NextEntryOffset == 0 {
  463. break
  464. }
  465. offset += raw.NextEntryOffset
  466. // Error!
  467. if offset >= n {
  468. w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.")
  469. break
  470. }
  471. }
  472. if err := w.startRead(watch); err != nil {
  473. w.Errors <- err
  474. }
  475. }
  476. }
  477. func (w *Watcher) sendEvent(name string, mask uint64) bool {
  478. if mask == 0 {
  479. return false
  480. }
  481. event := newEvent(name, uint32(mask))
  482. select {
  483. case ch := <-w.quit:
  484. w.quit <- ch
  485. case w.Events <- event:
  486. }
  487. return true
  488. }
  489. func toWindowsFlags(mask uint64) uint32 {
  490. var m uint32
  491. if mask&sysFSACCESS != 0 {
  492. m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
  493. }
  494. if mask&sysFSMODIFY != 0 {
  495. m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
  496. }
  497. if mask&sysFSATTRIB != 0 {
  498. m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
  499. }
  500. if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
  501. m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
  502. }
  503. return m
  504. }
  505. func toFSnotifyFlags(action uint32) uint64 {
  506. switch action {
  507. case syscall.FILE_ACTION_ADDED:
  508. return sysFSCREATE
  509. case syscall.FILE_ACTION_REMOVED:
  510. return sysFSDELETE
  511. case syscall.FILE_ACTION_MODIFIED:
  512. return sysFSMODIFY
  513. case syscall.FILE_ACTION_RENAMED_OLD_NAME:
  514. return sysFSMOVEDFROM
  515. case syscall.FILE_ACTION_RENAMED_NEW_NAME:
  516. return sysFSMOVEDTO
  517. }
  518. return 0
  519. }