def put(self, item, block=True, timeout=None): if self.hub is getcurrent(): if self.getters: getter = self.getters.popleft() getter.switch(item) return raise Full if not block: timeout = 0 waiter = Waiter() # pylint:disable=undefined-variable item = (item, waiter) self.putters.append(item) timeout = Timeout._start_new_or_dummy(timeout, Full) try: if self.getters: self._schedule_unlock() result = waiter.get() if result is not waiter: raise InvalidSwitchError( "Invalid switch into Channel.put: %r" % (result, )) except: _safe_remove(self.putters, item) raise finally: timeout.cancel()
def _wait_core(self, timeout, catch=Timeout): # The core of the wait implementation, handling # switching and linking. If *catch* is set to (), # a timeout that elapses will be allowed to be raised. # Returns a true value if the wait succeeded without timing out. switch = getcurrent().switch # pylint:disable=undefined-variable self.rawlink(switch) try: with Timeout._start_new_or_dummy(timeout) as timer: try: if self.hub is None: self.hub = get_hub() result = self.hub.switch() if result is not self: # pragma: no cover raise InvalidSwitchError( 'Invalid switch into Event.wait(): %r' % (result, )) return True except catch as ex: if ex is not timer: raise # test_set_and_clear and test_timeout in test_threading # rely on the exact return values, not just truthish-ness return False finally: self.unlink(switch)
def join(self, timeout=None): """ join(timeout=None) -> None Wait until the greenlet finishes or *timeout* expires. Return ``None`` regardless. """ if self.ready(): return switch = getcurrent().switch # pylint:disable=undefined-variable self.rawlink(switch) try: t = Timeout._start_new_or_dummy(timeout) try: result = self.parent.switch() if result is not self: raise InvalidSwitchError( 'Invalid switch into Greenlet.join(): %r' % (result, )) finally: t.cancel() except Timeout as ex: self.unlink(switch) if ex is not t: raise except: self.unlink(switch) raise
def __get_or_peek(self, method, block, timeout): # Internal helper method. The `method` should be either # self._get when called from self.get() or self._peek when # called from self.peek(). Call this after the initial check # to see if there are items in the queue. if self.hub is getcurrent(): # special case to make get_nowait() or peek_nowait() runnable in the mainloop greenlet # there are no items in the queue; try to fix the situation by unlocking putters while self.putters: # Note: get() used popleft(), peek used pop(); popleft # is almost certainly correct. self.putters.popleft().put_and_switch() if self.qsize(): return method() raise Empty() if not block: # We can't block, we're not the hub, and we have nothing # to return. No choice... raise Empty() waiter = Waiter() # pylint:disable=undefined-variable timeout = Timeout._start_new_or_dummy(timeout, Empty) try: self.getters.append(waiter) if self.putters: self._schedule_unlock() result = waiter.get() if result is not waiter: raise InvalidSwitchError('Invalid switch into Queue.get: %r' % (result, )) return method() finally: timeout.cancel() _safe_remove(self.getters, waiter)
def _switch_to_hub(self, the_hub): self._drop_lock_for_switch_out() try: result = the_hub.switch() finally: self._acquire_lock_for_switch_in() if result is not self: # pragma: no cover raise InvalidSwitchError('Invalid switch into %s.wait(): %r' % ( self.__class__.__name__, result, ))
def _wait(self, timeout=None): if self.ready(): result = self._wait_return_value(False, False) # pylint:disable=assignment-from-none if self._notifier: # We're already notifying waiters; one of them must have run # and switched to us. switch = getcurrent().switch # pylint:disable=undefined-variable self._notifier.args[0].append(switch) switch_result = self.hub.switch() if switch_result is not self: # pragma: no cover raise InvalidSwitchError( 'Invalid switch into Event.wait(): %r' % (result, )) return result gotit = self._wait_core(timeout) return self._wait_return_value(True, gotit)
def wait(self, watcher): """ Wait until the *watcher* (which must not be started) is ready. The current greenlet will be unscheduled during this time. """ waiter = Waiter(self) # pylint:disable=undefined-variable watcher.start(waiter.switch, waiter) try: result = waiter.get() if result is not waiter: raise InvalidSwitchError( 'Invalid switch into %s: %r (expected %r)' % ( getcurrent(), # pylint:disable=undefined-variable result, waiter)) finally: watcher.stop()
def get(self, block=True, timeout=None): """ get(block=True, timeout=None) -> object Return the result the greenlet has returned or re-raise the exception it has raised. If block is ``False``, raise :class:`gevent.Timeout` if the greenlet is still alive. If block is ``True``, unschedule the current greenlet until the result is available or the timeout expires. In the latter cases, :class:`gevent.Timeout` is raised. """ if self.ready(): if self.successful(): return self.value self._raise_exception() if not block: raise Timeout() switch = getcurrent().switch # pylint:disable=undefined-variable self.rawlink(switch) try: t = Timeout._start_new_or_dummy(timeout) try: result = self.parent.switch() if result is not self: raise InvalidSwitchError( 'Invalid switch into Greenlet.get(): %r' % (result, )) finally: t.cancel() except: # unlinking in 'except' instead of finally is an optimization: # if switch occurred normally then link was already removed in _notify_links # and there's no need to touch the links set. # Note, however, that if "Invalid switch" assert was removed and invalid switch # did happen, the link would remain, causing another invalid switch later in this greenlet. self.unlink(switch) raise if self.ready(): if self.successful(): return self.value self._raise_exception()
def put(self, item, block=True, timeout=None): """Put an item into the queue. If optional arg *block* is true and *timeout* is ``None`` (the default), block if necessary until a free slot is available. If *timeout* is a positive number, it blocks at most *timeout* seconds and raises the :class:`Full` exception if no free slot was available within that time. Otherwise (*block* is false), put an item on the queue if a free slot is immediately available, else raise the :class:`Full` exception (*timeout* is ignored in that case). """ if self._maxsize == -1 or self.qsize() < self._maxsize: # there's a free slot, put an item right away self._put(item) if self.getters: self._schedule_unlock() elif self.hub is getcurrent(): # We're in the mainloop, so we cannot wait; we can switch to other greenlets though. # Check if possible to get a free slot in the queue. while self.getters and self.qsize( ) and self.qsize() >= self._maxsize: getter = self.getters.popleft() getter.switch(getter) if self.qsize() < self._maxsize: self._put(item) return raise Full elif block: waiter = ItemWaiter(item, self) self.putters.append(waiter) timeout = Timeout._start_new_or_dummy(timeout, Full) try: if self.getters: self._schedule_unlock() result = waiter.get() if result is not waiter: raise InvalidSwitchError( "Invalid switch into Queue.put: %r" % (result, )) finally: timeout.cancel() _safe_remove(self.putters, waiter) else: raise Full
def __wait_to_be_notified(self, rawlink): # pylint:disable=too-many-branches # We've got to watch where we could potentially release the GIL. # Decisions we make based an the state of this object must be in blocks # that cannot release the GIL. resume_this_greenlet = None watcher = None current_hub = get_hub() send = None while 1: my_hub = self.hub if my_hub is current_hub: break # We're owned by another hub. if my_hub.dead: # dead is a property, this could have released the GIL. # We have the GIL back. Did anything change? if my_hub is not self.hub: continue # start over. # The other hub is dead, so we can take ownership. self.hub = current_hub break # Some other hub owns this object. We must ask it to wake us # up. We can't use a Python-level ``Lock`` because # (1) it doesn't support a timeout on all platforms; and # (2) we don't want to block this hub from running. So we need to # do so in a way that cooperates with *two* hubs. That's what an # async watcher is built for. # # Allocating and starting the watcher *could* release the GIL. # with the libev corcext, allocating won't, but starting briefly will. # With other backends, allocating might, and starting might also. # So...XXX: Race condition here, tiny though it may be. watcher = current_hub.loop.async_() send = watcher.send_ignoring_arg if rawlink: # Make direct calls to self.rawlink, the most common case, # so cython can more easily optimize. self.rawlink(send) else: self._notifier.args[0].append(send) watcher.start(getcurrent().switch, self) # pylint:disable=undefined-variable break if self.hub is current_hub: resume_this_greenlet = getcurrent().switch # pylint:disable=undefined-variable if rawlink: self.rawlink(resume_this_greenlet) else: self._notifier.args[0].append(resume_this_greenlet) try: self._drop_lock_for_switch_out() result = current_hub.switch() # Probably releases # If we got here, we were automatically unlinked already. resume_this_greenlet = None if result is not self: # pragma: no cover raise InvalidSwitchError('Invalid switch into %s.wait(): %r' % ( self.__class__.__name__, result, )) finally: self._acquire_lock_for_switch_in() self.__unlink_all(resume_this_greenlet) self.__unlink_all(send) if watcher is not None: watcher.stop() watcher.close()