Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[READY] Properly shut down on Windows #282

Merged
merged 5 commits into from Jul 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions .coveragerc
@@ -1,4 +1,14 @@
[report]
omit =
*/third_party/requests/*
*/tests/*
*/__init__.py

[run]
parallel = True
source =
ycmd

[paths]
source =
ycmd/
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -31,6 +31,7 @@ pip-log.txt

# Unit test / coverage reports
.coverage
.coverage.*
cover/
.tox
nosetests.xml
Expand Down
5 changes: 5 additions & 0 deletions ci/travis/travis_install.sh
Expand Up @@ -57,6 +57,11 @@ pip install -U pip wheel setuptools
pip install -r test_requirements.txt
npm install -g typescript

# Enable coverage for Python subprocesses. See:
# http://coverage.readthedocs.org/en/coverage-4.0.3/subprocess.html
echo -e "import coverage\ncoverage.process_startup()" > \
${PYENV_ROOT}/versions/${PYENV_VERSION}/lib/python${YCMD_PYTHON_VERSION}/site-packages/sitecustomize.py

############
# rust setup
############
Expand Down
4 changes: 2 additions & 2 deletions examples/example_client.py
Expand Up @@ -113,10 +113,10 @@ def IsReady( self, include_subservers = False ):

def Shutdown( self ):
if self.IsAlive():
self._popen_handle.terminate()
self.PostToHandlerAndLog( 'shutdown' )


def PostToHandlerAndLog( self, handler, data ):
def PostToHandlerAndLog( self, handler, data = None ):
self._CallHttpie( 'post', handler, data )


Expand Down
4 changes: 3 additions & 1 deletion run_tests.py
Expand Up @@ -192,7 +192,9 @@ def NoseTests( parsed_args, extra_nosetests_args ):
nosetests_args.extend( COMPLETERS[ key ][ 'test' ] )

if parsed_args.coverage:
nosetests_args += [ '--with-coverage', '--cover-package=ycmd',
nosetests_args += [ '--with-coverage',
'--cover-erase',
'--cover-package=ycmd',
'--cover-html' ]

if extra_nosetests_args:
Expand Down
1 change: 1 addition & 0 deletions test_requirements.txt
Expand Up @@ -6,6 +6,7 @@ WebTest>=2.0.20
ordereddict>=1.1
nose-exclude>=0.4.1
unittest2>=1.1.0
psutil>=3.3.0
# This needs to be kept in sync with submodule checkout in third_party
future==0.15.2
codecov>=2.0.5
26 changes: 15 additions & 11 deletions ycmd/__main__.py
Expand Up @@ -36,16 +36,14 @@
import logging
import json
import argparse
import waitress
import signal
import os
import base64
from ycmd import user_options_store
from ycmd import extra_conf_store
from ycmd import utils
from ycmd.watchdog_plugin import WatchdogPlugin

from ycmd import extra_conf_store, user_options_store, utils
from ycmd.hmac_plugin import HmacPlugin
from ycmd.utils import ToBytes, ReadFile, OpenForStdHandle
from ycmd.wsgi_server import StoppableWSGIServer


def YcmCoreSanityCheck():
Expand Down Expand Up @@ -104,6 +102,9 @@ def ParseArguments():
'[debug|info|warning|error|critical]' )
parser.add_argument( '--idle_suicide_seconds', type = int, default = 0,
help = 'num idle seconds before server shuts down')
parser.add_argument( '--check_interval_seconds', type = int, default = 600,
help = 'interval in seconds to check server '
'inactivity' )
parser.add_argument( '--options_file', type = str, required = True,
help = 'file with user options, in JSON format' )
parser.add_argument( '--stdout', type = str, default = None,
Expand Down Expand Up @@ -163,20 +164,23 @@ def Main():

PossiblyDetachFromTerminal()

# This can't be a top-level import because it transitively imports
# These can't be top-level imports because they transitively import
# ycm_core which we want to be imported ONLY after extra conf
# preload has executed.
from ycmd import handlers
from ycmd.watchdog_plugin import WatchdogPlugin
handlers.UpdateUserOptions( options )
handlers.SetHmacSecret( hmac_secret )
SetUpSignalHandler( args.stdout, args.stderr, args.keep_logfiles )
handlers.app.install( WatchdogPlugin( args.idle_suicide_seconds ) )
handlers.app.install( WatchdogPlugin( args.idle_suicide_seconds,
args.check_interval_seconds ) )
handlers.app.install( HmacPlugin( hmac_secret ) )
CloseStdin()
waitress.serve( handlers.app,
host = args.host,
port = args.port,
threads = 30 )
handlers.wsgi_server = StoppableWSGIServer( handlers.app,
host = args.host,
port = args.port,
threads = 30 )
handlers.wsgi_server.Run()


if __name__ == "__main__":
Expand Down
25 changes: 23 additions & 2 deletions ycmd/handlers.py
Expand Up @@ -29,6 +29,7 @@
import logging
import traceback
from bottle import request
from threading import Thread

import ycm_core
from ycmd import extra_conf_store, hmac_plugin, server_state, user_options_store
Expand All @@ -46,6 +47,7 @@
_hmac_secret = bytes()
_logger = logging.getLogger( __name__ )
app = bottle.Bottle()
wsgi_server = None


@app.post( '/event_notification' )
Expand Down Expand Up @@ -223,6 +225,14 @@ def DebugInfo():
return _JsonResponse( '\n'.join( output ) )


@app.post( '/shutdown' )
def Shutdown():
_logger.info( 'Received shutdown request' )
ServerShutdown()

return _JsonResponse( True )


# The type of the param is Bottle.HTTPError
def ErrorHandler( httperror ):
body = _JsonResponse( BuildExceptionResponse( httperror.exception,
Expand Down Expand Up @@ -259,9 +269,20 @@ def _GetCompleterForRequestData( request_data ):
return _server_state.GetFiletypeCompleter( [ completer_target ] )


@atexit.register
def ServerShutdown():
_logger.info( 'Server shutting down' )
def Terminator():
if wsgi_server:
wsgi_server.Shutdown()

# Use a separate thread to let the server send the response before shutting
# down.
terminator = Thread( target = Terminator )
terminator.daemon = True
terminator.start()


@atexit.register
def ServerCleanup():
if _server_state:
_server_state.Shutdown()
extra_conf_store.Shutdown()
Expand Down