Example #1
0
    def _discard_session(self, session, should_discard):
        if session.connection_count > 0:
            raise RuntimeError("Should not be discarding a session with open connections")
        log.debug("Discarding session %r last in use %r milliseconds ago", session.id, session.milliseconds_since_last_unsubscribe)

        session_context = self._session_contexts[session.id]

        # session.destroy() wants the document lock so it can shut down the document
        # callbacks.
        def do_discard():
            # while we yielded for the document lock, the discard-worthiness of the
            # session may have changed.
            # However, since we have the document lock, our own lock will cause the
            # block count to be 1. If there's any other block count besides our own,
            # we want to skip session destruction though.
            if should_discard(session) and session.expiration_blocked_count == 1:
                session.destroy()
                del self._sessions[session.id]
                del self._session_contexts[session.id]
            else:
                log.debug("Session %r was scheduled to discard but came back to life", session.id)
        yield session.with_document_locked(do_discard)

        # session lifecycle hooks are supposed to be called outside the document lock,
        # we only run these if we actually ended up destroying the session.
        if session_context.destroyed:
            try:
                result = self._application.on_session_destroyed(session_context)
                yield yield_for_all_futures(result)
            except Exception as e:
                log.error("Failed to run session destroy hooks %r", e, exc_info=True)

        raise gen.Return(None)
Example #2
0
 def with_locked_document(self, func):
     if self._session is None:
         # this means we are in on_session_created, so no locking yet,
         # we have exclusive access
         yield yield_for_all_futures(func(self._document))
     else:
         self._session.with_document_locked(func, self._document)
Example #3
0
 def with_locked_document(self, func):
     if self._session is None:
         # this means we are in on_session_created, so no locking yet,
         # we have exclusive access
         yield yield_for_all_futures(func(self._document))
     else:
         self._session.with_document_locked(func, self._document)
Example #4
0
def test__yield_for_all_futures():
    loop = IOLoop()
    loop.make_current()

    @gen.coroutine
    def several_steps():
        value = 0
        value += yield async_value(1)
        value += yield async_value(2)
        value += yield async_value(3)
        raise gen.Return(value)

    result = {}

    def on_done(future):
        result["value"] = future.result()
        loop.stop()

    loop.add_future(yield_for_all_futures(several_steps()), on_done)

    try:
        loop.start()
    except KeyboardInterrupt as e:
        print("keyboard interrupt")

    assert 6 == result["value"]

    loop.close()
    def _discard_session(self, session, should_discard):
        if session.connection_count > 0:
            raise RuntimeError("Should not be discarding a session with open connections")
        log.debug("Discarding session %r last in use %r milliseconds ago", session.id, session.milliseconds_since_last_unsubscribe)

        session_context = self._session_contexts[session.id]

        # session.destroy() wants the document lock so it can shut down the document
        # callbacks.
        def do_discard():
            # while we yielded for the document lock, the discard-worthiness of the
            # session may have changed.
            # However, since we have the document lock, our own lock will cause the
            # block count to be 1. If there's any other block count besides our own,
            # we want to skip session destruction though.
            if should_discard(session) and session.expiration_blocked_count == 1:
                session.destroy()
                del self._sessions[session.id]
                del self._session_contexts[session.id]
            else:
                log.debug("Session %r was scheduled to discard but came back to life", session.id)
        yield session.with_document_locked(do_discard)

        # session lifecycle hooks are supposed to be called outside the document lock,
        # we only run these if we actually ended up destroying the session.
        if session_context.destroyed:
            try:
                result = self._application.on_session_destroyed(session_context)
                yield yield_for_all_futures(result)
            except Exception as e:
                log.error("Failed to run session destroy hooks %r", e, exc_info=True)

        raise gen.Return(None)
def test__yield_for_all_futures():
    loop = IOLoop()
    loop.make_current()

    @gen.coroutine
    def several_steps():
        value = 0
        value += yield async_value(1)
        value += yield async_value(2)
        value += yield async_value(3)
        raise gen.Return(value)

    result = {}

    def on_done(future):
        result['value'] = future.result()
        loop.stop()

    loop.add_future(yield_for_all_futures(several_steps()), on_done)

    try:
        loop.start()
    except KeyboardInterrupt:
        print("keyboard interrupt")

    assert 6 == result['value']

    loop.close()
Example #7
0
 def _needs_document_lock_wrapper(self, *args, **kwargs):
     # while we wait for and hold the lock, prevent the session
     # from being discarded. This avoids potential weirdness
     # with the session vanishing in the middle of some async
     # task.
     self.block_expiration()
     try:
         with (yield self._lock.acquire()):
             if self._pending_writes is not None:
                 raise RuntimeError("internal class invariant violated: _pending_writes " + \
                                    "should be None if lock is not held")
             self._pending_writes = []
             try:
                 result = yield yield_for_all_futures(
                     func(self, *args, **kwargs))
             finally:
                 # we want to be very sure we reset this or we'll
                 # keep hitting the RuntimeError above as soon as
                 # any callback goes wrong
                 pending_writes = self._pending_writes
                 self._pending_writes = None
             for p in pending_writes:
                 yield p
         raise gen.Return(result)
     finally:
         self.unblock_expiration()
Example #8
0
 def _needs_document_lock_wrapper(self, *args, **kwargs):
     # while we wait for and hold the lock, prevent the session
     # from being discarded. This avoids potential weirdness
     # with the session vanishing in the middle of some async
     # task.
     self.block_expiration()
     try:
         with (yield self._lock.acquire()):
             if self._pending_writes is not None:
                 raise RuntimeError("internal class invariant violated: _pending_writes " + \
                                    "should be None if lock is not held")
             self._pending_writes = []
             try:
                 result = yield yield_for_all_futures(func(self, *args, **kwargs))
             finally:
                 # we want to be very sure we reset this or we'll
                 # keep hitting the RuntimeError above as soon as
                 # any callback goes wrong
                 pending_writes = self._pending_writes
                 self._pending_writes = None
             for p in pending_writes:
                 yield p
         raise gen.Return(result)
     finally:
         self.unblock_expiration()
Example #9
0
    def create_session_if_needed(self, session_id, request=None):
        # this is because empty session_ids would be "falsey" and
        # potentially open up a way for clients to confuse us
        if len(session_id) == 0:
            raise ProtocolError("Session ID must not be empty")

        if session_id not in self._sessions and \
           session_id not in self._pending_sessions:
            future = self._pending_sessions[session_id] = gen.Future()

            doc = Document()


            session_context = BokehSessionContext(session_id,
                                                  self.server_context,
                                                  doc)
            # using private attr so users only have access to a read-only property
            session_context._request = request

            # expose the session context to the document
            # use the _attribute to set the public property .session_context
            doc._session_context = session_context


            try:
                yield yield_for_all_futures(self._application.on_session_created(session_context))
            except Exception as e:
                log.error("Failed to run session creation hooks %r", e, exc_info=True)

            self._application.initialize_document(doc)

            session = ServerSession(session_id, doc, io_loop=self._loop)
            del self._pending_sessions[session_id]
            self._sessions[session_id] = session
            session_context._set_session(session)
            self._session_contexts[session_id] = session_context

            # notify anyone waiting on the pending session
            future.set_result(session)

        if session_id in self._pending_sessions:
            # another create_session_if_needed is working on
            # creating this session
            session = yield self._pending_sessions[session_id]
        else:
            session = self._sessions[session_id]

        raise gen.Return(session)
Example #10
0
    def create_session_if_needed(self, session_id, request=None):
        # this is because empty session_ids would be "falsey" and
        # potentially open up a way for clients to confuse us
        if len(session_id) == 0:
            raise ProtocolError("Session ID must not be empty")

        if session_id not in self._sessions and \
           session_id not in self._pending_sessions:
            future = self._pending_sessions[session_id] = gen.Future()

            doc = Document()

            session_context = BokehSessionContext(session_id,
                                                  self.server_context, doc)
            # using private attr so users only have access to a read-only property
            session_context._request = _RequestProxy(request)

            # expose the session context to the document
            # use the _attribute to set the public property .session_context
            doc._session_context = session_context

            try:
                yield yield_for_all_futures(
                    self._application.on_session_created(session_context))
            except Exception as e:
                log.error("Failed to run session creation hooks %r",
                          e,
                          exc_info=True)

            self._application.initialize_document(doc)

            session = ServerSession(session_id, doc, io_loop=self._loop)
            del self._pending_sessions[session_id]
            self._sessions[session_id] = session
            session_context._set_session(session)
            self._session_contexts[session_id] = session_context

            # notify anyone waiting on the pending session
            future.set_result(session)

        if session_id in self._pending_sessions:
            # another create_session_if_needed is working on
            # creating this session
            session = yield self._pending_sessions[session_id]
        else:
            session = self._sessions[session_id]

        raise gen.Return(session)
Example #11
0
    def create_session_if_needed(self, session_id):
        # this is because empty session_ids would be "falsey" and
        # potentially open up a way for clients to confuse us
        if len(session_id) == 0:
            raise ProtocolError("Session ID must not be empty")

        if session_id not in self._sessions and \
           session_id not in self._pending_sessions:
            future = self._pending_sessions[session_id] = gen.Future()

            doc = Document()

            session_context = BokehSessionContext(session_id,
                                                  self._server_context, doc)
            try:
                result = yield yield_for_all_futures(
                    self._application.on_session_created(session_context))
            except Exception as e:
                log.error("Failed to run session creation hooks %r",
                          e,
                          exc_info=True)

            self._application.initialize_document(doc)

            session = ServerSession(session_id, doc, io_loop=self._loop)
            del self._pending_sessions[session_id]
            self._sessions[session_id] = session
            session_context._set_session(session)
            self._session_contexts[session_id] = session_context

            # notify anyone waiting on the pending session
            future.set_result(session)

        if session_id in self._pending_sessions:
            # another create_session_if_needed is working on
            # creating this session
            session = yield self._pending_sessions[session_id]
        else:
            session = self._sessions[session_id]

        raise gen.Return(session)