Sprinter: добавить отладку C-исходников и интеграцию VS Code
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
"""Один владелец файлового backend; timeout инвалидирует канал mutations."""
|
||||
from __future__ import annotations
|
||||
import fcntl
|
||||
import json
|
||||
from pathlib import Path
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
from .build import write_json
|
||||
|
||||
|
||||
class BridgeError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class FileBridge:
|
||||
def __init__(self, directory, session, timeout=5):
|
||||
self.directory = Path(directory)
|
||||
self.session = session
|
||||
self.timeout = timeout
|
||||
self.generation = None
|
||||
self.invalid = False
|
||||
self._mutex = threading.Lock()
|
||||
self._owner = (self.directory/'owner.lock').open('a')
|
||||
try:
|
||||
fcntl.flock(self._owner, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except OSError:
|
||||
self._owner.close()
|
||||
raise BridgeError('У backend уже есть управляющая сессия')
|
||||
|
||||
def close(self):
|
||||
with self._mutex:
|
||||
self._owner.close()
|
||||
|
||||
def request(self, command, **args):
|
||||
with self._mutex:
|
||||
if self._owner.closed:
|
||||
raise BridgeError('Канал закрыт')
|
||||
if self.invalid:
|
||||
raise BridgeError('Канал инвалидирован после timeout; требуется новая сессия')
|
||||
identity = str(uuid.uuid4().int)
|
||||
request = self.directory/f'req_{identity}.json'
|
||||
response = self.directory/f'resp_{identity}.json'
|
||||
write_json(request, {'session': self.session, 'generation': self.generation,
|
||||
'command': command, 'args': args})
|
||||
deadline = time.monotonic()+self.timeout
|
||||
while time.monotonic() < deadline:
|
||||
if response.exists():
|
||||
try:
|
||||
value = json.loads(response.read_text())
|
||||
if not isinstance(value, dict) or not {'session', 'generation', 'ok'} <= value.keys():
|
||||
raise ValueError('Неверная структура ответа')
|
||||
except (OSError, ValueError) as error:
|
||||
self.invalid = True
|
||||
raise BridgeError('Повреждённый ответ backend: ' + str(error)) from error
|
||||
response.unlink()
|
||||
if value['session'] != self.session:
|
||||
self.invalid = True
|
||||
raise BridgeError('Ответ от другой сессии')
|
||||
self.generation = value['generation']
|
||||
if not value['ok']:
|
||||
raise BridgeError(value['error'])
|
||||
return value['result']
|
||||
time.sleep(.005)
|
||||
self.invalid = True
|
||||
request.unlink(missing_ok=True)
|
||||
raise BridgeError('Timeout: результат команды неизвестен; автоматический повтор запрещён')
|
||||
|
||||
def wait_stopped(self, timeout=5):
|
||||
deadline = time.monotonic()+timeout
|
||||
while time.monotonic() < deadline:
|
||||
snapshot = self.request('snapshot')
|
||||
if snapshot['state']=='stopped':
|
||||
return snapshot
|
||||
if snapshot['state']=='invalidated':
|
||||
raise BridgeError('Сессия MAME инвалидирована reset/load')
|
||||
time.sleep(.005)
|
||||
raise BridgeError('CPU не остановился за отведённое время')
|
||||
|
||||
def handshake(self):
|
||||
response = self.request('hello')
|
||||
if response['protocol'] != 1:
|
||||
raise BridgeError('Неподдержанная версия протокола')
|
||||
return response
|
||||
Reference in New Issue
Block a user