Skip to content

Commit

Permalink
fsevents: improve documentation (#52)
Browse files Browse the repository at this point in the history
This PR adds comprehensive doc comments to all exported
constants, mostly derived from the Apple documentation
but with changes to make them directly relevant to this package's API.

It also uses C constants directly rather than relying on an implicit
association between `1<<iota` and the actual enum values.

It also unexports `GetStreamRefPaths`, `GetStreamRefDescription`
and `GetStreamRefDeviceID`  because none of them are usable
with this package's API because there is no way to get an
`FSEventStreamRef` value because it's hidden inside an unexported
field inside the `EventStream` struct.

Also unexport the `CFRunLoopRef` because it's similarly
not useful or usable.

Other than the above unexports, the API remains identical.

Unfortunately I am unable to get the tests to pass, which
makes me think that something is wrong somewhere, but
that applies to the original code too, not just the code with
these changes applied.
  • Loading branch information
rogpeppe authored Oct 14, 2022
1 parent 66dfb65 commit 0bd000f
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 94 deletions.
133 changes: 61 additions & 72 deletions fsevents.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,74 +10,29 @@ import (
"time"
)

// CreateFlags for creating a New stream.
type CreateFlags uint32

// kFSEventStreamCreateFlag...
const (
// use CoreFoundation types instead of raw C types (disabled)
useCFTypes CreateFlags = 1 << iota

// NoDefer sends events on the leading edge (for interactive applications).
// By default events are delivered after latency seconds (for background tasks).
NoDefer

// WatchRoot for a change to occur to a directory along the path being watched.
WatchRoot

// IgnoreSelf doesn't send events triggered by the current process (macOS 10.6+).
IgnoreSelf

// FileEvents sends events about individual files, generating significantly
// more events (macOS 10.7+) than directory level notifications.
FileEvents
)

// EventFlags passed to the FSEventStreamCallback function.
type EventFlags uint32

// kFSEventStreamEventFlag...
const (
// MustScanSubDirs indicates that events were coalesced hierarchically.
MustScanSubDirs EventFlags = 1 << iota
// UserDropped or KernelDropped is set alongside MustScanSubDirs
// to help diagnose the problem.
UserDropped
KernelDropped

// EventIDsWrapped indicates the 64-bit event ID counter wrapped around.
EventIDsWrapped

// HistoryDone is a sentinel event when retrieving events sinceWhen.
HistoryDone

// RootChanged indicates a change to a directory along the path being watched.
RootChanged

// Mount for a volume mounted underneath the path being monitored.
Mount
// Unmount event occurs after a volume is unmounted.
Unmount

// The following flags are only set when using FileEvents.

ItemCreated
ItemRemoved
ItemInodeMetaMod
ItemRenamed
ItemModified
ItemFinderInfoMod
ItemChangeOwner
ItemXattrMod
ItemIsFile
ItemIsDir
ItemIsSymlink
)

// Event represents a single file system notification.
type Event struct {
// Path holds the path to the item that's changed, relative
// to its device's root.
// Use DeviceForPath to determine the absolute path that's
// being referred to.
Path string

// Flags holds details what has happened.
Flags EventFlags

// ID holds the event ID.
//
// Each event ID comes from the most recent event being reported
// in the corresponding directory named in the EventStream.Paths field
// Event IDs all come from a single global source.
// They are guaranteed to always be increasing, usually in leaps
// and bounds, even across system reboots and moving drives from
// one machine to another. If you were to
// stop processing events from this stream after this event
// and resume processing them later from a newly-created
// EventStream, this is the value you would pass for the
// EventStream.EventID along with Resume=true.
ID uint64
}

Expand All @@ -99,19 +54,49 @@ func DeviceForPath(path string) (int32, error) {
// es.Stop()
// ...
type EventStream struct {
stream FSEventStreamRef
rlref CFRunLoopRef
stream fsEventStreamRef
rlref cfRunLoopRef
hasFinalizer bool
registryID uintptr
uuid string

// Events holds the channel on which events will be sent.
// It's initialized by EventStream.Start if nil.
Events chan []Event

// Paths holds the set of paths to watch, each
// specifying the root of a filesystem hierarchy to be
// watched for modifications.
Paths []string

// Flags specifies what events to receive on the stream.
Flags CreateFlags
EventID uint64

// Resume specifies that watching should resume from the event
// specified by EventID.
Resume bool

// EventID holds the most recent event ID.
//
// NOTE: this is updated asynchronously by the
// watcher and should not be accessed while
// the stream has been started.
EventID uint64

// Latency holds the number of seconds the service should wait after hearing
// about an event from the kernel before passing it along to the
// client via its callback. Specifying a larger value may result
// in more effective temporal coalescing, resulting in fewer
// callbacks and greater overall efficiency.
Latency time.Duration
// syscall represents this with an int32

// When Device is non-zero, the watcher will watch events on the
// device with this ID, and the paths in the Paths field are
// interpreted relative to the device's root.
//
// The device ID is the same as the st_dev field from a stat
// structure of a file on that device or the f_fsid[0] field of
// a statfs structure.
Device int32
}

Expand Down Expand Up @@ -149,7 +134,8 @@ func (r *eventStreamRegistry) Delete(i uintptr) {
delete(r.m, i)
}

// Start listening to an event stream.
// Start listening to an event stream. This creates es.Events if it's not already
// a valid channel.
func (es *EventStream) Start() {
if es.Events == nil {
es.Events = make(chan []Event)
Expand All @@ -163,12 +149,14 @@ func (es *EventStream) Start() {
es.start(es.Paths, cbInfo)
}

// Flush events that have occurred but haven't been delivered.
// Flush flushes events that have occurred but haven't been delivered.
// If sync is true, it will block until all the events have been delivered,
// otherwise it will return immediately.
func (es *EventStream) Flush(sync bool) {
flush(es.stream, sync)
}

// Stop listening to the event stream.
// Stop stops listening to the event stream.
func (es *EventStream) Stop() {
if es.stream != nil {
stop(es.stream, es.rlref)
Expand All @@ -180,7 +168,8 @@ func (es *EventStream) Stop() {
es.registryID = 0
}

// Restart listening.
// Restart restarts the event listener. This
// can be used to change the current watch flags.
func (es *EventStream) Restart() {
es.Stop()
es.Resume = true
Expand Down
7 changes: 5 additions & 2 deletions fsevents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ func TestBasicExample(t *testing.T) {
if err != nil {
t.Fatal(err)
}

<-wait
select{
case <-wait:
case <-time.After(5 * time.Second):
t.Fatal("timed out waiting for event")
}
}
Loading

0 comments on commit 0bd000f

Please sign in to comment.