Separating RPL and application timeslots on TSCH Schedule (Contiki-NG) - contiki

I'm trying to build a TSCH Schedule that makes the RPL transmit into a timeslot and my application transmit in another timeslot, does someone know if is that possible?
I tried to use the following function to schedule my TSCH slotframe, but currently I can't figure out how to make TSCH identify RPL and application messages.
void my_tsch_scheduler(int advertising, int rx, int tx) {
struct tsch_slotframe *sf_min;
tsch_schedule_remove_all_slotframes();
sf_min = tsch_schedule_add_slotframe(0, TSCH_SCHEDULE_DEFAULT_LENGTH);
tsch_schedule_add_link(sf_min, LINK_OPTION_TX | LINK_OPTION_RX,
LINK_TYPE_ADVERTISING, &tsch_broadcast_address, 0, 0);
tsch_schedule_add_link(sf_min, LINK_OPTION_TX | LINK_OPTION_RX,
LINK_TYPE_NORMAL, &tsch_broadcast_address, 3, 0);
}

Let's say you have two data slots scheduled at cells (3, 0) and (4, 0) and you want to use the first one for sending RPL messages and the second one for sending data:
void my_tsch_scheduler(int advertising, int rx, int tx) {
struct tsch_slotframe *sf_min;
tsch_schedule_remove_all_slotframes();
sf_min = tsch_schedule_add_slotframe(0, TSCH_SCHEDULE_DEFAULT_LENGTH);
tsch_schedule_add_link(sf_min, LINK_OPTION_TX | LINK_OPTION_RX,
LINK_TYPE_ADVERTISING, &tsch_broadcast_address, 0, 0);
tsch_schedule_add_link(sf_min, LINK_OPTION_TX | LINK_OPTION_RX,
LINK_TYPE_NORMAL, &tsch_broadcast_address, 3, 0); /* RPL */
tsch_schedule_add_link(sf_min, LINK_OPTION_TX | LINK_OPTION_RX,
LINK_TYPE_NORMAL, &tsch_broadcast_address, 4, 0); /* data */
}
The idea is to use a packet selector - a callback function that will inspect th packet and tell TSCH which cell to use.
The selector needs to have specific declaration like:
int my_callback_packet_ready(void);
In the selector you can look at packetbuf attributes to figure out whether this is a RPL message, an EB message, or a regular data message:
int my_callback_packet_ready(void) {
const uint16_t slotframe = 0;
const uint16_t channel_offset = 0;
uint16_t timeslot = 0xffff;
if(packetbuf_attr(PACKETBUF_ATTR_FRAME_TYPE) == FRAME802154_BEACONFRAME) {
/* EB packet */
timeslot = 0;
} else if (packetbuf_attr(PACKETBUF_ATTR_NETWORK_ID) == UIP_PROTO_ICMP6
&& (packetbuf_attr(PACKETBUF_ATTR_CHANNEL) >> 8) == ICMP6_RPL) {
/* RPL packet */
timeslot = 3;
} else {
/* data packet */
timeslot = 4;
}
#if TSCH_WITH_LINK_SELECTOR
packetbuf_set_attr(PACKETBUF_ATTR_TSCH_SLOTFRAME, slotframe);
packetbuf_set_attr(PACKETBUF_ATTR_TSCH_TIMESLOT, timeslot);
packetbuf_set_attr(PACKETBUF_ATTR_TSCH_CHANNEL_OFFSET, channel_offset);
#endif
return 1;
}
The you need to enable selector, and define the selector callback in application's configuration:
#define TSCH_CONF_WITH_LINK_SELECTOR 1
#define TSCH_CALLBACK_PACKET_READY my_callback_packet_ready

Related

LWIP threads seems to block any other threads on my FREERTOS setup on a arty Z7

I am learning freertos and lwip on a Arty Z7.
I have managed to launch several tasks without problem but when I try to setup a lwip server to receive TCP packets, the server works perfectly but the other tasks won't continue their work.
So when I run the following code, the xil_printf of the "dumb_task" is writing correctly its message until the PHY autonegation is complete. Then, Nothing will happen from the dumb_task but the connection from the tcp port are accepted by the fpga. (I have commented by purpose the receive packet thread as it's changing nothing).
Do you have any ideas of what could be the problem?
Thank you!
Here is my code:
what is in my main file:
static sys_thread_t g_server_th_handle;
void dumb_task(void *p){
while(1){
xil_printf("dummy!\n");
vTaskDelay(10);
}
}
int main()
{
xTaskCreate (dumb_task, "TestTask", 512, NULL, 4, NULL);
g_server_th_handle = create_server_thread(2);
vTaskStartScheduler();
while(1);
return 0;
}
what is in a .cpp/.h file for the server:
static sys_thread_t g_server_thread_handle;
static int complete_nw_thread;
struct netif server_netif;
int g_server_tasks_priority = DEFAULT_THREAD_PRIO;
void setting_thread(void *p)
{
/* the mac address of the board. this should be unique per board */
u8_t mac_ethernet_address[] = { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };
/* Add network interface to the netif_list, and set it as default */
if (!xemac_add(&server_netif, NULL, NULL, NULL, mac_ethernet_address,
PLATFORM_EMAC_BASEADDR)) {
xil_printf("Error adding N/W interface\r\n");
return;
}
netif_set_default(&server_netif);
/* specify that the network if is up */
netif_set_up(&server_netif);
/* start packet receive thread - required for lwIP operation */
sys_thread_new("xemacif_input_thread",
(void(*)(void*))xemacif_input_thread, &server_netif,
THREAD_STACKSIZE, g_server_tasks_priority);
complete_nw_thread = 1;
vTaskResume(g_server_thread_handle);
vTaskDelete(NULL);
}
void accept_loop()
{
int sock, new_sd;
int opt=1;
struct sockaddr_in address, remote;
int size;
// set up address to connect to
memset(&address, 0, sizeof(address));
if ((sock = lwip_socket(AF_INET, SOCK_STREAM, 0)) < 0) {
xil_printf("TCP server: Error creating Socket\r\n");
return;
}
address.sin_family = AF_INET;
address.sin_port = htons(TCP_CONN_PORT);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr *)&address, sizeof (address)) < 0) {
xil_printf("TCP server: Unable to bind to port %d\r\n",
TCP_CONN_PORT);
close(sock);
return;
}
ioctl(sock,FIONBIO,&opt);
if (listen(sock, 0) < 0) {
xil_printf("TCP server: tcp_listen failed\r\n");
close(sock);
return;
}
size = sizeof(remote);
xil_printf("Server set and listening\n\r");
for(;;) {
if ((new_sd = accept(sock, (struct sockaddr *)&remote,
(socklen_t *)&size)) > 0){
char *ip = inet_ntoa(((struct sockaddr_in*) &remote)->sin_addr);
gTCP_connection_count +=1;
xil_printf("New connection from %s. Number of client : %d\n\r",
ip,gTCP_connection_count);
/*sys_thread_new("TCP_recv thread",
tcp_recv_traffic, (void*)&new_sd,
TCP_SERVER_THREAD_STACKSIZE,
g_server_tasks_priority);*/
}
vTaskDelay(pdMS_TO_TICKS( 1UL ));
}
}
void server_thread(void *p)
{
// /* initialize lwIP before calling sys_thread_new */
lwip_init();
/* any thread using lwIP should be created using sys_thread_new */
sys_thread_new("nw_thread", setting_thread, NULL,
THREAD_STACKSIZE, g_server_tasks_priority);
/* Suspend Task until auto-negotiation is completed */
if (!complete_nw_thread){
vTaskSuspend(NULL);
}
assign_default_ip(&(server_netif.ip_addr), &(server_netif.netmask),
&(server_netif.gw));
print_ip_settings(&(server_netif.ip_addr), &(server_netif.netmask),
&(server_netif.gw));
/* start the application*/
accept_loop();
vTaskDelete(NULL);
return;
}
sys_thread_t create_server_thread(int priority){
g_server_tasks_priority = priority;
g_server_thread_handle = sys_thread_new("server_thread", server_thread, 0,
THREAD_STACKSIZE, priority );
return g_server_thread_handle;
}

OpenCL: buffering inputs for the same kernel

I have been trying to read a file and load it into a buffer of a kernel in OpenCL, while the kernel is processing another buffer. However, it seems to not like that: for some reason, the results are wrong.
First, I tried setting the Args for the same kernel every time before enqueueing a task. Then, I tried enqueuing tasks for 2 kernels of the same function like below, without changing the arguments:
krnl_1.setArg(0, buffer_a));
krnl_1.setArg(1, output_buffer));
krnl_2.setArg(0, buffer_b));
krnl_2.setArg(1, output_buffer));
void* ptr[2];
ptr[0] = q.enqueueMapBuffer(buffer_a, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, buffer_size_in_bytes, NULL, NULL, &err);
ptr[1] = q.enqueueMapBuffer(buffer_b, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, buffer_size_in_bytes, NULL, NULL, &err);
int sel = 0;
long long bytes_sent = 0;
// Fill buffer_a
bytes_sent += pread(myFd, (void*)ptr[sel], buffer_size_in_bytes, bytes_sent);
while (bytes_sent < total_size_in_bytes){
if (sel == 0){ // If buffer_a was just filled
q.enqueueTask(krnl_1);
sel = 1; // Fill buffer_b
} else { // If buffer_b was just filled
q.enqueueTask(krnl_2);
sel = 0; // Fill buffer_a
}
if (bytes_sent >= total_size_in_bytes) // If this is the last task
q.enqueueMigrateMemObjects({output_buffer},CL_MIGRATE_MEM_OBJECT_HOST);
else // Fill the buffer that is not being processed
bytes_sent += pread(myFd, (void*)ptr[sel], buffer_size_in_bytes, bytes_sent);
q.finish();
}
If I do it serially, it is working fine:
void* ptr[2];
ptr[0] = q.enqueueMapBuffer(buffer_a, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, buffer_size_in_bytes, NULL, NULL, &err);
ptr[1] = q.enqueueMapBuffer(buffer_b, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, buffer_size_in_bytes, NULL, NULL, &err);
int sel = 0;
long long bytes_sent = 0;
while (bytes_sent < total_size_in_bytes){
bytes_sent += pread(myFd, (void*)ptr[sel], buffer_size_in_bytes, bytes_sent);
if (sel == 0){
q.enqueueTask(krnl_1);
sel = 1;
} else {
q.enqueueTask(krnl_2);
sel = 0;
}
if (bytes_sent >= total_size_in_bytes) //if this is the last task
q.enqueueMigrateMemObjects({output_buffer},CL_MIGRATE_MEM_OBJECT_HOST);
q.finish();
}
I feel like I must have miscomprehended the way OpenCL treats arguments and enqueues tasks, but I cannot find any similar examples.
First, reads and writes by a kernel executing on a device to a memory region mapped for writing are undefined (for more information please see Accessing mapped regions of a memory object section for clEnqueueMapBuffer). Therefore, it is necessary to unmap the buffer before run the task.
Second, bytes_sent is incremented before it is checked in while() condition in the first example. If the first call of pread() reads all data, the loop won't be executed.
Therefore, I expect that the code should look something like this:
krnl_1.setArg(0, buffer_a);
krnl_1.setArg(1, output_buffer);
krnl_2.setArg(0, buffer_b);
krnl_2.setArg(1, output_buffer);
int sel = 0;
long long bytes_processed = 0;
void* buf_ptr = q.enqueueMapBuffer(buffer_a, CL_TRUE, CL_MAP_WRITE, 0, buffer_size_in_bytes, NULL, NULL, &err);
long long bytes_to_process = pread(myFd, buf_ptr, buffer_size_in_bytes, bytes_processed);
err = q.enqueueUnmapMemObject(buffer_a, buf_ptr);
while (bytes_processed < total_size_in_bytes)
{
if (sel == 0){ // If buffer_a was just filled
q.enqueueTask(krnl_1);
sel = 1; // Fill buffer_b
} else { // If buffer_b was just filled
q.enqueueTask(krnl_2);
sel = 0; // Fill buffer_a
}
bytes_processed += bytes_to_process;
if (bytes_processed < total_size_in_bytes) { // Fill the buffer that is not being processed
auto& buffer = sel ? buffer_b : buffer_a;
buf_ptr = q.enqueueMapBuffer(buffer, CL_TRUE, CL_MAP_WRITE, 0, buffer_size_in_bytes, NULL, NULL, &err);
bytes_to_process = pread(myFd, buf_ptr, buffer_size_in_bytes, bytes_processed);
err = q.enqueueUnmapMemObject(buffer, buf_ptr);
}
else { // If this is the last task
buf_ptr = q.enqueueMapBuffer(output_buffer, CL_TRUE, CL_MAP_READ, 0, output_buffer_size_in_bytes, NULL, NULL, &err);
}
}

Should we use mutex with semaphore to make a correct synchronization and to prevent a race condition?

I am trying to see the race condition happens in the comsumer-producser problem,
so I made multiple producers and mulitple consumers.
From what I know that I need to provide mutex with semaphore:
Mutex for the race conditions, because muliple producers can access the buffer at the same time. then the data might be corrupted.
And semaphore to provide signaling between the producers and the consumers
The problem here that the sync is happening correctly while I am not using the Mutex (i am using the Semaphore only). is my understanding correct or is there anything wrong to do in the code below:
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
int buffer;
int loops = 0;
sem_t empty;
sem_t full;
sem_t mutex; //Adding MUTEX
void put(int value) {
buffer = value;
}
int get() {
int b = buffer;
return b;
}
void *producer(void *arg) {
int i;
for (i = 0; i < loops; i++) {
sem_wait(&empty);
//sem_wait(&mutex);
put(i);
//printf("Data Set from %s, Data=%d\n", (char*) arg, i);
//sem_post(&mutex);
sem_post(&full);
}
}
void *consumer(void *arg) {
int i;
for (i = 0; i < loops; i++) {
sem_wait(&full);
//sem_wait(&mutex);
int b = get();
//printf("Data recieved from %s, %d\n", (char*) arg, b);
printf("%d\n", b);
//sem_post(&mutex);
sem_post(&empty);
}
}
int main(int argc, char *argv[])
{
if(argc < 2 ){
printf("Needs 2nd arg for loop count variable.\n");
return 1;
}
loops = atoi(argv[1]);
sem_init(&empty, 0, 1);
sem_init(&full, 0, 0);
sem_init(&mutex, 0, 1);
pthread_t pThreads[3];
pthread_t cThreads[3];
pthread_create(&cThreads[0], 0, consumer, (void*)"Consumer1");
pthread_create(&cThreads[1], 0, consumer, (void*)"Consumer2");
pthread_create(&cThreads[2], 0, consumer, (void*)"Consumer3");
//Passing the name of the thread as paramter, Ignore attr
pthread_create(&pThreads[0], 0, producer, (void*)"Producer1");
pthread_create(&pThreads[1], 0, producer, (void*)"Producer2");
pthread_create(&pThreads[2], 0, producer, (void*)"Producer3");
pthread_join(pThreads[0], NULL);
pthread_join(pThreads[1], NULL);
pthread_join(pThreads[2], NULL);
pthread_join(cThreads[0], NULL);
pthread_join(cThreads[1], NULL);
pthread_join(cThreads[2], NULL);
return 0;
}
I believe I have the problem figured out. Here's what is happening
When initializing your semaphores you set empty's number of threads to 1 and full's to 0
sem_init(&empty, 0, 1);
sem_init(&full, 0, 0);
sem_init(&mutex, 0, 1);
This means that there is only one "space" for the thread to get into the critical region. In other words, what your program is doing is
produce (empty is now 0, full has 1)
consume (full is now 0, empty has 0)
produce (empty is now 0, full has 1)
...
It's as if you had a token (or, if you like, a mutex), and you pass that token between consumers and producers. That is actually what the consumer-producer problem is all about, only that in most cases we are worried about having several consumers and producers working at the same time (which means you have more than one token). Here, because you have only one token, you basically have what one mutex would do.
Hope it helped :)

How to read UDP packet data containing non-string elements?

Im fairly new to IOS programming and objective-c. I have an embedded system that runs a program written in C that is sending UDP packet to iPhone app I am working on.
I am able to read the packet data (NSData) if it only contains a string but, cannot if the data is structured with additional markup.
Here is the C code that sends the packet.
typedef struct s_msg_temp_report {
uint8_t id0;
uint8_t id1;
uint8_t name[9];
uint8_t led;
uint32_t temp;
} t_msg_temp_report;
static t_msg_temp_report msg_temp_report =
{
.id0 = 0,
.id1 = 2,
.name = DEMO_PRODUCT_NAME,
.led = 0,
.temp = 0,
};
/* Send client report. */
msg_temp_report.temp = (uint32_t)(at30tse_read_temperature() * 100);
msg_temp_report.led = !port_pin_get_output_level(LED_0_PIN);
ret = sendto(tx_socket, &msg_temp_report, sizeof(t_msg_temp_report),
0,(struct sockaddr *)&addr, sizeof(addr));
if (ret == M2M_SUCCESS) {
puts("Assignment 3.3: sensor report sent");
} else {
puts("Assignment 3.3: failed to send status report !");
}
What is the best way to to process (NSData) object data into a usable object for string conversion?

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