chainsyncer/chainsyncer/driver.py

124 lines
3.5 KiB
Python

# standard imports
import logging
import time
# local imports
from chainsyncer.error import (
SyncDone,
NoBlockForYou,
)
from chainsyncer.session import SyncSession
logg = logging.getLogger(__name__)
NS_DIV = 1000000000
class SyncDriver:
running_global = True
def __init__(self, conn, store, pre_callback=None, post_callback=None, block_callback=None, idle_callback=None):
self.store = store
self.running = True
self.pre_callback = pre_callback
self.post_callback = post_callback
self.block_callback = block_callback
self.idle_callback = idle_callback
self.last_start = 0
self.clock_id = time.CLOCK_MONOTONIC_RAW
self.session = SyncSession(self.store)
def __sig_terminate(self, sig, frame):
logg.warning('got signal {}'.format(sig))
self.terminate()
def terminate(self):
"""Set syncer to terminate as soon as possible.
"""
logg.info('termination requested!')
SyncDriver.running_global = False
self.running = False
def run(self):
while self.running_global:
item = self.store.next_item()
logg.debug('item {}'.format(item))
if item == None:
self.running = False
self.running_global = False
break
self.loop(item)
def idle(self, interval):
interval *= NS_DIV
idle_start = time.clock_gettime_ns(self.clock_id)
delta = idle_start - self.last_start
if delta > interval:
interval /= NS_DIV
time.sleep(interval)
return
if self.idle_callback != None:
r = True
while r:
before = time.clock_gettime_ns(self.clock_id)
r = self.idle_callback(interval)
after = time.clock_gettime_ns(self.clock_id)
delta = after - before
if delta < 0:
return
interval -= delta
if interval < 0:
return
interval /= NS_DIV
time.sleep(interval)
def loop(self, item):
while self.running and SyncDriver.running_global:
self.last_start = time.clock_gettime_ns(self.clock_id)
if self.pre_callback != None:
self.pre_callback()
while True and self.running:
if start_tx > 0:
start_tx -= 1
continue
try:
block = self.get(conn)
except SyncDone as e:
logg.info('all blocks sumitted for processing: {}'.format(e))
return self.backend.get()
except NoBlockForYou as e:
break
if self.block_callback != None:
self.block_callback(block, None)
last_block = block
try:
self.process(conn, block)
except IndexError:
self.backend.set(block.number + 1, 0)
start_tx = 0
time.sleep(self.yield_delay)
if self.post_callback != None:
self.post_callback()
self.idle(interval)
def process_single(self, conn, block, tx):
self.session.filter(conn, block, tx)
def process(self, conn, block):
raise NotImplementedError()
def get(self, conn):
raise NotImplementedError()