Question

I have a chat server in C/Linux using TCP sockets. When using libev I am able to create an ev_io watcher for read events once for a socket. Something like:

ev_io* new_watcher = (ev_io*)malloc(sizeof(ev_io));

//initialize the watcher
ev_init(new_watcher, read_cb);

//set the fd and event to fire on write
ev_io_set(new_watcher, watcher->fd, EV_READ);

//start watching
ev_io_start(loop, new_watcher);

and this works fine because the read event will only fire when there is data to read. However, I have to treat write events differently because they are constantly firing even when I don't have data to write. To solve this problem I have my read_callback create an ev_io watcher for a write data only when there is data ready to be written, and then the write_callback will delete the watcher after it has sent it's message.

This means that I am allocating, initialising, setting, watching, unwatching and deallocating the write watcher each time I need to handle a message. I worry that I may be handling this incorrectly and inefficiently.

What is the best method for handling write_callback events in libev?

Thanks in advance.

Was it helpful?

Solution

The allocation can add some overhead, you could use a static variable instead of malloc, or malloc once and only free after the event loop is finished. You only need to set before write and unset after it succeeds. But yes, that's how it needs to be done.

OTHER TIPS

Easy, there is also ev_io_stop, so what you do is not start the write watcher unless you have anything to write, and inside the callback, you call ev_io_stop when you have written the whole buffer.

In the common case where you rarely overflow trhe write buffer (because your data is small and you don't wirte too often), you can make this more efficient by trying to write the data directly (if the watcher is not active) and only buffering the data and starting the write watcher if you couldn't completely writee it.

Under the assumptions above, this means you almost never need to start the write watcher. The drawback is vastly more complex code, so in many cases, it's best to start witht he simple "append data to write buffer, start the watcher, inside the watcher stop it if buffer has been written fully" logic.

The way I solved this situation was to have a function for writing data that takes a pointer to the buffer and the length. It stores the pointer and length in a queue data structure and enables the write event.

When the write event callback fires, it checks the write queue to see if there are any pending writes. If there are any, it takes the next pending write in the queue and writes it to the file descriptor. Then just before the write callback exits, it checks to see if the pending write queue is empty. If so, then it disables the write event.

If you make your read/write event objects global variables, then they will only get allocated and freed once. You enable the write event whenever you know there is data to be written and you disable it once there is no more data to be written.

My code is a bit more complicated than the above description but I'll post a link here just so you can take a look. The code I'm talking about specifically is in the aiofd.h and aiofd.c (aiofd == Asynchronous I/O File Descriptor): https://bitbucket.org/wookie/cutil/

I hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top