Is gevent.core.active_event thread safe?

227 views
Skip to first unread message

Jim Fulton

unread,
Jan 22, 2012, 5:11:31 PM1/22/12
to gev...@googlegroups.com
I assume it is, as the thread pool implementation (at least when I
looked a few weeks ago) depends on it.

I assume it's like Twisted callFromThread, except that it doesn't wake
up the gevent equivalent of the reactor.

If gevent.core.active_event is thread safe, it would be great if the
documentation would say so.

Better yet, it would be great of there was a call_from_thread method
somewhere in gevent.

(My use case, fwiw, is integrating a 3rd-party library, zookeeper,
that has asyncronous callbacks from it's own thread with a
gevent-based application.)

Jim

--
Jim Fulton
http://www.linkedin.com/in/jimfulton

Denis Bilenko

unread,
Jan 23, 2012, 1:53:53 AM1/23/12
to gev...@googlegroups.com
In gevent-0.x, nothing is thread-safe.

In gevent-1.x, async's watcher send() method is thread-safe and is
specifically designed to notify the even loop from threads and signal
handlers.

On Mon, Jan 23, 2012 at 5:11 AM, Jim Fulton <j...@zope.com> wrote:
> I assume it is, as the thread pool implementation (at least when I
> looked a few weeks ago) depends on it.

Which thread pool implementation? The one 1.0b1 uses async watcher.

If there's a thread pool for gevent-0.x that uses core.active_event
from another thread then it has a race condition in it.

> I assume it's like Twisted callFromThread, except that it doesn't wake
> up the gevent equivalent of the reactor.
>

> Better yet, it would be great of there was a call_from_thread method
> somewhere in gevent.

This implements call_from_thread:

>>> from gevent.threadpool import ThreadPool
>>> ThreadPool(1).apply(myfunc) # calling myfunc() in thread

> (My use case, fwiw, is integrating a 3rd-party library, zookeeper,
> that has asyncronous callbacks from it's own thread with a
> gevent-based application.)

Can you try gevent 1.0b1 for this?
You can get it from http://code.google.com/p/gevent/downloads/list

Cheers,
Denis.

Jim Fulton

unread,
Jan 25, 2012, 6:47:10 AM1/25/12
to gev...@googlegroups.com
On Mon, Jan 23, 2012 at 1:53 AM, Denis Bilenko <denis....@gmail.com> wrote:
> In gevent-0.x, nothing is thread-safe.

Hm, OK. gevent.core.active_event is implemented by an extension
so it's protected by the GIL. Is it unsafe at the C level?

>
> In gevent-1.x, async's watcher send() method is thread-safe and is
> specifically designed to notify the even loop from threads and signal
> handlers.

Cool. Where would I learn more about this?

I can't tell what you're refering to. I didn't see anything in the
new docs and there's neither an async or a watcher module.

>
> On Mon, Jan 23, 2012 at 5:11 AM, Jim Fulton <j...@zope.com> wrote:
>> I assume it is, as the thread pool implementation (at least when I
>> looked a few weeks ago) depends on it.
>
> Which thread pool implementation? The one 1.0b1 uses async watcher.

https://bitbucket.org/denis/gevent-playground/src/49d1cdcdf643/geventutil/threadpool.py

I interpreted this in terms of the 0.x code. MTAsyncResult.set calls
AsyncResult.set, which, in 0.x, calls gevent.core.active_event.

> If there's a thread pool for gevent-0.x that uses core.active_event
> from another thread then it has a race condition in it.

Can you briefly describe the race? Is this in interacting with
libevent at the C level?

>
>> I assume it's like Twisted callFromThread, except that it doesn't wake
>> up the gevent equivalent of the reactor.
>>
>> Better yet, it would be great of there was a call_from_thread method
>> somewhere in gevent.
>
> This implements call_from_thread:
>
>>>> from gevent.threadpool import ThreadPool

>>>> ThreadPool(1).apply(myfunc)  # calling myfunc() in thread

Sorry, I didn't communicate clearly. Let me try again without
reference to Twisted APIs.

I have a gevent application. gevent is running in the main thread.

A third-party thread needs to notify my application of some event. I
register a handler for that event with the third-party library. When
my handler is called, it's called in the third-party library's
thread. My handler then needs to communicate with the rest of my
application which is running in greenlet's in the main thread. How do
I do this?

I'm guessing, looking at the 1.0b1 code in event.py and hub.py that I
want to call

ahub.loop.callback()(myfunc)

where ahub is the hub for the main thread (or whatever thread I want to
call into).

>
>> (My use case, fwiw, is integrating a 3rd-party library, zookeeper,
>> that has asyncronous callbacks from it's own thread with a
>> gevent-based application.)
>
> Can you try gevent 1.0b1 for this?
> You can get it from http://code.google.com/p/gevent/downloads/list

Sure. 1.0b1 exposed a race condition in my tests, which is good, but
took some time to debug.

I like the fact that I no longer need to build libevent and that I can
run gevent in multiple threads or even one thread other than the main
thread. I *really* like the latter.

FWIW, my application runs ~13% slower with 1.0b1 than with 0.13.6.

(My application is a sort of a WSGI proxy,
http://svn.zope.org/Sandbox/J1m/resumelb/. A process running a
gevent.pywsgi.WSGIServer forwards (marshalled WSGI) requests to a
separate worker process. In my test, I ran ab against a front-end
process with a single worker running a trivial WSGI app. With 0.13.6,
I got ~1650 requests per second with a concurrency of 20 and with
1.0b1, I got around 1450 requests per second.)

Denis Bilenko

unread,
Jan 26, 2012, 2:52:04 AM1/26/12
to gev...@googlegroups.com
On Wed, Jan 25, 2012 at 6:47 PM, Jim Fulton <j...@zope.com> wrote:
> On Mon, Jan 23, 2012 at 1:53 AM, Denis Bilenko <denis....@gmail.com> wrote:
>> In gevent-0.x, nothing is thread-safe.
>
> Hm, OK.  gevent.core.active_event is implemented by an extension
> so it's protected by the GIL.  Is it unsafe at the C level?

> Can you briefly describe the race? Is this in interacting with


> libevent at the C level?

GIL does not help here, because core.dispatch() releases it while
calling event_dispatch().

So event_dispatch() and active_event() both access the active events
queue from different threads which is not documented as thread-safe so
I assume it isn't.

>> In gevent-1.x, async's watcher send() method is thread-safe and is
>> specifically designed to notify the even loop from threads and signal
>> handlers.
>
> Cool. Where would I learn more about this?

The 1.0b1 does not have much documentation yet, so you have to read the source.

Regarding the gevent.core module, it is a relatively thin wrapper
around libev, which has pretty good documentation at
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod

> I can't tell what you're refering to.  I didn't see anything in the
> new docs and there's neither an async or a watcher module.

Here's the async watcher definition:
https://bitbucket.org/denis/gevent/src/tip/gevent/core.ppyx#cl-772
Here's relevant libev documentation:
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev_async_code_how_to_wake_up_an

Quick sample:

>>> import gevent
>>> async_watcher = gevent.get_hub().loop.async()
>>> async_watcher.start(sys.stderr.write, '<NOTIFICATION>\n')
>>> async_watcher.send() # this is thread-safe
>>> gevent.sleep(1)
<NOTIFICATION>


>> This implements call_from_thread:
>>
>>>>> from gevent.threadpool import ThreadPool
>>>>> ThreadPool(1).apply(myfunc)  # calling myfunc() in thread
>
> Sorry, I didn't communicate clearly.  Let me try again without
> reference to Twisted APIs.

Actually I confused twisted's callFromThread and callInThread. The
above implements callInThread.

> I have a gevent application.  gevent is running in the main thread.
>
> A third-party thread needs to notify my application of some event.  I
> register a handler for that event with the third-party library. When
> my handler is called, it's called in the third-party library's
> thread.  My handler then needs to communicate with the rest of my
> application which is running in greenlet's in the main thread. How do
> I do this?
>
> I'm guessing, looking at the 1.0b1 code in event.py and hub.py that I
> want to call
>
>  ahub.loop.callback()(myfunc)
>
> where ahub is the hub for the main thread (or whatever thread I want to
> call into).

No, that would not be thread-safe.

Take a look at gevent.threadpool.ThreadResult. This is what ThreadPool
uses to pass results from worker threads to the main thread.

Quick sample:

>>> import gevent.threadpool
>>> def func(result): print 'got result: %r' % (result.value, )
...
>>> result = gevent.threadpool.ThreadResult(func)
>>> gevent.threadpool.ThreadPool(1).apply(lambda: result.set(2+2)) # 2+2 is calculated in another thread
got result: 4

ThreadResult might not suit you completely as it is a one-shot type of
communication. But it shows how to do this sort of thing. In the
future I think we'll have more tools for communicating with the main
thread from the worker thread, like queues.

> I like the fact that I no longer need to build libevent and that I can
> run gevent in multiple threads or even one thread other than the main
> thread. I *really* like the latter.
>
> FWIW, my application runs ~13% slower with 1.0b1 than with 0.13.6.

That's interesting. I'll try to do some benchmarks later and if I
cannot reproduce slow down, I'll contact you for more details if you
don't mind.

Jim Fulton

unread,
Jan 26, 2012, 10:57:55 AM1/26/12
to gev...@googlegroups.com
On Thu, Jan 26, 2012 at 2:52 AM, Denis Bilenko <denis....@gmail.com> wrote:
> On Wed, Jan 25, 2012 at 6:47 PM, Jim Fulton <j...@zope.com> wrote:
>> On Mon, Jan 23, 2012 at 1:53 AM, Denis Bilenko <denis....@gmail.com> wrote:

...

>> I have a gevent application.  gevent is running in the main thread.
>>
>> A third-party thread needs to notify my application of some event.  I


>> register a handler for that event with the third-party library. When
>> my handler is called, it's called in the third-party library's

>> thread.  My handler then needs to communicate with the rest of my


>> application which is running in greenlet's in the main thread. How do
>> I do this?

...

> Take a look at gevent.threadpool.ThreadResult. This is what ThreadPool
> uses to pass results from worker threads to the main thread.
>
> Quick sample:
>
>>>> import gevent.threadpool
>>>> def func(result): print 'got result: %r' % (result.value, )
> ...
>>>> result = gevent.threadpool.ThreadResult(func)

>>>> gevent.threadpool.ThreadPool(1).apply(lambda: result.set(2+2))   # 2+2 is calculated in another thread


> got result: 4
>
> ThreadResult might not suit you completely as it is a one-shot type of
> communication. But it shows how to do this sort of thing. In the
> future I think we'll have more tools for communicating with the main
> thread from the worker thread, like queues.

I can't say I follow this atm, but I'll study it. :)

>> I like the fact that I no longer need to build libevent and that I can
>> run gevent in multiple threads or even one thread other than the main
>> thread. I *really* like the latter.
>>
>> FWIW, my application runs ~13% slower with 1.0b1 than with 0.13.6.
>
> That's interesting. I'll try to do some benchmarks later and if I
> cannot reproduce slow down, I'll contact you for more details if you
> don't mind.

Of course, I don't mind.

Thanks.

Reply all
Reply to author
Forward
0 new messages