new TCP timer object; solved a bunch of warnings; completely implemented the TCP termination sequence
This commit is contained in:
+1
-1
@@ -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) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
if (option_count > 0) {
|
||||
#warning "TODO: Handle IP options"
|
||||
// TODO: Handle IP options
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+23
-7
@@ -187,7 +187,6 @@ static void send_arp_packet(void *data, mac_address_t dst)
|
||||
frame->proto = cpu_to_net_u16(ETHERNET_PROTOCOL_ARP);
|
||||
|
||||
// TODO: What about the CRC?
|
||||
#warning "TODO: Calculate Ethernet CRC"
|
||||
|
||||
int n = mtcp->callbacks.send(mtcp->callbacks.data, buffer->data, buffer->used);
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
#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;
|
||||
push_unlinked_socket_into_free_list(mtcp, socket);
|
||||
goto unlock_and_exit;
|
||||
@@ -870,10 +870,22 @@ void microtcp_close(microtcp_socket_t *socket)
|
||||
|
||||
microtcp_t *mtcp = socket->mtcp;
|
||||
|
||||
#warning "sockets should unregister from all multiplexers"
|
||||
|
||||
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) {
|
||||
|
||||
case SOCKET_LISTENER:
|
||||
@@ -1038,7 +1050,10 @@ size_t microtcp_recv(microtcp_socket_t *socket,
|
||||
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
|
||||
}
|
||||
}
|
||||
unlock_and_exit:
|
||||
|
||||
goto unlock_and_exit; // Warning
|
||||
unlock_and_exit:
|
||||
|
||||
UNLOCK_WHEN_THREADED(mtcp);
|
||||
|
||||
if (errcode)
|
||||
@@ -1082,7 +1097,8 @@ size_t microtcp_send(microtcp_socket_t *socket,
|
||||
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
|
||||
}
|
||||
}
|
||||
unlock_and_exit:
|
||||
goto unlock_and_exit; // Warning
|
||||
unlock_and_exit:
|
||||
UNLOCK_WHEN_THREADED(mtcp);
|
||||
|
||||
if (errcode)
|
||||
|
||||
@@ -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->free_listener_list = tcp_state->listener_pool;
|
||||
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
|
||||
while (tcp_state->used_listener_list != NULL)
|
||||
tcp_listener_destroy(tcp_state->used_listener_list);
|
||||
// It's not clear to me how to free
|
||||
// all of this stuff up at the moment :/
|
||||
tcp_timerset_free(&state->timers);
|
||||
}
|
||||
|
||||
void tcp_seconds_passed(tcp_state_t *state, size_t seconds)
|
||||
{
|
||||
(void) state;
|
||||
(void) seconds;
|
||||
tcp_timerset_step(&state->timers, seconds);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
{
|
||||
tcp_listener_t *listener = connection->listener;
|
||||
tcp_state_t *state = listener->state;
|
||||
|
||||
@@ -337,22 +338,79 @@ static bool ack_until(tcp_connection_t *connection, uint32_t ack_no)
|
||||
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
|
||||
really_close_connection(tcp_connection_t *connection)
|
||||
{
|
||||
tcp_listener_t *listener = connection->listener;
|
||||
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)
|
||||
connection->prev->next = connection->next;
|
||||
else
|
||||
listener->accepted_list = connection->next;
|
||||
connection->prev->next = next;
|
||||
else if (listener->accepted_list == connection)
|
||||
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
|
||||
connection->prev = NULL;
|
||||
connection->next = state->free_connection_list;
|
||||
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,
|
||||
@@ -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);
|
||||
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);
|
||||
|
||||
// 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)) {
|
||||
|
||||
TCP_DEBUG_LOG("FIN-WAIT-1 -> TIME-WAIT");
|
||||
|
||||
connection->rcv_nxt++; // FIN ghost byte
|
||||
emit_segment(connection, TCP_FLAG_ACK, 0);
|
||||
|
||||
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) {
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
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_create(tcp_state_t *state, uint16_t port, void *callback_data,
|
||||
void (*callback_ready_to_accept)(void*))
|
||||
tcp_listener_create(tcp_state_t *state, uint16_t port, bool reuse,
|
||||
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
|
||||
TCP_DEBUG_LOG("Faile to create listener on port %d because there already exists one", port);
|
||||
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;
|
||||
if (listener)
|
||||
listener->closed = false;
|
||||
else {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
void tcp_listener_destroy(tcp_listener_t *listener)
|
||||
{
|
||||
#warning "The previously accepted connections should't be closed with their listener.. I think"
|
||||
|
||||
// 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;
|
||||
listener->closed = true;
|
||||
}
|
||||
|
||||
tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener, void *callback_data,
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
|
||||
#ifndef MICROTCP_AMALGAMATION
|
||||
#include "defs.h"
|
||||
#include "tcp_timer.h"
|
||||
#endif
|
||||
|
||||
#define TCP_TIMEOUT_TIME_WAIT 240
|
||||
|
||||
#define TCP_MAX_TIMEOUTS 1024
|
||||
#define TCP_MAX_LISTENERS 32
|
||||
#define TCP_MAX_SOCKETS 1024
|
||||
#define TCP_INPUT_BUFFER_SIZE 1024
|
||||
@@ -35,12 +39,19 @@ typedef struct {
|
||||
static_assert(sizeof(tcp_segment_t) == 20);
|
||||
|
||||
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 {
|
||||
tcp_state_t *state;
|
||||
tcp_listener_t *prev;
|
||||
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;
|
||||
tcp_connection_t *accepted_list;
|
||||
tcp_connection_t *non_established_list;
|
||||
@@ -138,20 +149,26 @@ typedef struct {
|
||||
} tcp_callbacks_t;
|
||||
|
||||
struct tcp_state_t {
|
||||
|
||||
ip_address_t ip;
|
||||
tcp_callbacks_t callbacks;
|
||||
|
||||
tcp_timerset_t timers;
|
||||
|
||||
tcp_connection_t *free_connection_list;
|
||||
tcp_connection_t connection_pool[TCP_MAX_SOCKETS];
|
||||
|
||||
tcp_listener_t *used_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_free(tcp_state_t *tcp_state);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
|
||||
+141
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
Reference in New Issue
Block a user