Skip to content

Commit

Permalink
Merge 7a5d627 into 89a6afb
Browse files Browse the repository at this point in the history
  • Loading branch information
vheon committed Jun 10, 2016
2 parents 89a6afb + 7a5d627 commit 552ab1f
Show file tree
Hide file tree
Showing 14 changed files with 291 additions and 219 deletions.
19 changes: 15 additions & 4 deletions ycmd/completers/completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ class Completer( with_metaclass( abc.ABCMeta, object ) ):
custom cleanup logic on server shutdown.
If your completer uses an external server process, then it can be useful to
implement the ServerIsReady member function to handle the /ready request. This
is very useful for the test suite."""
implement the ServerIsHealthy member function to handle the /healthy request.
This is very useful for the test suite."""

def __init__( self, user_options ):
self.user_options = user_options
Expand Down Expand Up @@ -245,7 +245,14 @@ def ComputeCandidatesInner( self, request_data ):


def DefinedSubcommands( self ):
return sorted( self.GetSubcommandsMap().keys() )
subcommands = sorted( self.GetSubcommandsMap().keys() )
try:
# We don't want expose this subcommand because it is not really needed
# for the user but it is useful in tests for tearing down the server
subcommands.remove( 'StopServer' )
except ValueError:
pass
return subcommands


def GetSubcommandsMap( self ):
Expand Down Expand Up @@ -365,7 +372,11 @@ def Shutdown( self ):


def ServerIsReady( self ):
"""Called by the /ready handler to check if the underlying completion
return self.ServerIsHealthy()


def ServerIsHealthy( self ):
"""Called by the /healthy handler to check if the underlying completion
server is started and ready to receive requests. Returns bool."""
return True

Expand Down
8 changes: 2 additions & 6 deletions ycmd/completers/cs/cs_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,6 @@ def FilterAndSortCandidates( self, candidates, query ):

def GetSubcommandsMap( self ):
return {
'StartServer' : ( lambda self, request_data, args:
self._SolutionSubcommand( request_data,
method = '_StartServer',
no_request_data = True ) ),
'StopServer' : ( lambda self, request_data, args:
self._SolutionSubcommand( request_data,
method = '_StopServer',
Expand Down Expand Up @@ -418,9 +414,9 @@ def _CleanupAfterServerStop( self ):
self._omnisharp_phandle = None
if ( not self._keep_logfiles ):
if self._filename_stdout:
os.unlink( self._filename_stdout )
utils.RemoveIfExists( self._filename_stdout )
if self._filename_stderr:
os.unlink( self._filename_stderr )
utils.RemoveIfExists( self._filename_stderr )


def _RestartServer( self ):
Expand Down
8 changes: 0 additions & 8 deletions ycmd/completers/go/go_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,6 @@ def ComputeCandidatesInner( self, request_data ):
return [ _ConvertCompletionData( x ) for x in resultdata[ 1 ] ]


def DefinedSubcommands( self ):
# We don't want to expose this sub-command because it is not really needed
# for the user but it is useful in tests for tearing down the server.
subcommands = super( GoCompleter, self ).DefinedSubcommands()
subcommands.remove( 'StopServer' )
return subcommands


def GetSubcommandsMap( self ):
return {
'StopServer' : ( lambda self, request_data, args:
Expand Down
236 changes: 113 additions & 123 deletions ycmd/completers/javascript/tern_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,15 @@ def __init__( self, user_options ):
self._server_keep_logfiles = user_options[ 'server_keep_logfiles' ]

# Used to ensure that starting/stopping of the server is synchronised
self._server_state_mutex = threading.Lock()
self._server_state_mutex = threading.RLock()

self._do_tern_project_check = False

with self._server_state_mutex:
self._server_stdout = None
self._server_stderr = None
self._Reset()
self._StartServerNoLock()
self._StartServer()


def _WarnIfMissingTernProject( self ):
Expand Down Expand Up @@ -209,8 +209,8 @@ def OnFileReadyToParse( self, request_data ):

def GetSubcommandsMap( self ):
return {
'StartServer': ( lambda self, request_data, args:
self._StartServer() ),
'RestartServer': ( lambda self, request_data, args:
self._RestartServer() ),
'StopServer': ( lambda self, request_data, args:
self._StopServer() ),
'GoToDefinition': ( lambda self, request_data, args:
Expand All @@ -233,36 +233,37 @@ def SupportedFiletypes( self ):


def DebugInfo( self, request_data ):
if self._server_handle is None:
# server is not running because we haven't tried to start it.
return ' * Tern server is not running'

if not self._ServerIsRunning():
# The handle is set, but the process isn't running. This means either it
# crashed or we failed to start it.
return ( ' * Tern server is not running (crashed)'
+ '\n * Server stdout: '
+ self._server_stdout
+ '\n * Server stderr: '
+ self._server_stderr )

# Server is up and running.
return ( ' * Tern server is running on port: '
+ str( self._server_port )
+ ' with PID: '
+ str( self._server_handle.pid )
+ '\n * Server stdout: '
+ self._server_stdout
+ '\n * Server stderr: '
+ self._server_stderr )
with self._server_state_mutex:
if self._server_handle is None:
# server is not running because we haven't tried to start it.
return ' * Tern server is not running'

if not self._ServerIsRunning():
# The handle is set, but the process isn't running. This means either it
# crashed or we failed to start it.
return ( ' * Tern server is not running (crashed)'
+ '\n * Server stdout: '
+ self._server_stdout
+ '\n * Server stderr: '
+ self._server_stderr )

# Server is up and running.
return ( ' * Tern server is running on port: '
+ str( self._server_port )
+ ' with PID: '
+ str( self._server_handle.pid )
+ '\n * Server stdout: '
+ self._server_stdout
+ '\n * Server stderr: '
+ self._server_stderr )


def Shutdown( self ):
_logger.debug( "Shutting down Tern server" )
self._StopServer()


def ServerIsReady( self, request_data = {} ):
def ServerIsHealthy( self, request_data = {} ):
if not self._ServerIsRunning():
return False

Expand All @@ -275,18 +276,17 @@ def ServerIsReady( self, request_data = {} ):


def _Reset( self ):
"""Callers must hold self._server_state_mutex"""

if not self._server_keep_logfiles:
if self._server_stdout:
utils.RemoveIfExists( self._server_stdout )
if self._server_stderr:
utils.RemoveIfExists( self._server_stderr )

self._server_handle = None
self._server_port = 0
self._server_stdout = None
self._server_stderr = None
with self._server_state_mutex:
if not self._server_keep_logfiles:
if self._server_stdout:
utils.RemoveIfExists( self._server_stdout )
if self._server_stderr:
utils.RemoveIfExists( self._server_stderr )

self._server_handle = None
self._server_port = 0
self._server_stdout = None
self._server_stderr = None


def _PostRequest( self, request, request_data ):
Expand Down Expand Up @@ -358,102 +358,92 @@ def MakeTernLocation( request_data ):


def _StartServer( self ):
if not self._ServerIsRunning():
with self._server_state_mutex:
self._StartServerNoLock()


def _StartServerNoLock( self ):
"""Start the server, under the lock.
Callers must hold self._server_state_mutex"""

if self._ServerIsRunning():
return

_logger.info( 'Starting Tern.js server...' )

self._server_port = utils.GetUnusedLocalhostPort()

if _logger.isEnabledFor( logging.DEBUG ):
extra_args = [ '--verbose' ]
else:
extra_args = []
with self._server_state_mutex:
if self._ServerIsRunning():
return

command = [ PATH_TO_NODE,
PATH_TO_TERNJS_BINARY,
'--port',
str( self._server_port ),
'--host',
SERVER_HOST,
'--persistent',
'--no-port-file' ] + extra_args
_logger.info( 'Starting Tern.js server...' )

_logger.debug( 'Starting tern with the following command: '
+ ' '.join( command ) )
self._server_port = utils.GetUnusedLocalhostPort()

try:
logfile_format = os.path.join( utils.PathToCreatedTempDir(),
u'tern_{port}_{std}.log' )

self._server_stdout = logfile_format.format(
port = self._server_port,
std = 'stdout' )

self._server_stderr = logfile_format.format(
port = self._server_port,
std = 'stderr' )

# We need to open a pipe to stdin or the Tern server is killed.
# See https://github.com/ternjs/tern/issues/740#issuecomment-203979749
# For unknown reasons, this is only needed on Windows and for Python 3.4+
# on other platforms.
with utils.OpenForStdHandle( self._server_stdout ) as stdout:
with utils.OpenForStdHandle( self._server_stderr ) as stderr:
self._server_handle = utils.SafePopen( command,
stdin = PIPE,
stdout = stdout,
stderr = stderr )
except Exception:
_logger.warning( 'Unable to start Tern.js server: '
+ traceback.format_exc() )
self._Reset()
if _logger.isEnabledFor( logging.DEBUG ):
extra_args = [ '--verbose' ]
else:
extra_args = []

command = [ PATH_TO_NODE,
PATH_TO_TERNJS_BINARY,
'--port',
str( self._server_port ),
'--host',
SERVER_HOST,
'--persistent',
'--no-port-file' ] + extra_args

_logger.debug( 'Starting tern with the following command: '
+ ' '.join( command ) )

try:
logfile_format = os.path.join( utils.PathToCreatedTempDir(),
u'tern_{port}_{std}.log' )

self._server_stdout = logfile_format.format(
port = self._server_port,
std = 'stdout' )

self._server_stderr = logfile_format.format(
port = self._server_port,
std = 'stderr' )

# We need to open a pipe to stdin or the Tern server is killed.
# See https://github.com/ternjs/tern/issues/740#issuecomment-203979749
# For unknown reasons, this is only needed on Windows and for Python
# 3.4+ on other platforms.
with utils.OpenForStdHandle( self._server_stdout ) as stdout:
with utils.OpenForStdHandle( self._server_stderr ) as stderr:
self._server_handle = utils.SafePopen( command,
stdin = PIPE,
stdout = stdout,
stderr = stderr )
except Exception:
_logger.warning( 'Unable to start Tern.js server: '
+ traceback.format_exc() )
self._Reset()

if self._server_port > 0 and self._ServerIsRunning():
_logger.info( 'Tern.js Server started with pid: ' +
str( self._server_handle.pid ) +
' listening on port ' +
str( self._server_port ) )
_logger.info( 'Tern.js Server log files are: ' +
self._server_stdout +
' and ' +
self._server_stderr )

self._do_tern_project_check = True
else:
_logger.warning( 'Tern.js server did not start successfully' )

if self._server_port > 0 and self._ServerIsRunning():
_logger.info( 'Tern.js Server started with pid: ' +
str( self._server_handle.pid ) +
' listening on port ' +
str( self._server_port ) )
_logger.info( 'Tern.js Server log files are: ' +
self._server_stdout +
' and ' +
self._server_stderr )

self._do_tern_project_check = True
else:
_logger.warning( 'Tern.js server did not start successfully' )
def _RestartServer( self ):
with self._server_state_mutex:
self._StopServer()
self._StartServer()


def _StopServer( self ):
with self._server_state_mutex:
self._StopServerNoLock()

if self._ServerIsRunning():
_logger.info( 'Stopping Tern.js server with PID '
+ str( self._server_handle.pid )
+ '...' )

def _StopServerNoLock( self ):
"""Stop the server, under the lock.
self._server_handle.terminate()
self._server_handle.wait()

Callers must hold self._server_state_mutex"""
if self._ServerIsRunning():
_logger.info( 'Stopping Tern.js server with PID '
+ str( self._server_handle.pid )
+ '...' )
_logger.info( 'Tern.js server terminated.' )

self._server_handle.terminate()
self._server_handle.wait()

_logger.info( 'Tern.js server terminated.' )

self._Reset()
self._Reset()


def _ServerIsRunning( self ):
Expand Down
Loading

0 comments on commit 552ab1f

Please sign in to comment.