Vladimir Afinello | 23 Jun 2012 15:26
Picon

Correctly pass watcher to another loop

Greetings,

I have several threads in my program, each running an event loop.
After some period of time I need to pass one client (socket fd) from one loop to another (and stop monitoring it in the current loop)

In the current loop/thread I perform
watcher.stop();
ev_unref(thread1_loop_pointer);
then pass the client data to second thread (using some kind of blocking fifo) and then wake the secon thread using ev_async watcher
In second thread I perform:
pClientData->watcher.set(thread2_loop_pointer);
pClientData->watcher.set<&client_class::static_callback_fn>(pClientData);
pClientData->watcher.start(pClientData->fd, ev::READ);

sometimes it works, but in general the program breaks on watcher.start(pClientData->fd, ev::READ) with:
ev.c:3250: ev_io_stop: Assertion ("libev: ev_io_stop called with illegal fd (must stay constant after start!), w->fd >= 0 && w->fd <= ((loop)->anfdmax) failed)

Howether I still can see in the debugger, that the fd is a valid int.

What is the correct way to pass the watcher from one thread/loop to another thread/loop?
(I use Linux x86 with epoll backend)

Best regards, Vladimir

_______________________________________________
libev mailing list
libev <at> lists.schmorp.de
http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev
Marc Lehmann | 24 Jun 2012 02:55
Picon
Favicon

Re: Correctly pass watcher to another loop

On Sat, Jun 23, 2012 at 05:26:54PM +0400, Vladimir Afinello <vovan.af <at> gmail.com> wrote:
> In the current loop/thread I perform
> watcher.stop();
> ev_unref(thread1_loop_pointer);

Do you also have a corresponding ev_ref? (and is there a reaosn you would
need explciit ref/unref calls?).

> pClientData->watcher.set(thread2_loop_pointer);
> pClientData->watcher.set<&client_class::static_callback_fn>(pClientData);
> pClientData->watcher.start(pClientData->fd, ev::READ);
> 
> sometimes it works, but in general the program breaks on
> watcher.start(pClientData->fd, ev::READ) with:

It's hard to believe that your program actually does what you describe,
and here is why: the assertion is inside ev_io_stop, which is called by
by start only when the watcher is still active. That contradicts the
watcher.stop earlier.

> What is the correct way to pass the watcher from one thread/loop to another
> thread/loop?

The above would be correct, without the ev_unref (and shouldn't cause that
problem even with the ev_unref), so best check your code to see what it
really does.

--

-- 
                The choice of a       Deliantra, the free code+content MORPG
      -----==-     _GNU_              http://www.deliantra.net
      ----==-- _       generation
      ---==---(_)__  __ ____  __      Marc Lehmann
      --==---/ / _ \/ // /\ \/ /      schmorp <at> schmorp.de
      -=====/_/_//_/\_,_/ /_/\_\
Vladimir Afinello | 24 Jun 2012 14:10
Picon

Re: Correctly pass watcher to another loop

Greetings,
thanks for the answer


It's hard to believe that your program actually does what you describe,
and here is why: the assertion is inside ev_io_stop, which is called by
by start only when the watcher is still active. That contradicts the
watcher.stop earlier.


One more addition.
I pass a group of watchers at a time (at least 5), so at this moment I'm in a callback of a one of that watchers.
Should I wake the current thread loop to perform stop() on the other watchers in a group?
I thought, the loop isn't in a wait state at that time since the callback is executed.

Best regards, Vladimir
_______________________________________________
libev mailing list
libev <at> lists.schmorp.de
http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev
Marc Lehmann | 24 Jun 2012 14:53
Picon
Favicon

Re: Correctly pass watcher to another loop

On Sun, Jun 24, 2012 at 04:10:41PM +0400, Vladimir Afinello <vovan.af <at> gmail.com> wrote:
> I pass a group of watchers at a time (at least 5), so at this moment I'm in
> a callback of a one of that watchers.

That is no problem - after you stop() a watcher the loop will not access
it in any way anymore.

> Should I wake the current thread loop to perform stop() on the other
> watchers in a group?

I am not sure what that means, but again, if the watchers are stopped,
then the loop will no longer access them.

> I thought, the loop isn't in a wait state at that time since the callback
> is executed.

The loop state is safely serialised to memory while a watcher callback is
executed. Basically, when you are inside a callback, you are in the same
state as code running outside the loop.

--

-- 
                The choice of a       Deliantra, the free code+content MORPG
      -----==-     _GNU_              http://www.deliantra.net
      ----==-- _       generation
      ---==---(_)__  __ ____  __      Marc Lehmann
      --==---/ / _ \/ // /\ \/ /      schmorp <at> schmorp.de
      -=====/_/_//_/\_,_/ /_/\_\
Vladimir Afinello | 24 Jun 2012 15:14
Picon

Re: Correctly pass watcher to another loop


> Should I wake the current thread loop to perform stop() on the other
> watchers in a group?

I am not sure what that means, but again, if the watchers are stopped,
then the loop will no longer access them.


I mean, when I receive some data from one client in it's watcher receive callback - I need to pass this watcher and 5 other watchers from this thread/loop to another.
And I perform stop() on all of them being in that receive callback function. So, does these 5+1 stop()'s affect the thread loop immediatelly? If not - I have no idea why these watchers remain active when I start them in another thread.

Best regards, Vladimir
_______________________________________________
libev mailing list
libev <at> lists.schmorp.de
http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev
Vladimir Afinello | 24 Jun 2012 15:44
Picon

Re: Correctly pass watcher to another loop

Greetings,

I also haven't found the watcher.set(struct ev_loop*) implementation in the sources. I use it before watcher.start() to associate the watcher with the new thread loop.
Shouldn't it throw an exception on set() method if the watcher was pending at that moment?

Best regards, Vladimir

_______________________________________________
libev mailing list
libev <at> lists.schmorp.de
http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev
Marc Lehmann | 24 Jun 2012 17:24
Picon
Favicon

Re: Correctly pass watcher to another loop

On Sun, Jun 24, 2012 at 05:14:41PM +0400, Vladimir Afinello <vovan.af <at> gmail.com> wrote:
> > I am not sure what that means, but again, if the watchers are stopped,
> > then the loop will no longer access them.
>
> I mean, when I receive some data from one client in it's watcher receive
> callback - I need to pass this watcher and 5 other watchers from this
> thread/loop to another.
> And I perform stop() on all of them being in that receive callback
> function. So, does these 5+1 stop()'s affect the thread loop immediatelly?

Yes, as I said, the loop will not touch watchers once stopped, regardless
of when you stop them.

> If not - I have no idea why these watchers remain active when I start them
> in another thread.

Me neither, but the assert can only trigger on a started watcher.

On Sun, Jun 24, 2012 at 05:44:40PM +0400, Vladimir Afinello <vovan.af <at> gmail.com> wrote:
> I also haven't found the watcher.set(struct ev_loop*) implementation in the

It's in ev++.h, first set method in the file.

> Shouldn't it throw an exception on set() method if the watcher was pending
> at that moment?

Calling that method on an active watcher is always a bug, not an
exceptional condition. And in this case, the assert did catch the bug.

--

-- 
                The choice of a       Deliantra, the free code+content MORPG
      -----==-     _GNU_              http://www.deliantra.net
      ----==-- _       generation
      ---==---(_)__  __ ____  __      Marc Lehmann
      --==---/ / _ \/ // /\ \/ /      schmorp <at> schmorp.de
      -=====/_/_//_/\_,_/ /_/\_\
Vladimir Afinello | 24 Jun 2012 17:39
Picon

Re: Correctly pass watcher to another loop

Thanks a lot for your answers.
The problem seems to desappear since I started to wake the current thread loop after stopping a group of watchers and also splitted one read/write watcher into 2 different watchers. Did it just in case, but it seems to work now.

Best regards, Vladimir

_______________________________________________
libev mailing list
libev <at> lists.schmorp.de
http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev
Marc Lehmann | 25 Jun 2012 11:40
Picon
Favicon

Re: Correctly pass watcher to another loop

On Sun, Jun 24, 2012 at 07:39:18PM +0400, Vladimir Afinello <vovan.af <at> gmail.com> wrote:
> The problem seems to desappear since I started to wake the current thread
> loop after stopping a group of watchers and also splitted one read/write

I am still confused about this thread issue - when you stop a watcher, you
normally _are_ in the "current thread". The only time you need to wake up the
loop would be if you locked the loop with acquire/release callbacks, using an
ev_async watcher.

If you stop the watcher while ev_run executes in another thread, then
thats your problem, and waking up the loop will not solve your problem,
just hide the race condition for somewhat later, or trash libevs internal
data structures, which is likely whats happening (and the assertion
probably doesn't trigger when you start the watcher, but when you stop
it).

If that isn't your problem, the bug must be somewhere else in your code -
something almost certainly trashes libevs data structures, and that almost
certainly is lack of locking in your code somewhere.

In general, twiddling around with threads until the program doesn't crash
anymore is a sure way to get random crash bugs later.

--

-- 
                The choice of a       Deliantra, the free code+content MORPG
      -----==-     _GNU_              http://www.deliantra.net
      ----==-- _       generation
      ---==---(_)__  __ ____  __      Marc Lehmann
      --==---/ / _ \/ // /\ \/ /      schmorp <at> schmorp.de
      -=====/_/_//_/\_,_/ /_/\_\

Gmane