Comments (22)
I just updated the package with a fix. Please see if it works for you.
from atom-youcompleteme.
closer, but in handler.reset on Mac at least, the process parameter is a promise object, not a bufferedprocess object...
reset = ->
realReset = (process) ->
alert ycmdProcess
process?.kill?()
ycmdProcess = null
port = null
hmacSecret = null
Promise.resolve ycmdProcess
.then realReset, realReset
from atom-youcompleteme.
so the call the kill?() is never executed and the ycmd process remains... this doesn't seem to happen to me on Linux... Atom, 1.7.3 on both...
from atom-youcompleteme.
If I do this on Mac OSX
reset = ->
realReset = (process) ->
alert process
process?.kill?()
ycmdProcess = null
port = null
hmacSecret = null
Promise.resolve ycmdProcess
.then realReset, realReset
Just add an 'alert process' into reset, it Alert's [Object object] and ycmd process always is killed on Atom exit. If I remove the alert, the ycmd process always, 100%, remains... weird
from atom-youcompleteme.
ok, actually happens to me on linux as well. It seems if I re-open and it restores a previously open C++ file on startup, the ycmd process remains. If I close all tabs, then restart atom. I see no ycmd process. If I open the C++, ycmd comes online. Quiting Atom at this point kills the ycmd process.
If I now reopen Atom again, it will restore that window with the C++ file open on startup, ycmd server will start. Then I exit Atom and ycmd remains...
Something to do with how the process is initialized if Atom was a C++ file loaded on startup?
from atom-youcompleteme.
the problem is with handler.prepare() and handler.reset()
prepare() is called as part of the chain to handler.request(), but handler.reset() is also called in the event observer
observeConfig = ->
atom.config.observe 'you-complete-me', (value) ->
handler.reset()
When Atom is restoring C++ files on startup, there is a race and prepare() is called first. While waiting for the setTimeout to trigger, the observeConfig event is fired, calling reset(). This obviously causes issue.
As a test, I just commented out the handler.reset() in the observeConfig event and both platforms start and stop ycmd correctly, 100% of the time. Of course, what is lost is the ability to reset the server if the config changes...
from atom-youcompleteme.
Doing this seems to be working...
diff -Naur atom-youcompleteme/lib/handler.coffee /home/jroback/.atom/packages/you-complete-me/lib/handler.coffee
--- atom-youcompleteme/lib/handler.coffee 2016-05-18 15:15:50.540490063 -0600
+++ /home/jroback/.atom/packages/you-complete-me/lib/handler.coffee 2016-05-18 15:15:11.413401716 -0600
@@ -83,16 +83,14 @@
.then startServer
prepare = ->
- ycmdProcess ?= launch -> ycmdProcess = null
+ if not ycmdProcess?
+ launch(-> reset).then (process) -> ycmdProcess ?= process
reset = ->
- realReset = (process) ->
- process?.kill?()
- ycmdProcess = null
- port = null
- hmacSecret = null
- Promise.resolve ycmdProcess
- .then realReset, realReset
+ ycmdProcess?.kill()
+ ycmdProcess = null
+ port = null
+ hmacSecret = null
request = (method, endpoint, parameters = null) -> prepare().then ->
generateHmac = (data, encoding) ->
from atom-youcompleteme.
ok if its not obvious, that doesn't work either. the real problem is the async'ness of the setTimeout in startServer. seems like you either want a singleton like pattern with the handler, or some indication that start is pending, queue promises to fulfill once the startServer promise is fulfilled from setTimeout...
even setTimeout() should probably loop using the /healthy endpoint of ycmd.
from atom-youcompleteme.
Here is the solution I came up with. Flag to indicate there is a pending launch. If pending, put all requests (deferred promises) into an array that will resolved as part of the final fulfillment of the launch.
This situation happens when Atom is launched and is restoring a window with multiple C++ files open. It will attempt to "launch" as many ycmd processes as it can within the window of "setTimeout".. The following patch should prevent that.
diff --git a/lib/handler.coffee b/lib/handler.coffee
index 1b7f5bc..2ccff27 100644
--- a/lib/handler.coffee
+++ b/lib/handler.coffee
@@ -11,6 +11,8 @@ url = require 'url'
utility = require './utility'
ycmdProcess = null
+ycmdProcessPending = false
+ycmdProcessPendingPromises = []
port = null
hmacSecret = null
@@ -57,7 +59,7 @@ launch = (exit) ->
reject error
startServer = (optionsFile) -> new Promise (fulfill, reject) ->
- process = new BufferedProcess
+ ycmdProcess = new BufferedProcess
command: atom.config.get 'you-complete-me.pythonExecutable'
args: [
path.resolve atom.config.get('you-complete-me.ycmdPath'), 'ycmd'
@@ -76,23 +78,36 @@ launch = (exit) ->
when 5 then reject new Error 'YCM core library compiled for Python 3 but loaded in Python 2. Set the Python Executable config to a Python 3 interpreter path.'
when 6 then reject new Error 'YCM core library compiled for Python 2 but loaded in Python 3. Set the Python Executable config to a Python 2 interpreter path.'
when 7 then reject new Error 'YCM core library too old; PLEASE RECOMPILE by running the install.py script. See the documentation for more details.'
- setTimeout (-> fulfill process), 1000
+
+ setTimeout (->
+ ycmdProcessPending = false
+ pendingPromise.resolve() for pendingPromise in ycmdProcessPendingPromises
+ ycmdProcessPendingPromises = []
+ fulfill()), 1000
Promise.all [findUnusedPort, generateRandomSecret, readDefaultOptions]
.then processData
.then startServer
prepare = ->
- ycmdProcess ?= launch -> ycmdProcess = null
+ if ycmdProcess is null and not ycmdProcessPending
+ ycmdProcessPending = true
+ launch reset
+ else if ycmdProcessPending
+ pendingPromise = Promise.defer()
+ ycmdProcessPendingPromises.push pendingPromise
+ pendingPromise.promise
+ else
+ Promise.resolve()
reset = ->
- realReset = (process) ->
- process?.kill?()
- ycmdProcess = null
- port = null
- hmacSecret = null
- Promise.resolve ycmdProcess
- .then realReset, realReset
+ ycmdProcessPending = false
+ pendingPromise.reject new Error 'YCM reset.' for pendingPromise in ycmdProcessPendingPromises
+ ycmdProcessPendingPromises = []
+ ycmdProcess?.kill()
+ ycmdProcess = null
+ port = null
+ hmacSecret = null
request = (method, endpoint, parameters = null) -> prepare().then ->
generateHmac = (data, encoding) ->
from atom-youcompleteme.
what problem are you trying to fix?
currently the promise from launch() is reused and all pending requests should be handled correctly.
from atom-youcompleteme.
atom/atom#7252
atom/atom#8294
Found two interesting issues.
from atom-youcompleteme.
if i open atom and it restores multiple C++ files, multiple ycmdProcess attempt to launch, best case as it is without my changes, are multiple ycmd exist where only the last launch one is used. also exiting Atom, ycmd doesn't exit without my changes.
from atom-youcompleteme.
with my last patch, I've used Atom all day today at work with multiple C++ projects, one really large one, and not a single problem. ycmd correctly stops and starts without any ECONNREFUSED errors, etc, on startup. But if you have a large C++ project and open Atom with say 3-10 tabs open, Atom YouCompleteMe currently blows up pretty hard on both OSX and Linux.
from atom-youcompleteme.
atom/atom#7252 is not interesting here, because there are no child processes. The python process launched is the only process involved. The problems come from the Atom YouCompleteMe code, when launch is called multiple times at startup, the ycmdProcess variable gets in a corrupt state, so when handler.reset() is called in deactivate(), its pointing to a Promise (sometimes), instead of a BufferedProcess, so the call to process?.kill?() does nothing because kill() isn't a valid Promise method.. nor should it be.. and the ycmd process is not terminated on deactivate.
from atom-youcompleteme.
and these are all race conditions that are increased because launch uses setTimeout.. which should be obvious why, since many launch attempts can happen in the 1 second setTimeout is waiting for.
from atom-youcompleteme.
My changes will add a "pending" state, and queue up all prepare() calls while waiting for launch to finish, then resolve all promises when setTimeout in launch fires. From that point on, ycmdProcess != null and ycmdProcessPending == false, so prepare() just falls through with Promise.resolve()
from atom-youcompleteme.
And as an example on a large project, multiple ycmds hanging around is not good..
$ ps ax | grep ycmd
24129 ? Ssl 0:12 /usr/bin/python /home/jroback/Downloads/ycmd/ycmd --port=45008 --options_file=/tmp/AtomYcmOptions-1463688989385 --idle_suicide_seconds=600
$ cat /proc/24129/status | grep '^VmRSS'
VmRSS: 341060 kB
That one instance of ycmd uses 341MiB of resident memory. If you get 3,4,5 of them because Atom restored a bunch of files, you have to wait 10 minutes for suicide to get 1+ GiB of memory back...
from atom-youcompleteme.
Also,
prepare = ->
ycmdProcess ?= launch -> ycmdProcess = null
If multiple prepare()'s are called while the first launch is in setTimeout(), they all don't use the same launch promise. ie. its not reused. I am unsure of your statement there really.
Since ycmdProcess is not set until setTimeout() invokes fulfill, they all launch ycmd processes separately.
from atom-youcompleteme.
you can see it compiles to
prepare = function() {
return ycmdProcess != null ? ycmdProcess : ycmdProcess = launch(function() {
return ycmdProcess = null;
});
};
from atom-youcompleteme.
ok
prepare = function() {
return ycmdProcess != null ? ycmdProcess : ycmdProcess = launch(function() {
return ycmdProcess = null;
});
};
I still think that is wrong. as the when multiple prepare()s are called, ycmdProcess will be null until the first launch() returns (after setTimeout). launch() will be called multiple times. its 100% reproducible for me. All I have to do is re-open a project with multiple CPP files open and it will launch multiple YCMD processes...
from atom-youcompleteme.
startServer
is actually called only once here. I have no idea what's going wrong.
from atom-youcompleteme.
I would agree. Sorry about that. I think I may have been looking at the git repo in another directory, while running the older version in Atom, thinking I was running the latest version. This seems to work now, even with larger C++ files and projects.
from atom-youcompleteme.
Related Issues (20)
- how to get ctags completion?
- BufferedProcess will fail with 'NOENT' if core.projectHome directory doesn't exist.
- Uncaught Error: spawn EACCES HOT 6
- Failure to generate an Hmac HOT 6
- go-to-definition error HOT 1
- Support tilde in `ycmdPath`
- Failed to spawn command HOT 1
- YCM error when in Python 3 file HOT 2
- nested struct complete
- Errors still displayed after cause of error fixed
- Only allow the .ycm_extra_conf.py to be loaded once
- Error when writing. PLEASE HELP!
- [YCM] TypeError HOT 3
- Typescript doesn't seem to be working on OSX 10.12.6, Atom 1.21.0 HOT 1
- How can I use it in Windows10?
- global extra config did not work
- Server message filter
- Failed to activate the you-complete-me package HOT 2
- YouCompleteMe not working HOT 1
- [YCM] IOError, but YCMD worked in other editors HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from atom-youcompleteme.