Skip to content

Commit

Permalink
Merge pull request #866 from garden-io/fix-watch-segfault
Browse files Browse the repository at this point in the history
fix(core): chokidar watcher on mac could segfault after reloading config
  • Loading branch information
edvald authored Jun 23, 2019
2 parents 1c962f8 + b950823 commit 0502520
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 38 deletions.
7 changes: 3 additions & 4 deletions garden-service/src/garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export class Garden {
private modulesScanned: boolean
private readonly registeredPlugins: { [key: string]: PluginFactory }
private readonly taskGraph: TaskGraph
private readonly watcher: Watcher
private watcher: Watcher

public readonly configStore: ConfigStore
public readonly globalConfigStore: GlobalConfigStore
Expand Down Expand Up @@ -167,7 +167,6 @@ export class Garden {

this.taskGraph = new TaskGraph(this, this.log)
this.events = new EventBus(this.log)
this.watcher = new Watcher(this, this.log)

// Register plugins
for (const [name, pluginFactory] of Object.entries({ ...builtinPlugins, ...params.plugins })) {
Expand Down Expand Up @@ -234,7 +233,7 @@ export class Garden {
* Clean up before shutting down.
*/
async close() {
this.watcher.stop()
this.watcher && this.watcher.stop()
}

getPluginContext(providerName: string) {
Expand All @@ -255,7 +254,7 @@ export class Garden {
*/
async startWatcher(graph: ConfigGraph) {
const modules = await graph.getModules()
this.watcher.start(modules)
this.watcher = new Watcher(this, this.log, modules)
}

private registerPlugin(name: string, moduleOrFactory: RegisterPluginParam) {
Expand Down
2 changes: 1 addition & 1 deletion garden-service/src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export type Unpacked<T> =

const MAX_BUFFER_SIZE = 1024 * 1024

export function shutdown(code) {
export function shutdown(code?: number) {
// This is a good place to log exitHookNames if needed.
process.exit(code)
}
Expand Down
67 changes: 34 additions & 33 deletions garden-service/src/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,26 @@ import * as klaw from "klaw"
import { registerCleanupFunction } from "./util/util"
import * as Bluebird from "bluebird"
import { some } from "lodash"
import { isConfigFilename } from "./util/fs"
import { isConfigFilename, Ignorer } from "./util/fs"

// IMPORTANT: We must use a single global instance of the watcher, because we may otherwise get
// segmentation faults on macOS! See https://github.com/fsevents/fsevents/issues/273
let watcher: FSWatcher | undefined
let ignorer: Ignorer
let projectRoot: string

const ignored = (path: string, _: any) => {
const relpath = relative(projectRoot, path)
return relpath && ignorer.ignores(relpath)
}

// The process hangs after tests if we don't do this
registerCleanupFunction("stop watcher", () => {
if (watcher) {
watcher.close()
watcher = undefined
}
})

export type ChangeHandler = (module: Module | null, configChanged: boolean) => Promise<void>

Expand All @@ -27,52 +46,34 @@ export type ChangeHandler = (module: Module | null, configChanged: boolean) => P
export class Watcher {
private watcher: FSWatcher

constructor(private garden: Garden, private log: LogEntry) {
}

/**
* Starts the file watcher. Idempotent.
*
* @param modules All configured modules in the project.
*/
start(modules: Module[]) {
// Only run one watcher for the process
if (this.watcher) {
return
}

const projectRoot = this.garden.projectRoot
const ignorer = this.garden.ignorer
constructor(private garden: Garden, private log: LogEntry, modules: Module[]) {
projectRoot = this.garden.projectRoot
ignorer = this.garden.ignorer

this.log.debug(`Watcher: Watching ${projectRoot}`)

this.watcher = watch(projectRoot, {
ignored: (path: string, _: any) => {
const relpath = relative(projectRoot, path)
return relpath && ignorer.ignores(relpath)
},
ignoreInitial: true,
persistent: true,
})
if (watcher === undefined) {
watcher = watch(projectRoot, {
ignored,
ignoreInitial: true,
persistent: true,
})
}

this.watcher = watcher

this.watcher
.on("add", this.makeFileAddedHandler(modules))
.on("change", this.makeFileChangedHandler("modified", modules))
.on("unlink", this.makeFileChangedHandler("removed", modules))
.on("addDir", this.makeDirAddedHandler(modules))
.on("unlinkDir", this.makeDirRemovedHandler(modules))

registerCleanupFunction("clearFileWatches", () => {
this.stop()
})
}

stop(): void {
if (this.watcher) {
this.log.debug(`Watcher: Stopping`)

this.watcher.close()
delete this.watcher
this.log.debug(`Watcher: Clearing handlers`)
this.watcher.removeAllListeners()
}
}

Expand Down
1 change: 1 addition & 0 deletions garden-service/test/mocha.integ.opts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
--watch-extensions js,json,pegjs
--reporter spec
--timeout 0
--exit
build/test/setup.js
build/test/integ/src/**/*.js
1 change: 1 addition & 0 deletions garden-service/test/mocha.opts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
--watch-extensions js,json,pegjs
--reporter spec
--timeout 20000
--exit
build/test/setup.js
build/test/unit/**/*.js

0 comments on commit 0502520

Please sign in to comment.