0

I am currently trying to get the following application to work:

  1. Await incoming client connection.
  2. Start async. timer in another class.
  3. While the timer runs repeatedly, do other stuff such as async_read and async_write.

Current source code:

#define BOOST_ASIO_ENABLE_HANDLER_TRACKING

#include <WinSock2.h>
#include <Mswsock.h>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include "TimerClass.hpp"

using namespace boost::asio;
using namespace boost::asio::ip;

TimerClass *timerClass;

void acceptHandler(const boost::system::error_code &errorCode, tcp::socket *socket) {
    timerClass = new TimerClass(socket);
    timerClass->startTimer();
    while(true) {
        // Do other suff such as async_write, ...
    }
}

int main(int argc, char** argv) {
    io_service ioService;
    tcp::socket socket(ioService);
    tcp::acceptor acceptor{ ioService, tcp::endpoint{ tcp::v4(), 12345 } };
    acceptor.listen();
    acceptor.async_accept(socket, boost::bind(acceptHandler, _1, &socket));
    ioService.run();
    return EXIT_SUCCESS;
}

TimerClass.hpp:

#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

using namespace boost::asio;
using namespace boost::posix_time;

class TimerClass {
public:
    TimerClass(ip::tcp::socket *socket);
    void startTimer();
    void timerHandler(const boost::system::error_code& errorCode);
    deadline_timer timer;
};

TimerClass.cpp:

#include <boost/bind.hpp>
#include "TimerClass.hpp"

TimerClass::TimerClass(ip::tcp::socket *socket) : timer(socket->get_io_service(), boost::posix_time::seconds(1)) {}

void TimerClass::startTimer() {
    timer.async_wait(boost::bind(&TimerClass::timerHandler, this, boost::asio::placeholders::error));
}

void TimerClass::timerHandler(const boost::system::error_code& errorCode) {
    timer.expires_at(timer.expires_at() + boost::posix_time::seconds(1));
    timer.async_wait(boost::bind(&TimerClass::timerHandler, this, boost::asio::placeholders::error));
}

Handler Tracking Output:

@asio|1461070492.111630|0*1|socket@000000000021FBD0.async_accept
@asio|1461070498.527997|>1|ec=system:0

Questions:

  1. Why won't it even call async_wait in startTimer? Debugging shows that startTimer gets called but I can't find anything in the Handler Tracking output. Why is that?
  2. Am I correctly passing the socket to the TimerClass?
  3. Without the infinite while(true) loop in the acceptHandler the acceptHandler returns but the application crashes before the io_service properly returns. How is that?
chrisp
  • 569
  • 4
  • 24
  • I compiled your code and I actually see a call to `async_wait`. Which boost version are you using (tried on 1.54). Handler tracking output: `@asio|1461081908.437388|0*1|socket@003BFE2C.async_accept @asio|1461081983.220840|>1|ec=system:0 @asio|1461081983.221817|1*2|deadline_timer@001C1318.async_wait` – Nacho Apr 19 '16 at 16:07
  • @Nacho I'm using version 1.59.0 - Does the timer actually work in your execution? It should be executed every second. – chrisp Apr 19 '16 at 16:11
  • Yes actually, I had to remove the `while(true)` though. Oh, I know what might be the problem. Do you have include guards on your `TimerClass.hpp`? – Nacho Apr 19 '16 at 16:16
  • @Nacho Check my third question on the very bottom of my original post. My application crashes when I remove the while(true). async_wait doesn't get called though. The acceptHandler returns before that (`@asio|1461083049.697883|0*1|socket@00000000002CFC80.async_accept @asio|1461083052.420039|>1|ec=system:0 @asio|1461083052.421039|<1|`) – chrisp Apr 19 '16 at 16:23
  • @Nacho I have the same behaviour with #pragma once at the very top in the header file. – chrisp Apr 19 '16 at 16:24
  • @Nacho Hmmm ... I've just moved the implementations of the functions in the .hpp file directly. That way it showed async_wait in the Handler Tracking output. How is that? – chrisp Apr 19 '16 at 16:57

1 Answers1

2

I compiled your code and it works for me (using boost version 1.54).

With your code I get the following output:

@asio|1461081908.437388|0*1|socket@003BFE2C.async_accept 
@asio|1461081983.220840|>1|ec=system:0    
@asio|1461081983.221817|1*2|deadline_timer@001C1318.async_wait

To make it run properly I had to remove the while(true) on your acceptHandler, obtaining the following output (added a std::cout inside the handler):

@asio|1461083707.104424|0*1|socket@0030FB6C.async_accept
@asio|1461083709.061824|>1|ec=system:0
@asio|1461083709.062803|1*2|deadline_timer@00641318.async_wait
@asio|1461083709.062803|<28158494073611763|
@asio|1461083710.064992|>2|ec=system:0
@asio|1461083710.064992|2|deadline_timer@00641318.cancel
@asio|1461083710.064992|2*3|deadline_timer@00641318.async_wait
TimerHandler executed...
@asio|1461083710.065971|<28169626628843099|
@asio|1461083711.065223|>3|ec=system:0
@asio|1461083711.065223|3|deadline_timer@00641318.cancel
@asio|1461083711.065223|3*4|deadline_timer@00641318.async_wait
TimerHandler executed...

I actually did this test using only the header TimerClass.hpp (defining the methods directly within it -I was lazy-) and it worked like a charm, the problem seems to be when using the .cpp file, that's why I asked if you were using include guards (not the issue though, already tested).


You should consider changing your design approach though, i.e. do not use blocking loops in you handlers, just call another asynchronous operation if needed (like async_read or async_write).

Take a look at this question and corresponding accepted answer for a nice server implementation idea. Or try to adapt some of the boost examples to your needs.

As per the segmentation fault you get when separating declaration from definition in the corresponding header and implementation files, you might want to check this other question.

Community
  • 1
  • 1
Nacho
  • 1,104
  • 1
  • 13
  • 30
  • Do you have an idea why it doesn't work with the .cpp? I'd rather not work with .hpp files exclusively. – chrisp Apr 19 '16 at 17:53
  • 1
    @chrisp Yes, there is a problem with your socket pointers. No need to pass a `socket *` to the `TimerClass`, you only need the `io_service`. But I am still trying to fix the code in a fancy manner without changing your entire implmentation. Give me a few minutes – Nacho Apr 19 '16 at 17:58
  • Hmm, I tried it using a socket pointer, socket reference, io_service pointer, io_service reference ... nothing seems to work. Also, what's wrong with the while loop? The timer is an async. event. Why would I need to remove the loop for it to work? I need it to process other events in my application. I guess I'll just wait for your solution. – chrisp Apr 19 '16 at 18:51
  • You don't need the loop because you should use the asynchronous features of boost.asio: launch an `async_read` and within its handler launch an `asyc_write` for instance. – Nacho Apr 19 '16 at 19:03
  • I have a queue that receives pushes on another thread. In the loop I start an async_write as soon as an element is found in the queue. I don't know how I should solve that without an infinite loop that looks for new elements in the queue. I guess I'll have to place the loop or timer on an extra thread. – chrisp Apr 19 '16 at 19:14
  • @chrisp I edited my question with a few tips, unfortunately some work came up and I cannot provide any further assistance right now. Hope I could be of help. – Nacho Apr 19 '16 at 19:43