def __heat_fs_path_in_reactor(self, base_dir, base_dir_id, old_path, new_path): """ Similar to C{__heat_fs_path}, but runs definitely in reactor thread. """ assert in_main_thread() with self.__cooling_down_to_store_lock: _call_to_store_fs_change = \ self.__cooling_down_to_store.get(old_path) if _call_to_store_fs_change is not None: # Some file is being cooled down already. # Remove it from the collection, # and stop the associated callLater object. logger.debug('Reheating the file: %r (%r)', old_path, new_path) del self.__cooling_down_to_store[old_path] try: _call_to_store_fs_change.cancel() except (internet_error.AlreadyCancelled, internet_error.AlreadyCalled): pass else: # Cooling down a completely new file.. logger.verbose('Starting to cool down the file: %r', new_path) # pylint:disable=E1101,C0103 _callLater = reactor.callLater # pylint:enable=E1101,C0103 self.__cooling_down_to_store[new_path] = \ _callLater(FILE_COOL_DOWN_TO_STORE.total_seconds(), lambda: callInThread( self.__store_fs_change_after_cooling, base_dir, base_dir_id, new_path))
def __try_start_transaction(self): """Try to start the PROGRESS transaction. It is possible that you don't need to start it actually, if the data is empty. @note: Deferred callback: exceptions logged. """ assert not in_main_thread() logger.debug('Trying to start chunks progress...') should_launch_tr = False with self.__notification_storage_lock: if self.__notification_storage and self.__waiting_for_transaction: logger.debug('Starting chunks progress!') should_launch_tr = True # and leave the lock asap! # NotificationGetter will deal with the lock properly itself. self.__waiting_for_transaction = False if should_launch_tr: notif_getter = ChunkProgressNotificator.NotificationGetter(self) tr = self.app.tr_manager.create_new_transaction( name='PROGRESS', src=self.app.host, dst=self.app.primary_node, parent=None, # PROGRESS-specific host_chunks_map_getter=notif_getter)
def _iterate(self, delay=None, fromqt=False): _assert(in_main_thread()) """ See twisted.internet.interfaces.IReactorCore.iterate. """ self.runUntilCurrent() self.doIteration(delay, fromqt)
def doIteration(self, delay=None, fromqt=False): _assert(in_main_thread()) """ This method is called by a Qt timer or by network activity on a file descriptor. If called becuase of network activiy then control should not be handed back to Qt as this would cause recursion. """ if not self.running and self._blockApp: self._blockApp.quit() self._timer.stop() delay = max(delay, 1) if not fromqt: self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000) if self.timeout() is None: timeout = 0.1 elif self.timeout() == 0: timeout = 0 else: timeout = self.timeout() self._timer.setInterval(timeout * 1000) self._timer.start()
def removeAll(self): _assert(in_main_thread()) """ Remove all selectables, and return a list of them. """ rv = self._removeAll(self._reads, self._writes) return rv
def _add_transaction(self, tr, state): """Add the transaction to the transaction storage, thread-safely. @type tr: AbstractTransaction @type state: AbstractTransaction.State """ assert not _TMP_NOTHING_IN_REACTOR_THREAD or not in_main_thread() logger.debug("__add %r", tr) assert tr.uuid not in self.__transactions_by_uuid, (tr, self.__transactions_by_uuid) with self.__transactions_lock: self.__transactions_by_uuid[tr.uuid] = tr with self.__fdbw_factory() as fdbw: FDBQueries.Transactions.add_transaction( type_=tr.type, uuid=tr.uuid, src_uuid=state.tr_src_uuid, dst_uuid=state.tr_dst_uuid, ts=state.tr_start_time, state=state, parent_uuid=None if tr.parent is None else tr.parent.uuid, fdbw=fdbw, )
def _del_transaction(self, tr): """ Delete a transaction from the transaction storage, in a thread-safe way. @type tr: AbstractTransaction """ assert not _TMP_NOTHING_IN_REACTOR_THREAD or not in_main_thread() logger.debug('__del %r', tr) assert tr.uuid in self.__transactions_by_uuid, \ (tr, self.__transactions_by_uuid) with self.__transactions_lock: del self.__transactions_by_uuid[tr.uuid] with self.__fdbw_factory() as fdbw: FDBQueries.Transactions.del_transaction(uuid=tr.uuid, fdbw=fdbw) # Also: # All children in progress, unfortunately, should be deleted too children_copy = set(tr.children_in_progress) map(self._del_transaction, children_copy)
def _forward_events(self): assert in_main_thread() events = self.__forward_later_events self.__forward_later_events = set() self.__events_to_delay = set() for event in events: self._forward_event(event)
def _add_transaction(self, tr, state): """Add the transaction to the transaction storage, thread-safely. @type tr: AbstractTransaction @type state: AbstractTransaction.State """ assert not _TMP_NOTHING_IN_REACTOR_THREAD or not in_main_thread() logger.debug('__add %r', tr) assert tr.uuid not in self.__transactions_by_uuid, \ (tr, self.__transactions_by_uuid) with self.__transactions_lock: self.__transactions_by_uuid[tr.uuid] = tr with self.__fdbw_factory() as fdbw: FDBQueries.Transactions.add_transaction( type_=tr.type, uuid=tr.uuid, src_uuid=state.tr_src_uuid, dst_uuid=state.tr_dst_uuid, ts=state.tr_start_time, state=state, parent_uuid=None if tr.parent is None else tr.parent.uuid, fdbw=fdbw)
def shutdown(self): _assert(in_main_thread()) self.notifier.setEnabled(False) self.disconnect(self.notifier, SIGNAL("activated(int)"), self.fn) self.fn = self.watcher = None self.notifier.deleteLater() self.deleteLater()
def __on_restore_retry_delay_elapsed(self): assert not in_main_thread() with self.open_state(for_update=True) as state: success = self.__request_restoring_more_chunks(state) if not success: self.__restore_ops_failed() # outside the state context!
def __try_save_next_bunch_of_file_states(self): """Check if we have multiple states to store to the DB, and do it.""" assert not in_main_thread() with self.__file_states_ready_to_write_lock: all_states = self.__file_states_ready_to_write.values() self.__file_states_ready_to_write = {} # "states" contains tuples like (base_dir_id, state). # Group them by base_dir_id, and write multiple file states at once. if all_states: logger.debug('Writing %i file state(s) at once', len(all_states)) grouped_by_base_dir = sorted_groupby(all_states, key=itemgetter(0)) for base_dir_id, per_base_dir in grouped_by_base_dir: states_to_write = imap(itemgetter(1), per_base_dir) logger.debug('Writing states for base dir %r', base_dir_id) with db.RDB() as rdbw: HostQueries.HostFiles.add_file_states( base_dir_id, states_to_write, rdbw) logger.debug('Wrote the states')
def mark_as_just_seen_alive(self, key, urls): """Implements the @abstractmethod from C{AbstractPeerBook}. @type urls: col.Iterable @type key: UUID """ assert not in_main_thread() with self.__fdbw_factory() as fdbw: last_revive_ts = FDBQueries.Users.update_host_info( host_uuid=HostUUID.safe_cast_uuid(key), urls=list(urls), timestamp=datetime.utcnow(), fdbw=fdbw) if last_revive_ts is None: # But is this code path supported now? logger.debug('Marking %s as alive for the first time', key) else: was_dead = datetime.utcnow() - last_revive_ts > HOST_DEATH_TIME if was_dead: logger.debug('Marking %s as just seen alive, was dead', key) else: logger.verbose('Marking %s as just seen alive, was alive', key)
def add_message(cls, msg, bdbw): """Add a new outgoing message. @param msg: an outgoing message to put in the message pool. @type msg: AbstractMessage @param bdbw: BigDB wrapper. @type bdbw: abstract_docstorewrapper.AbstractDocStoreWrapper """ assert not in_main_thread() # get_body() may take long # Mandatory args = { 'name': msg.name, 'ack': msg.is_ack, 'active': True, 'uuid': msg.uuid, 'src': msg.src.uuid, 'dst': msg.dst.uuid, } # Optional if msg.status_code != 0: args['status_code'] = msg.status_code bdbw.retried_sync(bdbw.gfs_messages.put, ''.join(msg.get_body()), **args)
def _received_http_response(self, response_tuple, orig_message): """Overrides method from C{HostApp}.""" assert in_main_thread() self.network_connection_is_working = True return super(UHostApp, self)._received_http_response(response_tuple, orig_message)
def unwatch_paths(self, paths): """Stop watching several paths. @type paths: col.Iterable """ assert in_main_thread() for path in paths: self.unwatch_path(path)
def __on_event(self, event): assert in_main_thread() if event in self.__events_to_delay: self.__forward_later_events.add(event) else: self.__forward_now_events.add(event) self.__events_to_delay.add(event) self.do_forward_events()
def __on_next_iteration_of_file_state_bunch(self): assert in_main_thread() d = threads.deferToThread( lambda: exceptions_logged(logger)( self.__try_save_next_bunch_of_file_states)()) d.addBoth(lambda ignore: exceptions_logged(logger)( self.__do_next_iteration_of_file_state_bunch)())
def do_forward_events(self): assert in_main_thread() events = self.__forward_now_events self.__forward_now_events = set() for event in events: self._forward_event(event) if not self._delayed_forward.active(): self.__relaunch_delayed_forward()
def get_tr_by_uuid(self, tr_uuid): """Implementation of interface from C{AbstractTransactionManager}. @rtype: AbstractTransaction, NoneType """ assert not _TMP_NOTHING_IN_REACTOR_THREAD or not in_main_thread() return self.__transactions_by_uuid.get(tr_uuid)
def run(self, installSignalHandlers=True): _assert(in_main_thread()) if self._ownApp: self._blockApp = self.qApp else: self._blockApp = QEventLoop() self.runReturn() self._blockApp.exec_()
def __do_next_iteration_of_file_state_bunch(self): """Go the next iteration of file state bunch write to the DB.""" assert in_main_thread() # pylint:disable=E1101,C0103 _callLater = reactor.callLater # pylint:enable=E1101,C0103 _callLater(FILE_BUNCH_COOL_DOWN_TO_STORE.total_seconds(), self.__on_next_iteration_of_file_state_bunch)
def wait_for_message_for_peer(self, inh, prefer_msg_uuid, still_wait_checker, d=None): """Implementation of interface from C{AbstractTransactionManager}. @type inh: AbstractInhabitant @type prefer_msg_uuid: MessageUUID, NoneType @type still_wait_checker: col.Callable @type d: defer.Deferred, NoneType @rtype: defer.Deferred @todo: C{still_wait_checker} is not used; instead, the user should probably delete the C{Deferred} from C{__outgoing_message_notifs_by_host_uuid}. """ assert not in_main_thread() d = defer.Deferred() if d is None else d with self.__outgoing_messages_lock: # Do we have a message with the preferred UUID? candidate_msg = \ self.__outgoing_messages_by_uuid.get(prefer_msg_uuid, None) # Do we have a message for a particular peer? reply_msg = self.deliver_message_for_peer(inh, prefer_msg_uuid) # What if we would try to deliver a message with a particular # UUID? Too lazy to implement it now (cause it's not needed at the # moment), but... could it help us? # if reply_msg is not None: # For now, if we have a reply message for some particular peer, # this takes precedence over the reply message # we could send to. # This might be wrong though. # Note that coalesce() may return a msg, # and reply_msg may be None, so we'd better compare them # only if reply_msg is definitely not None. if coalesce(candidate_msg, reply_msg) != reply_msg: logger.warning("Could've deliver %r, but using %r instead", candidate_msg, reply_msg) # We have an outgoing message for inh already! d.callback(reply_msg) else: # Unfortunately, we don't have a message yet. # We have to put a deferred callback to the queue # for this peer. # Whenever a message directed to this host is added, # the message adder will call this callback. self.__outgoing_message_notifs_by_host_uuid[inh.uuid] \ .append(d) return d
def wait_for_message_for_peer(self, inh, prefer_msg_uuid, still_wait_checker, d=None): """Implementation of interface from C{AbstractTransactionManager}. @type inh: AbstractInhabitant @type prefer_msg_uuid: MessageUUID, NoneType @type still_wait_checker: col.Callable @type d: defer.Deferred, NoneType @returns: a deferred that fires with the message, or maybe a {error.ConnectionClosed()}. @rtype: defer.Deferred """ assert not in_main_thread() if d is None: logger.debug('Checking for message for %r', inh) else: logger.debug('1 second passed, polling a message for %r', inh) d = defer.Deferred() if d is None else d if not still_wait_checker(): logger.debug("Seems like we don't need to wait for %r anymore", inh) d.errback(error.ConnectionClosed(u'No need to wait on {!r}' .format(inh))) else: logger.debug("Let's deliver a message for %r, preferrably %r", inh, prefer_msg_uuid) reply_msg = self.deliver_message_for_peer(inh, prefer_msg_uuid) assert isinstance(reply_msg, (AbstractMessage, NoneType)), \ repr(reply_msg) if reply_msg is not None: # We have an outgoing message for inh already! logger.verbose('Going to deliver a message for %r: %r', inh, reply_msg) d.callback(reply_msg) else: # Unfortunately, we don't have a message yet. # Let's recall this function in, say, a second. logger.verbose('No messages for %r, retrying in %r', inh, POLL_FOR_OUTGOING_MESSAGES_PERIOD) callFromThread( task.deferLater, reactor, POLL_FOR_OUTGOING_MESSAGES_PERIOD.total_seconds(), lambda: callInThread( self.__wait_for_message_for_peer_ignore_result, inh, prefer_msg_uuid, still_wait_checker, d)) return d
def _add(self, xer, primary, type): _assert(in_main_thread()) """ Private method for adding a descriptor from the event loop. It takes care of adding it if new or modifying it if already added for another state (read -> read/write for example). """ if xer not in primary: primary[xer] = TwistedSocketNotifier(None, self, xer, type)
def add(self, dst_peer, chunks, end_ts, duration): """ Add a notification that some chunks were successfully uploaded to the dst_peer on end_ts, taking duration. @type chunks: (list, set) @precondition: consists_of(chunks, Chunk) @param dst_peer: The Host, to which the chunks were uploaded before. @type dst_peer: Host @param end_ts: When the upload of these chunks was completed? @type end_ts: datetime @param duration: How much time did the upload of the chunks took? @type duration: timedelta @rtype: Deferred """ assert not in_main_thread() result_deferred = Deferred() _notification = \ ProgressNotificationPerHostWithDeferred(chunks=chunks, end_ts=end_ts, duration=duration, deferred=result_deferred) logger.debug('Adding progress... ') should_launch_calllater = False with self.__notification_storage_lock: # First, create and add the information about the group of chunks. self.__notification_storage.setdefault(dst_peer, []) \ .append(_notification) # Then, if the transaction is not yet pending, # let's plan to run the transaction in several seconds. if not self.__waiting_for_transaction: should_launch_calllater = self.__waiting_for_transaction = True # ... and leave the lock ASAP! # Since this moment, self.__notification_storage_lock # is redundant. # Outside of the lock!!! if should_launch_calllater: callLaterInThread(WAIT_TO_COLLECT_PROGRESS_BEFORE_SEND, self.__try_start_transaction) logger.debug('Will wait for %i chunks to %r with %r', len(chunks), dst_peer, result_deferred) return result_deferred
def _when_starting_normal_mode(self): """Overrides method from C{HostApp}.""" assert in_main_thread() super(UHostApp, self)._when_starting_normal_mode() _callLater = reactor.callLater # pylint:disable=E1101,C0103 self.__syncer_starter = \ _callLater(START_SYNCING_AFTER.total_seconds(), lambda: callInThread(self.__start_syncing))
def _handle_changes_in_file(self, path): assert not in_main_thread() try: mode = os.stat(path).st_mode except OSError: # file deletion will handled on directory changed signal return else: assert stat.S_ISREG(mode), (path, mode) self._send_event(ModifyEvent, path)
def _remove(self, xer, primary): _assert(in_main_thread()) """ Private method for removing a descriptor from the event loop. It does the inverse job of _add, and also add a check in case of the fd has gone away. """ if xer in primary: notifier = primary.pop(xer) notifier.shutdown()
def post_message(self, message): """Implementation of interface from C{AbstractTransactionManager}. @type message: AbstractMessage """ assert not _TMP_NOTHING_IN_REACTOR_THREAD or not in_main_thread() super(TransactionManagerInFastDBBigDB, self).post_message(message) logger.verbose("Posting message %r", message) with self.__bdbw_factory() as bdbw: BDBQueries.Messages.add_message(msg=message, bdbw=bdbw)
def __on_create_dataset_timer(self): assert not in_main_thread() if (self.is_started and self.do_send_messages and self.do_heartbeats_revive): # We are running the "main living loop" logger.debug('Trying to create dataset...') try: self.__backup_snapshotted_files_if_needed() except Exception: logger.exception('A error occured during ' 'regular backup attempt.')
def post_message(self, message): """Implementation of interface from C{AbstractTransactionManager}. @type message: AbstractMessage """ assert not _TMP_NOTHING_IN_REACTOR_THREAD or not in_main_thread() super(TransactionManagerInFastDBBigDB, self).post_message(message) logger.verbose('Posting message %r', message) with self.__bdbw_factory() as bdbw: BDBQueries.Messages.add_message(msg=message, bdbw=bdbw)
def _error_on_send_http_request(self, failure, message): """Overrides method from C{HostApp}.""" assert in_main_thread() if failure.check(internet_error.ConnectionRefusedError, internet_error.ConnectionDone, internet_error.ConnectionLost, internet_error.DNSLookupError): self.network_connection_is_working = False return super(UHostApp, self)._error_on_send_http_request(failure, message)
def deliver_message_for_peer(self, inh, prefer_msg_uuid): """Implementation of interface from C{AbstractTransactionManager}. @type inh: AbstractInhabitant @type prefer_msg_uuid: MessageUUID, NoneType @rtype: AbstractMessage, NoneType """ assert not _TMP_NOTHING_IN_REACTOR_THREAD or not in_main_thread() with self.__bdbw_factory() as bdbw: return BDBQueries.Messages.take_message_for_peer( my_node=self.app.server_process.me, dst_uuid=inh.uuid, prefer_msg_uuid=prefer_msg_uuid, bdbw=bdbw )
def __init__(self, parent, reactor, watcher, socketType): _assert(in_main_thread()) QObject.__init__(self, parent) self.reactor = reactor self.watcher = watcher fd = watcher.fileno() self.notifier = QSocketNotifier(fd, socketType, parent) self.notifier.setEnabled(True) if socketType == QSocketNotifier.Read: self.fn = self.read else: self.fn = self.write QObject.connect(self.notifier, SIGNAL("activated(int)"), self.fn)
def __cbDigestMatch(self, matched, username, host_uuids, msgtype): """ @returns: Either the tuple of user of the peer being authenticated and its possible host candidates; or a C{Failure} object. @rtype: AuthAvatar, Failure """ assert in_main_thread() logger.debug("Digest for %r: %s", username, "matched" if matched else "mismatched") if matched: return AuthAvatar(username=username, host_uuid_candidates=host_uuids, do_create=(msgtype == "LOGIN")) else: return Failure(cred_error.UnauthorizedLogin())