def enter(self): with Lock(self): assert not self.opened.is_set(), 'loop has been opened' self.opened.set() while True: pop = None timeout = None with Lock(self): if self.targets: timeout = self.targets[0]['until'] - time.monotonic() if timeout <= 0: pop = self.targets.pop(0) elif self.closer.is_set(): self.closed.set() break if pop is None: self.wait.clear() if pop is not None: try: pop['target'](*pop['args'], **pop['kwargs']) except BaseException as e: try: callable(pop['log']) and pop['log'](e) except BaseException: pass else: self.wait.wait(timeout=timeout)
def wrapper(self, *args, **kwargs): with Lock(self): assert getvar(self, '__withas__', d=0) == i and setvar( self, '__withas__', i + 1) try: return function(self, *args, **kwargs) except BaseException: with Lock(self): assert setvar(self, '__withas__', -1) raise
def _recv(self): with Lock(self): self.blocking = True try: return self.recver.recv() except TimeoutError: pass except EOFError: raise except BaseException as e: logger.exception(e) finally: with Lock(self): if not self.blocking: raise EOFError self.blocking = False
def mark(self): assert threading.current_thread() is not self, 'may lead to deadlock' with Lock(self): if self.queue: mark = threading.Event() self.queue[-1].append(mark) return mark
def plant(self, *twigs, seize=False): ret = [] if self.t < 0: for twig in twigs: b, r, = twig() if not b: break ret.append(r) ret += [ Tree.SEIZE, ] * (len(twigs) - len(ret)) elif self.t == 0: for twig in twigs: b, r, = twig() ret.append(r if b else Tree.SEIZE) else: with Lock(self): targets = [{ 'twig': twig, 'event': threading.Event(), } for twig in twigs] self.targets.extend(targets) for i in range(min(len(targets), self.t - self.running)): threading.Thread(target=self._run).start() self.running += 1 for target in targets: target['event'].wait() b, r, = target['ret'] ret.append(r if b else Tree.SEIZE) if not seize: for i in range(len(ret) - 1, -1, -1): if ret[i] is Tree.SEIZE: ret.pop(i) return ret
def done(self): tid = threading.get_ident() with Lock(self): assert tid not in self.recv_black_l, 'mailbox already in use' if tid in self.marks: self.marks.pop(tid) self._gc()
def _wake(self): if self.closer.is_set(): with Lock(self): if self.blocking: if self.handle_t.empty(): self.blocking = False self._shutdown() self.wait.set()
def _run(self): while True: with Lock(self): if self.targets: target = self.targets.pop(0) else: self.running -= 1 break target['ret'] = target['twig']() target['event'].set()
def want(self): tid = threading.get_ident() with Lock(self): assert tid not in self.recv_black_l, 'mailbox already in use' if tid not in self.marks: self.marks[tid] = len(self.queue) - 1 else: if self.marks[tid] < len(self.queue) - 1: self.marks[tid] += 1 self._gc()
def _gc(self): with Lock(self): if not self.marks: gc = len(self.queue) - 1 else: gc = min(self.marks.values()) if gc > 0: for _ in range(gc): self.queue.pop(0) for k in self.marks: self.marks[k] -= gc
def update(self, key, timeout=None): with Lock(self): if key in self._data: return self._data[key] else: if key not in self._wait: self._wait[key] = threading.Event() wait = self._wait[key] if not wait.wait(timeout=timeout): raise TimeoutError return wait.value
def recv(self, timeout=None): tid = threading.get_ident() with Lock(self): assert tid not in self.recv_black_l, 'mailbox already in use' assert tid in self.marks, 'lack of calling want' event = self.queue[self.marks[tid]] if not event.wait(timeout=timeout): raise TimeoutError if event.data is None: raise EOFError return event.data
def run(self): while True: self.wait.wait() with Lock(self): data = self.queue[0].pop(0) eof = data is None self._handle(data) del data with Lock(self): for mark in self.queue.pop(0): mark.set() mark = None if eof: break if not self.queue: self.wait.clear() wake = True else: wake = False if wake: self.waker() self.closed.set()
def run(self): closed = False while not closed: self.wait.wait() with Lock(self): if self.closed is not None: closed = True queue = self.queue.copy() self.queue.clear() self.wait.clear() while queue: self._send(queue.pop(0)) self.closed.set()
def _send(self, data, *cc): cc = list(cc) with Lock(self): while cc: cc.pop(0)(data) if data is not None: if self.marks: self.queue[-1].data = data self.queue[-1].set() self.queue.append(threading.Event()) else: self.queue[-1].data = None self.queue[-1].set()
def loads(self): primary = self._data.primary if primary is not None: with Database(**self._data._database) as cursor: cursor.execute( 'select data from ' + self._data._table + ' where id=%s', (primary, )) row = cursor.fetchone() return json.loads((row['data'] if row else '') or '{}').get(self._name, {}) else: with Lock(self._data): return json.loads(self._data._data or '{}').get(self._name, {})
def saves(self, updates): primary = self._data.primary if primary is not None: with Database(**self._data._database) as cursor: cursor.execute( 'select data from ' + self._data._table + ' where id=%s for update', (primary, )) row = cursor.fetchone() obj = json.loads((row['data'] if row else '') or '{}') if updates is None: if self._name in obj: obj.pop(self._name) else: obj[self._name] = value_not_none( safe_sum(obj.get(self._name, {}), updates)) if not obj[self._name]: obj.pop(self._name) assert isjson(obj) cursor.execute( 'update ' + self._data._table + ' set data=%s where id=%s', ( json.dumps(obj) if obj else '', primary, )) else: with Lock(self._data): obj = json.loads(self._data._data or '{}') if updates is None: if self._name in obj: obj.pop(self._name) else: obj[self._name] = value_not_none( safe_sum(obj.get(self._name, {}), updates)) if not obj[self._name]: obj.pop(self._name) assert isjson(obj) self._data._data = json.dumps(obj) if obj else ''
def empty(self): with Lock(self): return not self.queue
def __enter__(self): tid = threading.get_ident() with Lock(self): self.recv_black_l.append(tid)
def request(self, session, arguments): with Lock(session): with Throw(session) as r: if not r: type(self).load(session)
def __exit__(self, t, v, tb): tid = threading.get_ident() with Lock(self): self.recv_black_l.remove(tid)
def handle(self, data): with Lock(self): self.queue.append([data, ]) self.wait.set()
def close(self): with Lock(self): if self.closed is None: self.closed = threading.Event() self.wait.set() self.closed.wait()
def send(self, data): with Lock(self): assert self.closed is None, 'send has been closed' self.queue.append(data) self.wait.set()
def exit(self): with Lock(self): self.closer.set() if not self.opened.is_set() and not self.targets: self.closed.set() self.wait.set()