mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 06:00:33 +02:00
Store: publish a search's completion and hand over its queue atomically
A query arriving between the queue handover and the completion event could be queued with nobody to start it, leaving the source 'loading' forever. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5ac109c381
commit
4fd8bb79af
2 files changed
+24
-2
No files matched your search
@@ -95,6 +95,28 @@ class SearchTests(SettingsTest):
|
|||||||
finally:
|
finally:
|
||||||
release.set()
|
release.set()
|
||||||
|
|
||||||
|
def test_query_arriving_as_a_search_finishes_is_not_stranded(self):
|
||||||
|
mod = fake()
|
||||||
|
source = mod.sources()[0]
|
||||||
|
arrived = []
|
||||||
|
|
||||||
|
class Event(threading.Event):
|
||||||
|
def set(self):
|
||||||
|
if not arrived: # a request lands just as the first search completes
|
||||||
|
arrived.append(None)
|
||||||
|
t = threading.Thread(target=lambda: arrived.append(search._launch(mod, source, 'second', 50)))
|
||||||
|
t.start()
|
||||||
|
t.join(.3) # blocks on search._lock if completion is published atomically
|
||||||
|
super().set()
|
||||||
|
with patch.object(search, 'threading', types.SimpleNamespace(Event=Event, Thread=threading.Thread)):
|
||||||
|
search._launch(mod, source, 'first', 50)
|
||||||
|
for _ in range(200):
|
||||||
|
if len(arrived) == 2:
|
||||||
|
break
|
||||||
|
time.sleep(.01)
|
||||||
|
self.assertTrue(arrived[1]['event'].wait(2))
|
||||||
|
self.assertEqual(arrived[1]['query'], ('second', 50))
|
||||||
|
|
||||||
def test_set_enabled_does_not_hold_search_lock_in_source(self):
|
def test_set_enabled_does_not_hold_search_lock_in_source(self):
|
||||||
free = []
|
free = []
|
||||||
def set_enabled(source_id, enabled):
|
def set_enabled(source_id, enabled):
|
||||||
|
|||||||
@@ -249,11 +249,11 @@ def _start(key, task):
|
|||||||
task['error'] = str(e)
|
task['error'] = str(e)
|
||||||
task['limited'] = isinstance(e, SourceLimited)
|
task['limited'] = isinstance(e, SourceLimited)
|
||||||
finally:
|
finally:
|
||||||
with _lock:
|
with _lock: # completion and queue handover are one step for _launch
|
||||||
queued = _pending.pop(key, None)
|
queued = _pending.pop(key, None)
|
||||||
if queued:
|
if queued:
|
||||||
_running[key] = queued
|
_running[key] = queued
|
||||||
task['event'].set()
|
task['event'].set()
|
||||||
if queued:
|
if queued:
|
||||||
_start(key, queued)
|
_start(key, queued)
|
||||||
threading.Thread(target=run, daemon=True).start()
|
threading.Thread(target=run, daemon=True).start()
|
||||||
|
|||||||
Reference in new issue
Block a user