コード例 #1
0
ファイル: patch.py プロジェクト: breezechen/syncless
 def run(self):
   self.parent.switch()  # Resume to patch_gevent() after first startup.
   main_loop_tasklet = coio.get_main_loop_tasklet()
   assert self is not main_loop_tasklet._greenlet
   while True:
     assert stackless.current is main_loop_tasklet
     main_loop_tasklet._greenlet.switch()
コード例 #2
0
ファイル: patch.py プロジェクト: olopez32/syncless
 def run(self):
     self.parent.switch(
     )  # Resume to patch_gevent() after first startup.
     main_loop_tasklet = coio.get_main_loop_tasklet()
     assert self is not main_loop_tasklet._greenlet
     while True:
         assert stackless.current is main_loop_tasklet
         main_loop_tasklet._greenlet.switch()
コード例 #3
0
ファイル: patch.py プロジェクト: olopez32/syncless
def gevent_hub_main():
    """Run the gevent hub (+ Syncless) main loop forever.

  This function is a drop-in replacement of gevent.hub.get_hub.switch() with
  re-raising the GreeenletExit as SystemExit.

  See also patch_gevent() for more documentation and restrictions.
  """
    if 'syncless.coio' not in sys.modules:
        from syncless import best_greenlet
        return best_greenlet.gevent_hub_main()
    from gevent import hub
    if not getattr(hub, 'is_syncless_fake_hub', None):
        patch_gevent()
    from syncless import coio
    main_loop_tasklet = coio.get_main_loop_tasklet()
    hub_obj = hub.get_hub()
    hub_type = str(type(hub_obj))
    assert hub_type.startswith('<class '), hub_type
    assert hub_type.endswith(".SynclessFakeHub'>"), hub_type
    assert hub_obj, 'gevent hub not running'
    assert hub_obj._tasklet is main_loop_tasklet
    import stackless
    assert stackless.current is not main_loop_tasklet
    if getattr(hub.greenlet, 'is_pts_greenlet_emulated', None):
        from syncless import greenlet_using_stackless
        assert hub.greenlet is greenlet_using_stackless.greenlet
        assert greenlet_using_stackless.current is hub.greenlet.getcurrent()
        assert hub.MAIN is hub.greenlet.getcurrent()
        greenlet_using_stackless.current = hub_obj
        greenlet_using_stackless._insert_after_current_tasklet(
            main_loop_tasklet)
    else:
        # Implement _insert_after_current_tasklet(main_loop_tasklet).
        if stackless.current.next is stackless.current:
            main_loop_tasklet.insert()
        elif stackless.current.next is not main_loop_tasklet:
            # Below we insert main_loop_tasklet after stackless.current. The
            # implementation is tricky, see the details in
            # greenlet_using_stackless.py (search for next.next). Just calling
            # main_loop_tasklet.insert() would insert main_loop_tasklet before
            # stackless.current.
            #
            # TODO(pts): Present this implementation trick on the conference.
            main_loop_tasklet.remove()
            helper_tasklet = stackless.tasklet(
                lambda: stackless.current.next.next.remove().run())()
            helper_tasklet.insert()
            main_loop_tasklet.insert()
            helper_tasklet.run()
            helper_tasklet.remove()

        if hub.greenlet.getcurrent() is hub.MAIN:
            try:
                return stackless.schedule_remove()
            except stackless.greenlet.GreenletExit:
                raise SystemExit
    return stackless.schedule_remove()
コード例 #4
0
ファイル: patch.py プロジェクト: olopez32/syncless
 def RaiseInvalidHubSwitch():
     xtra = ''
     if stackless.current is stackless.main:
         xtra = ('; define a Main function, and call us from '
                 'gevent.hub.spawn_raw(Main)')
         raise AssertionError(
             'gevent.hub.get_hub().switch() called from the wrong tasklet (%r), '
             'expected main_loop_tasklet %r%s' %
             (stackless.current, coio.get_main_loop_tasklet(), xtra))
コード例 #5
0
ファイル: patch.py プロジェクト: breezechen/syncless
 def RaiseInvalidHubSwitch():
   xtra = ''
   if stackless.current is stackless.main:
     xtra = ('; define a Main function, and call us from '
             'gevent.hub.spawn_raw(Main)')
     raise AssertionError(
       'gevent.hub.get_hub().switch() called from the wrong tasklet (%r), '
       'expected main_loop_tasklet %r%s' %
       (stackless.current, coio.get_main_loop_tasklet(), xtra))
コード例 #6
0
ファイル: patch.py プロジェクト: breezechen/syncless
def gevent_hub_main():
  """Run the gevent hub (+ Syncless) main loop forever.

  This function is a drop-in replacement of gevent.hub.get_hub.switch() with
  re-raising the GreeenletExit as SystemExit.

  See also patch_gevent() for more documentation and restrictions.
  """
  if 'syncless.coio' not in sys.modules:
    from syncless import best_greenlet
    return best_greenlet.gevent_hub_main()
  from gevent import hub
  if not getattr(hub, 'is_syncless_fake_hub', None):
    patch_gevent()
  from syncless import coio
  main_loop_tasklet = coio.get_main_loop_tasklet()
  hub_obj = hub.get_hub()
  hub_type = str(type(hub_obj))
  assert hub_type.startswith('<class '), hub_type
  assert hub_type.endswith(".SynclessFakeHub'>"), hub_type
  assert hub_obj, 'gevent hub not running'
  assert hub_obj._tasklet is main_loop_tasklet
  import stackless
  assert stackless.current is not main_loop_tasklet
  if getattr(hub.greenlet, 'is_pts_greenlet_emulated', None):
    from syncless import greenlet_using_stackless
    assert hub.greenlet is greenlet_using_stackless.greenlet
    assert greenlet_using_stackless.current is hub.greenlet.getcurrent()
    assert hub.MAIN is hub.greenlet.getcurrent()
    greenlet_using_stackless.current = hub_obj
    greenlet_using_stackless._insert_after_current_tasklet(main_loop_tasklet)
  else:
    # Implement _insert_after_current_tasklet(main_loop_tasklet).
    if stackless.current.next is stackless.current:
      main_loop_tasklet.insert()
    elif stackless.current.next is not main_loop_tasklet:
      # Below we insert main_loop_tasklet after stackless.current. The
      # implementation is tricky, see the details in
      # greenlet_using_stackless.py (search for next.next). Just calling
      # main_loop_tasklet.insert() would insert main_loop_tasklet before
      # stackless.current.
      #
      # TODO(pts): Present this implementation trick on the conference.
      main_loop_tasklet.remove()
      helper_tasklet = stackless.tasklet(
          lambda: stackless.current.next.next.remove().run())()
      helper_tasklet.insert()
      main_loop_tasklet.insert()
      helper_tasklet.run()
      helper_tasklet.remove()

    if hub.greenlet.getcurrent() is hub.MAIN:
      try:
        return stackless.schedule_remove()
      except stackless.greenlet.GreenletExit:
        raise SystemExit
  return stackless.schedule_remove()
コード例 #7
0
ファイル: patch.py プロジェクト: olopez32/syncless
 def switch(self):
     main_loop_tasklet = coio.get_main_loop_tasklet()
     if stackless.current is not main_loop_tasklet:
         RaiseInvalidHubSwitch()
     cur = greenlet.getcurrent()
     assert cur is not main_loop_tasklet._greenlet, (
         'Cannot switch to MAINLOOP from MAINLOOP')
     switch_out = getattr(cur, 'switch_out', None)
     if switch_out is not None:
         try:
             switch_out()
         except:
             traceback.print_exc()
     # `return main_loop_tasklet._greenlet.switch()' would be straightforward
     # here, but greenlet seems to call self.run anyway after accept (proably
     # because it's C code ignoring the override of self.switch).
     return greenlet.switch(self)
コード例 #8
0
ファイル: patch.py プロジェクト: breezechen/syncless
 def switch(self):
   main_loop_tasklet = coio.get_main_loop_tasklet()
   if stackless.current is not main_loop_tasklet:
     RaiseInvalidHubSwitch()
   cur = greenlet.getcurrent()
   assert cur is not main_loop_tasklet._greenlet, (
       'Cannot switch to MAINLOOP from MAINLOOP')
   switch_out = getattr(cur, 'switch_out', None)
   if switch_out is not None:
     try:
       switch_out()
     except:
       traceback.print_exc()
   # `return main_loop_tasklet._greenlet.switch()' would be straightforward
   # here, but greenlet seems to call self.run anyway after accept (proably
   # because it's C code ignoring the override of self.switch).
   return greenlet.switch(self)
コード例 #9
0
ファイル: patch.py プロジェクト: olopez32/syncless
def patch_gevent(do_emulate_greenlet=None):
    """Patch gevent so it works with Syncless in the same process.

  Tested with gevent-0.12.2, please upgrade your gevent if it's older.

  Please note that patch_gevent() imports syncless.coio.

  Ctrl-<C> (KeyboardInterrupt) works as intended: it aborts the process by
  default with the KeyboardInterrupt exception in the main tasklet/greenlet.

  Since gevent is based on greenlet, and Syncless is based on Stackless, one
  of those has to be emulated. patch_gevent() takes care of this by using
  the greenlet emulation provided by syncless.greenlet_using_stackless if
  necessary.

  Please note that if you just want to use gevent without the Syncless
  non-blocking functionality, just do this:

    import syncless.best_greenlet  # To emulate greenlet with Stackless.
    #from syncless.best_greenlet.greenlet import greenlet  # If used.
    ...
    import gevent.hub ...
    ...
    if __name__ == '__main__':
      ...  # Create the server sockets, but don't accept() yet.
      gevent.hub.spawn_raw(Listener, ...)  # accept() in Listener().
      syncless.best_greenlet.gevent_hub_main()

   If you want to use gevent and Syncless together, import like this:
   
    import gevent.hub ...
    from syncless import coio
    from syncless import patch
    ...
    if __name__ == '__main__':
      ...  # Create the server sockets, but don't accept() yet.
      gevent.hub.spawn_raw(Listener, ...)  # accept() in Listener().
      ...  # Create and start non-blocking Syncless tasklets.
      patch.gevent_hub_main()  # Runs forever.

  See examples/demo_gevent.py for a comprehensive example program.

  Please note the following simple limitations of the gevent + Syncless
  cooperation:

  * Your program must not say any of

      from gevent.hub import greenlet    
      from gevent.hub import getcurrent
      from gevent.hub import GreenletExit
      from gevent.hub import get_hub

    , but it must say `from gevent import hub', and then use `hub.<symbol>'
    -- or you must call patch.patch_gevent() before doing these forbidden
    imports. (If you fail to do so, your gevent greenlets may silently get
    ignored.)

  * If you subclass gevent.greenlet.Greenlet, call patch.patch_gevent()
    before that. (If you fail to do so, you may not always get an error
    mesage, but your code won't work as expected. You will get an error
    message the first time you instantiate that class after
    patch.patch_gevent().)

  * You have to compile gevent and Syncless with the same version of libevent
    (e.g. libevent1 >= 1.4.13 or libevent2 >= 2.0.4). libev won't work, because
    gevent doesn't support libev. The same-version limitation is there because
    the Syncless main loop must be able to wait (poll) for events registered
    by gevent.
    
    Here is how to compile Syncless with libevent1:

      $ SYNCLESS_USE_LIBEVENT1=1 python setup.py build    
      $ sudo python setup.py install

    patch.patch_gevent() verifies that both the version and the notification
    method (e.g. select or epoll) used by Syncless and gevent match.
    Furthermore, the library file must be the same (e.g. it's not OK to
    compile Syncless with libevent.so in /usr/lib, and gevent with
    libevent.so in /usr/local/lib). This is not checked.

  * All non-blocking gevent I/O operations must be initiated from a greenlet
    created by the gevent hub. So you may create a server socket (and bind
    to it) in the main greenlet, but you may only accept() it in another
    greenlet, created by the gevent hub. So put your accept() to a function
    named Listener, and do a gevent.hub.spawn_raw(Listener) or
    gevent.swawn(Listener). There is no such restriction for Syncless I/O
    operations: you can do them anywhere.

  * Communication and synchronization between gevent greenlets and
    Syncless/Stackless tasklets in the same process is not possible, i.e. a
    tasklet cannot wait for data generated by a greenlet or vice versa.
    (Please note that data sharing in the process memory works as intended,
    because both greenlets and tasklets are coroutine-based.) If you need
    that, currently your only option is to create a pipe or a socket, and
    send notification bytes through it. Please note that you don't have to
    serialize the actual data, since you can store them in a variable
    accessible to both the tasklet and the greenlet.

  * Don't do Syncless non-blocking I/O before importing gevent (because the
    event_init() called when importing gevent.core makes all
    registered Syncless events vanish, without ever triggering).

  * The gevent.hub.get_hub().shutdown() function is not supported. Please
    use sys.exit() from your main greenlet, and gevent.hub.MAIN.throw()
    (or gevent.hub.MAIN.throw(SystemExit)) form all non-blocking greenlets
    started by the gevent hub.

  TODO(pts): Measure performance (will be slower than pure Syncless or pure
  gevent, because either Stackless or gevent is emulated).

  Note: It was very tricky to get exception handling right, especially
  making the exception handler in gevent.core.__event_handler ignore the
  TaskletExit exceptions at exit time.

  Args:
    do_emulate_greenlet: If True, use syncless.greenlet_useing_stackless.
      If False, use the built-in greenlet. If None (by default), autodetect.
  """
    if ('gevent.hub' in sys.modules and getattr(sys.modules['gevent.hub'],
                                                'is_syncless_fake_hub', None)):
        return  # Already patched.

    from syncless import coio
    assert sys.modules.get('stackless') is coio.stackless, (
        'stackless and coio.stackless modules do not match')
    stackless = coio.stackless
    if do_emulate_greenlet is None:
        # We emulate greenlet if we have native Stackless.
        do_emulate_greenlet = type(
            stackless.getcurrent) is not types.FunctionType
    if do_emulate_greenlet:
        # We might have a native greenlet compiled and loaded, but we will be
        # using the emulated greenlet (syncless.greenlet_using_stackless).
        from syncless import greenlet_using_stackless
        greenlet = greenlet_using_stackless.greenlet  # The greenlet class.
        assert getattr(greenlet, 'is_pts_greenlet_emulated',
                       None) is True, ('properly emulated greenlet not found')
    else:
        greenlet_using_stackless = None
        greenlet = stackless.greenlet
        assert not getattr(greenlet, 'is_pts_greenlet_emulated', None), (
            'found emulated greenlet, expected non-emulated')
        assert type(greenlet.getcurrent) is not types.FunctionType, (
            'expected native greenlet')

    from gevent import core  # core.pyx C extension, doesn't use the hub.
    gevent_info = (core.get_method(), core.get_version())
    syncless_info = (coio.method(), coio.version())
    assert gevent_info == syncless_info, (
        'event library mismatch: gevent uses %r, Syncless uses %r; '
        'to fix, recompile Syncless with the proper SYNCLESS_USE_... '
        'setting, or recompile gevent properly' % (gevent_info, syncless_info))

    # gevent.hub is the only module in gevent which imports greenlet. So we
    # import it first, and patch it so it will use our greenlet.
    from gevent import hub
    # We can't check this, because simple gevent initialization (such as
    # calling gevent.hub.spawn_raw) has already created a hub.
    main_greenlet = greenlet.getcurrent()
    if greenlet is hub.greenlet:
        assert main_greenlet is hub.MAIN
    else:
        assert not hasattr(hub._threadlocal, 'hub'), (
            'too late, gevent hub already created; please call '
            'patch.patch_gevent() before creating (spawning) gevent greenlets')
        # Replace hub.greenlet with our greenlet class.
        old_hub_greenlet = hub.greenlet
        old_getcurrent = old_hub_greenlet.getcurrent
        old_GreenletExit = old_hub_greenlet.GreenletExit
        old_error = getattr(old_hub_greenlet, 'error', [])
        old_MAIN = hub.MAIN
        for name in sorted(dir(hub)):
            value = getattr(hub, name)
            if value is old_hub_greenlet:
                setattr(hub, name, greenlet)
            elif value is old_getcurrent:
                setattr(hub, name, greenlet.getcurrent)
            elif value is old_GreenletExit:
                setattr(hub, name, greenlet.GreenletExit)
            elif value is old_error:
                setattr(hub, name, greenlet.error)
            elif value is old_MAIN:
                setattr(hub, name, main_greenlet)
        hub.MAIN = main_greenlet

        # Now replace GreenletExit and getcurrent in other modules. This is
        # needed because gevent.socket does a `from gevent.hub import getcurrent'.
        for module_name in sorted(dir(sys.modules['gevent']) + ['']):
            if module_name:
                module = getattr(sys.modules['gevent'], module_name)
            else:
                module = sys.modules['gevent']
            if type(module) is type(sys):
                for name in sorted(dir(module)):
                    value = getattr(module, name)
                    if value is old_getcurrent:
                        setattr(module, name, greenlet.getcurrent)
                    elif value is old_GreenletExit:
                        setattr(module, name, greenlet.GreenletExit)
                    elif value is old_hub_greenlet:
                        setattr(module, name, greenlet)

        old_Greenlet = sys.modules['gevent.greenlet'].Greenlet
        new_dict = dict(old_Greenlet.__dict__)
        new_dict.pop('__dict__', None)
        # Create the new greenlet class with the correct base class.
        new_Greenlet = type(old_Greenlet.__name__, (greenlet, ), new_dict)
        sys.modules['gevent.greenlet'].Greenlet = new_Greenlet

        def New(*args, **kwargs):
            # It would be awesome if we could find and fix classes already created,
            # but we can't.
            assert 0, ('please call patch.patch_gevent() '
                       'before subclassing gevent.greenlet.Greenlet')

        old_Greenlet.__new__ = classmethod(New)

        # Add trampolines so old bound methods continue to work.
        # Example for an old bound method: `spawn = Greenlet.spawn' in
        # gevent/__init__.py .
        #
        # This is getting very ugly. It would be simpler to change the __bases__
        # of old_Greenlet to greenlet, but Python doesn't allow that here
        # (because that would change the deallocator).
        old_Greenlet._trampto__ = new_Greenlet
        import re
        for name in sorted(new_dict):
            value = new_dict[name]
            if (isinstance(value, classmethod)
                    and re.match(r'[_a-zA-Z]\w*\Z', name)):
                src_template = ('lambda cls, *args, **kwargs: '
                                'cls._trampto__.$(*args, **kwargs)')
                code_obj = compile(src_template.replace('$', name), '<tramp>',
                                   'eval')
                old_value = new_dict[name].__get__(0).im_func
                setattr(
                    new_Greenlet, name,
                    classmethod(
                        types.FunctionType(old_value.func_code,
                                           old_value.func_globals, None,
                                           old_value.func_defaults)))
                old_value.func_code = eval(code_obj, {}).func_code
                old_value.func_defaults = ()
            elif isinstance(value, staticmethod):
                raise NotImplementedError(
                    'static methods in Greenlet not supported')

        del old_Greenlet
        del old_hub_greenlet
        del old_getcurrent
        del old_GreenletExit
        del old_error
        del old_MAIN

    import traceback
    hub_obj = getattr(getattr(hub, '_threadlocal', hub), 'hub', None)
    if hasattr(hub_obj, 'switch'):
        # A hub is not a switch :-). Seriously, we execute this branch if there
        # is already a greenlet hub. This assertion prevents the possibility
        # that the patching happens inside an libevent event handler executing
        # in the gevent hub.
        #
        # `not hub_obj' tests that the hub greenlet has not been started yet.
        assert not hub_obj, 'too late, gevent hub already running'
    hub.__dict__.pop('thread', None)
    hub.is_syncless_fake_hub = True

    def RaiseInvalidHubSwitch():
        xtra = ''
        if stackless.current is stackless.main:
            xtra = ('; define a Main function, and call us from '
                    'gevent.hub.spawn_raw(Main)')
            raise AssertionError(
                'gevent.hub.get_hub().switch() called from the wrong tasklet (%r), '
                'expected main_loop_tasklet %r%s' %
                (stackless.current, coio.get_main_loop_tasklet(), xtra))

    class SynclessFakeHub(greenlet):
        if do_emulate_greenlet:
            # This is needed so late TaskletExit exceptions in
            # syncless.greenlet_using_stackless will be properly ignored.
            is_gevent_hub = True

            def switch(self):
                if self._tasklet.scheduled:
                    RaiseInvalidHubSwitch()
                cur = greenlet_using_stackless.current  # greenlet.getcurrent()
                assert cur is not self, (
                    'Cannot switch to MAINLOOP from MAINLOOP')
                switch_out = getattr(cur, 'switch_out', None)
                if switch_out is not None:
                    try:
                        switch_out()
                    except:
                        traceback.print_exc()
                return greenlet.switch(self)

            @property
            def run(self):
                assert 0, 'internal logic error: FakeHub().run requested'
        else:  # With real (non-emulated) greenlet.

            def switch(self):
                main_loop_tasklet = coio.get_main_loop_tasklet()
                if stackless.current is not main_loop_tasklet:
                    RaiseInvalidHubSwitch()
                cur = greenlet.getcurrent()
                assert cur is not main_loop_tasklet._greenlet, (
                    'Cannot switch to MAINLOOP from MAINLOOP')
                switch_out = getattr(cur, 'switch_out', None)
                if switch_out is not None:
                    try:
                        switch_out()
                    except:
                        traceback.print_exc()
                # `return main_loop_tasklet._greenlet.switch()' would be straightforward
                # here, but greenlet seems to call self.run anyway after accept (proably
                # because it's C code ignoring the override of self.switch).
                return greenlet.switch(self)

            def run(self):
                self.parent.switch(
                )  # Resume to patch_gevent() after first startup.
                main_loop_tasklet = coio.get_main_loop_tasklet()
                assert self is not main_loop_tasklet._greenlet
                while True:
                    assert stackless.current is main_loop_tasklet
                    main_loop_tasklet._greenlet.switch()

    fake_hub = SynclessFakeHub()
    if not do_emulate_greenlet:
        greenlet.switch(fake_hub)  # Start the hub for the first time.
    fake_hub._tasklet = coio.get_main_loop_tasklet()
    hub._threadlocal = type(hub)('fake_threadlocal')
    # Make existing references to the old get_hub() work.
    hub.hub = hub._threadlocal.hub = fake_hub

    f = lambda fake_hub=fake_hub: fake_hub
    hub.get_hub.func_code = f.func_code
    hub.get_hub.func_defaults = f.func_defaults

    def ErrorNoNewHub(self):
        assert 0, 'too late creating a new Hub'

    hub.Hub.__new__ = classmethod(lambda *args: ErrorNoNewHub())
    del hub.Hub
    SynclessFakeHub.__new__ = classmethod(lambda *args: ErrorNoNewHub())

    best_greenlet = sys.modules.get('sycnless.best_greenlet')
    if best_greenlet is not None:
        best_greenlet.gevent_hub_main = gevent_hub_main
        best_greenlet.greenlet.gevent_hub_main = gevent_hub_main
コード例 #10
0
ファイル: patch.py プロジェクト: breezechen/syncless
def patch_gevent(do_emulate_greenlet=None):
  """Patch gevent so it works with Syncless in the same process.

  Tested with gevent-0.12.2, please upgrade your gevent if it's older.

  Please note that patch_gevent() imports syncless.coio.

  Ctrl-<C> (KeyboardInterrupt) works as intended: it aborts the process by
  default with the KeyboardInterrupt exception in the main tasklet/greenlet.

  Since gevent is based on greenlet, and Syncless is based on Stackless, one
  of those has to be emulated. patch_gevent() takes care of this by using
  the greenlet emulation provided by syncless.greenlet_using_stackless if
  necessary.

  Please note that if you just want to use gevent without the Syncless
  non-blocking functionality, just do this:

    import syncless.best_greenlet  # To emulate greenlet with Stackless.
    #from syncless.best_greenlet.greenlet import greenlet  # If used.
    ...
    import gevent.hub ...
    ...
    if __name__ == '__main__':
      ...  # Create the server sockets, but don't accept() yet.
      gevent.hub.spawn_raw(Listener, ...)  # accept() in Listener().
      syncless.best_greenlet.gevent_hub_main()

   If you want to use gevent and Syncless together, import like this:
   
    import gevent.hub ...
    from syncless import coio
    from syncless import patch
    ...
    if __name__ == '__main__':
      ...  # Create the server sockets, but don't accept() yet.
      gevent.hub.spawn_raw(Listener, ...)  # accept() in Listener().
      ...  # Create and start non-blocking Syncless tasklets.
      patch.gevent_hub_main()  # Runs forever.

  See examples/demo_gevent.py for a comprehensive example program.

  Please note the following simple limitations of the gevent + Syncless
  cooperation:

  * Your program must not say any of

      from gevent.hub import greenlet    
      from gevent.hub import getcurrent
      from gevent.hub import GreenletExit
      from gevent.hub import get_hub

    , but it must say `from gevent import hub', and then use `hub.<symbol>'
    -- or you must call patch.patch_gevent() before doing these forbidden
    imports. (If you fail to do so, your gevent greenlets may silently get
    ignored.)

  * If you subclass gevent.greenlet.Greenlet, call patch.patch_gevent()
    before that. (If you fail to do so, you may not always get an error
    mesage, but your code won't work as expected. You will get an error
    message the first time you instantiate that class after
    patch.patch_gevent().)

  * You have to compile gevent and Syncless with the same version of libevent
    (e.g. libevent1 >= 1.4.13 or libevent2 >= 2.0.4). libev won't work, because
    gevent doesn't support libev. The same-version limitation is there because
    the Syncless main loop must be able to wait (poll) for events registered
    by gevent.
    
    Here is how to compile Syncless with libevent1:

      $ SYNCLESS_USE_LIBEVENT1=1 python setup.py build    
      $ sudo python setup.py install

    patch.patch_gevent() verifies that both the version and the notification
    method (e.g. select or epoll) used by Syncless and gevent match.
    Furthermore, the library file must be the same (e.g. it's not OK to
    compile Syncless with libevent.so in /usr/lib, and gevent with
    libevent.so in /usr/local/lib). This is not checked.

  * All non-blocking gevent I/O operations must be initiated from a greenlet
    created by the gevent hub. So you may create a server socket (and bind
    to it) in the main greenlet, but you may only accept() it in another
    greenlet, created by the gevent hub. So put your accept() to a function
    named Listener, and do a gevent.hub.spawn_raw(Listener) or
    gevent.swawn(Listener). There is no such restriction for Syncless I/O
    operations: you can do them anywhere.

  * Communication and synchronization between gevent greenlets and
    Syncless/Stackless tasklets in the same process is not possible, i.e. a
    tasklet cannot wait for data generated by a greenlet or vice versa.
    (Please note that data sharing in the process memory works as intended,
    because both greenlets and tasklets are coroutine-based.) If you need
    that, currently your only option is to create a pipe or a socket, and
    send notification bytes through it. Please note that you don't have to
    serialize the actual data, since you can store them in a variable
    accessible to both the tasklet and the greenlet.

  * Don't do Syncless non-blocking I/O before importing gevent (because the
    event_init() called when importing gevent.core makes all
    registered Syncless events vanish, without ever triggering).

  * The gevent.hub.get_hub().shutdown() function is not supported. Please
    use sys.exit() from your main greenlet, and gevent.hub.MAIN.throw()
    (or gevent.hub.MAIN.throw(SystemExit)) form all non-blocking greenlets
    started by the gevent hub.

  TODO(pts): Measure performance (will be slower than pure Syncless or pure
  gevent, because either Stackless or gevent is emulated).

  Note: It was very tricky to get exception handling right, especially
  making the exception handler in gevent.core.__event_handler ignore the
  TaskletExit exceptions at exit time.

  Args:
    do_emulate_greenlet: If True, use syncless.greenlet_useing_stackless.
      If False, use the built-in greenlet. If None (by default), autodetect.
  """
  if ('gevent.hub' in sys.modules and
      getattr(sys.modules['gevent.hub'], 'is_syncless_fake_hub', None)):
    return   # Already patched.

  from syncless import coio
  assert sys.modules.get('stackless') is coio.stackless, (
      'stackless and coio.stackless modules do not match')
  stackless = coio.stackless
  if do_emulate_greenlet is None:
    # We emulate greenlet if we have native Stackless.
    do_emulate_greenlet = type(stackless.getcurrent) is not types.FunctionType
  if do_emulate_greenlet:
    # We might have a native greenlet compiled and loaded, but we will be
    # using the emulated greenlet (syncless.greenlet_using_stackless).
    from syncless import greenlet_using_stackless
    greenlet = greenlet_using_stackless.greenlet  # The greenlet class.
    assert getattr(greenlet, 'is_pts_greenlet_emulated', None) is True, (
        'properly emulated greenlet not found')
  else:
    greenlet_using_stackless = None
    greenlet = stackless.greenlet
    assert not getattr(greenlet, 'is_pts_greenlet_emulated', None), (
        'found emulated greenlet, expected non-emulated')
    assert type(greenlet.getcurrent) is not types.FunctionType, (
        'expected native greenlet')

  from gevent import core  # core.pyx C extension, doesn't use the hub.
  gevent_info = (core.get_method(), core.get_version())
  syncless_info = (coio.method(), coio.version())
  assert gevent_info == syncless_info, (
      'event library mismatch: gevent uses %r, Syncless uses %r; '
      'to fix, recompile Syncless with the proper SYNCLESS_USE_... '
      'setting, or recompile gevent properly' %
      (gevent_info, syncless_info))

  # gevent.hub is the only module in gevent which imports greenlet. So we
  # import it first, and patch it so it will use our greenlet.
  from gevent import hub
  # We can't check this, because simple gevent initialization (such as
  # calling gevent.hub.spawn_raw) has already created a hub.
  main_greenlet = greenlet.getcurrent()
  if greenlet is hub.greenlet:
    assert main_greenlet is hub.MAIN
  else:
    assert not hasattr(hub._threadlocal, 'hub'), (
        'too late, gevent hub already created; please call '
        'patch.patch_gevent() before creating (spawning) gevent greenlets')
    # Replace hub.greenlet with our greenlet class.
    old_hub_greenlet = hub.greenlet
    old_getcurrent = old_hub_greenlet.getcurrent
    old_GreenletExit = old_hub_greenlet.GreenletExit
    old_error = getattr(old_hub_greenlet, 'error', [])
    old_MAIN = hub.MAIN
    for name in sorted(dir(hub)):
      value = getattr(hub, name)
      if value is old_hub_greenlet:
        setattr(hub, name, greenlet)
      elif value is old_getcurrent:
        setattr(hub, name, greenlet.getcurrent)
      elif value is old_GreenletExit:
        setattr(hub, name, greenlet.GreenletExit)
      elif value is old_error:
        setattr(hub, name, greenlet.error)
      elif value is old_MAIN:
        setattr(hub, name, main_greenlet)
    hub.MAIN = main_greenlet

    # Now replace GreenletExit and getcurrent in other modules. This is
    # needed because gevent.socket does a `from gevent.hub import getcurrent'.
    for module_name in sorted(dir(sys.modules['gevent']) + ['']):
      if module_name:
        module = getattr(sys.modules['gevent'], module_name)
      else:
        module = sys.modules['gevent']
      if type(module) is type(sys):
        for name in sorted(dir(module)):
          value = getattr(module, name)
          if value is old_getcurrent:
            setattr(module, name, greenlet.getcurrent)
          elif value is old_GreenletExit:
            setattr(module, name, greenlet.GreenletExit)
          elif value is old_hub_greenlet:
            setattr(module, name, greenlet)

    old_Greenlet = sys.modules['gevent.greenlet'].Greenlet
    new_dict = dict(old_Greenlet.__dict__)
    new_dict.pop('__dict__', None)
    # Create the new greenlet class with the correct base class.
    new_Greenlet = type(old_Greenlet.__name__, (greenlet,), new_dict)
    sys.modules['gevent.greenlet'].Greenlet = new_Greenlet

    def New(*args, **kwargs):
      # It would be awesome if we could find and fix classes already created,
      # but we can't.
      assert 0, ('please call patch.patch_gevent() '
                 'before subclassing gevent.greenlet.Greenlet')
    old_Greenlet.__new__ = classmethod(New)

    # Add trampolines so old bound methods continue to work.
    # Example for an old bound method: `spawn = Greenlet.spawn' in
    # gevent/__init__.py .
    #
    # This is getting very ugly. It would be simpler to change the __bases__
    # of old_Greenlet to greenlet, but Python doesn't allow that here
    # (because that would change the deallocator).
    old_Greenlet._trampto__ = new_Greenlet
    import re
    for name in sorted(new_dict):
      value = new_dict[name]
      if (isinstance(value, classmethod) and
          re.match(r'[_a-zA-Z]\w*\Z', name)):
        src_template = ('lambda cls, *args, **kwargs: '
                        'cls._trampto__.$(*args, **kwargs)')
        code_obj = compile(src_template.replace('$', name), '<tramp>', 'eval')
        old_value = new_dict[name].__get__(0).im_func
        setattr(new_Greenlet, name, classmethod(types.FunctionType(
            old_value.func_code, old_value.func_globals,
            None, old_value.func_defaults)))
        old_value.func_code = eval(code_obj, {}).func_code
        old_value.func_defaults = ()
      elif isinstance(value, staticmethod):
        raise NotImplementedError('static methods in Greenlet not supported')
    
    del old_Greenlet
    del old_hub_greenlet
    del old_getcurrent
    del old_GreenletExit
    del old_error
    del old_MAIN

  import traceback
  hub_obj = getattr(getattr(hub, '_threadlocal', hub), 'hub', None)
  if hasattr(hub_obj, 'switch'):
    # A hub is not a switch :-). Seriously, we execute this branch if there
    # is already a greenlet hub. This assertion prevents the possibility
    # that the patching happens inside an libevent event handler executing
    # in the gevent hub.
    #
    # `not hub_obj' tests that the hub greenlet has not been started yet.
    assert not hub_obj, 'too late, gevent hub already running'
  hub.__dict__.pop('thread', None)
  hub.is_syncless_fake_hub = True
  def RaiseInvalidHubSwitch():
    xtra = ''
    if stackless.current is stackless.main:
      xtra = ('; define a Main function, and call us from '
              'gevent.hub.spawn_raw(Main)')
      raise AssertionError(
        'gevent.hub.get_hub().switch() called from the wrong tasklet (%r), '
        'expected main_loop_tasklet %r%s' %
        (stackless.current, coio.get_main_loop_tasklet(), xtra))
  class SynclessFakeHub(greenlet):
    if do_emulate_greenlet:
      # This is needed so late TaskletExit exceptions in
      # syncless.greenlet_using_stackless will be properly ignored.
      is_gevent_hub = True
      def switch(self):
        if self._tasklet.scheduled:
          RaiseInvalidHubSwitch()
        cur = greenlet_using_stackless.current  # greenlet.getcurrent()
        assert cur is not self, (
            'Cannot switch to MAINLOOP from MAINLOOP')
        switch_out = getattr(cur, 'switch_out', None)
        if switch_out is not None: 
          try:
            switch_out()
          except:
            traceback.print_exc()  
        return greenlet.switch(self)
      @property
      def run(self):
        assert 0, 'internal logic error: FakeHub().run requested'
    else:  # With real (non-emulated) greenlet.
      def switch(self):
        main_loop_tasklet = coio.get_main_loop_tasklet()
        if stackless.current is not main_loop_tasklet:
          RaiseInvalidHubSwitch()
        cur = greenlet.getcurrent()
        assert cur is not main_loop_tasklet._greenlet, (
            'Cannot switch to MAINLOOP from MAINLOOP')
        switch_out = getattr(cur, 'switch_out', None)
        if switch_out is not None:
          try:
            switch_out()
          except:
            traceback.print_exc()
        # `return main_loop_tasklet._greenlet.switch()' would be straightforward
        # here, but greenlet seems to call self.run anyway after accept (proably
        # because it's C code ignoring the override of self.switch).
        return greenlet.switch(self)
      def run(self):
        self.parent.switch()  # Resume to patch_gevent() after first startup.
        main_loop_tasklet = coio.get_main_loop_tasklet()
        assert self is not main_loop_tasklet._greenlet
        while True:
          assert stackless.current is main_loop_tasklet
          main_loop_tasklet._greenlet.switch()
  fake_hub = SynclessFakeHub()
  if not do_emulate_greenlet:
    greenlet.switch(fake_hub)  # Start the hub for the first time.
  fake_hub._tasklet = coio.get_main_loop_tasklet()
  hub._threadlocal = type(hub)('fake_threadlocal')
  # Make existing references to the old get_hub() work.
  hub.hub = hub._threadlocal.hub = fake_hub

  f = lambda fake_hub=fake_hub: fake_hub
  hub.get_hub.func_code = f.func_code
  hub.get_hub.func_defaults = f.func_defaults
  def ErrorNoNewHub(self):
    assert 0, 'too late creating a new Hub'
  hub.Hub.__new__ = classmethod(lambda *args: ErrorNoNewHub())
  del hub.Hub
  SynclessFakeHub.__new__ = classmethod(lambda *args: ErrorNoNewHub())

  best_greenlet = sys.modules.get('sycnless.best_greenlet')
  if best_greenlet is not None:
    best_greenlet.gevent_hub_main = gevent_hub_main
    best_greenlet.greenlet.gevent_hub_main = gevent_hub_main