Does anyone know why does libuv segfault when using a functor structure instead of an actual function as a callback. In my example below, I created the structure CB to use instead of connection_cb. The segfault happens when in the operator().
#include <uv.h>
#include <stdio.h>
#include <stdlib.h>
uv_buf_t alloc_buffer(uv_handle_t * handle, size_t size);
void connection_cb(uv_stream_t * server, int status);
void read_cb(uv_stream_t * stream, ssize_t nread, uv_buf_t buf);
struct CB {
State *state_;
CB(State *state) : state_(state) {}
void operator()(uv_stream_t *server, int status) {
uv_tcp_t * client = (uv_tcp_t *)malloc(sizeof(uv_tcp_t));
/* if status not zero there was an error */
if (status == -1) {
printf("error 2\n");
}
/* initialize the new client */
uv_tcp_init(loop, client);
/* now let bind the client to the server to be used for incomings */
if (uv_accept(server, (uv_stream_t *) client) == 0) {
/* start reading from stream */
int r = uv_read_start((uv_stream_t *) client, (uv_alloc_cb)alloc_buffer, read_cb);
if (r) {
printf("error 3\n");
}
state_->onConnect();
} else {
/* close client stream on error */
uv_close((uv_handle_t *) client, NULL);
}
}
};
CB cb;
uv_tcp_t server;
uv_loop_t * loop;
int main() {
loop = uv_default_loop();
/* convert a humanreadable ip address to a c struct */
struct sockaddr_in addr;
uv_ip4_addr("127.0.0.1", 3005, &addr);
/* initialize the server */
uv_tcp_init(loop, &server);
/* bind the server to the address above */
uv_tcp_bind(&server, (const struct sockaddr *)&addr, 0);
/* let the server listen on the address for new connections */
int r = uv_listen((uv_stream_t *) &server, 128, (uv_connection_cb)&cb);
if (r) {
printf("error 1\n");
return -1;
}
/* execute all tasks in queue */
return uv_run(loop, UV_RUN_DEFAULT);
}
/**
* Callback which is executed on each new connection.
*/
void connection_cb(uv_stream_t * server, int status) {
/* dynamically allocate a new client stream object on conn */
uv_tcp_t * client = (uv_tcp_t *)malloc(sizeof(uv_tcp_t));
/* if status not zero there was an error */
if (status == -1) {
printf("error 2\n");
}
/* initialize the new client */
uv_tcp_init(loop, client);
/* now let bind the client to the server to be used for incomings */
if (uv_accept(server, (uv_stream_t *) client) == 0) {
/* start reading from stream */
//int r = uv_read_start((uv_stream_t *) client, (uv_alloc_cb)alloc_buffer, read_cb);
int r = 0;
if (r) {
printf("error 3\n");
}
} else {
/* close client stream on error */
uv_close((uv_handle_t *) client, NULL);
}
}
/**
* Callback which is executed on each readable state.
*/
void read_cb(uv_stream_t * stream, ssize_t nread, uv_buf_t buf) {
/* dynamically allocate memory for a new write task */
uv_write_t * req = (uv_write_t *) malloc(sizeof(uv_write_t));
/* if read bytes counter -1 there is an error or EOF */
if (nread == -1) {
printf("error 4\n");
uv_close((uv_handle_t *) stream, NULL);
}
/* write sync the incoming buffer to the socket */
int r = uv_write(req, stream, &buf, 1, NULL);
if (r) {
printf("error 5\n");
}
}
/**
* Allocates a buffer which we can use for reading.
*/
uv_buf_t alloc_buffer(uv_handle_t * handle, size_t size) {
return uv_buf_init((char *) malloc(size), size);
}
The pasted code will not work because libuv expects a function pointer in uv_listen
- what you are providing is a pointer to a structure. This structure happens to have an operator()
, but that does not make the address of your structure an address where the processor can jump to and execute code. The operator()
is just like any other method of your struct, but that you can call using simply ()
or .operator()
to make the code more readable. Additionally, since the operator()
is non-static, it expects an implicit reference to this
which libuv will not provide since it does not expect to have to.
To achieve what you are trying to do, you should provide a normal C function callback and store the extra context data in the .data
field of your handle:
Instead of:
int r = uv_listen((uv_stream_t *) &server, 128, (uv_connection_cb)&cb);
Use:
server.data = &cb;
int r = uv_listen((uv_stream_t *) &server, 128, [](uv_stream_t *server, int status){
(*(CB*)server->data)(server, status);
});
The .data
field on uv handles is provided exactly for that purpose.