Code Monkey home page Code Monkey logo

Comments (22)

Qusic avatar Qusic commented on September 20, 2024

I just updated the package with a fix. Please see if it works for you.

from atom-youcompleteme.

joeroback avatar joeroback commented on September 20, 2024

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

screen shot 2016-05-18 at 12 34 25 pm

from atom-youcompleteme.

joeroback avatar joeroback commented on September 20, 2024

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.

joeroback avatar joeroback commented on September 20, 2024

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.

joeroback avatar joeroback commented on September 20, 2024

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.

joeroback avatar joeroback commented on September 20, 2024

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.

joeroback avatar joeroback commented on September 20, 2024

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.

joeroback avatar joeroback commented on September 20, 2024

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.

joeroback avatar joeroback commented on September 20, 2024

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.

Qusic avatar Qusic commented on September 20, 2024

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.

Qusic avatar Qusic commented on September 20, 2024

atom/atom#7252
atom/atom#8294
Found two interesting issues.

from atom-youcompleteme.

joeroback avatar joeroback commented on September 20, 2024

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.

joeroback avatar joeroback commented on September 20, 2024

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.

joeroback avatar joeroback commented on September 20, 2024

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.

joeroback avatar joeroback commented on September 20, 2024

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.

joeroback avatar joeroback commented on September 20, 2024

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.

joeroback avatar joeroback commented on September 20, 2024

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.

joeroback avatar joeroback commented on September 20, 2024

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.

Qusic avatar Qusic commented on September 20, 2024

you can see it compiles to

prepare = function() {
  return ycmdProcess != null ? ycmdProcess : ycmdProcess = launch(function() {
    return ycmdProcess = null;
  });
};

from atom-youcompleteme.

joeroback avatar joeroback commented on September 20, 2024

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.

Qusic avatar Qusic commented on September 20, 2024

startServer is actually called only once here. I have no idea what's going wrong.

from atom-youcompleteme.

joeroback avatar joeroback commented on September 20, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.