Search code examples
c++libevent

How to manipulate libevent bufferevents outside of callbacks


I use libevent to write client-server application, on client side i want to wait for intput from the console continuously. I tried to run event_base_dispatch in thread, and in the main thread, I asked for input string and added it to bufferevent.

    std::thread libevthr(libev_start, base);    

    std::string s;
    do
    {
        cin >> s;
        bufferevent_write(bev, "Hello, world!", 13); 

    } while(s != "xxx");
    
    libevthr.join();      

For some reason, this doesn't work, but if I put bufferevent_write inside one of the callbacks, it works fine

void event_cb(struct bufferevent *bev, short events, void *ptr)
{
    if (events & BEV_EVENT_CONNECTED) {
         /* We're connected to 127.0.0.1   Ordinarily we'd do
            something here, like start reading or writing. */
        bufferevent_write(bev, "Hello, world!", 13);
        std::cout << "Connected" << std::endl;
    }
    
    if (events & BEV_EVENT_ERROR) {  
        if (EVUTIL_SOCKET_ERROR() == 10054) 
            cout << "Server stopped working!" << endl;
        else 
            cout  << "Error has happened" << endl;
    }

    if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
    {
        bufferevent_free(bev);   
    }     
}

Can you explain how should I write this correctly?

Full code here:

#include "UClient.h"
#include <iostream>
#include "event2/event.h"
#include "event2/listener.h"
#include "event2/bufferevent.h"
#include "event2/buffer.h"

#include <thread>

using std::cout, std::cin, std::endl;

void event_cb(struct bufferevent *bev, short events, void *ptr)
{
    if (events & BEV_EVENT_CONNECTED) {
         /* We're connected to 127.0.0.1   Ordinarily we'd do
            something here, like start reading or writing. */
        bufferevent_write(bev, "Hello, world!", 13);
        std::cout << "Connected" << std::endl;
    }
    
    if (events & BEV_EVENT_ERROR) {  
        if (EVUTIL_SOCKET_ERROR() == 10054) 
            cout << "Server stopped working!" << endl;
        else 
            cout  << "Error has happened" << endl;
    }

    if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
    {
        bufferevent_free(bev);     
    } 

}

void write_cb(struct bufferevent *bev, void *ctx)
{
    cout << 'Data was written' << endl;
}



void libev_start(event_base *base)
{
    event_base_dispatch(base);    
}

int main()
{
    int port = 9554;
    
    struct event_base *base;         
    struct bufferevent *bev;        
    struct sockaddr_in cl_inf;         

    if (!initWinsock()) {
        perror("Failed to initialize Winsock");
        return 1;        
    }

    base = event_base_new();

    ZeroMemory(&cl_inf, sizeof(cl_inf));
    in_addr serv_ip;

    inet_pton(AF_INET, "127.0.0.1", &serv_ip);

    cl_inf.sin_family = AF_INET;
    cl_inf.sin_addr = serv_ip;
    cl_inf.sin_port = htons(port);

    bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);

    bufferevent_setcb(bev, NULL, write_cb, event_cb, NULL);  

    if (bufferevent_socket_connect(bev,
        (struct sockaddr *)&cl_inf, sizeof(cl_inf)) < 0) {
        /* Error starting connection */
        std::cout << "Can't connect to server!" << std::endl;
        bufferevent_free(bev);
        return -1;
    }

    bufferevent_enable(bev, EV_READ | EV_WRITE);

    
    std::thread libevthr(libev_start, base);    

    std::string s;
    do
    {
        cin >> s;
    } while(s != "xxx");
    
    libevthr.join();        
    std::cout << "client finished working";
    return 0;

}

Solution

  • You need to 'tell' libevent that 'we are in multi-threaded context', and then libevent will do some synchronizing stuff in its APIs, i.e. bufferevent_write.

    To do so, place evthread_use_windows_threads(); (since you are calling some Windows API) before any other libevent calling.

    Good luck :)