Skip to content

Commit 8103219

Browse files
committed
correct caching
1 parent b140122 commit 8103219

File tree

6 files changed

+91
-85
lines changed

6 files changed

+91
-85
lines changed

.vscode/launch.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@
5353
"justMyCode": false,
5454
"cwd": "${workspaceFolder}",
5555
},
56+
{
57+
"name": "Python: Pytest All Test Parallel",
58+
"type": "python",
59+
"request": "launch",
60+
"module": "pytest",
61+
"args": [
62+
"-n",
63+
"8",
64+
"."
65+
],
66+
"console": "integratedTerminal",
67+
"justMyCode": false,
68+
"cwd": "${workspaceFolder}",
69+
},
5670
{
5771
"name": "Python: Pytest All Test Coverage",
5872
"type": "python",
@@ -233,4 +247,4 @@
233247
"default": "5678",
234248
}
235249
]
236-
}
250+
}

robotcode/language_server/common/parts/window.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ async def progress(
117117
self.progress_begin(
118118
p.token,
119119
message,
120-
int(current * 100 / max) if percentage is None and current is not None and max is not None else percentage,
120+
int(current * 100 / max) if percentage is None and current is not None and max else percentage,
121121
cancellable,
122122
title,
123123
)

robotcode/language_server/common/text_document.py

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import collections
34
import inspect
45
import io
56
import weakref
@@ -19,9 +20,10 @@ class InvalidRangeError(Exception):
1920

2021

2122
class CacheEntry:
22-
def __init__(self, data: Any = None) -> None:
23-
self.data = data
24-
self.lock = Lock()
23+
def __init__(self) -> None:
24+
self.data: Any = None
25+
self.has_data: bool = False
26+
self.lock: Lock = Lock()
2527

2628

2729
class TextDocument:
@@ -46,7 +48,7 @@ def __init__(
4648
self._orig_text = text
4749
self._orig_version = version
4850
self._lines: Optional[List[str]] = None
49-
self._cache: Dict[weakref.ref[Any], CacheEntry] = {}
51+
self._cache: Dict[weakref.ref[Any], CacheEntry] = collections.defaultdict(CacheEntry)
5052
self._data: weakref.WeakKeyDictionary[Any, Any] = weakref.WeakKeyDictionary()
5153
self.opened_in_editor = False
5254

@@ -163,13 +165,11 @@ async def invalidate_data(self) -> None:
163165
self._invalidate_data()
164166

165167
async def __remove_cache_entry_safe(self, _ref: Any) -> None:
166-
if _ref in self._cache:
167-
async with self._lock:
168-
if _ref in self._cache:
169-
self._cache.pop(_ref)
168+
async with self._lock:
169+
if _ref in self._cache:
170+
self._cache.pop(_ref)
170171

171172
def __remove_cache_entry(self, ref: Any) -> None:
172-
173173
create_sub_task(self.__remove_cache_entry_safe(ref))
174174

175175
def __get_cache_reference(self, entry: Callable[..., Any], /, *, add_remove: bool = True) -> weakref.ref[Any]:
@@ -189,23 +189,16 @@ async def get_cache(
189189
) -> _T:
190190

191191
reference = self.__get_cache_reference(entry)
192-
e = self._cache.get(reference, None)
193-
194-
if e is None:
195-
async with self._lock:
196-
e = self._cache.get(reference, None)
197-
if e is None:
198192

199-
e = CacheEntry()
200-
201-
self._cache[reference] = e
193+
async with self._lock:
194+
e = self._cache[reference]
202195

203-
if e.data is None:
204-
async with e.lock:
205-
if e.data is None:
206-
e.data = await entry(self, *args, **kwargs)
196+
async with e.lock:
197+
if not e.has_data:
198+
e.data = await entry(self, *args, **kwargs)
199+
e.has_data = True
207200

208-
return cast("_T", e.data)
201+
return cast(_T, e.data)
209202

210203
@_logger.call
211204
async def remove_cache_entry(

robotcode/language_server/robotframework/parts/documents_cache.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -199,37 +199,41 @@ def get_tokens(_source: str, _data_only: bool = False) -> Generator[Token, None,
199199

200200
async def get_general_model(self, document: TextDocument, data_only: bool = True) -> ast.AST:
201201
if data_only:
202-
return await document.get_cache(self.__get_general_model_data_only)
203-
return await document.get_cache(self.__get_general_model)
202+
return await document.get_cache(
203+
self.__get_general_model_data_only, await self.get_general_tokens(document, True)
204+
)
205+
return await document.get_cache(self.__get_general_model, await self.get_general_tokens(document))
204206

205-
async def __get_general_model_data_only(self, document: TextDocument) -> ast.AST:
206-
return self.__get_model(document, await self.get_general_tokens(document, True), DocumentType.GENERAL)
207+
async def __get_general_model_data_only(self, document: TextDocument, tokens: Iterable[Any]) -> ast.AST:
208+
return self.__get_model(document, tokens, DocumentType.GENERAL)
207209

208-
async def __get_general_model(self, document: TextDocument) -> ast.AST:
209-
return self.__get_model(document, await self.get_general_tokens(document), DocumentType.GENERAL)
210+
async def __get_general_model(self, document: TextDocument, tokens: Iterable[Any]) -> ast.AST:
211+
return self.__get_model(document, tokens, DocumentType.GENERAL)
210212

211213
async def get_resource_model(self, document: TextDocument, data_only: bool = True) -> ast.AST:
212214
if data_only:
213-
return await document.get_cache(self.__get_resource_model_data_only)
215+
return await document.get_cache(
216+
self.__get_resource_model_data_only, await self.get_resource_tokens(document, True)
217+
)
214218

215-
return await document.get_cache(self.__get_resource_model)
219+
return await document.get_cache(self.__get_resource_model, await self.get_resource_tokens(document))
216220

217-
async def __get_resource_model_data_only(self, document: TextDocument) -> ast.AST:
218-
return self.__get_model(document, await self.get_resource_tokens(document, True), DocumentType.RESOURCE)
221+
async def __get_resource_model_data_only(self, document: TextDocument, tokens: Iterable[Any]) -> ast.AST:
222+
return self.__get_model(document, tokens, DocumentType.RESOURCE)
219223

220-
async def __get_resource_model(self, document: TextDocument) -> ast.AST:
221-
return self.__get_model(document, await self.get_resource_tokens(document), DocumentType.RESOURCE)
224+
async def __get_resource_model(self, document: TextDocument, tokens: Iterable[Any]) -> ast.AST:
225+
return self.__get_model(document, tokens, DocumentType.RESOURCE)
222226

223227
async def get_init_model(self, document: TextDocument, data_only: bool = True) -> ast.AST:
224228
if data_only:
225-
return await document.get_cache(self.__get_init_model_data_only)
226-
return await document.get_cache(self.__get_init_model)
229+
return await document.get_cache(self.__get_init_model_data_only, await self.get_init_tokens(document, True))
230+
return await document.get_cache(self.__get_init_model, await self.get_init_tokens(document))
227231

228-
async def __get_init_model_data_only(self, document: TextDocument) -> ast.AST:
229-
return self.__get_model(document, await self.get_init_tokens(document, True), DocumentType.INIT)
232+
async def __get_init_model_data_only(self, document: TextDocument, tokens: Iterable[Any]) -> ast.AST:
233+
return self.__get_model(document, tokens, DocumentType.INIT)
230234

231-
async def __get_init_model(self, document: TextDocument) -> ast.AST:
232-
return self.__get_model(document, await self.get_init_tokens(document), DocumentType.INIT)
235+
async def __get_init_model(self, document: TextDocument, tokens: Iterable[Any]) -> ast.AST:
236+
return self.__get_model(document, tokens, DocumentType.INIT)
233237

234238
async def get_namespace(self, document: TextDocument) -> Namespace:
235239
return await document.get_cache(self.__get_namespace)

robotcode/utils/async_cache.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from collections import defaultdict
2-
from dataclasses import dataclass
32
from typing import Any, Awaitable, Callable, Dict, List, Tuple, TypeVar, cast
43

54
from .async_tools import Lock
@@ -13,11 +12,11 @@ def _freeze(v: Any) -> Any:
1312
return v
1413

1514

16-
@dataclass
1715
class CacheEntry:
18-
value: Any = None
19-
has_value: bool = False
20-
lock: Lock = Lock()
16+
def __init__(self) -> None:
17+
self.data: Any = None
18+
self.has_data: bool = False
19+
self.lock: Lock = Lock()
2120

2221

2322
class AsyncSimpleCache:
@@ -34,21 +33,20 @@ async def has(self, *args: Any, **kwargs: Any) -> bool:
3433
async def get(self, func: Callable[..., Awaitable[_T]], *args: Any, **kwargs: Any) -> _T:
3534
key = self._make_key(*args, **kwargs)
3635

37-
async with self._lock:
38-
entry = self._cache[key]
39-
if entry.has_value:
40-
return cast(_T, entry.value)
36+
# async with self._lock:
37+
entry = self._cache[key]
4138

4239
async with entry.lock:
43-
entry.value = await func(*args, **kwargs)
44-
entry.has_value = True
40+
if not entry.has_data:
41+
entry.data = await func(*args, **kwargs)
42+
entry.has_data = True
4543

46-
self._order.insert(0, key)
44+
self._order.insert(0, key)
4745

48-
if len(self._order) > self.max_items:
49-
del self._cache[self._order.pop()]
46+
if len(self._order) > self.max_items:
47+
del self._cache[self._order.pop()]
5048

51-
return entry.value
49+
return cast(_T, entry.data)
5250

5351
@staticmethod
5452
def _make_key(*args: Any, **kwargs: Any) -> Tuple[Any, ...]:

robotcode/utils/async_tools.py

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -575,51 +575,46 @@ def __repr__(self) -> str:
575575
return f"<{res[1:-1]} [{extra}]>"
576576

577577
@asynccontextmanager
578-
async def __inner_lock(self) -> AsyncGenerator[bool, None]:
578+
async def __inner_lock(self) -> AsyncGenerator[Any, None]:
579579
b = self._lock.acquire(blocking=False)
580580
while not b:
581-
await asyncio.sleep(0.01)
581+
await asyncio.sleep(0)
582582
b = self._lock.acquire(blocking=False)
583583

584584
try:
585-
yield b
585+
yield None
586586
finally:
587-
if b:
588-
self._lock.release()
587+
self._lock.release()
588+
# with self._lock:
589+
# yield None
590+
# yield None
589591

590592
async def acquire(self) -> bool:
591593
async with self.__inner_lock():
592594
if not self._locked and (self._waiters is None or all(w.cancelled() for w in self._waiters)):
593595
self._locked = True
594596
return True
595597

596-
if self._waiters is None:
597-
self._waiters = deque()
598+
if self._waiters is None:
599+
self._waiters = deque()
598600

599-
fut = create_sub_future()
600-
601-
with self._lock:
601+
fut = create_sub_future()
602602
self._waiters.append(fut)
603603

604604
try:
605605
try:
606-
await asyncio.wait_for(fut, 15) # TODO remove this hack
607-
except asyncio.TimeoutError:
608-
pass
606+
await fut
609607
finally:
610-
self._waiters.remove(fut)
608+
async with self.__inner_lock():
609+
self._waiters.remove(fut)
611610
except asyncio.CancelledError:
612-
wakeup = False
613611
async with self.__inner_lock():
614-
wakeup = not self._locked
615-
616-
if wakeup:
617-
self._wake_up_first()
612+
if self._locked:
613+
await self._wake_up_first()
618614
raise
619615

620616
async with self.__inner_lock():
621617
self._locked = True
622-
623618
return True
624619

625620
async def release(self) -> None:
@@ -629,24 +624,26 @@ async def release(self) -> None:
629624
else:
630625
raise RuntimeError("Lock is not acquired.")
631626

632-
self._wake_up_first()
627+
await self._wake_up_first()
633628

634-
def _wake_up_first(self) -> None:
629+
async def _wake_up_first(self) -> None:
635630
if not self._waiters:
636631
return
632+
637633
try:
638634
fut = next(iter(self._waiters))
639635
except StopIteration:
640636
return
641637

642638
def s() -> None:
643-
fut.set_result(True)
639+
if not fut.done():
640+
fut.set_result(True)
644641

645-
if not fut.done():
646-
if fut._loop == asyncio.get_running_loop():
642+
if fut._loop == asyncio.get_running_loop():
643+
if not fut.done():
647644
fut.set_result(True)
648-
else:
649-
fut._loop.call_soon_threadsafe(s)
645+
else:
646+
fut._loop.call_soon_threadsafe(s)
650647

651648

652649
class FutureInfo:

0 commit comments

Comments
 (0)