Skip to content

Commit 8660396

Browse files
committed
Fix file descriptors (sockets) leak due to not closed urlopen requests
1 parent 131b182 commit 8660396

File tree

5 files changed

+49
-1
lines changed

5 files changed

+49
-1
lines changed

autoload/youcompleteme.vim

+2-1
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,7 @@ function! s:AbortAutohoverRequest() abort
773773
let request = requests[ request_id ]
774774
if request.origin == 'autohover'
775775
call remove( s:pollers.command.requests, request_id )
776+
py3 ycm_state.CancelCommandRequest( int( vim.eval( "request_id" ) ) )
776777
call request.callback( '' )
777778
endif
778779
endfor
@@ -1551,7 +1552,7 @@ function! s:PollCommands( timer_id ) abort
15511552

15521553
" This request is done
15531554
call remove( s:pollers.command.requests, request_id )
1554-
py3 ycm_state.FlushCommandRequest( vim.eval( "request_id" ) )
1555+
py3 ycm_state.FlushCommandRequest( int( vim.eval( "request_id" ) ) )
15551556
call request[ 'callback' ]( result )
15561557
endfor
15571558

python/ycm/client/base_request.py

+25
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,21 @@ def PostDataToHandlerAsync( data, handler, timeout = _READ_TIMEOUT_SEC ):
145145
return BaseRequest._TalkToHandlerAsync( data, handler, 'POST', timeout )
146146

147147

148+
# This method allows to cancel an async request and close the
149+
# underlying request socket without waiting for a response;
150+
# the method uses internal limbo thread pool where
151+
# request objects are unwrapped from futures and immediately closed.
152+
@staticmethod
153+
def ConsumeResponseInLimboAsync( future ):
154+
def _Consume( future ):
155+
try:
156+
future.result().close()
157+
except HTTPError as response:
158+
response.close()
159+
160+
BaseRequest.LimboExecutor().submit( _Consume, future )
161+
162+
148163
# This returns a future! Use HandleFuture to get the value.
149164
# |method| is either 'POST' or 'GET'.
150165
# |timeout| is num seconds to tolerate no response from server before giving
@@ -213,6 +228,16 @@ def Executor( cls ):
213228
return cls.executor
214229

215230

231+
@classmethod
232+
def LimboExecutor( cls ):
233+
try:
234+
return cls.limbo_executor
235+
except AttributeError:
236+
from ycm.unsafe_thread_pool_executor import UnsafeThreadPoolExecutor
237+
cls.limbo_executor = UnsafeThreadPoolExecutor( max_workers = 1 )
238+
return cls.limbo_executor
239+
240+
216241
server_location = ''
217242
hmac_secret = ''
218243

python/ycm/client/command_request.py

+6
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ def Response( self ):
7878
return self._response
7979

8080

81+
def Detach( self ):
82+
if self._response is None and self._response_future is not None:
83+
self.ConsumeResponseInLimboAsync( self._response_future )
84+
self._response_future = None
85+
86+
8187
def RunPostCommandActionsIfNeeded( self,
8288
modifiers,
8389
buffer_command = DEFAULT_BUFFER_COMMAND ):

python/ycm/client/event_notification.py

+7
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,15 @@ def Response( self ):
5555
return self._cached_response if self._cached_response else []
5656

5757

58+
def Detach( self ):
59+
if self._cached_response is None and self._response_future is not None:
60+
self.ConsumeResponseInLimboAsync( self._response_future )
61+
self._response_future = None
62+
63+
5864
def SendEventNotificationAsync( event_name,
5965
buffer_number = None,
6066
extra_data = None ):
6167
event = EventNotification( event_name, buffer_number, extra_data )
6268
event.Start()
69+
event.Detach()

python/ycm/youcompleteme.py

+9
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,15 @@ def GetCommandRequest( self, request_id ):
509509
return self._command_requests.get( request_id )
510510

511511

512+
def CancelCommandRequest( self, request_id ):
513+
request = self._command_requests.pop( request_id, None )
514+
if request is not None:
515+
# As a request can't be canceled, we simply detach it now,
516+
# it will be consumed in the background and
517+
# immediately closed without response parsing.
518+
request.Detach()
519+
520+
512521
def FlushCommandRequest( self, request_id ):
513522
self._command_requests.pop( request_id, None )
514523

0 commit comments

Comments
 (0)