Exemple #1
0
    def blocking_call_from_thread(self, callback, timeout):
        """Call the given function from a thread, and wait for the result synchronously
        for as long as the timeout will allow.

        Args:
            callback: Callable function to be invoked from the thread.
            timeout (:obj: int): Number of seconds to wait for the response before
                raising an exception.

        Returns:
            The results from the callback, or a timeout exception.
        """
        manual_event = ManualResetEventSlim(False)
        result_placeholder = {'manual_event': manual_event}
        ThreadPool.QueueUserWorkItem(WaitCallback(callback),
                                     result_placeholder)

        # Wait with timeout results bool indicating success/failure
        if timeout and manual_event.Wait(timeout * 1000,
                                         self.cancellation_token):
            return result_placeholder

        # Wait without timeouts raises in case of failure triggered by cancellation
        if not timeout:
            manual_event.Wait(self.cancellation_token)
            return result_placeholder

        self.raise_timeout_exception()
Exemple #2
0
class CliEventLoopManager(object):
    """Manage the main event loop using .NET threads.

    For the time being, this implementation is pretty light
    and mostly relies on .NET async doing "the right thing(tm)"
    with a sprinkle of threading here and there.
    """
    def __init__(self):
        self._init_cancellation()
        self._disconnect_event = ManualResetEventSlim(False)

    def _init_cancellation(self):
        """Initialize the cancellation source and token."""
        self.cancellation_token_source = CancellationTokenSource()
        self.cancellation_token = self.cancellation_token_source.Token
        self.cancellation_token.Register(
            lambda: LOGGER.debug('Started token cancelation'))

    def run_forever(self):
        """Kick-starts a blocking loop while the ROS client is connected."""
        self._disconnect_event.Wait(self.cancellation_token)
        LOGGER.debug('Received disconnect event on main loop')

    def call_later(self, delay, callback):
        """Call the given function after a certain period of time has passed.

        Args:
            delay (:obj:`int`): Number of seconds to wait before invoking the callback.
            callback (:obj:`callable`): Callable function to be invoked when the delay has elapsed.
        """

        # NOTE: Maybe there's a more elegant way of doing this
        def closure():
            Thread.Sleep(delay * 1000)
            callback()

        Task.Factory.StartNew(closure, self.cancellation_token)

    def call_in_thread(self, callback):
        """Call the given function on a thread.

        Args:
            callback (:obj:`callable`): Callable function to be invoked in a thread.
        """
        Task.Factory.StartNew(callback, self.cancellation_token)

    def terminate(self):
        """Signals the termination of the main event loop."""
        self._disconnect_event.Set()

        if self.cancellation_token_source:
            self.cancellation_token_source.Cancel()

        # Renew to allow re-connects
        self._init_cancellation()
Exemple #3
0
    def start_listening(self):
        """Starts listening asynchronously while the socket is open.

        The inter-thread synchronization between this and the async
        reception threads is sync'd with a manual reset event."""
        try:
            LOGGER.debug('About to start listening, socket state: %s',
                         self.socket.State)

            while self.socket and self.socket.State == WebSocketState.Open:
                mre = ManualResetEventSlim(False)
                content = []
                buffer = Array.CreateInstance(Byte, RECEIVE_CHUNK_SIZE)

                self.receive_chunk_async(
                    None, dict(buffer=buffer, content=content, mre=mre))

                LOGGER.debug('Waiting for messages...')
                try:
                    mre.Wait(self.factory.manager.cancellation_token)
                except SystemError:
                    LOGGER.debug(
                        'Cancellation detected on listening thread, exiting...'
                    )
                    break

                try:
                    message_payload = ''.join(content)
                    LOGGER.debug('Message reception completed|<pre>%s</pre>',
                                 message_payload)
                    self.on_message(message_payload)
                except Exception:
                    LOGGER.exception(
                        'Exception on start_listening while trying to handle message received.'
                        +
                        'It could indicate a bug in user code on message handlers. Message skipped.'
                    )
        except Exception:
            LOGGER.exception(
                'Exception on start_listening, processing will be aborted')
            raise
        finally:
            LOGGER.debug('Leaving the listening thread')
 def __init__(self):
     self._init_cancellation()
     self._disconnect_event = ManualResetEventSlim(False)
Exemple #5
0
class CliEventLoopManager(object):
    """Manage the main event loop using .NET threads.

    For the time being, this implementation is pretty light
    and mostly relies on .NET async doing "the right thing(tm)"
    with a sprinkle of threading here and there.
    """
    def __init__(self):
        self._init_cancellation()
        self._disconnect_event = ManualResetEventSlim(False)

    def _init_cancellation(self):
        """Initialize the cancellation source and token."""
        self.cancellation_token_source = CancellationTokenSource()
        self.cancellation_token = self.cancellation_token_source.Token
        self.cancellation_token.Register(
            lambda: LOGGER.debug('Started token cancellation'))

    def run(self):
        """Kick-starts a non-blocking event loop.

        In this implementation, this is a no-op."""
        pass

    def run_forever(self):
        """Kick-starts a blocking loop while the ROS client is connected."""
        self._disconnect_event.Wait(self.cancellation_token)
        LOGGER.debug('Received disconnect event on main loop')

    def call_later(self, delay, callback):
        """Call the given function after a certain period of time has passed.

        Args:
            delay (:obj:`int`): Number of seconds to wait before invoking the callback.
            callback (:obj:`callable`): Callable function to be invoked when the delay has elapsed.
        """

        # NOTE: Maybe there's a more elegant way of doing this
        def closure():
            Thread.Sleep(delay * 1000)
            callback()

        Task.Factory.StartNew(closure, self.cancellation_token)

    def call_in_thread(self, callback):
        """Call the given function on a thread.

        Args:
            callback (:obj:`callable`): Callable function to be invoked in a thread.
        """
        Task.Factory.StartNew(callback, self.cancellation_token)

    def blocking_call_from_thread(self, callback, timeout):
        """Call the given function from a thread, and wait for the result synchronously
        for as long as the timeout will allow.

        Args:
            callback: Callable function to be invoked from the thread.
            timeout (:obj: int): Number of seconds to wait for the response before
                raising an exception.

        Returns:
            The results from the callback, or a timeout exception.
        """
        manual_event = ManualResetEventSlim(False)
        result_placeholder = {'manual_event': manual_event}
        ThreadPool.QueueUserWorkItem(WaitCallback(callback),
                                     result_placeholder)

        # Wait with timeout results bool indicating success/failure
        if timeout and manual_event.Wait(timeout * 1000,
                                         self.cancellation_token):
            return result_placeholder

        # Wait without timeouts raises in case of failure triggered by cancellation
        if not timeout:
            manual_event.Wait(self.cancellation_token)
            return result_placeholder

        self.raise_timeout_exception()

    def raise_timeout_exception(self, _result=None, _timeout=None):
        """Callback called on timeout.

        Args:
            _result: Unused--required by Twister.
            _timeout: Unused--required by Twister.

        Raises:
            An exception.
        """
        raise Exception('No service response received')

    def get_inner_callback(self, result_placeholder):
        """Get the callback which, when called, provides result_placeholder with the result.

        Args:
            result_placeholder: (:obj: dict): Object in which to store the result.

        Returns:
            A callable which provides result_placeholder with the result in the case of success.
        """
        def inner_callback(result):
            result_placeholder['result'] = result
            result_placeholder['manual_event'].Set()

        return inner_callback

    def get_inner_errback(self, result_placeholder):
        """Get the errback which, when called, provides result_placeholder with the error.

        Args:
            result_placeholder: (:obj: dict): Object in which to store the result.

        Returns:
            A callable which provides result_placeholder with the error in the case of failure.
        """
        def inner_errback(error):
            result_placeholder['exception'] = error
            result_placeholder['manual_event'].Set()

        return inner_errback

    def terminate(self):
        """Signals the termination of the main event loop."""
        self._disconnect_event.Set()

        if self.cancellation_token_source:
            self.cancellation_token_source.Cancel()

        # Renew to allow re-connects
        self._init_cancellation()