class BlockingExecutor(impl_pooledexecutor.PooledExecutor): """A message executor which blocks the current thread. The blocking executor's start() method functions as a request processing loop - i.e. it blocks, processes messages and only returns when stop() is called from a dispatched method. Method calls are dispatched in the current thread, so only a single method call can be executing at once. This executor is likely to only be useful for simple demo programs. """ _executor_cls = lambda __, ___: futurist.SynchronousExecutor() _thread_cls = FakeBlockingThread def __init__(self, *args, **kwargs): super(BlockingExecutor, self).__init__(*args, **kwargs) def execute(self): '''Explicitly run the executor in the current context.''' # NOTE(mdbooth): Splitting start into start and execute for the # blocking executor closes a potential race. On a non-blocking # executor, calling start performs some initialisation synchronously # before starting the executor and returning control to the caller. In # the non-blocking caller there was no externally visible boundary # between the completion of initialisation and the start of execution, # meaning the caller cannot indicate to another thread that # initialisation is complete. With the split, the start call for the # blocking executor becomes analogous to the non-blocking case, # indicating that initialisation is complete. The caller can then # synchronously call execute. if self._poller is not None: self._poller.execute()
def setUp(self): super(TestDecisionEngineThreadPool, self).setUp() self.m_function = mock.Mock() self.m_function.return_value = None self.m_do_while_function = mock.Mock() self.m_do_while_function.return_value = None # override the underlying threadpool for testing # this is like a 'fixture' were the original state of the singleton # is restored after these tests finish but the threadpool can still # be used as intended with its methods self.p_threadool = mock.patch.object( threading, 'DecisionEngineThreadPool', new=threading.DecisionEngineThreadPool) self.m_threadpool = self.p_threadool.start() self.addCleanup(self.p_threadool.stop) # bind unbound patched methods for python 2.7 compatibility # class methods can be used unbounded in Python 3.x self.m_threadpool.submit = self.m_threadpool.submit.__get__( self.m_threadpool, threading.DecisionEngineThreadPool) # perform all tests synchronously self.m_threadpool._threadpool = futurist.SynchronousExecutor()
class ProxyExecutor(object): KIND_TO_FACTORY = { 'threaded': (lambda: futurist.ThreadPoolExecutor(max_workers=1)), 'synchronous': lambda: futurist.SynchronousExecutor(), } # Provide a few common aliases... KIND_TO_FACTORY['thread'] = KIND_TO_FACTORY['threaded'] KIND_TO_FACTORY['threading'] = KIND_TO_FACTORY['threaded'] KIND_TO_FACTORY['sync'] = KIND_TO_FACTORY['synchronous'] DEFAULT_KIND = 'threaded' def __init__(self, driver_name, default_executor_factory): self.default_executor_factory = default_executor_factory self.driver_name = driver_name self.started = False self.executor = None self.internally_owned = True @classmethod def build(cls, driver_name, options): default_executor_fact = cls.KIND_TO_FACTORY[cls.DEFAULT_KIND] if 'executor' in options: executor_kind = options['executor'] try: default_executor_fact = cls.KIND_TO_FACTORY[executor_kind] except KeyError: executors_known = sorted(list(cls.KIND_TO_FACTORY)) raise tooz.ToozError("Unknown executor" " '%s' provided, accepted values" " are %s" % (executor_kind, executors_known)) return cls(driver_name, default_executor_fact) def start(self): if self.started: return self.executor = self.default_executor_factory() self.started = True def stop(self): executor = self.executor self.executor = None if executor is not None: executor.shutdown() self.started = False def submit(self, cb, *args, **kwargs): if not self.started: raise tooz.ToozError("%s driver asynchronous executor" " has not been started" % self.driver_name) try: return self.executor.submit(cb, *args, **kwargs) except RuntimeError: raise tooz.ToozError("%s driver asynchronous executor has" " been shutdown" % self.driver_name)
class BlockingExecutor(impl_pooledexecutor.PooledExecutor): """A message executor which blocks the current thread. The blocking executor's start() method functions as a request processing loop - i.e. it blocks, processes messages and only returns when stop() is called from a dispatched method. Method calls are dispatched in the current thread, so only a single method call can be executing at once. This executor is likely to only be useful for simple demo programs. """ _executor_cls = lambda __, ___: futurist.SynchronousExecutor() _thread_cls = FakeBlockingThread
def setUp(self): super(BaseTest, self).setUp() if not self.IS_FUNCTIONAL: self.init_test_conf() self.session = db.get_writer_session() engine = self.session.get_bind() db.Base.metadata.create_all(engine) engine.connect() self.addCleanup(engine.dispose) plugins_base._HOOKS_MGR = None node_cache._SEMAPHORES = lockutils.Semaphores() patch = mock.patch.object(i18n, '_', lambda s: s) patch.start() # 'p=patch' magic is due to how closures work self.addCleanup(lambda p=patch: p.stop()) utils._EXECUTOR = futurist.SynchronousExecutor(green=True)
def test_sync_executor_creation(self): with futurist.SynchronousExecutor() as e: eng = self._create_engine(executor=e) self.assertIsInstance(eng._task_executor, executor.ParallelThreadTaskExecutor)
def runner(bot, slack_client, slack_sender): try: self_id = slack_client.server.login_data['self']['id'] except (TypeError, KeyError): self_id = None if not self_id: return attachments = [ { 'pretext': ("Initiating %s that runs with" " cron schedule `%s`." % (what, period)), 'mrkdwn_in': ['pretext'], }, ] # This sends an initial 'kick' message that the rest of the # work will be rooted from (ie a subthread of). resp = slack_sender.post_send(attachments=attachments, channel=channel, as_user=True, link_names=True, text="Good %s." % bot.date_wrangler.get_when()) m_headers = { m.VALIDATED_HEADER: True, m.TO_ME_HEADER: True, m.CHECK_AUTH_HEADER: False, m.ARGS_HEADER: args.copy(), m.DIRECT_CLS_HEADER: target_cls, m.IS_INTERNAL_HEADER: True, } m_channel_id = resp["channel"] m_body = munch.Munch({ 'channel': m_channel_id, 'channel_name': channel, 'channel_kind': su.ChannelKind.convert(m_channel_id), 'ts': resp['ts'], 'thread_ts': None, 'directed': False, 'targets': [ self_id, ], 'user_id': self_id, 'user_name': '', # NOTE: not used since args header passed in which turns the # match that is made into a explicit match (which then means # the text is not matched). 'text': '', 'text_no_links': '', }) message = slack_watcher.SlackMessage("slack/message", m_headers, m_body, slack_sender) su.insert_quick_link(message, slack_base_url=bot.config.slack.get('base_url')) fut = bot.submit_message( message, c.TARGETED, # Avoid using the bot executors so that we don't cause # cycles (a user can trigger a periodic to manually run # and doing that will cause a thread to wait; so it can # be possible to deplete both pools if this is abused). executor=futurist.SynchronousExecutor()) fut.add_done_callback( finishers.notify_slack_on_fail(bot, message, log=log)) fut.result()
def __init__(self, callables, log=None, executor_factory=None, cond_cls=threading.Condition, event_cls=threading.Event, schedule_strategy='last_started', now_func=utils.now, on_failure=None): """Creates a new worker using the given periodic callables. :param callables: a iterable of tuple objects previously decorated with the :py:func:`.periodic` decorator, each item in the iterable is expected to be in the format of ``(cb, args, kwargs)`` where ``cb`` is the decorated function and ``args`` and ``kwargs`` are any positional and keyword arguments to send into the callback when it is activated (both ``args`` and ``kwargs`` may be provided as none to avoid using them) :type callables: iterable :param log: logger to use when creating a new worker (defaults to the module logger if none provided), it is currently only used to report callback failures (if they occur) :type log: logger :param executor_factory: factory callable that can be used to generate executor objects that will be used to run the periodic callables (if none is provided one will be created that uses the :py:class:`~futurist.SynchronousExecutor` class) :type executor_factory: ExecutorFactory or any callable :param cond_cls: callable object that can produce ``threading.Condition`` (or compatible/equivalent) objects :type cond_cls: callable :param event_cls: callable object that can produce ``threading.Event`` (or compatible/equivalent) objects :type event_cls: callable :param schedule_strategy: string to select one of the built-in strategies that can return the next time a callable should run :type schedule_strategy: string :param now_func: callable that can return the current time offset from some point (used in calculating elapsed times and next times to run); preferably this is monotonically increasing :type now_func: callable :param on_failure: callable that will be called whenever a periodic function fails with an error, it will be provided four positional arguments and one keyword argument, the first positional argument being the callable that failed, the second being the type of activity under which it failed (``IMMEDIATE`` or ``PERIODIC``), the third being the spacing that the callable runs at and the fourth ``exc_info`` tuple of the failure. The keyword argument ``traceback`` will also be provided that may be be a string that caused the failure (this is required for executors which run out of process, as those can not *currently* transfer stack frames across process boundaries); if no callable is provided then a default failure logging function will be used instead (do note that any user provided callable should not raise exceptions on being called) :type on_failure: callable """ if on_failure is not None and not six.callable(on_failure): raise ValueError("On failure callback %r must be" " callable" % on_failure) self._tombstone = event_cls() self._waiter = cond_cls() self._dead = event_cls() self._active = event_cls() self._cond_cls = cond_cls self._watchers = [] self._works = [] for (cb, args, kwargs) in callables: if not six.callable(cb): raise ValueError("Periodic callback %r must be callable" % cb) missing_attrs = _check_attrs(cb) if missing_attrs: raise ValueError("Periodic callback %r missing required" " attributes %s" % (cb, missing_attrs)) if cb._is_periodic: # Ensure these aren't none and if so replace them with # something more appropriate... if args is None: args = self._NO_OP_ARGS if kwargs is None: kwargs = self._NO_OP_KWARGS.copy() cb_metrics = self._INITIAL_METRICS.copy() work = Work(utils.get_callback_name(cb), cb, args, kwargs) watcher = Watcher(cb_metrics, work) self._works.append(work) self._watchers.append((cb_metrics, watcher)) try: strategy = self.BUILT_IN_STRATEGIES[schedule_strategy] self._schedule_strategy = strategy[0] self._initial_schedule_strategy = strategy[1] except KeyError: valid_strategies = sorted(self.BUILT_IN_STRATEGIES.keys()) raise ValueError("Scheduling strategy '%s' must be one of" " %s selectable strategies" % (schedule_strategy, valid_strategies)) self._immediates, self._schedule = _build( now_func, self._works, self._initial_schedule_strategy) self._log = log or LOG if executor_factory is None: executor_factory = lambda: futurist.SynchronousExecutor() if on_failure is None: on_failure = functools.partial(_on_failure_log, self._log) self._on_failure = on_failure self._executor_factory = executor_factory self._now_func = now_func
def __init__(self): self._executor = futurist.SynchronousExecutor()
def _executor_factory(): return futurist.SynchronousExecutor()
#!/usr/bin/env python # -*- coding: utf-8 -*- # NOTE: enable printing timestamp for additional data import datetime import eventlet import futurist def delayed_func(): print("started") eventlet.sleep(3) print("done") print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) e = futurist.SynchronousExecutor() print("before submit") fut = e.submit(delayed_func) eventlet.sleep(1) print("Hello") eventlet.sleep(1) e.shutdown() print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))