Discussion:
[asio-users] immediately stop io_service from executing further work (but not with strands??)
Amir Taaki
2012-06-26 01:00:24 UTC
Permalink
According to this link:

http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service.html

In the section titled "Stopping the io_service from running out of work", it says that invocating stop() on the io_service will cause the run() function to return as soon as possible, abandoning unfinished operations without dispatching them.

I created a simple program to test this and it works fine. After calling stop, all the waiting handlers are discarded and the io_service finishes immediately. See http://ideone.com/1dWDK

However, if I then use a strand, it will not finish but continue on until *all* the pending work has been completed. This is really annoying because my use is serialising access to a shared resource. Each query takes a very long time, and there's many of them. This means that when the program wants to exit, it needs to wait for all these very long pending pieces of work to complete before the database can be shut down.

Real code:


https://gitorious.org/libbitcoin/libbitcoin/blobs/master/src/blockchain/bdb/bdb_blockchain.cpp


Example testcase:


http://ideone.com/ND9tu

#include <iostream>
#include <thread>

#include <boost/asio.hpp>

using boost::asio::io_service;

class foo
{
public:
    foo(io_service& service)
      : strand_(service) {}

    void bar()
    {
        strand_.post(
            [this]
            {
                std::cout << __PRETTY_FUNCTION__ << std::endl;
                sleep(1);
            });
    }
private:
    io_service::strand strand_;
};

int main()
{
    io_service service;
    io_service::work* work = new io_service::work(service);
    std::thread t([&service] { service.run(); });

    foo f(service);
    // Only 5 bar()s should get called, but instead all 10 are allowed to finish...
    for (size_t i = 0; i < 10; ++i)
        f.bar();
    sleep(5);
    std::cout << "Closing" << std::endl;

    //delete work;
    //work = nullptr;

    service.stop();
    t.join();
    return 0;
}
Gruenke, Matt
2012-06-26 16:10:12 UTC
Permalink
Are you saying that if I post one event to a strand, then call stop, and then post a second event (before the first has finished processing), that the second event will be processed before io_service::run() returns? If that's so, then I'd file a bug in the boost bug tracker.

I just want to make sure that you're not expecting ioservice::stop() to suspend a handler mid-execution. That will not happen. ASIO only works on the granularity of entire callbacks. If you want to interrupt (and later resume) a single handler, you need to find a way to break it into multiple events, or to use something besides ASIO.


Matt


-----Original Message-----
From: Amir Taaki [mailto:***@yahoo.com]
Sent: Monday, June 25, 2012 9:00 PM
To: asio-***@lists.sourceforge.net
Subject: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)

According to this link:

http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service.html

In the section titled "Stopping the io_service from running out of work", it says that invocating stop() on the io_service will cause the run() function to return as soon as possible, abandoning unfinished operations without dispatching them.

I created a simple program to test this and it works fine. After calling stop, all the waiting handlers are discarded and the io_service finishes immediately. See http://ideone.com/1dWDK

However, if I then use a strand, it will not finish but continue on until *all* the pending work has been completed. This is really annoying because my use is serialising access to a shared resource. Each query takes a very long time, and there's many of them. This means that when the program wants to exit, it needs to wait for all these very long pending pieces of work to complete before the database can be shut down.

Real code:


https://gitorious.org/libbitcoin/libbitcoin/blobs/master/src/blockchain/bdb/bdb_blockchain.cpp


Example testcase:


http://ideone.com/ND9tu

#include <iostream>
#include <thread>

#include <boost/asio.hpp>

using boost::asio::io_service;

class foo
{
public:
    foo(io_service& service)
      : strand_(service) {}

    void bar()
    {
        strand_.post(
            [this]
            {
                std::cout << __PRETTY_FUNCTION__ << std::endl;
                sleep(1);
            });
    }
private:
    io_service::strand strand_;
};

int main()
{
    io_service service;
    io_service::work* work = new io_service::work(service);
    std::thread t([&service] { service.run(); });

    foo f(service);
    // Only 5 bar()s should get called, but instead all 10 are allowed to finish...
    for (size_t i = 0; i < 10; ++i)
        f.bar();
    sleep(5);
    std::cout << "Closing" << std::endl;

    //delete work;
    //work = nullptr;

    service.stop();
    t.join();
    return 0;
}

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Amir Taaki
2012-06-26 16:43:11 UTC
Permalink
I should note that the workaround I found was to use:

service_.post(strand.wrap(handler))

Rather than:

strand_.post(handler)


----- Original Message -----
From: Amir Taaki <***@yahoo.com>
To: "asio-***@lists.sourceforge.net" <asio-***@lists.sourceforge.net>
Cc:
Sent: Tuesday, June 26, 2012 6:40 PM
Subject: Re: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)

No, I attached some source code to demonstrate.

Event takes 1 second to complete.

If I submit 10 events to io_service, wait 5 seconds and call service_.stop() then 6 events complete (as expected).

If I submit 10 events to io_service::strand, wait 5 seconds and call service_.stop() then the io_service continues on until *all* 10 events complete (unexpected).

http://ideone.com/ND9tu


----- Original Message -----
From: "Gruenke, Matt" <***@Tycoint.com>
To: asio-***@lists.sourceforge.net
Cc:
Sent: Tuesday, June 26, 2012 6:10 PM
Subject: Re: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)

Are you saying that if I post one event to a strand, then call stop, and then post a second event (before the first has finished processing), that the second event will be processed before io_service::run() returns?  If that's so, then I'd file a bug in the boost bug tracker.

I just want to make sure that you're not expecting ioservice::stop() to suspend a handler mid-execution.  That will not happen.  ASIO only works on the granularity of entire callbacks.  If you want to interrupt (and later resume) a single handler, you need to find a way to break it into multiple events, or to use something besides ASIO.


Matt


-----Original Message-----
From: Amir Taaki [mailto:***@yahoo.com]
Sent: Monday, June 25, 2012 9:00 PM
To: asio-***@lists.sourceforge.net
Subject: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)

According to this link:

http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service.html

In the section titled "Stopping the io_service from running out of work", it says that invocating stop() on the io_service will cause the run() function to return as soon as possible, abandoning unfinished operations without dispatching them.

I created a simple program to test this and it works fine. After calling stop, all the waiting handlers are discarded and the io_service finishes immediately. See http://ideone.com/1dWDK

However, if I then use a strand, it will not finish but continue on until *all* the pending work has been completed. This is really annoying because my use is serialising access to a shared resource. Each query takes a very long time, and there's many of them. This means that when the program wants to exit, it needs to wait for all these very long pending pieces of work to complete before the database can be shut down.

Real code:


https://gitorious.org/libbitcoin/libbitcoin/blobs/master/src/blockchain/bdb/bdb_blockchain.cpp


Example testcase:


http://ideone.com/ND9tu

#include <iostream>
#include <thread>

#include <boost/asio.hpp>

using boost::asio::io_service;

class foo
{
public:
    foo(io_service& service)
      : strand_(service) {}

    void bar()
    {
        strand_.post(
            [this]
            {
                std::cout << __PRETTY_FUNCTION__ << std::endl;
                sleep(1);
            });
    }
private:
    io_service::strand strand_;
};

int main()
{
    io_service service;
    io_service::work* work = new io_service::work(service);
    std::thread t([&service] { service.run(); });

    foo f(service);
    // Only 5 bar()s should get called, but instead all 10 are allowed to finish...
    for (size_t i = 0; i < 10; ++i)
        f.bar();
    sleep(5);
    std::cout << "Closing" << std::endl;

    //delete work;
    //work = nullptr;

    service.stop();
    t.join();
    return 0;
}

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Gruenke, Matt
2012-06-26 17:07:34 UTC
Permalink
I consider that a bug. I recommend filing it in the boost bugtracker.


Matt


-----Original Message-----
From: Amir Taaki [mailto:***@yahoo.com]
Sent: Tuesday, June 26, 2012 12:43 PM
To: asio-***@lists.sourceforge.net
Subject: Re: [asio-users] immediately stop io_service from executingfurtherwork (but not with strands??)

I should note that the workaround I found was to use:

service_.post(strand.wrap(handler))

Rather than:

strand_.post(handler)


----- Original Message -----
From: Amir Taaki <***@yahoo.com>
To: "asio-***@lists.sourceforge.net" <asio-***@lists.sourceforge.net>
Cc:
Sent: Tuesday, June 26, 2012 6:40 PM
Subject: Re: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)

No, I attached some source code to demonstrate.

Event takes 1 second to complete.

If I submit 10 events to io_service, wait 5 seconds and call service_.stop() then 6 events complete (as expected).

If I submit 10 events to io_service::strand, wait 5 seconds and call service_.stop() then the io_service continues on until *all* 10 events complete (unexpected).

http://ideone.com/ND9tu


----- Original Message -----
From: "Gruenke, Matt" <***@Tycoint.com>
To: asio-***@lists.sourceforge.net
Cc:
Sent: Tuesday, June 26, 2012 6:10 PM
Subject: Re: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)

Are you saying that if I post one event to a strand, then call stop, and then post a second event (before the first has finished processing), that the second event will be processed before io_service::run() returns?  If that's so, then I'd file a bug in the boost bug tracker.

I just want to make sure that you're not expecting ioservice::stop() to suspend a handler mid-execution.  That will not happen.  ASIO only works on the granularity of entire callbacks.  If you want to interrupt (and later resume) a single handler, you need to find a way to break it into multiple events, or to use something besides ASIO.


Matt


-----Original Message-----
From: Amir Taaki [mailto:***@yahoo.com]
Sent: Monday, June 25, 2012 9:00 PM
To: asio-***@lists.sourceforge.net
Subject: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)

According to this link:

http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service.html

In the section titled "Stopping the io_service from running out of work", it says that invocating stop() on the io_service will cause the run() function to return as soon as possible, abandoning unfinished operations without dispatching them.

I created a simple program to test this and it works fine. After calling stop, all the waiting handlers are discarded and the io_service finishes immediately. See http://ideone.com/1dWDK

However, if I then use a strand, it will not finish but continue on until *all* the pending work has been completed. This is really annoying because my use is serialising access to a shared resource. Each query takes a very long time, and there's many of them. This means that when the program wants to exit, it needs to wait for all these very long pending pieces of work to complete before the database can be shut down.

Real code:


https://gitorious.org/libbitcoin/libbitcoin/blobs/master/src/blockchain/bdb/bdb_blockchain.cpp


Example testcase:


http://ideone.com/ND9tu

#include <iostream>
#include <thread>

#include <boost/asio.hpp>

using boost::asio::io_service;

class foo
{
public:
    foo(io_service& service)
      : strand_(service) {}

    void bar()
    {
        strand_.post(
            [this]
            {
                std::cout << __PRETTY_FUNCTION__ << std::endl;
                sleep(1);
            });
    }
private:
    io_service::strand strand_;
};

int main()
{
    io_service service;
    io_service::work* work = new io_service::work(service);
    std::thread t([&service] { service.run(); });

    foo f(service);
    // Only 5 bar()s should get called, but instead all 10 are allowed to finish...
    for (size_t i = 0; i < 10; ++i)
        f.bar();
    sleep(5);
    std::cout << "Closing" << std::endl;

    //delete work;
    //work = nullptr;

    service.stop();
    t.join();
    return 0;
}

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Lijo Antony
2012-06-27 05:28:45 UTC
Permalink
Post by Amir Taaki
service_.post(strand.wrap(handler))
strand_.post(handler)
This helped me to understand their difference,
http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg=5

-lijo
Amir Taaki
2012-06-27 11:16:46 UTC
Permalink
I emailed Chris Kohlhoff (author of boost asio) and he confirmed it is a problem and proposed a workaround:

Hi Amir, On Tue, Jun 26, 2012, at 02:28 PM, Amir Taaki wrote: > Hi Chris, > > I posted a problem I was having with boost::asio to the asio-users > mailing list, with a test-case. People got different behaviour on > their systems and told me to report a bug to boost-users. I just want > to check it by you first before submitting something. > > According to this link: > > http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service.html > > In the section titled "Stopping the io_service from running out of work", > it says that invocating stop() on the io_service will cause the run() > function to return as soon as possible, abandoning unfinished operations > without dispatching them. > > I created a simple program to test this and it works fine. After calling > stop, all the waiting handlers are discarded and the io_service finishes > immediately. See http://ideone.com/1dWDK > > However, if I then use a strand, it will not finish
but continue on until > *all* the pending work has been completed.
Ok, turns out this is because a strand executes all of the "ready"
handlers in a batch without referring back to the host io_service. This
is done to prevent starvation of a strand relative to other non-strand
work on an io_service (previously the N waiting handlers in a strand
were given equal weight to 1 handler on the main io_service). It also
reduces the locking overhead associated with the strand. Strictly speaking I would not call this a bug, as the phrase "as soon as
possible" deliberately gives some wiggle room :) Of course, it's not
particularly desirable in your use case, but unfortunately checking
whether the host io_service is stopped mid-batch is not (currently) a
cheap operation. Bottom line: this needs thought, and is probably not
something that will change in the near future, if at all. It's not all bad news though... > This is really annoying because my use is serialising access to a > shared resource. Each query takes a very long time, and there's many > of them. This means that when the program wants to exit, it needs to > wait for all these very long pending pieces of work to complete before > the database can be shut down. To me this doesn't sound like the best place to use a strand, due to the
fact that your work "takes a very long time". Generally I prefer to
think of a strand as the asynchronous equivalent of a mutex, and one
normally wouldn't do long running work while holding a mutex either. Although it's a bit more work, for now I'd suggest managing the queuing
yourself. One way to do this is to combine a strand for thread safety, a
deadline_timer for the queuing, and a count of how many queued items
there are. To illustrate in hopefully correct pseudo-code: initialisation()
{ timer.expires_at(infin); count = 0
} enqueue(item)
{ strand.dispatch( [](){ timer.async_wait(work_wrapper(item)); if (count++ == 0) timer.cancel_one(); // We're first, schedule item });
} work_wrapper(item)
{ item(); // Do work outside strand strand.dispatch( [](){ if (--count > 0) timer.cancel_one(); // Schedule next item });
} Hope that helps a bit. Let me know how you get on and at some point I'll
try to make an example out of it. Cheers,
Chris


----- Original Message -----
From: Lijo Antony <***@one.com>
To: asio-***@lists.sourceforge.net
Cc:
Sent: Wednesday, June 27, 2012 7:28 AM
Subject: Re: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)
Post by Amir Taaki
service_.post(strand.wrap(handler))
strand_.post(handler)
This helped me to understand their difference,
http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg=5

-lijo

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Amir Taaki
2012-06-27 11:19:05 UTC
Permalink
Last email got formatted incorrectly. Repasting it below.


-------------


I emailed Chris Kohlhoff (author of boost asio) and he confirmed it is a problem and proposed a workaround:


Hi Amir,
Post by Amir Taaki
Hi Chris,
I posted a problem I was having with boost::asio to the asio-users
mailing list, with a test-case. People got different behaviour on
their systems and told me to report a bug to boost-users. I just want
to check it by you first before submitting something.
http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service.html
In the section titled "Stopping the io_service from running out of work",
it says that invocating stop() on the io_service will cause the run()
function to return as soon as possible, abandoning unfinished operations
without dispatching them.
I created a simple program to test this and it works fine. After calling
stop, all the waiting handlers are discarded and the io_service finishes
immediately. See http://ideone.com/1dWDK
However, if I then use a strand, it will not finish but continue on until
*all* the pending work has been completed.
Ok, turns out this is because a strand executes all of the "ready"
handlers in a batch without referring back to the host io_service. This
is done to prevent starvation of a strand relative to other non-strand
work on an io_service (previously the N waiting handlers in a strand
were given equal weight to 1 handler on the main io_service). It also
reduces the locking overhead associated with the strand.

Strictly speaking I would not call this a bug, as the phrase "as soon as
possible" deliberately gives some wiggle room :) Of course, it's not
particularly desirable in your use case, but unfortunately checking
whether the host io_service is stopped mid-batch is not (currently) a
cheap operation. Bottom line: this needs thought, and is probably not
something that will change in the near future, if at all.

It's not all bad news though...
Post by Amir Taaki
This is really annoying because my use is serialising access to a
shared resource. Each query takes a very long time, and there's many
of them. This means that when the program wants to exit, it needs to
wait for all these very long pending pieces of work to complete before
the database can be shut down.
To me this doesn't sound like the best place to use a strand, due to the
fact that your work "takes a very long time". Generally I prefer to
think of a strand as the asynchronous equivalent of a mutex, and one
normally wouldn't do long running work while holding a mutex either.

Although it's a bit more work, for now I'd suggest managing the queuing
yourself. One way to do this is to combine a strand for thread safety, a
deadline_timer for the queuing, and a count of how many queued items
there are. To illustrate in hopefully correct pseudo-code:

initialisation()
{
  timer.expires_at(infin);
  count = 0
}

enqueue(item)
{
  strand.dispatch(
    [](){
      timer.async_wait(work_wrapper(item));
      if (count++ == 0)
        timer.cancel_one(); // We're first, schedule item
    });
}

work_wrapper(item)
{
  item(); // Do work outside strand
  strand.dispatch(
    [](){
      if (--count > 0)
        timer.cancel_one(); // Schedule next item
    });
}

Hope that helps a bit. Let me know how you get on and at some point I'll
try to make an example out of it.

Cheers,
Chris


----- Original Message -----
From: Lijo Antony <***@one.com>
To: asio-***@lists.sourceforge.net
Cc:
Sent: Wednesday, June 27, 2012 7:28 AM
Subject: Re: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)
Post by Amir Taaki
service_.post(strand.wrap(handler))
strand_.post(handler)
This helped me to understand their difference,
http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg=5

-lijo

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Marat Abrarov
2012-06-27 13:35:54 UTC
Permalink
Post by Amir Taaki
Ok, turns out this is because a strand executes all of the "ready"
handlers in a batch without referring back to the host io_service.
Why there is no any limit for the max size of such batch. It can ever be user-configurable (optional second parameter of
strand's constructor)? And it would be cheap enough to check. Long time ago I wanted to add such feature to the strand
but still have no time for that.
Post by Amir Taaki
This
is done to prevent starvation of a strand relative to other non-strand
work on an io_service (previously the N waiting handlers in a strand
were given equal weight to 1 handler on the main io_service). It also
reduces the locking overhead associated with the strand.
Strictly speaking I would not call this a bug, as the phrase "as soon as
possible" deliberately gives some wiggle room :) Of course, it's not
particularly desirable in your use case, but unfortunately checking
whether the host io_service is stopped mid-batch is not (currently) a
cheap operation.
As I wrote above - limiting max size of a ready-handlers-batch can be cheap.
Post by Amir Taaki
Bottom line: this needs thought, and is probably not
something that will change in the near future, if at all.
May be my suggestion could change point of view of Chris?

Regards,
Marat Abrarov.
Gruenke, Matt
2012-06-27 15:47:59 UTC
Permalink
I wouldn't bother with batching. As you point out, it should be
inexpensive to check.

I consider it a bug that strands don't strictly respect
io_service::stop(). Since batching would still run afoul of that, I
consider it incorrect.

I'd file a bug on this in the boost bugtracker. Whether or not you have
time to contribute a patch, the issue should be logged. It would be
courteous to send Chris a note with a link to the bug and this thread.


Matt


-----Original Message-----
From: Marat Abrarov
Sent: Wednesday, June 27, 2012 9:36 AM
To: asio-***@lists.sourceforge.net
Subject: Re: [asio-users] immediately stop io_service fromexecuting
furtherwork (but not with strands??)
Post by Amir Taaki
Ok, turns out this is because a strand executes all of the "ready"
handlers in a batch without referring back to the host io_service.
Why there is no any limit for the max size of such batch. It can ever be
user-configurable (optional second parameter of strand's constructor)?
And it would be cheap enough to check. Long time ago I wanted to add
such feature to the strand but still have no time for that.
Post by Amir Taaki
This
is done to prevent starvation of a strand relative to other non-strand
work on an io_service (previously the N waiting handlers in a strand
were given equal weight to 1 handler on the main io_service). It also
reduces the locking overhead associated with the strand.
Strictly speaking I would not call this a bug, as the phrase "as soon
as possible" deliberately gives some wiggle room :) Of course, it's
not particularly desirable in your use case, but unfortunately
checking whether the host io_service is stopped mid-batch is not
(currently) a cheap operation.
As I wrote above - limiting max size of a ready-handlers-batch can be
cheap.
Post by Amir Taaki
Bottom line: this needs thought, and is probably not something that
will change in the near future, if at all.
May be my suggestion could change point of view of Chris?

Regards,
Marat Abrarov.



------------------------------------------------------------------------
------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond.
Discussions
will include endpoint security, mobile security and the latest in
malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Marat Abrarov
2012-06-27 16:57:57 UTC
Permalink
Post by Gruenke, Matt
I wouldn't bother with batching. As you point out, it should be
inexpensive to check.
I consider it a bug that strands don't strictly respect
io_service::stop(). Since batching would still run afoul of that, I
consider it incorrect.
Batching is needed. It's a classical multithread tradeoff. But it would be rightly to give users of strand an
opportunity to configure max size of such batching (optional second parameter of strand's constructor).

Regards,
Marat Abrarov.
Gruenke, Matt
2012-06-27 17:18:06 UTC
Permalink
Batching is an optimization. Optimizations should not violate
correctness (by default).

If you want to add batching, please make it configurable and set the
default batch size to be 1.


Matt


-----Original Message-----
From: Marat Abrarov [mailto:***@mail.ru]
Sent: Wednesday, June 27, 2012 12:58 PM
To: asio-***@lists.sourceforge.net
Subject: Re: [asio-users] immediately stopio_service fromexecuting
furtherwork (but not with strands??)
Post by Gruenke, Matt
I wouldn't bother with batching. As you point out, it should be
inexpensive to check.
I consider it a bug that strands don't strictly respect
io_service::stop(). Since batching would still run afoul of that, I
consider it incorrect.
Batching is needed. It's a classical multithread tradeoff. But it would
be rightly to give users of strand an opportunity to configure max size
of such batching (optional second parameter of strand's constructor).

Regards,
Marat Abrarov.



------------------------------------------------------------------------
------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond.
Discussions
will include endpoint security, mobile security and the latest in
malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

Amir Taaki
2012-06-26 16:40:49 UTC
Permalink
No, I attached some source code to demonstrate.

Event takes 1 second to complete.

If I submit 10 events to io_service, wait 5 seconds and call service_.stop() then 6 events complete (as expected).

If I submit 10 events to io_service::strand, wait 5 seconds and call service_.stop() then the io_service continues on until *all* 10 events complete (unexpected).

http://ideone.com/ND9tu


----- Original Message -----
From: "Gruenke, Matt" <***@Tycoint.com>
To: asio-***@lists.sourceforge.net
Cc:
Sent: Tuesday, June 26, 2012 6:10 PM
Subject: Re: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)

Are you saying that if I post one event to a strand, then call stop, and then post a second event (before the first has finished processing), that the second event will be processed before io_service::run() returns?  If that's so, then I'd file a bug in the boost bug tracker.

I just want to make sure that you're not expecting ioservice::stop() to suspend a handler mid-execution.  That will not happen.  ASIO only works on the granularity of entire callbacks.  If you want to interrupt (and later resume) a single handler, you need to find a way to break it into multiple events, or to use something besides ASIO.


Matt


-----Original Message-----
From: Amir Taaki [mailto:***@yahoo.com]
Sent: Monday, June 25, 2012 9:00 PM
To: asio-***@lists.sourceforge.net
Subject: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)

According to this link:

http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service.html

In the section titled "Stopping the io_service from running out of work", it says that invocating stop() on the io_service will cause the run() function to return as soon as possible, abandoning unfinished operations without dispatching them.

I created a simple program to test this and it works fine. After calling stop, all the waiting handlers are discarded and the io_service finishes immediately. See http://ideone.com/1dWDK

However, if I then use a strand, it will not finish but continue on until *all* the pending work has been completed. This is really annoying because my use is serialising access to a shared resource. Each query takes a very long time, and there's many of them. This means that when the program wants to exit, it needs to wait for all these very long pending pieces of work to complete before the database can be shut down.

Real code:


https://gitorious.org/libbitcoin/libbitcoin/blobs/master/src/blockchain/bdb/bdb_blockchain.cpp


Example testcase:


http://ideone.com/ND9tu

#include <iostream>
#include <thread>

#include <boost/asio.hpp>

using boost::asio::io_service;

class foo
{
public:
    foo(io_service& service)
      : strand_(service) {}

    void bar()
    {
        strand_.post(
            [this]
            {
                std::cout << __PRETTY_FUNCTION__ << std::endl;
                sleep(1);
            });
    }
private:
    io_service::strand strand_;
};

int main()
{
    io_service service;
    io_service::work* work = new io_service::work(service);
    std::thread t([&service] { service.run(); });

    foo f(service);
    // Only 5 bar()s should get called, but instead all 10 are allowed to finish...
    for (size_t i = 0; i < 10; ++i)
        f.bar();
    sleep(5);
    std::cout << "Closing" << std::endl;

    //delete work;
    //work = nullptr;

    service.stop();
    t.join();
    return 0;
}

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Marat Abrarov
2012-06-26 18:58:10 UTC
Permalink
Post by Amir Taaki
service_.post(strand.wrap(handler))
strand_.post(handler)
This doesn't work at Windows:
~~~~~~~~~~~~~~~~~~~
#if defined(WIN32)
#include <tchar.h>
#endif

#include <cstdlib>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>

namespace asio_io_service_stop_test {

const long seconds_until_stop = 5;
const long seconds_for_handler = 2;
const long handler_count = 50;

void cyclic_handler(int counter)
{
std::cout << "In cyclic_handler #" << counter << std::endl;
boost::this_thread::sleep(boost::posix_time::seconds(seconds_for_handler));
}

void run_test()
{
boost::asio::io_service io_service;
boost::asio::io_service::strand strand(io_service);
for (int i = 0; i != handler_count; ++i)
{
// io_service.post() works here the same way as strand.post()
io_service.post(strand.wrap(boost::bind(cyclic_handler, i)));
//strand.post(boost::bind(cyclic_handler, i)));
}

std::cout << "Startig work thread\n";
boost::thread work_thread(
boost::bind(&boost::asio::io_service::run, &io_service));

std::cout << "Waiting " << seconds_until_stop
<< " second(s) until call asio::io_service::stop()\n";
boost::this_thread::sleep(boost::posix_time::seconds(seconds_until_stop));

std::cout << "Stopping io_service\n";
io_service.stop();

std::cout << "io_service is stopped. Joining with work_thread\n";
work_thread.join();
std::cout << "work_thread finished\n";
}

} // namespace asio_io_service_stop_test

#if defined(WIN32)
int _tmain(int /*argc*/, _TCHAR* /*argv*/[])
#else
int main(int /*argc*/, char* /*argv*/[])
#endif
{
asio_io_service_stop_test::run_test();
return EXIT_SUCCESS;
}
~~~~~~~~~~~~~~~~~~~
Amir Taaki
2012-06-26 20:11:48 UTC
Permalink
Did you try my example?

http://ideone.com/ND9tu

I'm using it under Linux on boost 1.48



----- Original Message -----
From: Marat Abrarov <***@mail.ru>
To: asio-***@lists.sourceforge.net
Cc:
Sent: Tuesday, June 26, 2012 8:58 PM
Subject: Re: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)
Post by Amir Taaki
service_.post(strand.wrap(handler))
strand_.post(handler)
This doesn't work at Windows:
~~~~~~~~~~~~~~~~~~~
#if defined(WIN32)
#include <tchar.h>
#endif

#include <cstdlib>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>

namespace asio_io_service_stop_test {

const long seconds_until_stop  = 5;
const long seconds_for_handler = 2;
const long handler_count      = 50;

void cyclic_handler(int counter)
{
  std::cout << "In cyclic_handler #" << counter << std::endl;
  boost::this_thread::sleep(boost::posix_time::seconds(seconds_for_handler));
}

void run_test()

  boost::asio::io_service io_service;
  boost::asio::io_service::strand strand(io_service);
  for (int i = 0; i != handler_count; ++i)
  {
    // io_service.post() works here the same way as strand.post()
    io_service.post(strand.wrap(boost::bind(cyclic_handler, i)));
    //strand.post(boost::bind(cyclic_handler, i)));
  }

  std::cout << "Startig work thread\n";
  boost::thread work_thread(
      boost::bind(&boost::asio::io_service::run, &io_service));
 
  std::cout << "Waiting " << seconds_until_stop
            << " second(s) until call asio::io_service::stop()\n";
  boost::this_thread::sleep(boost::posix_time::seconds(seconds_until_stop));

  std::cout << "Stopping io_service\n";
  io_service.stop();

  std::cout << "io_service is stopped. Joining with work_thread\n"; 
  work_thread.join();
  std::cout << "work_thread finished\n"; 
}

} // namespace asio_io_service_stop_test

#if defined(WIN32)
int _tmain(int /*argc*/, _TCHAR* /*argv*/[])
#else
int main(int /*argc*/, char* /*argv*/[])
#endif
{         
  asio_io_service_stop_test::run_test();
  return EXIT_SUCCESS;
}
~~~~~~~~~~~~~~~~~~~


------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Marat Abrarov
2012-06-26 21:04:24 UTC
Permalink
Post by Amir Taaki
Did you try my example?
http://ideone.com/ND9tu
I'm using it under Linux on boost 1.48
Is there any (logical) difference between our examples (except MSVC 2010 fails to compile your example)?
The workaround you wrote about ( service_.post(strand.wrap(handler)) ) doesn't help at Windows (Boost 1.49, MSVC 2010,
Windows 7 Pro x64). At Ubuntu 12.04 (g++ 4.6.3, Boost 1.49) io_service::post() helps (I mean my example).
Post by Amir Taaki
~~~~~~~~~~~~~~~~~~~
#if defined(WIN32)
#include <tchar.h>
#endif
#include <cstdlib>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
namespace asio_io_service_stop_test {
const long seconds_until_stop  = 5;
const long seconds_for_handler = 2;
const long handler_count      = 50;
void cyclic_handler(int counter)
{
  std::cout << "In cyclic_handler #" << counter << std::endl;
  boost::this_thread::sleep(boost::posix_time::seconds(seconds_for_handler));
}
void run_test()
{
  boost::asio::io_service io_service;
  boost::asio::io_service::strand strand(io_service);
  for (int i = 0; i != handler_count; ++i)
  {
    // io_service.post() works here the same way as strand.post()
    io_service.post(strand.wrap(boost::bind(cyclic_handler, i)));
    //strand.post(boost::bind(cyclic_handler, i)));
  }
  std::cout << "Startig work thread\n";
  boost::thread work_thread(
      boost::bind(&boost::asio::io_service::run, &io_service));
  std::cout << "Waiting " << seconds_until_stop
            << " second(s) until call asio::io_service::stop()\n";
  boost::this_thread::sleep(boost::posix_time::seconds(seconds_until_stop));
  std::cout << "Stopping io_service\n";
  io_service.stop();
  std::cout << "io_service is stopped. Joining with work_thread\n";
  work_thread.join();
  std::cout << "work_thread finished\n";
}
} // namespace asio_io_service_stop_test
#if defined(WIN32)
int _tmain(int /*argc*/, _TCHAR* /*argv*/[])
#else
int main(int /*argc*/, char* /*argv*/[])
#endif
{
  asio_io_service_stop_test::run_test();
  return EXIT_SUCCESS;
}
~~~~~~~~~~~~~~~~~~~
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Amir Taaki
2012-06-26 21:21:13 UTC
Permalink
Ah, I thought you were saying you cannot reproduce the unexpected behaviour. I will email Chris Kohlhoff before reporting this as a bug.



----- Original Message -----
From: Marat Abrarov <***@mail.ru>
To: asio-***@lists.sourceforge.net
Cc:
Sent: Tuesday, June 26, 2012 11:04 PM
Subject: Re: [asio-users] immediately stop io_service from executing furtherwork (but not with strands??)
Post by Amir Taaki
Did you try my example?
http://ideone.com/ND9tu
I'm using it under Linux on boost 1.48
Is there any (logical) difference between our examples (except MSVC 2010 fails to compile your example)?
The workaround you wrote about ( service_.post(strand.wrap(handler)) ) doesn't help at Windows (Boost 1.49, MSVC 2010,
Windows 7 Pro x64). At Ubuntu 12.04 (g++ 4.6.3, Boost 1.49) io_service::post() helps (I mean my example).
Post by Amir Taaki
~~~~~~~~~~~~~~~~~~~
#if defined(WIN32)
#include <tchar.h>
#endif
#include <cstdlib>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
namespace asio_io_service_stop_test {
const long seconds_until_stop  = 5;
const long seconds_for_handler = 2;
const long handler_count       = 50;
void cyclic_handler(int counter)
{
  std::cout << "In cyclic_handler #" << counter << std::endl;
  boost::this_thread::sleep(boost::posix_time::seconds(seconds_for_handler));
}
void run_test()
{
  boost::asio::io_service io_service;
  boost::asio::io_service::strand strand(io_service);
  for (int i = 0; i != handler_count; ++i)
  {
    // io_service.post() works here the same way as strand.post()
    io_service.post(strand.wrap(boost::bind(cyclic_handler, i)));
    //strand.post(boost::bind(cyclic_handler, i)));
  }
  std::cout << "Startig work thread\n";
  boost::thread work_thread(
      boost::bind(&boost::asio::io_service::run, &io_service));
  std::cout << "Waiting " << seconds_until_stop
            << " second(s) until call asio::io_service::stop()\n";
  boost::this_thread::sleep(boost::posix_time::seconds(seconds_until_stop));
  std::cout << "Stopping io_service\n";
  io_service.stop();
  std::cout << "io_service is stopped. Joining with work_thread\n";
  work_thread.join();
  std::cout << "work_thread finished\n";
}
} // namespace asio_io_service_stop_test
#if defined(WIN32)
int _tmain(int /*argc*/, _TCHAR* /*argv*/[])
#else
int main(int /*argc*/, char* /*argv*/[])
#endif
{
  asio_io_service_stop_test::run_test();
  return EXIT_SUCCESS;
}
~~~~~~~~~~~~~~~~~~~
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Loading...