fix(config): Apply config change from Atom, process locking

Summary:
d1c44dcb54

Also lock config across processes to prevent the user from being logged out when the main process reads config before it's finished writing. This is what is causing the login window to appear.

Test Plan: Rapidly tap between the two display modes. Note that it no longer reverts to the wrong one intermittently.

Reviewers: evan

Reviewed By: evan

Differential Revision: https://phab.nylas.com/D1931
This commit is contained in:
Ben Gotow 2015-08-25 16:00:09 -07:00
parent 30ba6e6023
commit bb887a4ae8
4 changed files with 66 additions and 9 deletions

View file

@ -486,6 +486,7 @@ describe "Config", ->
atom.config.set('foo.bar.baz', "value 1")
expect(observeHandler).toHaveBeenCalledWith("value 1")
advanceClock(100) # complete pending save that was requested in ::set
observeHandler.reset()
atom.config.loadUserConfig()
@ -772,6 +773,22 @@ describe "Config", ->
expect(warnSpy).toHaveBeenCalled()
expect(warnSpy.mostRecentCall.args[0]).toContain "foo.int"
describe "when there is a pending save", ->
it "does not change the config settings", ->
fs.writeFileSync atom.config.configFilePath, "'*': foo: bar: 'baz'"
atom.config.set("foo.bar", "quux")
atom.config.loadUserConfig()
expect(atom.config.get("foo.bar")).toBe "quux"
advanceClock(100)
expect(atom.config.save.callCount).toBe 1
expect(atom.config.get("foo.bar")).toBe "quux"
atom.config.loadUserConfig()
expect(atom.config.get("foo.bar")).toBe "baz"
describe ".observeUserConfig()", ->
updatedHandler = null

View file

@ -1,10 +1,10 @@
Config = require '../config'
AtomWindow = require './atom-window'
BrowserWindow = require 'browser-window'
WindowManager = require './window-manager'
ApplicationMenu = require './application-menu'
AutoUpdateManager = require './auto-update-manager'
NylasProtocolHandler = require './nylas-protocol-handler'
SharedFileManager = require './shared-file-manager'
_ = require 'underscore'
fs = require 'fs-plus'
@ -73,13 +73,16 @@ class Application
global.application = this
@sharedFileManager = new SharedFileManager()
@nylasProtocolHandler = new NylasProtocolHandler(@resourcePath, @safeMode)
Config = require '../config'
@config = new Config({configDirPath, @resourcePath})
@config.load()
@windowManager = new WindowManager({@resourcePath, @config, @devMode, @safeMode})
@autoUpdateManager = new AutoUpdateManager(@version, @config, @specMode)
@applicationMenu = new ApplicationMenu(@version)
@nylasProtocolHandler = new NylasProtocolHandler(@resourcePath, @safeMode)
@_databasePhase = 'setup'
@listenForArgumentsFromNewProcess()

View file

@ -0,0 +1,15 @@
class SharedFileManager
constructor: ->
@_inflight = {}
processWillWriteFile: (filePath) ->
@_inflight[filePath] += 1
processDidWriteFile: (filePath) ->
@_inflight[filePath] -= 1
processCanReadFile: (filePath) ->
!@_inflight[filePath] or @_inflight[filePath] is 0
module.exports = SharedFileManager

View file

@ -13,6 +13,11 @@ Color = require './color'
ScopedPropertyStore = require 'scoped-property-store'
ScopeDescriptor = require './scope-descriptor'
if global.application
app = global.application
else
app = require('remote').getGlobal('application')
# Essential: Used to access all of Atom's configuration details.
#
# An instance of this class is always available as the `atom.config` global.
@ -334,9 +339,17 @@ class Config
@configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson'])
@configFilePath ?= path.join(@configDirPath, 'config.cson')
@transactDepth = 0
@savePending = false
@debouncedSave = _.debounce(@save, 100)
@requestLoad = _.debounce(@loadUserConfig, 100)
@debouncedLoad = _.debounce(@loadUserConfig, 100)
@requestSave = =>
@savePending = true
debouncedSave.call(this)
save = =>
@savePending = false
@save()
debouncedSave = _.debounce(save, 100)
###
Section: Config Subscription
@ -607,7 +620,7 @@ class Config
else
@setRawValue(keyPath, value)
@debouncedSave() if source is @getUserConfigPath() and shouldSave and not @configFileHasErrors
@requestSave() if source is @getUserConfigPath() and shouldSave and not @configFileHasErrors
true
# Essential: Restore the setting at `keyPath` to its default value.
@ -637,7 +650,7 @@ class Config
_.setValueForKeyPath(settings, keyPath, undefined)
settings = withoutEmptyObjects(settings)
@set(null, settings, {scopeSelector, source, priority: @priorityForSource(source)}) if settings?
@debouncedSave()
@requestSave()
else
@scopedSettingsStore.removePropertiesForSourceAndSelector(source, scopeSelector)
@emitChangeEvent()
@ -816,11 +829,17 @@ class Config
fs.copySync(templateConfigDirPath, @configDirPath)
loadUserConfig: ->
manager = app.sharedFileManager
if not manager.processCanReadFile(@configFilePath)
@requestLoad()
return
unless fs.existsSync(@configFilePath)
fs.makeTreeSync(path.dirname(@configFilePath))
CSON.writeFileSync(@configFilePath, {})
try
unless @savePending
userConfig = CSON.readFileSync(@configFilePath)
@resetUserSettings(userConfig)
@configFileHasErrors = false
@ -840,7 +859,7 @@ class Config
observeUserConfig: ->
try
@watchSubscription ?= pathWatcher.watch @configFilePath, (eventType) =>
@debouncedLoad() if eventType is 'change' and @watchSubscription?
@requestLoad() if eventType is 'change' and @watchSubscription?
catch error
@notifyFailure """
Unable to watch path: `#{path.basename(@configFilePath)}`. Make sure you have permissions to
@ -857,9 +876,12 @@ class Config
console.log(errorMessage, detail)
save: ->
manager = app.sharedFileManager
manager.processWillWriteFile(@configFilePath)
allSettings = {'*': @settings}
allSettings = _.extend allSettings, @scopedSettingsStore.propertiesForSource(@getUserConfigPath())
CSON.writeFileSync(@configFilePath, allSettings)
manager.processDidWriteFile(@configFilePath)
###
Section: Private methods managing global settings