new TCP timer object; solved a bunch of warnings; completely implemented the TCP termination sequence

This commit is contained in:
cozis
2023-05-21 21:56:21 +02:00
parent 96ca7ae2c9
commit 04ce79bcf0
8 changed files with 345 additions and 76 deletions
+4
View File
@@ -117,6 +117,8 @@ build/microtcp.c: 3p/include/tinycthread.h 3p/src/tinycthread.c $(wildcard src/*
cat src/icmp.h >> $@ cat src/icmp.h >> $@
printf "\n#line 1 \"src/ip.h\"\n" >> $@ printf "\n#line 1 \"src/ip.h\"\n" >> $@
cat src/ip.h >> $@ cat src/ip.h >> $@
printf "\n#line 1 \"src/tcp_timer.h\"\n" >> $@
cat src/tcp_timer.h >> $@
printf "\n#line 1 \"src/tcp.h\"\n" >> $@ printf "\n#line 1 \"src/tcp.h\"\n" >> $@
cat src/tcp.h >> $@ cat src/tcp.h >> $@
printf "\n#line 1 \"src/endian.c\"\n" >> $@ printf "\n#line 1 \"src/endian.c\"\n" >> $@
@@ -127,6 +129,8 @@ build/microtcp.c: 3p/include/tinycthread.h 3p/src/tinycthread.c $(wildcard src/*
cat src/icmp.c >> $@ cat src/icmp.c >> $@
printf "\n#line 1 \"src/ip.c\"\n" >> $@ printf "\n#line 1 \"src/ip.c\"\n" >> $@
cat src/ip.c >> $@ cat src/ip.c >> $@
printf "\n#line 1 \"src/tcp_timer.c\"\n" >> $@
cat src/tcp_timer.c >> $@
printf "\n#line 1 \"src/tcp.c\"\n" >> $@ printf "\n#line 1 \"src/tcp.c\"\n" >> $@
cat src/tcp.c >> $@ cat src/tcp.c >> $@
printf "\n#line 1 \"src/microtcp.c\"\n" >> $@ printf "\n#line 1 \"src/microtcp.c\"\n" >> $@
+1 -1
View File
@@ -89,7 +89,7 @@ void icmp_process_packet(icmp_state_t *state, ip_address_t ip, const void *src,
} }
if (state->output_ptr == NULL || state->output_len < len) { if (state->output_ptr == NULL || state->output_len < len) {
ICMP_DEBUG_LOG("Ignoring ECHO REQUEST because the output buffer is too small for an ECHO REPLY (have %ld, need %ld)", state->output_len, len); ICMP_DEBUG_LOG("Ignoring ECHO REQUEST because the output buffer is too small for an ECHO REPLY (have %d, need %d)", (int) state->output_len, (int) len);
return; return;
} }
+1 -1
View File
@@ -239,7 +239,7 @@ void ip_process_packet(ip_state_t *ip_state, const void *packet, size_t len)
size_t option_count = header_length - sizeof(ip_packet_t)/4; size_t option_count = header_length - sizeof(ip_packet_t)/4;
if (option_count > 0) { if (option_count > 0) {
#warning "TODO: Handle IP options" // TODO: Handle IP options
return; return;
} }
+23 -7
View File
@@ -187,7 +187,6 @@ static void send_arp_packet(void *data, mac_address_t dst)
frame->proto = cpu_to_net_u16(ETHERNET_PROTOCOL_ARP); frame->proto = cpu_to_net_u16(ETHERNET_PROTOCOL_ARP);
// TODO: What about the CRC? // TODO: What about the CRC?
#warning "TODO: Calculate Ethernet CRC"
int n = mtcp->callbacks.send(mtcp->callbacks.data, buffer->data, buffer->used); int n = mtcp->callbacks.send(mtcp->callbacks.data, buffer->data, buffer->used);
if (n < 0) if (n < 0)
@@ -828,9 +827,10 @@ microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port,
goto unlock_and_exit; // Socket limit reached goto unlock_and_exit; // Socket limit reached
} }
tcp_listener_t *listener = tcp_listener_create(&mtcp->tcp_state, port, socket, ready_to_accept); tcp_listener_t *listener = tcp_listener_create(&mtcp->tcp_state, port, socket, false, ready_to_accept);
if (listener == NULL) { if (listener == NULL) {
#warning "This error code should be more specific, but the TCP module isn't stable yet" // FIXME: This error code should be more specific,
// but the TCP module isn't stable yet
errcode2 = MICROTCP_ERRCODE_TCPERROR; errcode2 = MICROTCP_ERRCODE_TCPERROR;
push_unlinked_socket_into_free_list(mtcp, socket); push_unlinked_socket_into_free_list(mtcp, socket);
goto unlock_and_exit; goto unlock_and_exit;
@@ -870,10 +870,22 @@ void microtcp_close(microtcp_socket_t *socket)
microtcp_t *mtcp = socket->mtcp; microtcp_t *mtcp = socket->mtcp;
#warning "sockets should unregister from all multiplexers"
LOCK_WHEN_THREADED(mtcp); LOCK_WHEN_THREADED(mtcp);
{ {
#ifdef MICROTCP_USING_MUX
// Unregister from all multiplexers
while (socket->mux_list) {
// The unregister operation only has
// an effect when all of the triggered
// events of the socket are consumed,
// so to unregister immediately we need
// to untrigger the events
socket->mux_list->triggered_events = 0;
microtcp_mux_unregister(socket->mux_list->mux, socket, ~0);
}
#endif
switch (socket->type) { switch (socket->type) {
case SOCKET_LISTENER: case SOCKET_LISTENER:
@@ -1038,7 +1050,10 @@ size_t microtcp_recv(microtcp_socket_t *socket,
errcode2 = MICROTCP_ERRCODE_CANTBLOCK; errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
} }
} }
unlock_and_exit:
goto unlock_and_exit; // Warning
unlock_and_exit:
UNLOCK_WHEN_THREADED(mtcp); UNLOCK_WHEN_THREADED(mtcp);
if (errcode) if (errcode)
@@ -1082,7 +1097,8 @@ size_t microtcp_send(microtcp_socket_t *socket,
errcode2 = MICROTCP_ERRCODE_CANTBLOCK; errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
} }
} }
unlock_and_exit: goto unlock_and_exit; // Warning
unlock_and_exit:
UNLOCK_WHEN_THREADED(mtcp); UNLOCK_WHEN_THREADED(mtcp);
if (errcode) if (errcode)
+125 -63
View File
@@ -31,19 +31,20 @@ void tcp_init(tcp_state_t *tcp_state, ip_address_t ip, tcp_callbacks_t callbacks
tcp_state->listener_pool[TCP_MAX_LISTENERS-1].next = NULL; tcp_state->listener_pool[TCP_MAX_LISTENERS-1].next = NULL;
tcp_state->free_listener_list = tcp_state->listener_pool; tcp_state->free_listener_list = tcp_state->listener_pool;
tcp_state->used_listener_list = NULL; tcp_state->used_listener_list = NULL;
tcp_timerset_init(&tcp_state->timers);
} }
void tcp_free(tcp_state_t *tcp_state) void tcp_free(tcp_state_t *state)
{ {
// Destroy all listening connections // It's not clear to me how to free
while (tcp_state->used_listener_list != NULL) // all of this stuff up at the moment :/
tcp_listener_destroy(tcp_state->used_listener_list); tcp_timerset_free(&state->timers);
} }
void tcp_seconds_passed(tcp_state_t *state, size_t seconds) void tcp_seconds_passed(tcp_state_t *state, size_t seconds)
{ {
(void) state; tcp_timerset_step(&state->timers, seconds);
(void) seconds;
} }
static tcp_connection_t* static tcp_connection_t*
@@ -155,7 +156,7 @@ calculate_checksum(const slice_list_t *slices, size_t num_slices)
} }
static void emit_segment(tcp_connection_t *connection, uint8_t flags, size_t payload) static void emit_segment(tcp_connection_t *connection, uint8_t flags, size_t payload)
{ {
tcp_listener_t *listener = connection->listener; tcp_listener_t *listener = connection->listener;
tcp_state_t *state = listener->state; tcp_state_t *state = listener->state;
@@ -337,22 +338,79 @@ static bool ack_until(tcp_connection_t *connection, uint32_t ack_no)
return true; return true;
} }
static void
really_close_listener(tcp_listener_t *listener)
{
tcp_state_t *state = listener->state;
// Pop listener from used list
{
// Update the reference to the listener of
// the one that precedes it in the list
if (listener->prev)
listener->prev->next = listener->next;
else
state->used_listener_list = listener->next;
// Update the reference to the listener of
// the one that follows it in the list
if (listener->next != NULL)
listener->next->prev = listener->prev;
}
// Push the listener in the free list
listener->next = state->free_listener_list;
state->free_listener_list = listener;
}
static bool listener_has_no_connections(const tcp_listener_t *listener)
{
return listener->accepted_list == NULL
&& listener->non_established_list == NULL
&& listener->non_accepted_queue_head == NULL;
}
static void static void
really_close_connection(tcp_connection_t *connection) really_close_connection(tcp_connection_t *connection)
{ {
tcp_listener_t *listener = connection->listener; tcp_listener_t *listener = connection->listener;
tcp_state_t *state = listener->state; tcp_state_t *state = listener->state;
// Pop connection from the accepted_list // Pop connection from the list it's in
tcp_connection_t *next = connection->next;
if (connection->prev) if (connection->prev)
connection->prev->next = connection->next; connection->prev->next = next;
else else if (listener->accepted_list == connection)
listener->accepted_list = connection->next; listener->accepted_list = next;
else if (listener->non_established_list == connection)
listener->non_established_list = next;
else if (listener->non_accepted_queue_head == connection)
listener->non_accepted_queue_head = next;
tcp_connection_t *prev = connection->prev;
if (next)
connection->next->prev = prev;
else if (listener->non_accepted_queue_tail == connection)
listener->non_accepted_queue_tail = prev;
// Push it into the free connection list // Push it into the free connection list
connection->prev = NULL; connection->prev = NULL;
connection->next = state->free_connection_list; connection->next = state->free_connection_list;
state->free_connection_list = connection; state->free_connection_list = connection;
// If the listener is waiting to be closed
// and this was the last connection it was
// holding, then free the listener
if (listener->closed == true && listener_has_no_connections(listener))
really_close_listener(listener);
}
static void timeout_callback_time_wait(void *data)
{
tcp_connection_t *connection = data;
assert(connection->state == TCP_STATE_TIME_WAIT);
// We can finally free up this connection structure!
really_close_connection(connection);
} }
void tcp_process_segment(tcp_state_t *state, ip_address_t sender, void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
@@ -388,6 +446,13 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
tcp_connection_t *connection = find_connection_associated_to_listener(listener, sender, reordered_src_port); tcp_connection_t *connection = find_connection_associated_to_listener(listener, sender, reordered_src_port);
if (!connection) { if (!connection) {
if (listener->closed) {
// Listener was closed by the user, so already
// open connections are ok but new ones are not
// allowed.
return;
}
TCP_DEBUG_LOG("Connection request from port %d", reordered_src_port); TCP_DEBUG_LOG("Connection request from port %d", reordered_src_port);
// Something sent to an open listener. // Something sent to an open listener.
@@ -534,10 +599,17 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
if (segment->flags & (TCP_FLAG_FIN | TCP_FLAG_ACK)) { if (segment->flags & (TCP_FLAG_FIN | TCP_FLAG_ACK)) {
TCP_DEBUG_LOG("FIN-WAIT-1 -> TIME-WAIT"); TCP_DEBUG_LOG("FIN-WAIT-1 -> TIME-WAIT");
connection->rcv_nxt++; // FIN ghost byte connection->rcv_nxt++; // FIN ghost byte
emit_segment(connection, TCP_FLAG_ACK, 0); emit_segment(connection, TCP_FLAG_ACK, 0);
connection->state = TCP_STATE_TIME_WAIT; connection->state = TCP_STATE_TIME_WAIT;
// Don't close the connection just now but
// wait for a given amount of time first.
if (!tcp_timer_create(&state->timers, TCP_TIMEOUT_TIME_WAIT, timeout_callback_time_wait, connection))
assert(0);
} else if (segment->flags & TCP_FLAG_ACK) { } else if (segment->flags & TCP_FLAG_ACK) {
TCP_DEBUG_LOG("FIN-WAIT-1 -> FIN-WAIT-2"); TCP_DEBUG_LOG("FIN-WAIT-1 -> FIN-WAIT-2");
@@ -567,6 +639,11 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
emit_segment(connection, TCP_FLAG_ACK, 0); emit_segment(connection, TCP_FLAG_ACK, 0);
connection->state = TCP_STATE_TIME_WAIT; connection->state = TCP_STATE_TIME_WAIT;
// Don't close the connection just now but
// wait for a given amount of time first.
if (!tcp_timer_create(&state->timers, TCP_TIMEOUT_TIME_WAIT, timeout_callback_time_wait, connection))
assert(0);
break; break;
case TCP_STATE_CLOSE_WAIT: case TCP_STATE_CLOSE_WAIT:
@@ -609,70 +686,55 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
} }
tcp_listener_t* tcp_listener_t*
tcp_listener_create(tcp_state_t *state, uint16_t port, void *callback_data, tcp_listener_create(tcp_state_t *state, uint16_t port, bool reuse,
void (*callback_ready_to_accept)(void*)) void *callback_data, void (*callback_ready_to_accept)(void*))
{ {
if (find_listener_with_port(state, port)) { tcp_listener_t *listener;
listener = find_listener_with_port(state, port);
if (listener && (!listener->closed || !reuse)) {
// ERROR: A connection is already listening on this port // ERROR: A connection is already listening on this port
TCP_DEBUG_LOG("Faile to create listener on port %d because there already exists one", port); TCP_DEBUG_LOG("Faile to create listener on port %d because there already exists one", port);
return NULL; return NULL;
} }
// Pop a listener connection structure from the free list if (listener)
if (state->free_listener_list == NULL) { listener->closed = false;
// ERROR: Reached listener connection limit else {
TCP_DEBUG_LOG("TCP connection limit");
return NULL; // Pop a listener connection structure from the free list
if (state->free_listener_list == NULL) {
// ERROR: Reached listener connection limit
TCP_DEBUG_LOG("TCP connection limit");
return NULL;
}
listener = state->free_listener_list;
state->free_listener_list = listener->next;
// Initialize listener structure
listener->state = state;
listener->closed = false;
listener->port = port;
listener->accepted_list = NULL;
listener->non_established_list = NULL;
listener->non_accepted_queue_head = NULL;
listener->non_accepted_queue_tail = NULL;
listener->callback_data = callback_data;
listener->callback_ready_to_accept = callback_ready_to_accept;
// Push listener connection structure to the used list
listener->prev = NULL;
listener->next = state->used_listener_list;
if (state->used_listener_list)
state->used_listener_list->prev = listener;
state->used_listener_list = listener;
} }
tcp_listener_t *listener = state->free_listener_list;
state->free_listener_list = listener->next;
// Initialize listener structure
listener->state = state;
listener->port = port;
listener->accepted_list = NULL;
listener->non_established_list = NULL;
listener->non_accepted_queue_head = NULL;
listener->non_accepted_queue_tail = NULL;
listener->callback_data = callback_data;
listener->callback_ready_to_accept = callback_ready_to_accept;
// Push listener connection structure to the used list
listener->prev = NULL;
listener->next = state->used_listener_list;
if (state->used_listener_list)
state->used_listener_list->prev = listener;
state->used_listener_list = listener;
return listener; return listener;
} }
void tcp_listener_destroy(tcp_listener_t *listener) void tcp_listener_destroy(tcp_listener_t *listener)
{ {
#warning "The previously accepted connections should't be closed with their listener.. I think" listener->closed = true;
// TODO: Close all connections
tcp_state_t *state = listener->state;
// Pop listener from used list
{
// Update the reference to the listener of
// the one that precedes it in the list
if (listener->prev)
listener->prev->next = listener->next;
else
state->used_listener_list = listener->next;
// Update the reference to the listener of
// the one that follows it in the list
if (listener->next != NULL)
listener->next->prev = listener->prev;
}
// Push the listener in the free list
listener->next = state->free_listener_list;
state->free_listener_list = listener;
} }
tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener, void *callback_data, tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener, void *callback_data,
+21 -4
View File
@@ -3,8 +3,12 @@
#ifndef MICROTCP_AMALGAMATION #ifndef MICROTCP_AMALGAMATION
#include "defs.h" #include "defs.h"
#include "tcp_timer.h"
#endif #endif
#define TCP_TIMEOUT_TIME_WAIT 240
#define TCP_MAX_TIMEOUTS 1024
#define TCP_MAX_LISTENERS 32 #define TCP_MAX_LISTENERS 32
#define TCP_MAX_SOCKETS 1024 #define TCP_MAX_SOCKETS 1024
#define TCP_INPUT_BUFFER_SIZE 1024 #define TCP_INPUT_BUFFER_SIZE 1024
@@ -35,12 +39,19 @@ typedef struct {
static_assert(sizeof(tcp_segment_t) == 20); static_assert(sizeof(tcp_segment_t) == 20);
typedef struct tcp_connection_t tcp_connection_t; typedef struct tcp_connection_t tcp_connection_t;
typedef struct tcp_listener_t tcp_listener_t; typedef struct tcp_listener_t tcp_listener_t;
struct tcp_listener_t { struct tcp_listener_t {
tcp_state_t *state; tcp_state_t *state;
tcp_listener_t *prev; tcp_listener_t *prev;
tcp_listener_t *next; tcp_listener_t *next;
bool closed; // When a listener is closed while one or more connections that is
// previously accepted are open, the structure isn't deallocated
// but just marked lazily as "closed". A listener in the "closed-but-not-deallocated"
// state will not accept new connections but will serve the ones
// its still holding.
// In this state the listener can be reopened by setting the "closed"
// flag to true (and keeping the old connections intact).
uint16_t port; uint16_t port;
tcp_connection_t *accepted_list; tcp_connection_t *accepted_list;
tcp_connection_t *non_established_list; tcp_connection_t *non_established_list;
@@ -138,20 +149,26 @@ typedef struct {
} tcp_callbacks_t; } tcp_callbacks_t;
struct tcp_state_t { struct tcp_state_t {
ip_address_t ip; ip_address_t ip;
tcp_callbacks_t callbacks; tcp_callbacks_t callbacks;
tcp_timerset_t timers;
tcp_connection_t *free_connection_list; tcp_connection_t *free_connection_list;
tcp_connection_t connection_pool[TCP_MAX_SOCKETS];
tcp_listener_t *used_listener_list; tcp_listener_t *used_listener_list;
tcp_listener_t *free_listener_list; tcp_listener_t *free_listener_list;
tcp_listener_t listener_pool[TCP_MAX_LISTENERS];
tcp_listener_t listener_pool[TCP_MAX_LISTENERS];
tcp_connection_t connection_pool[TCP_MAX_SOCKETS];
}; };
void tcp_init(tcp_state_t *tcp_state, ip_address_t ip, tcp_callbacks_t callbacks); void tcp_init(tcp_state_t *tcp_state, ip_address_t ip, tcp_callbacks_t callbacks);
void tcp_free(tcp_state_t *tcp_state); void tcp_free(tcp_state_t *tcp_state);
void tcp_seconds_passed(tcp_state_t *state, size_t seconds); void tcp_seconds_passed(tcp_state_t *state, size_t seconds);
void tcp_process_segment(tcp_state_t *state, ip_address_t sender, tcp_segment_t *segment, size_t len); void tcp_process_segment(tcp_state_t *state, ip_address_t sender, tcp_segment_t *segment, size_t len);
tcp_listener_t *tcp_listener_create(tcp_state_t *state, uint16_t port, void *data, void (*callback)(void*)); tcp_listener_t *tcp_listener_create(tcp_state_t *state, uint16_t port, bool reuse, void *data, void (*callback)(void*));
void tcp_listener_destroy(tcp_listener_t *listener); void tcp_listener_destroy(tcp_listener_t *listener);
tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener, void *callback_data, void (*callback_ready_to_recv)(void*), void (*callback_ready_to_send)(void*)); tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener, void *callback_data, void (*callback_ready_to_recv)(void*), void (*callback_ready_to_send)(void*));
void tcp_connection_destroy(tcp_connection_t *connection); void tcp_connection_destroy(tcp_connection_t *connection);
+141
View File
@@ -0,0 +1,141 @@
#ifndef MICROTCP_AMALGAMATION
#include "tcp_timer.h"
#endif
void tcp_timerset_init(tcp_timerset_t *set)
{
for (size_t i = 0; i < TCP_MAX_TIMEOUTS-1; i++)
set->pool[i].next = set->pool + i+1;
set->pool[TCP_MAX_TIMEOUTS-1].next = NULL;
set->free_list = set->pool;
set->used_list = NULL;
}
void tcp_timerset_free(tcp_timerset_t *set)
{
(void) set;
}
void tcp_timer_disable(tcp_timer_t *timer)
{
tcp_timerset_t *set = timer->set;
// Pop the timer from the used list
if (timer->prev)
timer->prev->next = timer->next;
else
set->used_list = timer->next;
if (timer->next)
timer->next->prev = timer->prev;
// Push it into the free list
timer->prev = NULL;
timer->next = set->free_list;
set->free_list = timer;
}
tcp_timer_t *tcp_timer_create(tcp_timerset_t *set, size_t seconds,
void (*callback)(void*), void *data)
{
assert(callback);
if (set->free_list == NULL)
// Out of timers! This is really bad.
// What can be done to mitigate this?
return NULL;
tcp_timer_t *timer = set->free_list;
set->free_list = timer->next;
// NOTE: Since the free list is singly linked, there's
// no need to change the prev member of the new
// first item of the free list.
timer->set = set;
timer->deadline = set->current_time + seconds;
timer->data = data;
timer->callback = callback;
// Insert the timer structure into the timer list
// in an orderly fashon
if (set->used_list == NULL) {
// This is the first timer of the list
set->used_list = timer;
timer->prev = NULL;
timer->next = NULL;
} else if (timer->deadline < set->used_list->deadline) {
// This timer should be the first of the list
timer->prev = NULL;
timer->next = set->used_list;
set->used_list->prev = timer;
} else {
// The timer isn't the first of the list. We need to
// determine at which position it should be inserted
//
// Scan the list until the timer that needs to come
// after the inserted one is reached
tcp_timer_t *cursor = set->used_list;
while (cursor->next && cursor->next->deadline <= timer->deadline)
cursor = cursor->next;
tcp_timer_t *prev = cursor;
cursor = cursor->next;
if (cursor) {
// The cursor points to the element that needs to
// come after. Since we know the inserted item won't
// be the first, then this one isn't the first element
// of the list either, so its "prev" isn't NULL.
assert(cursor->prev);
timer->prev = cursor->prev;
timer->next = cursor;
cursor->prev->next = timer;
cursor->prev = timer;
} else {
// No element that needs to come after was found,
// so its position should be the last.
prev->next = timer;
timer->prev = prev;
timer->next = NULL;
}
}
return timer;
}
void tcp_timerset_step(tcp_timerset_t *set, size_t seconds)
{
set->current_time += seconds;
if (set->used_list == NULL || set->used_list->deadline > set->current_time)
// No timeouts triggered
return;
tcp_timer_t *timedout_head = set->used_list; // We know that at least one timeout triggered
tcp_timer_t *timedout_tail; // This has to be determined by the following loop
// Scan through all of the timeouts that just triggered
tcp_timer_t *timeout = set->used_list;
while (timeout) {
if (timeout->deadline > set->current_time)
// This timeout didn't trigger, so the last
// timed out timeout was the previous one.
break;
// Trigger the callback
timeout->callback(timeout->data);
timedout_tail = timeout;
timeout = timeout->next;
}
// Now put the list of timed out timeouts back
// into the free list
if (timedout_tail->next)
timedout_tail->next->prev = NULL;
set->used_list = timedout_tail->next;
timedout_tail->prev = NULL;
timedout_tail->next = set->free_list;
set->free_list = timedout_head;
}
+29
View File
@@ -0,0 +1,29 @@
#include <stdint.h>
#define TCP_MAX_TIMERS 1024
typedef struct tcp_timerset_t tcp_timerset_t;
typedef struct tcp_timer_t tcp_timer_t;
struct tcp_timer_t {
tcp_timerset_t *set;
tcp_timer_t *prev;
tcp_timer_t *next;
uint64_t deadline;
void (*callback)(void *data);
void *data;
};
struct tcp_timerset_t {
uint64_t current_time;
tcp_timer_t *used_list;
tcp_timer_t *free_list;
tcp_timer_t pool[TCP_MAX_TIMERS];
};
void tcp_timerset_step(tcp_timerset_t *set, size_t seconds);
void tcp_timerset_init(tcp_timerset_t *set);
void tcp_timerset_free(tcp_timerset_t *set);
void tcp_timer_disable(tcp_timer_t *timer);
tcp_timer_t *tcp_timer_create(tcp_timerset_t *set, size_t seconds,
void (*callback)(void*), void *data);