From f51a6872972f3a17b01628205cc5701c9a849a29 Mon Sep 17 00:00:00 2001 From: Strahinja Val Markovic Date: Mon, 23 Sep 2013 13:27:32 -0700 Subject: [PATCH] Server now shuts down cleanly on VimClose --- plugin/youcompleteme.vim | 6 ++++++ python/ycm/server/server.py | 26 +++++++++++++++++++++++--- python/ycm/utils.py | 16 ++++++++++++++++ python/ycm/youcompleteme.py | 33 +++++++++++++++++++++++---------- 4 files changed, 68 insertions(+), 13 deletions(-) diff --git a/plugin/youcompleteme.vim b/plugin/youcompleteme.vim index 020db8b2..939d2dea 100644 --- a/plugin/youcompleteme.vim +++ b/plugin/youcompleteme.vim @@ -160,6 +160,12 @@ let g:ycm_auto_stop_csharp_server = let g:ycm_csharp_server_port = \ get( g:, 'ycm_csharp_server_port', 2000 ) +let g:ycm_server_use_vim_stdout = + \ get( g:, 'ycm_server_use_vim_stdout', 0 ) + +let g:ycm_server_log_level = + \ get( g:, 'ycm_server_log_level', 'info' ) + " On-demand loading. Let's use the autoload folder and not slow down vim's " startup procedure. augroup youcompletemeStart diff --git a/python/ycm/server/server.py b/python/ycm/server/server.py index a9289427..5c6cfc8e 100755 --- a/python/ycm/server/server.py +++ b/python/ycm/server/server.py @@ -19,6 +19,7 @@ import sys import os +import threading # We want to have the YouCompleteMe/python directory on the Python PATH because # all the code already assumes that it's there. This is a relic from before the @@ -38,6 +39,7 @@ from bottle import run, request, response import server_state from ycm import extra_conf_store from ycm import user_options_store +from ycm import utils import argparse # num bytes for the request body buffer; request.json only works if the request @@ -66,10 +68,15 @@ def EventNotification(): getattr( SERVER_STATE.GetFiletypeCompleter( filetypes ), event_handler )( request_data ) - if hasattr( extra_conf_store, event_handler ): - getattr( extra_conf_store, event_handler )( request_data ) + try: + if hasattr( extra_conf_store, event_handler ): + getattr( extra_conf_store, event_handler )( request_data ) + except OSError as e: + LOGGER.exception( e ) + + if event_name == 'VimLeave': + _ScheduleServerShutdown() - # TODO: shut down the server on VimClose @app.post( '/run_completer_command' ) @@ -140,6 +147,19 @@ def _JsonResponse( data ): return json.dumps( data ) +def _ScheduleServerShutdown(): + # The reason why we want to schedule a shutdown in the near future instead of + # just shutting down right now is because we want the current request (the one + # that made us want to shutdown) to complete successfully first. + + def Shutdown(): + # sys.exit() doesn't work because we're not in the main thread. + utils.TerminateProcess( os.getpid() ) + + killer_thread = threading.Timer( 2, Shutdown ) + killer_thread.start() + + def Main(): global LOGGER parser = argparse.ArgumentParser() diff --git a/python/ycm/utils.py b/python/ycm/utils.py index d66a4fef..9f1ae775 100644 --- a/python/ycm/utils.py +++ b/python/ycm/utils.py @@ -19,6 +19,8 @@ import tempfile import os +import sys +import signal def IsIdentifierChar( char ): return char.isalnum() or char == '_' @@ -36,3 +38,17 @@ def ToUtf8IfNeeded( string_or_unicode ): def PathToTempDir(): return os.path.join( tempfile.gettempdir(), 'ycm_temp' ) + + +# From here: http://stackoverflow.com/a/8536476/1672783 +def TerminateProcess( pid ): + if sys.platform == 'win32': + import ctypes + PROCESS_TERMINATE = 1 + handle = ctypes.windll.kernel32.OpenProcess( PROCESS_TERMINATE, + False, + pid ) + ctypes.windll.kernel32.TerminateProcess( handle, -1 ) + ctypes.windll.kernel32.CloseHandle( handle ) + else: + os.kill( pid, signal.SIGTERM ) diff --git a/python/ycm/youcompleteme.py b/python/ycm/youcompleteme.py index b0b95773..c8b574c2 100644 --- a/python/ycm/youcompleteme.py +++ b/python/ycm/youcompleteme.py @@ -37,26 +37,39 @@ class YouCompleteMe( object ): self._user_options = user_options self._omnicomp = OmniCompleter( user_options ) self._current_completion_request = None + self._server_stdout = None + self._server_stderr = None + self._SetupServer() + + def _SetupServer( self ): server_port = SERVER_PORT_RANGE_START + os.getpid() command = ''.join( [ 'python ', _PathToServerScript(), ' --port=', - str( server_port ) ] ) + str( server_port ), + ' --log=', + self._user_options[ 'server_log_level' ] ] ) BaseRequest.server_location = 'http://localhost:' + str( server_port ) - filename_format = os.path.join( utils.PathToTempDir(), - 'server_{port}_{std}.log' ) + if self._user_options[ 'server_use_vim_stdout' ]: + subprocess.Popen( command, shell = True ) + else: + filename_format = os.path.join( utils.PathToTempDir(), + 'server_{port}_{std}.log' ) - self._server_stdout = filename_format.format( port=server_port, - std='stdout' ) - self._server_stderr = filename_format.format( port=server_port, - std='stderr' ) + self._server_stdout = filename_format.format( port = server_port, + std = 'stdout' ) + self._server_stderr = filename_format.format( port = server_port, + std = 'stderr' ) - with open( self._server_stderr, 'w' ) as fstderr: - with open( self._server_stdout, 'w' ) as fstdout: - subprocess.Popen( command, stdout=fstdout, stderr=fstderr, shell=True ) + with open( self._server_stderr, 'w' ) as fstderr: + with open( self._server_stdout, 'w' ) as fstdout: + subprocess.Popen( command, + stdout = fstdout, + stderr = fstderr, + shell = True ) def CreateCompletionRequest( self ):