Skip to content

Commit faee359

Browse files
committed
refactor(langserver): move core.utils.threading to core.concurrent and rename some function
1 parent a2bddf9 commit faee359

33 files changed

+105
-70
lines changed

packages/core/src/robotcode/core/async_tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
cast,
3232
)
3333

34+
from .concurrent import is_threaded_callable
3435
from .utils.inspect import ensure_coroutine
35-
from .utils.threading import is_threaded_callable
3636

3737
_T = TypeVar("_T")
3838

packages/core/src/robotcode/core/utils/threading.py renamed to packages/core/src/robotcode/core/concurrent.py

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,33 @@
11
import inspect
22
from concurrent.futures import CancelledError, Future
3-
from threading import Event, Thread, current_thread, local
4-
from typing import Any, Callable, Dict, Generic, Optional, Tuple, TypeVar, cast, overload
3+
from threading import Event, RLock, Thread, current_thread, local
4+
from typing import Any, Callable, Dict, Generic, List, Optional, Tuple, TypeVar, cast, overload
55

66
_F = TypeVar("_F", bound=Callable[..., Any])
77
_TResult = TypeVar("_TResult")
88

9-
__THREADED_MARKER = "__threaded__"
9+
__THREADED_MARKER = "__robotcode_threaded"
1010

1111

1212
class FutureEx(Future, Generic[_TResult]): # type: ignore[type-arg]
1313
def __init__(self) -> None:
1414
super().__init__()
15-
self.cancelation_requested = Event()
15+
self.cancelation_requested_event = Event()
16+
17+
@property
18+
def cancelation_requested(self) -> bool:
19+
return self.cancelation_requested_event.is_set()
1620

1721
def cancel(self) -> bool:
18-
self.cancelation_requested.set()
22+
self.cancelation_requested_event.set()
1923
return super().cancel()
2024

2125
def result(self, timeout: Optional[float] = None) -> _TResult:
2226
return cast(_TResult, super().result(timeout))
2327

28+
def add_done_callback(self, fn: Callable[["FutureEx[Any]"], Any]) -> None:
29+
super().add_done_callback(fn) # type: ignore[arg-type]
30+
2431

2532
@overload
2633
def threaded(__func: _F) -> _F:
@@ -63,44 +70,72 @@ def __init__(self) -> None:
6370
def _run_callable_in_thread_handler(
6471
future: FutureEx[_TResult], callable: Callable[..., _TResult], args: Tuple[Any, ...], kwargs: Dict[str, Any]
6572
) -> None:
73+
if not future.set_running_or_notify_cancel():
74+
return
75+
6676
_local_storage._local_future = future
67-
future.set_running_or_notify_cancel()
77+
6878
try:
6979
result = callable(*args, **kwargs)
7080
except Exception as e:
7181
# TODO: add traceback to exception e.traceback = format_exc()
72-
if not future.cancelled():
73-
future.set_exception(e)
82+
future.set_exception(e)
7483
else:
75-
if not future.cancelled():
76-
future.set_result(result)
84+
future.set_result(result)
7785
finally:
7886
_local_storage._local_future = None
7987

8088

81-
def is_thread_cancelled() -> bool:
89+
def is_current_thread_cancelled() -> bool:
8290
local_future = _local_storage._local_future
83-
return local_future is not None and local_future.cancelation_requested.is_set()
91+
return local_future is not None and local_future.cancelation_requested
8492

8593

86-
def check_thread_canceled(at_least_seconds: Optional[float] = None) -> None:
94+
def check_current_thread_canceled(at_least_seconds: Optional[float] = None) -> None:
8795
local_future = _local_storage._local_future
8896
if local_future is None:
8997
return
9098

9199
if at_least_seconds is None or at_least_seconds <= 0:
92-
if not local_future.cancelation_requested.is_set():
100+
if not local_future.cancelation_requested:
93101
return
94-
elif not local_future.cancelation_requested.wait(at_least_seconds):
102+
elif not local_future.cancelation_requested_event.wait(at_least_seconds):
95103
return
96104

97105
name = current_thread().name
98106
raise CancelledError(f"Thread {name+' ' if name else ' '}cancelled")
99107

100108

101-
def run_callable_in_thread(callable: Callable[..., _TResult], *args: Any, **kwargs: Any) -> FutureEx[_TResult]:
109+
_running_callables_lock = RLock()
110+
_running_callables: Dict[FutureEx[Any], Thread] = {}
111+
112+
113+
def _remove_future_from_running_callables(future: FutureEx[Any]) -> None:
114+
with _running_callables_lock:
115+
_running_callables.pop(future, None)
116+
117+
118+
def run_in_thread(callable: Callable[..., _TResult], *args: Any, **kwargs: Any) -> FutureEx[_TResult]:
102119
future: FutureEx[_TResult] = FutureEx()
120+
with _running_callables_lock:
121+
thread = Thread(
122+
target=_run_callable_in_thread_handler, args=(future, callable, args, kwargs), name=str(callable)
123+
)
124+
_running_callables[future] = thread
125+
future.add_done_callback(_remove_future_from_running_callables)
103126

104-
Thread(target=_run_callable_in_thread_handler, args=(future, callable, args, kwargs), name=str(callable)).start()
127+
thread.start()
105128

106129
return future
130+
131+
132+
def cancel_running_callables(timeout: Optional[float] = None) -> None:
133+
threads: List[Thread] = []
134+
with _running_callables_lock:
135+
for future, thread in _running_callables.items():
136+
if not future.cancelation_requested:
137+
future.cancel()
138+
threads.append(thread)
139+
for thread in threads:
140+
if thread is not current_thread():
141+
thread.join(timeout=timeout)

packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@
3838
create_sub_task,
3939
run_coroutine_in_thread,
4040
)
41+
from robotcode.core.concurrent import is_threaded_callable, run_in_thread
4142
from robotcode.core.utils.dataclasses import as_json, from_dict
4243
from robotcode.core.utils.inspect import ensure_coroutine, iter_methods
4344
from robotcode.core.utils.logging import LoggingDescriptor
44-
from robotcode.core.utils.threading import is_threaded_callable, run_callable_in_thread
4545

4646
__all__ = [
4747
"JsonRPCErrors",
@@ -742,7 +742,7 @@ async def handle_request(self, message: JsonRPCRequest) -> None:
742742
ensure_coroutine(cast(Callable[..., Any], e.method)), *params[0], **params[1]
743743
)
744744
else:
745-
task = asyncio.wrap_future(run_callable_in_thread(e.method, *params[0], **params[1]))
745+
task = asyncio.wrap_future(run_in_thread(e.method, *params[0], **params[1]))
746746
else:
747747
task = create_sub_task(
748748
ensure_coroutine(e.method)(*params[0], **params[1]),

packages/language_server/src/robotcode/language_server/common/parts/code_action.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from itertools import chain
33
from typing import TYPE_CHECKING, Any, Final, List, Optional, Union, cast
44

5+
from robotcode.core.concurrent import check_current_thread_canceled, threaded
56
from robotcode.core.event import event
67
from robotcode.core.lsp.types import (
78
CodeAction,
@@ -14,7 +15,6 @@
1415
TextDocumentIdentifier,
1516
)
1617
from robotcode.core.utils.logging import LoggingDescriptor
17-
from robotcode.core.utils.threading import check_thread_canceled, threaded
1818
from robotcode.jsonrpc2.protocol import rpc_method
1919
from robotcode.language_server.common.decorators import CODE_ACTION_KINDS_ATTR, HasCodeActionKinds, language_id_filter
2020
from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart
@@ -82,7 +82,7 @@ def _text_document_code_action(
8282
for result in self.collect(
8383
self, document, document.range_from_utf16(range), context, callback_filter=language_id_filter(document)
8484
):
85-
check_thread_canceled()
85+
check_current_thread_canceled()
8686

8787
if isinstance(result, BaseException):
8888
if not isinstance(result, CancelledError):
@@ -107,7 +107,7 @@ def _text_document_code_action_resolve(
107107
results: List[CodeAction] = []
108108

109109
for result in self.resolve(self, params):
110-
check_thread_canceled()
110+
check_current_thread_canceled()
111111

112112
if isinstance(result, BaseException):
113113
if not isinstance(result, CancelledError):

packages/language_server/src/robotcode/language_server/common/parts/code_lens.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import TYPE_CHECKING, Any, Final, List, Optional
66

77
from robotcode.core.async_tools import async_tasking_event, create_sub_task
8+
from robotcode.core.concurrent import threaded
89
from robotcode.core.lsp.types import (
910
CodeLens,
1011
CodeLensOptions,
@@ -13,7 +14,6 @@
1314
TextDocumentIdentifier,
1415
)
1516
from robotcode.core.utils.logging import LoggingDescriptor
16-
from robotcode.core.utils.threading import threaded
1717
from robotcode.jsonrpc2.protocol import rpc_method
1818
from robotcode.language_server.common.decorators import language_id_filter
1919
from robotcode.language_server.common.text_document import TextDocument

packages/language_server/src/robotcode/language_server/common/parts/commands.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from dataclasses import dataclass
77
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Final, List, Optional, cast
88

9+
from robotcode.core.concurrent import threaded
910
from robotcode.core.lsp.types import (
1011
ErrorCodes,
1112
ExecuteCommandOptions,
@@ -15,7 +16,6 @@
1516
)
1617
from robotcode.core.utils.dataclasses import from_dict
1718
from robotcode.core.utils.logging import LoggingDescriptor
18-
from robotcode.core.utils.threading import threaded
1919
from robotcode.jsonrpc2.protocol import JsonRPCErrorException, rpc_method
2020
from robotcode.language_server.common.decorators import get_command_id, is_command
2121
from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart

packages/language_server/src/robotcode/language_server/common/parts/completion.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from itertools import chain
33
from typing import TYPE_CHECKING, Any, Final, List, Optional, Union, cast
44

5+
from robotcode.core.concurrent import check_current_thread_canceled, threaded
56
from robotcode.core.event import event
67
from robotcode.core.lsp.types import (
78
CompletionContext,
@@ -18,7 +19,6 @@
1819
TextEdit,
1920
)
2021
from robotcode.core.utils.logging import LoggingDescriptor
21-
from robotcode.core.utils.threading import check_thread_canceled, threaded
2222
from robotcode.jsonrpc2.protocol import rpc_method
2323
from robotcode.language_server.common.decorators import (
2424
ALL_COMMIT_CHARACTERS_ATTR,
@@ -92,7 +92,7 @@ def _text_document_completion(
9292
results: List[Union[List[CompletionItem], CompletionList]] = []
9393

9494
if context is not None and context.trigger_kind == CompletionTriggerKind.TRIGGER_CHARACTER:
95-
check_thread_canceled(0.25)
95+
check_current_thread_canceled(0.25)
9696

9797
document = self.parent.documents.get(text_document.uri)
9898
if document is None:
@@ -101,7 +101,7 @@ def _text_document_completion(
101101
p = document.position_from_utf16(position)
102102

103103
for result in self.collect(self, document, p, context, callback_filter=language_id_filter(document)):
104-
check_thread_canceled()
104+
check_current_thread_canceled()
105105

106106
if isinstance(result, BaseException):
107107
if not isinstance(result, CancelledError):
@@ -114,7 +114,7 @@ def _text_document_completion(
114114
return None
115115

116116
for result in results:
117-
check_thread_canceled()
117+
check_current_thread_canceled()
118118

119119
if isinstance(result, CompletionList):
120120
for item in result.items:

packages/language_server/src/robotcode/language_server/common/parts/declaration.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from asyncio import CancelledError
44
from typing import TYPE_CHECKING, Any, Final, List, Optional, Union
55

6+
from robotcode.core.concurrent import check_current_thread_canceled, threaded
67
from robotcode.core.event import event
78
from robotcode.core.lsp.types import (
89
DeclarationParams,
@@ -13,7 +14,6 @@
1314
TextDocumentIdentifier,
1415
)
1516
from robotcode.core.utils.logging import LoggingDescriptor
16-
from robotcode.core.utils.threading import check_thread_canceled, threaded
1717
from robotcode.jsonrpc2.protocol import rpc_method
1818
from robotcode.language_server.common.decorators import language_id_filter
1919
from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart
@@ -64,7 +64,7 @@ def _text_document_declaration(
6464
return None
6565

6666
for result in self.collect(self, document, position, callback_filter=language_id_filter(document)):
67-
check_thread_canceled()
67+
check_current_thread_canceled()
6868

6969
if isinstance(result, BaseException):
7070
if not isinstance(result, CancelledError):

packages/language_server/src/robotcode/language_server/common/parts/definition.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from concurrent.futures import CancelledError
44
from typing import TYPE_CHECKING, Any, Final, List, Optional, Union
55

6+
from robotcode.core.concurrent import check_current_thread_canceled, threaded
67
from robotcode.core.event import event
78
from robotcode.core.lsp.types import (
89
DefinitionParams,
@@ -13,7 +14,6 @@
1314
TextDocumentIdentifier,
1415
)
1516
from robotcode.core.utils.logging import LoggingDescriptor
16-
from robotcode.core.utils.threading import check_thread_canceled, threaded
1717
from robotcode.jsonrpc2.protocol import rpc_method
1818
from robotcode.language_server.common.decorators import language_id_filter
1919
from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart
@@ -62,7 +62,7 @@ def _text_document_definition(
6262
for result in self.collect(
6363
self, document, document.position_from_utf16(position), callback_filter=language_id_filter(document)
6464
):
65-
check_thread_canceled()
65+
check_current_thread_canceled()
6666

6767
if isinstance(result, BaseException):
6868
if not isinstance(result, CancelledError):

packages/language_server/src/robotcode/language_server/common/parts/diagnostics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
check_canceled,
1818
create_sub_task,
1919
)
20+
from robotcode.core.concurrent import threaded
2021
from robotcode.core.lsp.types import (
2122
Diagnostic,
2223
DiagnosticOptions,
@@ -35,7 +36,6 @@
3536
)
3637
from robotcode.core.uri import Uri
3738
from robotcode.core.utils.logging import LoggingDescriptor
38-
from robotcode.core.utils.threading import threaded
3939
from robotcode.jsonrpc2.protocol import JsonRPCErrorException, rpc_method
4040
from robotcode.language_server.common.decorators import language_id_filter
4141
from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart

0 commit comments

Comments
 (0)