Reengineering the timeout manager. Allows to create an arbitrary number of timer

with the possibility to invoke a callback function on timeout.
This commit is contained in:
vemax78
2014-03-02 19:31:27 +01:00
parent aa87719432
commit 6042161d75
31 changed files with 1259 additions and 1138 deletions

View File

@ -297,6 +297,7 @@ void capwap_hash_add(struct capwap_hash* hash, const void* key, void* data) {
ASSERT(key != NULL);
hashvalue = hash->item_hash(key, hash->keysize, hash->count);
ASSERT(hashvalue < hash->count);
/* Search position where insert item */
search = hash->items[hashvalue];

View File

@ -309,21 +309,19 @@ int capwap_compare_ip(struct sockaddr_storage* addr1, struct sockaddr_storage* a
}
/* Wait receive packet with timeout */
int capwap_wait_recvready(struct pollfd* fds, int fdscount, struct timeout_control* timeout) {
int capwap_wait_recvready(struct pollfd* fds, int fdscount, struct capwap_timeout* timeout) {
int i;
int readysocket;
int polltimeout = -1;
int polltimeout = CAPWAP_TIMEOUT_INFINITE;
ASSERT(fds);
ASSERT(fdscount > 0);
/* Check timeout */
if (timeout) {
long indextimer;
capwap_timeout_update(timeout);
polltimeout = capwap_timeout_get(timeout, &indextimer);
if ((polltimeout <= 0) && (indextimer != CAPWAP_TIMER_UNDEF)) {
polltimeout = capwap_timeout_getcoming(timeout);
if (!polltimeout) {
capwap_timeout_hasexpired(timeout);
return CAPWAP_RECV_ERROR_TIMEOUT;
}
}
@ -343,12 +341,8 @@ int capwap_wait_recvready(struct pollfd* fds, int fdscount, struct timeout_contr
return CAPWAP_RECV_ERROR_SOCKET;
}
}
} else if (readysocket == 0) {
/* Update timer for detect timeout */
if (timeout) {
capwap_timeout_update(timeout);
}
} else if (!readysocket && timeout) {
capwap_timeout_hasexpired(timeout);
return CAPWAP_RECV_ERROR_TIMEOUT;
} else if (errno == EINTR) {
return CAPWAP_RECV_ERROR_INTR;
@ -451,7 +445,7 @@ int capwap_recvfrom_fd(int fd, void* buffer, int* size, struct sockaddr_storage*
}
/* Receive packet with timeout */
int capwap_recvfrom(struct pollfd* fds, int fdscount, void* buffer, int* size, struct sockaddr_storage* recvfromaddr, struct sockaddr_storage* recvtoaddr, struct timeout_control* timeout) {
int capwap_recvfrom(struct pollfd* fds, int fdscount, void* buffer, int* size, struct sockaddr_storage* recvfromaddr, struct sockaddr_storage* recvtoaddr, struct capwap_timeout* timeout) {
int index;
ASSERT(fds);

View File

@ -86,9 +86,9 @@ int capwap_compare_ip(struct sockaddr_storage* addr1, struct sockaddr_storage* a
int capwap_sendto(int sock, void* buffer, int size, struct sockaddr_storage* sendfromaddr, struct sockaddr_storage* sendtoaddr);
int capwap_sendto_fragmentpacket(int sock, struct capwap_list* fragmentlist, struct sockaddr_storage* sendfromaddr, struct sockaddr_storage* sendtoaddr);
int capwap_wait_recvready(struct pollfd* fds, int fdscount, struct timeout_control* timeout);
int capwap_wait_recvready(struct pollfd* fds, int fdscount, struct capwap_timeout* timeout);
int capwap_recvfrom_fd(int fd, void* buffer, int* size, struct sockaddr_storage* recvfromaddr, struct sockaddr_storage* recvtoaddr);
int capwap_recvfrom(struct pollfd* fds, int fdscount, void* buffer, int* size, struct sockaddr_storage* recvfromaddr, struct sockaddr_storage* recvtoaddr, struct timeout_control* timeout);
int capwap_recvfrom(struct pollfd* fds, int fdscount, void* buffer, int* size, struct sockaddr_storage* recvfromaddr, struct sockaddr_storage* recvtoaddr, struct capwap_timeout* timeout);
int capwap_ipv4_mapped_ipv6(struct sockaddr_storage* source, struct sockaddr_storage* dest);
int capwap_address_from_string(const char* ip, struct sockaddr_storage* address);

View File

@ -1,131 +1,314 @@
#include "capwap.h"
/* */
struct timeout_control* capwap_timeout_init(void) {
struct timeout_control* timeout;
#define CAPWAP_TIMEOUT_HASH_COUNT 128
timeout = (struct timeout_control*)capwap_alloc(sizeof(struct timeout_control));
memset(timeout, 0, sizeof(struct timeout_control));
/* */
static unsigned long capwap_timeout_hash_item_gethash(const void* key, unsigned long keysize, unsigned long hashsize) {
return (*(unsigned long*)key % hashsize);
}
/* */
static int capwap_timeout_hash_item_cmp(const void* key1, const void* key2, unsigned long keysize) {
unsigned long value1 = *(unsigned long*)key1;
unsigned long value2 = *(unsigned long*)key2;
return ((value1 == value2) ? 0 : ((value1 < value2) ? -1 : 1));
}
/* */
static long capwap_timeout_getdelta(struct timeval* time1, struct timeval* time2) {
return (time1->tv_sec - time2->tv_sec) * 1000 + (time1->tv_usec - time2->tv_usec) / 1000;
}
/* */
static unsigned long capwap_timeout_set_bitfield(struct capwap_timeout* timeout) {
int i, j;
ASSERT(timeout != NULL);
/* Search free bitfield */
for (i = 0; i < CAPWAP_TIMEOUT_BITFIELD_SIZE; i++) {
if (timeout->timeoutbitfield[i] != 0xffffffff) {
uint32_t bitfield = timeout->timeoutbitfield[i];
for (j = 0; j < 32; j++) {
if (!(bitfield & (1 << j))) {
timeout->timeoutbitfield[i] |= (1 << j);
return (i * 32 + j + 1);
}
}
}
}
return CAPWAP_TIMEOUT_INDEX_NO_SET;
}
/* */
static void capwap_timeout_clear_bitfield(struct capwap_timeout* timeout, unsigned long value) {
ASSERT(timeout != NULL);
ASSERT(value > 0);
timeout->timeoutbitfield[(value - 1) / 32] &= ~(1 << ((value - 1) % 32));
}
/* */
static void capwap_timeout_additem(struct capwap_list* itemstimeout, struct capwap_list_item* itemlist) {
struct capwap_list_item* search;
struct capwap_list_item* last = NULL;
struct capwap_timeout_item* item = (struct capwap_timeout_item*)itemlist->item;
/* */
search = itemstimeout->first;
while (search) {
struct capwap_timeout_item* itemsearch = (struct capwap_timeout_item*)search->item;
if (capwap_timeout_getdelta(&item->expire, &itemsearch->expire) < 0) {
capwap_itemlist_insert_before(itemstimeout, last, itemlist);
break;
}
/* Next */
last = search;
search = search->next;
}
/* */
if (!search) {
capwap_itemlist_insert_after(itemstimeout, NULL, itemlist);
}
}
/* */
static void capwap_timeout_setexpire(long durate, struct timeval* now, struct timeval* expire) {
expire->tv_sec = now->tv_sec + durate / 1000;
expire->tv_usec = now->tv_usec + durate % 1000;
if (expire->tv_usec >= 1000000) {
expire->tv_sec++;
expire->tv_usec -= 1000000;
}
}
/* */
struct capwap_timeout* capwap_timeout_init(void) {
struct capwap_timeout* timeout;
/* */
timeout = (struct capwap_timeout*)capwap_alloc(sizeof(struct capwap_timeout));
memset(timeout, 0, sizeof(struct capwap_timeout));
/* */
timeout->itemsreference = capwap_hash_create(CAPWAP_TIMEOUT_HASH_COUNT, sizeof(unsigned long), capwap_timeout_hash_item_gethash, capwap_timeout_hash_item_cmp, NULL);
timeout->itemstimeout = capwap_list_create();
return timeout;
}
/* */
void capwap_timeout_free(struct timeout_control* timeout) {
void capwap_timeout_free(struct capwap_timeout* timeout) {
ASSERT(timeout != NULL);
capwap_hash_free(timeout->itemsreference);
capwap_list_free(timeout->itemstimeout);
capwap_free(timeout);
}
/* */
void capwap_timeout_update(struct timeout_control* timeout) {
int i;
unsigned long capwap_timeout_createtimer(struct capwap_timeout* timeout) {
unsigned long index;
ASSERT(timeout != NULL);
/* Create new timeout index */
index = capwap_timeout_set_bitfield(timeout);
capwap_logging_debug("Create new timer: %lu", index);
return index;
}
/* */
void capwap_timeout_deletetimer(struct capwap_timeout* timeout, unsigned long index) {
ASSERT(timeout != NULL);
ASSERT(index != CAPWAP_TIMEOUT_INDEX_NO_SET);
capwap_logging_debug("Delete timer: %lu", index);
/* Unset timeout timer */
capwap_timeout_unset(timeout, index);
/* Release timer index */
capwap_timeout_clear_bitfield(timeout, index);
}
/* */
unsigned long capwap_timeout_set(struct capwap_timeout* timeout, unsigned long index, long durate, capwap_timeout_expire callback, void* context, void* param) {
struct capwap_list_item* itemlist;
struct capwap_timeout_item* item;
struct timeval now;
ASSERT(timeout);
ASSERT(timeout != NULL);
ASSERT(durate >= 0);
gettimeofday(&now, NULL);
for (i = 0; i < CAPWAP_MAX_TIMER; i++) {
if (timeout->items[i].enable && (timeout->items[i].delta >= 0)) {
timeout->items[i].delta = (timeout->items[i].timestop.tv_sec - now.tv_sec) * 1000 + (timeout->items[i].timestop.tv_usec - now.tv_usec) / 1000;
if (timeout->items[i].delta < 0) {
timeout->items[i].delta = 0;
} else if (timeout->items[i].delta > timeout->items[i].durate) {
/* Changed system time */
timeout->items[i].delta = timeout->items[i].durate;
memcpy(&timeout->items[i].timestop, &now, sizeof(struct timeval));
timeout->items[i].timestop.tv_sec += timeout->items[i].durate;
}
if (index == CAPWAP_TIMEOUT_INDEX_NO_SET) {
index = capwap_timeout_createtimer(timeout);
} else {
/* Check can update timeout timer */
itemlist = (struct capwap_list_item*)capwap_hash_search(timeout->itemsreference, &index);
if (itemlist) {
/* Remove from timeout list */
capwap_itemlist_remove(timeout->itemstimeout, itemlist);
/* Update timeout */
item = (struct capwap_timeout_item*)itemlist->item;
item->durate = durate;
capwap_timeout_setexpire(item->durate, &now, &item->expire);
item->callback = callback;
item->context = context;
item->param = param;
capwap_logging_debug("Update timeout: %lu %ld", item->index, item->durate);
/* Add itemlist into order list */
capwap_timeout_additem(timeout->itemstimeout, itemlist);
return index;
}
}
/* Create new timeout timer */
itemlist = capwap_itemlist_create(sizeof(struct capwap_timeout_item));
item = (struct capwap_timeout_item*)itemlist->item;
/* */
item->index = index;
item->durate = durate;
capwap_timeout_setexpire(item->durate, &now, &item->expire);
item->callback = callback;
item->context = context;
item->param = param;
capwap_logging_debug("Set timeout: %lu %ld", item->index, item->durate);
/* Add itemlist into hash for rapid searching */
capwap_hash_add(timeout->itemsreference, (const void*)&item->index, (void*)itemlist);
/* Add itemlist into order list */
capwap_timeout_additem(timeout->itemstimeout, itemlist);
return item->index;
}
/* */
long capwap_timeout_get(struct timeout_control* timeout, long* index) {
long i;
long delta = 0;
void capwap_timeout_unset(struct capwap_timeout* timeout, unsigned long index) {
struct capwap_list_item* itemlist;
ASSERT(timeout != NULL);
ASSERT(index != NULL);
*index = CAPWAP_TIMER_UNDEF;
for (i = 0; i < CAPWAP_MAX_TIMER; i++) {
if (timeout->items[i].enable) {
if (timeout->items[i].delta <= 0) {
*index = i;
delta = 0;
break;
} else if (!delta || (delta > timeout->items[i].delta)) {
*index = i;
delta = timeout->items[i].delta;
}
ASSERT(index != CAPWAP_TIMEOUT_INDEX_NO_SET);
/* */
itemlist = (struct capwap_list_item*)capwap_hash_search(timeout->itemsreference, &index);
if (itemlist) {
capwap_logging_debug("Unset timeout: %lu", index);
capwap_hash_delete(timeout->itemsreference, &index);
capwap_itemlist_free(capwap_itemlist_remove(timeout->itemstimeout, itemlist));
}
}
/* */
void capwap_timeout_unsetall(struct capwap_timeout* timeout) {
capwap_hash_deleteall(timeout->itemsreference);
capwap_list_flush(timeout->itemstimeout);
}
/* */
long capwap_timeout_getcoming(struct capwap_timeout* timeout) {
long delta;
struct timeval now;
struct capwap_list_item* search;
struct capwap_timeout_item* item;
ASSERT(timeout != NULL);
/* */
search = timeout->itemstimeout->first;
if (!search) {
return CAPWAP_TIMEOUT_INFINITE;
}
/* */
gettimeofday(&now, NULL);
item = (struct capwap_timeout_item*)search->item;
delta = capwap_timeout_getdelta(&item->expire, &now);
if (delta <= 0) {
return 0;
} else if (delta <= item->durate) {
return delta;
}
/* Recalculate all timeouts because delta > item->durate */
while (search) {
struct capwap_timeout_item* itemsearch = (struct capwap_timeout_item*)search->item;
capwap_timeout_setexpire(itemsearch->durate, &now, &itemsearch->expire);
search = search->next;
}
return item->durate;
}
/* */
unsigned long capwap_timeout_hasexpired(struct capwap_timeout* timeout) {
long delta;
struct capwap_timeout_item* item;
struct capwap_list_item* itemlist;
unsigned long index;
capwap_timeout_expire callback;
void* context;
void* param;
ASSERT(timeout != NULL);
/* */
delta = capwap_timeout_getcoming(timeout);
if ((delta > 0) || (delta == CAPWAP_TIMEOUT_INFINITE)) {
return 0;
}
/* */
itemlist = capwap_itemlist_remove_head(timeout->itemstimeout);
item = (struct capwap_timeout_item*)itemlist->item;
capwap_logging_debug("Expired timeout: %lu", item->index);
/* Cache callback before release timeout timer */
index = item->index;
callback = item->callback;
context = item->context;
param = item->param;
/* Free memory */
capwap_hash_delete(timeout->itemsreference, &index);
capwap_itemlist_free(itemlist);
/* */
if (callback) {
callback(timeout, index, context, param);
}
return index;
}
/* */
int capwap_timeout_wait(long durate) {
if (durate < 0) {
return -1;
} else if (durate > 0) {
if (usleep((useconds_t)durate * 1000)) {
return ((errno == EINTR) ? 0 : -1);
}
}
return delta;
}
/* */
void capwap_timeout_wait(struct timeout_control* timeout, unsigned long index) {
ASSERT(timeout != NULL);
ASSERT(index < CAPWAP_MAX_TIMER);
if (timeout->items[index].enable) {
for (capwap_timeout_update(timeout); timeout->items[index].delta > 0; capwap_timeout_update(timeout)) {
usleep((useconds_t)timeout->items[index].delta * 1000);
}
}
}
/* */
int capwap_timeout_isenable(struct timeout_control* timeout, unsigned long index) {
ASSERT(timeout != NULL);
ASSERT(index < CAPWAP_MAX_TIMER);
return (timeout->items[index].enable ? 1 : 0);
}
/* */
int capwap_timeout_hasexpired(struct timeout_control* timeout, unsigned long index) {
ASSERT(timeout != NULL);
ASSERT(index < CAPWAP_MAX_TIMER);
if (timeout->items[index].enable && (timeout->items[index].delta <= 0)) {
return 1;
}
return 0;
}
/* */
void capwap_timeout_set(unsigned long value, struct timeout_control* timeout, unsigned long index) {
ASSERT(timeout != NULL);
ASSERT(index < CAPWAP_MAX_TIMER);
/* Set timeout in ms */
timeout->items[index].enable = 1;
timeout->items[index].delta = value * 1000;
timeout->items[index].durate = timeout->items[index].delta;
gettimeofday(&timeout->items[index].timestop, NULL);
timeout->items[index].timestop.tv_sec += value;
}
/* */
void capwap_timeout_kill(struct timeout_control* timeout, unsigned long index) {
ASSERT(timeout != NULL);
ASSERT(index < CAPWAP_MAX_TIMER);
timeout->items[index].enable = 0;
}
/* */
void capwap_timeout_killall(struct timeout_control* timeout) {
long i;
ASSERT(timeout != NULL);
for (i = 0; i < CAPWAP_MAX_TIMER; i++) {
timeout->items[i].enable = 0;
}
return 1;
}

View File

@ -1,39 +1,49 @@
#ifndef __CAPWAP_TIMEOUT_HEADER__
#define __CAPWAP_TIMEOUT_HEADER__
#define CAPWAP_TIMER_UNDEF -1
#define CAPWAP_TIMER_CONTROL_CONNECTION 0
#define CAPWAP_TIMER_CONTROL_ECHO 1
#define CAPWAP_TIMER_DATA_KEEPALIVE 2
#define CAPWAP_TIMER_DATA_KEEPALIVEDEAD 3
#define CAPWAP_MAX_TIMER 4
#include "capwap_hash.h"
#include "capwap_list.h"
/* */
struct timeout_control_item {
int enable;
long delta;
unsigned long durate;
struct timeval timestop;
};
#define CAPWAP_TIMEOUT_BITFIELD_SIZE 128
#define CAPWAP_TIMEOUT_INFINITE -1
#define CAPWAP_TIMEOUT_INDEX_NO_SET 0
struct timeout_control {
struct timeout_control_item items[CAPWAP_MAX_TIMER];
/* */
struct capwap_timeout {
uint32_t timeoutbitfield[CAPWAP_TIMEOUT_BITFIELD_SIZE];
struct capwap_hash* itemsreference;
struct capwap_list* itemstimeout;
};
/* */
struct timeout_control* capwap_timeout_init(void);
void capwap_timeout_free(struct timeout_control* timeout);
typedef void (*capwap_timeout_expire)(struct capwap_timeout* timeout, unsigned long index, void* context, void* param);
long capwap_timeout_get(struct timeout_control* timeout, long* index);
void capwap_timeout_update(struct timeout_control* timeout);
void capwap_timeout_set(unsigned long value, struct timeout_control* timeout, unsigned long index);
struct capwap_timeout_item {
unsigned long index;
long durate;
struct timeval expire;
capwap_timeout_expire callback;
void* context;
void* param;
};
void capwap_timeout_wait(struct timeout_control* timeout, unsigned long index);
/* */
struct capwap_timeout* capwap_timeout_init(void);
void capwap_timeout_free(struct capwap_timeout* timeout);
int capwap_timeout_isenable(struct timeout_control* timeout, unsigned long index);
int capwap_timeout_hasexpired(struct timeout_control* timeout, unsigned long index);
/* */
unsigned long capwap_timeout_createtimer(struct capwap_timeout* timeout);
void capwap_timeout_deletetimer(struct capwap_timeout* timeout, unsigned long index);
void capwap_timeout_kill(struct timeout_control* timeout, unsigned long index);
void capwap_timeout_killall(struct timeout_control* timeout);
/* */
unsigned long capwap_timeout_set(struct capwap_timeout* timeout, unsigned long index, long durate, capwap_timeout_expire callback, void* context, void* param);
void capwap_timeout_unset(struct capwap_timeout* timeout, unsigned long index);
void capwap_timeout_unsetall(struct capwap_timeout* timeout);
long capwap_timeout_getcoming(struct capwap_timeout* timeout);
unsigned long capwap_timeout_hasexpired(struct capwap_timeout* timeout);
int capwap_timeout_wait(long durate);
#endif /* __CAPWAP_TIMEOUT_HEADER__ */