Why does this cause a Seg Fault? - pthreads

I'm working on an assignment dealing with threading and synchronization with pthreads. In the example code, the main thread creates two other threads which execute fine. The main thread is blocked until both these "child" threads terminate. At least, this is the way I understand it. When the main thread resumes execution, it seems to be getting a segmentation fault when it calls the destructor for AvionicsTask. Honestly, I have no idea why, except that I may not be initializing something correctly. Anyway, the code is as follows:
Task.h:
class Task {
protected:
/* -- NAME */
static const int MAX_NAME_LEN = 15;
char name[MAX_NAME_LEN];
/* -- IMPLEMENTATION */
pthread_t thread_id;
public:
/* -- CONSTRUCTOR/DESTRUCTOR */
Task(const char _name[]) {
std::strncpy(name, _name, MAX_NAME_LEN);
}
~Task(){}
/* -- ACCESSORS */
char * Name();
virtual void Start();
virtual void Run()= 0;
static void GracefullyExitMainThread();
};
Task.cpp:
#include "task.h"
std::vector<pthread_t> tasklist; //keep track of tasks created
void * thunkfunc(void * args) {
Task * task_instance = (Task *)args;
task_instance->Run();
return NULL;
}
void Task::Start(){
pthread_t threadmachine;
void * start_arg = NULL;
pthread_create(&threadmachine, NULL, thunkfunc, this);
tasklist.push_back(threadmachine);
}
void Task::GracefullyExitMainThread() {
void** return_value; //unused
for(int i = 0; i < tasklist.size(); i++){
pthread_join(tasklist[i], return_value);
}
}
char * Task::Name(){
return name;
}
Task_Test_step1.cpp:
#include <iostream>
using namespace std;
#include "task.h"
class RudderController : public Task {
public:
RudderController(char _name[]) : Task(_name) {}
void Run() {
cout << "Rudder Controller [" << name << "] running\n" << flush;
for (int i = 0; i < 10; i++) {
cout << name << " waiting for next sensor input\n" << flush;
usleep(1000000);
cout << name << " issueing rudder control command" << i << "\n" << flush;
usleep(10000);
}
}
};
class AvionicsTask : public Task {
public:
AvionicsTask(char _name[]) : Task(_name) {}
void Run() {
cout << "Avionics System [" << name << "] running\n" << flush;
for (int i = 0; i < 10; i++) {
cout << name << " waiting for next refresh interval\n" << flush;
usleep(700000);
cout << name << " refreshing avionics screen " << i << "\n" << flush;
usleep(12000);
}
}
};
int main(int argc, char * argv[]) {
/* -- CREATE TASKS */
RudderController task1("rudder control");
AvionicsTask task2("avionics task");
/* -- LAUNCH TASKS */
task1.Start();
task2.Start();
Task::GracefullyExitMainThread();
}
the output of the program with gdb:
Starting program: /home/ben/Desktop/Part 1/test
[Thread debugging using libthread_db enabled]
[New Thread 0x7ffff707e700 (LWP 6797)]
Rudder Controller [rudder control] running
rudder control waiting for next sensor input
[New Thread 0x7ffff687d700 (LWP 6798)]
Avionics System [avionics task] running
avionics task waiting for next refresh interval
avionics task refreshing avionics screen 0
...
//more messages from the threads
...
avionics task refreshing avionics screen 9
[Thread 0x7ffff687d700 (LWP 6798) exited]
rudder control issueing rudder control command7
rudder control waiting for next sensor input
rudder control issueing rudder control command8
rudder control waiting for next sensor input
rudder control issueing rudder control command9
[Thread 0x7ffff707e700 (LWP 6797) exited]
Program received signal SIGSEGV, Segmentation fault.
0x0000000000401ffa in AvionicsTask::~AvionicsTask (this=0xffffffffffffffc0,
__in_chrg=<optimized out>) at task_test_step1.cpp:21
21 class AvionicsTask : public Task {

The way you're using return_value is wrong:
void** return_value;
for(int i = 0; i < tasklist.size(); i++){
pthread_join(tasklist[i], return_value); // <== return_value has an indeterminate value
// who knows where pthread_join() will write to?
}
Just pass in NULL if you're not interested in what the thread you're joining passes to pthread_exit() (or equivalently what the thread function returns):
for(int i = 0; i < tasklist.size(); i++){
pthread_join(tasklist[i], NULL); // <== ignore what the thread returns
}
If you ever do decide you want to get the value returned by the thread, use something that looks like:
void* return_value;
for(int i = 0; i < tasklist.size(); i++){
pthread_join(tasklist[i], &return_value);
// return_value contains the data the thread returned...
}

Related

Libqmi - glib callback function not getting called

I am new to libqmi and wanted to start by just opening a new device. But the callback function is never getting called and therefore no device object returned.
I running the code on Ubuntu 64 Bit.
On this website: https://developer.gnome.org/gio/stable/GAsyncResult.html
I found how this should be handled and programmed it that way, but it still doesn't work.
#include <iostream>
#include <libqmi-glib/libqmi-glib.h>
#include <gio/gio.h>
using namespace std;
void device_create_start(const char* device_file);
void device_create_stop(GObject* obj, GAsyncResult* res, gpointer data);
int something = 0;
int main()
{
cout << "Start\n";
device_create_start("/dev/cdc-wdm0");
cout << "DEBUG: Something: " << something << "\n";
cout << "Stop\n";
return 0;
}
void device_create_start(const char* device_file)
{
GFile* file = g_file_new_for_path(device_file);
if(file)
{
GCancellable* cancellable = g_cancellable_new();
GAsyncReadyCallback callback = device_create_stop;
gpointer user_data = NULL;
cout << "INFO: qmi_device_new starting!\n";
qmi_device_new(file, cancellable, callback, user_data);
cout << "INFO: qmi_device_new started!\n";
cout << "INFO: Waiting!\n";
usleep(10000);
cout << "INFO: Is cancelled?: " << g_cancellable_is_cancelled(cancellable) << "\n";
cout << "INFO: canceling!\n";
g_cancellable_cancel(cancellable);
cout << "INFO: Waiting again!\n";
usleep(100000);
cout << "INFO: Is cancelled?: " << g_cancellable_is_cancelled(cancellable) << "\n";
something = 1;
}
else
{
cout << "ERROR: Could not create device file!\n";
}
}
void device_create_stop(GObject* obj, GAsyncResult* res, gpointer data)
{
cout << "INFO: device_create_stop\n";
something = 2;
cout << "INFO: qmi_device_new_finish starting\n";
GError *error;
QmiDevice* device = qmi_device_new_finish(res, &error);
cout << "INFO: qmi_device_new_finish started\n";
if(device == NULL)
{
cout << "ERROR: Could not create device!\n";
}
else
{
cout << "INFO: Device created!\n";
//device_open(device);
}
}
When I run this code the output is:
Start
INFO: qmi_device_new starting!
INFO: qmi_device_new started!
INFO: Waiting!
INFO: Is cancelled?: 0
INFO: canceling!
INFO: Waiting again!
INFO: Is cancelled?: 1
DEBUG: Something: 1
Stop
The code in the callback function is never called.
Update 1
I simplified the code and changed some things that I oversaw on the gnome reference site, like a static callback function. But this doesn't work either
#include <iostream>
#include <libqmi-glib/libqmi-glib.h>
#include <gio/gio.h>
#include <glib/gprintf.h>
using namespace std;
void device_create_start(const char* device_file);
static void device_create_stop(GObject* obj, GAsyncResult* res, gpointer data);
int something = 0;
int main()
{
g_printf ("Start\n");
device_create_start("/dev/cdc-wdm0");
cout << "DEBUG: Something: " << something << "\n";
while(true)
{
;
}
cout << "Stop\n";
return 0;
}
void device_create_start(const char* device_file)
{
GFile* file = g_file_new_for_path(device_file);
if(file)
{
cout << "INFO: qmi_device_new starting!\n";
qmi_device_new(file, NULL, device_create_stop, NULL);
cout << "INFO: qmi_device_new started!\n";
something = 1;
}
else
{
cout << "ERROR: Could not create device!\n";
}
}
static void device_create_stop(GObject* obj, GAsyncResult* res, gpointer data)
{
g_printf ("Hurray!\n");
something = 2;
}
The new output:
Start
INFO: qmi_device_new starting!
INFO: qmi_device_new started!
DEBUG: Something: 1
Does anyone has a clue why this is not working?
As Philip said (hey Philip!), you're missing the main loop. The qmi_device_new() function is an method that finishes asynchronously, and once finished, the result of the operation is provided in the callback function you provide. In order for the asynchronous function to even do something, you need to have a GMainLoop running for as long as your program logic runs.

child class of leafsystem generating sinusoidal signal

I am trying to make a child class of LeafSystem whose output is sinusoidal and its derivative.
I wrote the code and try to plot it but signal logger doesn't log correctly.
#include "drake/systems/framework/leaf_system.h"
#include "drake/systems/analysis/simulator.h"
#include "drake/systems/framework/diagram.h"
#include "drake/systems/framework/diagram_builder.h"
#include "drake/systems/primitives/signal_logger.h"
#include "drake/common/proto/call_python.h"
class Sinusoid : public drake::systems::LeafSystem<double>
{
public:
Sinusoid (double tstart, double freq, double amp, double offset) :
m_freq(freq), m_amp(amp), m_offset(offset), m_tstart(tstart) {
this->DeclareVectorOutputPort(
drake::systems::BasicVector<double>(2), &Sinusoid::output);
}
private:
void output(const drake::systems::Context<double>& c, drake::systems::BasicVector<double>* output) const {
double t(c.get_time());
double tknot(t - m_tstart);
if (t > m_tstart) {
output->SetAtIndex(0, std::sin(tknot*m_freq + m_offset)*m_amp);
output->SetAtIndex(1, std::cos(tknot*m_freq + m_offset)*m_amp*m_freq);
} else {
output->SetAtIndex(0, 0.0);
output->SetAtIndex(1, 0.0);
}
}
double m_freq{0.0}, m_amp{0.0}, m_offset{0.0}, m_tstart{0.0};
};
int main(int argc, char *argv[])
{
// Add System and Connect
drake::systems::DiagramBuilder<double> builder;
auto system = builder.AddSystem<Sinusoid>(1.0, 2.*M_PI*1., 3., 0.);
auto logger = LogOutput(system->get_output_port(0), &builder);
auto diagram = builder.Build();
// Construct Simulator
drake::systems::Simulator<double> simulator(*diagram);
// Run simulation
simulator.StepTo(100);
// Plot with Python
auto sample_time = logger->sample_times();
auto sample_data = logger->data();
std::cout << sample_time.size() << std::endl;
for (int i = 0; i < sample_time.size(); ++i) {
std::cout << sample_time(i) << " : " << sample_data(i, 0) << " " << sample_data(i, 1) << std::endl;
}
std::cout << "END" << std::endl;
return 0;
}
The output of the code is
2
0 : 0 0
0 : 0 0
END
Whatever number I used in StepTo function, signal logger only cate 2 data whose sampled times are both 0.
The code looks good. Note that TrajectorySource does this almost exactly (and used SingleOutputVectorSource as a base class, which you might consider, too). The only problem is that you do not have anything telling the simulator that there is a reason to evaluate the output port. The logger block will pull on that for every publish event, but you haven't told the simulator to publish.
The solution is to call
simulator.set_publish_every_timestep(true)
http://drake.mit.edu/doxygen_cxx/classdrake_1_1systems_1_1_simulator.html#aef1dc6aeb821503379ab1dd8c6044562
If you want to further control the timestep of the integrator, you could set the parameters of the integrator (e.g. simalator.get_integerator), then calls like set_fixed_step_mode.

pthread and condition variable

I'm wondering why the following code gives unexpected output: a can get 110...!
pthread_t th[nbT];
void * func(void *d)
{
while(a<100)
{
pthread_mutex_lock(&l);
cout <<a <<" in thread "<<pthread_self()<<"\n";
a+=1;
pthread_mutex_unlock(&l);
}
return NULL;
}
int main(int argc, const char* argv[])
{
for(int i=0;i<nbT;i++)
pthread_create(&(th[i]), NULL, func, NULL);
for(int i=0;i<nbT;i++)
pthread_join(th[i],NULL);
}
The problem is that you get the lock (mutex) after checking the condition, so you don't know if it's still true or not once you get the lock. You should just do a simple double-check:
while(a<100)
{
pthread_mutex_lock(&l);
cout <<a <<" in thread "<<pthread_self()<<"\n";
if (a<100) a+=1; // <== Added condition here!
pthread_mutex_unlock(&l);
}

Forcing a Lua script to exit

How do you end a long running Lua script?
I have two threads, one runs the main program and the other controls a user supplied Lua script. I need to kill the thread that's running Lua, but first I need the script to exit.
Is there a way to force a script to exit?
I have read that the suggested approach is to return a Lua exception. However, it's not garanteed that the user's script will ever call an api function ( it could be in a tight busy loop). Further, the user could prevent errors from causing his script to exit by using a pcall.
You could use setjmp and longjump, just like the Lua library does internally. That will get you out of pcalls and stuff just fine without need to continuously error, preventing the script from attempting to handle your bogus errors and still getting you out of execution. (I have no idea how well this plays with threads though.)
#include <stdio.h>
#include <setjmp.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
jmp_buf place;
void hook(lua_State* L, lua_Debug *ar)
{
static int countdown = 10;
if (countdown > 0)
{
--countdown;
printf("countdown: %d!\n", countdown);
}
else
{
longjmp(place, 1);
}
}
int main(int argc, const char *argv[])
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_sethook(L, hook, LUA_MASKCOUNT, 100);
if (setjmp(place) == 0)
luaL_dostring(L, "function test() pcall(test) print 'recursing' end pcall(test)");
lua_close(L);
printf("Done!");
return 0;
}
You could set a variable somewhere in your program and call it something like forceQuitLuaScript. Then, you use a hook, described here to run every n instructions. After n instructions, it'll run your hook which just checks if forceQuitLuaScript is set, and if it is do any clean up you need to do and kill the thread.
Edit: Here's a cheap example of how it could work, only this is single threaded. This is just to illustrate how you might handle pcall and such:
#include <stdlib.h>
#include "lauxlib.h"
void hook(lua_State* L, lua_Debug *ar)
{
static int countdown = 10;
if (countdown > 0)
{
--countdown;
printf("countdown: %d!\n", countdown);
}
else
{
// From now on, as soon as a line is executed, error
// keep erroring until you're script reaches the top
lua_sethook(L, hook, LUA_MASKLINE, 0);
luaL_error(L, "");
}
}
int main(int argc, const char *argv[])
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_sethook(L, hook, LUA_MASKCOUNT, 100);
// Infinitely recurse into pcalls
luaL_dostring(L, "function test() pcall(test) print 'recursing' end pcall(test)");
lua_close(L);
printf("Done!");
return 0;
}
The way to end a script is to raise an error by calling error. However, if the user has called the script via pcall then this error will be caught.
It seems like you could terminate the thread externally (from your main thread) since the lua script is user supplied and you can't signal it to exit.
If that isn't an option, you could try the debug API. You could use lua_sethook to enable you to regain control assuming you have a way to gracefully terminate your thread in the hook.
I haven't found a way to cleanly kill a thread that is executing a long running lua script without relying on some intervention from the script itself. Here are some approaches I have taken in the past:
If the script is long running it is most likely in some loop. The script can check the value of some global variable on each iteration. By setting this variable from outside of the script you can then terminate the thread.
You can start the thread by using lua_resume. The script can then exit by using yield().
You could provide your own implementation of pcall that checks for a specific type of error. The script could then call error() with a custom error type that your version of pcall could watch for:
function()
local there_is_an_error = do_something()
if (there_is_an_error) then
error({code = 900, msg = "Custom error"})
end
end
possibly useless, but in the lua I use (luaplayer or PGELua), I exit with
os.exit()
or
pge.exit()
If you're using coroutines to start the threads, you could maybe use coroutine.yield() to stop it.
You might wanna take look at
https://github.com/amilamad/preemptive-task-scheduler-for-lua
project. its preemptive scheduler for lua.
It uses a lua_yeild function inside the hook. So you can suspend your lua thread. It also uses longjmp inside but its is much safer.
session:destroy();
Use this single line code on that where you are want to destroy lua script.
lua_KFunction cont(lua_State* L);
int my_yield_with_res(lua_State* L, int res) {
cout << " my_yield_with_res \n" << endl;
return lua_yieldk(L, 0, lua_yield(L, res), cont(L));/* int lua_yieldk(lua_State * L, int res, lua_KContext ctx, lua_KFunction k);
Приостанавливает выполнение сопрограммы(поток). Когда функция C вызывает lua_yieldk, работающая
сопрограмма приостанавливает свое выполнение и вызывает lua_resume, которая начинает возврат данной сопрограммы.
Параметр res - это число значений из стека, которые будут переданы в качестве результатов в lua_resume.
Когда сопрограмма снова возобновит выполнение, Lua вызовет заданную функцию продолжения k для продолжения выполнения
приостановленной C функции(смотрите §4.7). */
};
int hookFunc(lua_State* L, lua_Debug* ar) {
cout << " hookFunc \n" << endl;
return my_yield_with_res(L, 0);// хук./
};
lua_KFunction cont(lua_State* L) {// функция продолжения.
cout << " hooh off \n" << endl;
lua_sethook(L, (lua_Hook)hookFunc, LUA_MASKCOUNT, 0);// отключить хук foo.
return 0;
};
struct Func_resume {
Func_resume(lua_State* L, const char* funcrun, unsigned int Args) : m_L(L), m_funcrun(funcrun), m_Args(Args) {}
//имена функций, кол-во агрументов.
private:
void func_block(lua_State* L, const char* functionName, unsigned int Count, unsigned int m_Args) {
lua_sethook(m_L, (lua_Hook)hookFunc, LUA_MASKCOUNT, Count); //вызов функции с заданной паузой.
if (m_Args == 0) {
lua_getglobal(L, functionName);// получить имя функции.
lua_resume(L, L, m_Args);
}
if (m_Args != 0) {
int size = m_Args + 1;
lua_getglobal(L, functionName);
for (int i = 1; i < size; i++) {
lua_pushvalue(L, i);
}
lua_resume(L, L, m_Args);
}
};
public:
void Update(float dt) {
unsigned int Count = dt * 100.0;// Время работы потока.
func_block(m_L, m_funcrun, Count, m_Args);
};
~Func_resume() {}
private:
lua_State* m_L;
const char* m_funcrun; // имя функции.
unsigned int m_Count;// число итерации.
unsigned int m_Args;
};
const char* LUA = R"(
function main(y)
--print(" func main arg, a = ".. a.." y = ".. y)
for i = 1, y do
print(" func main count = ".. i)
end
end
)";
int main(int argc, char* argv[]) {
lua_State* L = luaL_newstate();/*Функция создает новое Lua состояние. */
luaL_openlibs(L);
luaL_dostring(L, LUA);
//..pushlua(L, 12);
pushlua(L, 32);
//do {
Func_resume func_resume(L, "main", 2);
func_resume.Update(1.7);
lua_close(L);
// } while (LUA_OK != lua_status(L)); // Пока поток не завершен.
return 0;
};

A question of libevent example code: how is invoked?

I'm learning libev however the code is so hard to understand, so I choose to learn libevent first whose code is relatively clearer. But I encounter a problem when try the example (http://www.wangafu.net/~nickm/libevent-book/01_intro.html).
How is the code event_add(state->write_event, NULL) in do_read() make do_write() function invoked?
/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For fcntl */
#include <fcntl.h>
#include <event2/event.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define MAX_LINE 16384
void do_read(evutil_socket_t fd, short events, void *arg);
void do_write(evutil_socket_t fd, short events, void *arg);
char
rot13_char(char c)
{
return c;
/* We don't want to use isalpha here; setting the locale would change
* which characters are considered alphabetical. */
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
return c + 13;
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
return c - 13;
else
return c;
}
struct fd_state {
char buffer[MAX_LINE];
size_t buffer_used;
size_t n_written;
size_t write_upto;
struct event *read_event;
struct event *write_event;
};
struct fd_state *
alloc_fd_state(struct event_base *base, evutil_socket_t fd)
{
struct fd_state *state = malloc(sizeof(struct fd_state));
if (!state)
return NULL;
state->read_event = event_new(base, fd, EV_READ|EV_PERSIST, do_read, state);
if (!state->read_event) {
free(state);
return NULL;
}
state->write_event =
event_new(base, fd, EV_WRITE|EV_PERSIST, do_write, state);
if (!state->write_event) {
event_free(state->read_event);
free(state);
return NULL;
}
state->buffer_used = state->n_written = state->write_upto = 0;
assert(state->write_event);
return state;
}
void
free_fd_state(struct fd_state *state)
{
event_free(state->read_event);
event_free(state->write_event);
free(state);
}
void
do_read(evutil_socket_t fd, short events, void *arg)
{
struct fd_state *state = arg;
char buf[1024];
int i;
ssize_t result;
while (1) {
assert(state->write_event);
result = recv(fd, buf, sizeof(buf), 0);
if (result <= 0)
break;
for (i=0; i < result; ++i) {
if (state->buffer_used < sizeof(state->buffer))
state->buffer[state->buffer_used++] = rot13_char(buf[i]);
if (buf[i] == '\n') {
assert(state->write_event);
**event_add(state->write_event, NULL);**
state->write_upto = state->buffer_used;
}
}
}
if (result == 0) {
free_fd_state(state);
} else if (result < 0) {
if (errno == EAGAIN) // XXXX use evutil macro
return;
perror("recv");
free_fd_state(state);
}
}
void
**do_write(evutil_socket_t fd, short events, void *arg)**
{
struct fd_state *state = arg;
while (state->n_written < state->write_upto) {
ssize_t result = send(fd, state->buffer + state->n_written,
state->write_upto - state->n_written, 0);
if (result < 0) {
if (errno == EAGAIN) // XXX use evutil macro
return;
free_fd_state(state);
return;
}
assert(result != 0);
state->n_written += result;
}
if (state->n_written == state->buffer_used)
state->n_written = state->write_upto = state->buffer_used = 1;
event_del(state->write_event);
}
void
do_accept(evutil_socket_t listener, short event, void *arg)
{
struct event_base *base = arg;
struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int fd = accept(listener, (struct sockaddr*)&ss, &slen);
if (fd < 0) { // XXXX eagain??
perror("accept");
} else if (fd > FD_SETSIZE) {
close(fd); // XXX replace all closes with EVUTIL_CLOSESOCKET */
} else {
struct fd_state *state;
evutil_make_socket_nonblocking(fd);
state = alloc_fd_state(base, fd);
assert(state); /*XXX err*/
assert(state->write_event);
event_add(state->read_event, NULL);
}
}
void
run(void)
{
evutil_socket_t listener;
struct sockaddr_in sin;
struct event_base *base;
struct event *listener_event;
base = event_base_new();
if (!base)
return; /*XXXerr*/
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(40713);
listener = socket(AF_INET, SOCK_STREAM, 0);
evutil_make_socket_nonblocking(listener);
#ifndef WIN32
{
int one = 1;
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
#endif
if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
perror("bind");
return;
}
if (listen(listener, 16)<0) {
perror("listen");
return;
}
listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);
/*XXX check it */
event_add(listener_event, NULL);
event_base_dispatch(base);
}
int
main(int c, char **v)
{
setvbuf(stdout, NULL, _IONBF, 0);
run();
return 0;
}
I'm not sure if I'm answering the same question you asked - I understand it as:
How does calling event_add(state->write_event, NULL) in do_read() lead to do_write() being invoked?
The key to figuring this out is understanding what the do_read() function is actually doing. do_read() is a callback function associated with a socket which has data to be read: this is set up with allocate_fd_state():
struct fd_state *
alloc_fd_state(struct event_base *base, evutil_socket_t fd)
{
/*
* Allocate a new fd_state structure, which will hold our read and write events
* /
struct fd_state *state = malloc(sizeof(struct fd_state));
[...]
/*
* Initialize a read event on the given file descriptor: associate the event with
* the given base, and set up the do_read callback to be invoked whenever
* data is available to be read on the file descriptor.
* /
state->read_event = event_new(base, fd, EV_READ|EV_PERSIST, do_read, state);
[...]
/*
* Set up another event on the same file descriptor and base, which invoked the
* do_write callback anytime the file descriptor is ready to be written to.
*/
state->write_event =
event_new(base, fd, EV_WRITE|EV_PERSIST, do_write, state);
[...]
return state;
}
At this point, though, neither of these events have been event_add()'ed to the event_base base. The instructions for what to do are all written out, but no one is looking at them. So how does anything get read? state->read_event is event_add()'ed to the base after an incoming connection is made. Look at do_accept():
void
do_accept(evutil_socket_t listener, short event, void *arg)
{
[ ... accept a new connection and give it a file descriptor fd ... ]
/*
* If the file descriptor is invalid, close it.
*/
if (fd < 0) { // XXXX eagain??
perror("accept");
} else if (fd > FD_SETSIZE) {
close(fd); // XXX replace all closes with EVUTIL_CLOSESOCKET */
/*
* Otherwise, if the connection was successfully accepted...
*/
} else {
[ ... allocate a new fd_state structure, and make the file descriptor non-blocking ...]
/*
* Here's where the magic happens. The read_event created back in alloc_fd_state()
* is finally added to the base associated with it.
*/
event_add(state->read_event, NULL);
}
}
So right after accepting a new connection, the program tells libevent to wait until there's data available on the connection, and then run the do_read() callback. At this point, it's still impossible for do_write() to be called. It needs to be event_add()'ed. This happens in do_read():
void
do_read(evutil_socket_t fd, short events, void *arg)
{
/* Create a temporary buffer to receive some data */
char buf[1024];
while (1) {
[ ... Receive the data, copying it into buf ... ]
[ ... if there is no more data to receive, or there was an error, exit this loop... ]
[ ... else, result = number of bytes received ... ]
for (i=0; i < result; ++i) {
[ ... if there's room in the buffer, copy in the rot13() encoded
version of the received data ... ]
/*
* Boom, headshot. If we've reached the end of the incoming data
* (assumed to be a newline), then ...
*/
if (buf[i] == '\n') {
[...]
/*
* Have libevent start monitoring the write_event, which calls do_write
* as soon as the file descriptor is ready to be written to.
*/
event_add(state->write_event, NULL);
[...]
}
}
}
[...]
}
So, after reading in some data from a file descriptor, the program starts waiting until
the file descriptor is ready to be written to, and then invokes do_write(). Program
flow looks like this:
[ set up an event_base and start waiting for events ]
[ if someone tries to connect ]
[ accept the connection ]
[ ... wait until there is data to read on the connection ... ]
[ read in data from the connection until there is no more left ]
[ ....wait until the connection is ready to be written to ... ]
[ write out our rot13() encoded response ]
I hope that a) that was the correct interpretation of your question, and b) this was a helpful answer.

Resources