From 6456d8abe0c7bedfd735e41b233ceca6256569d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 2 Oct 2013 22:15:55 +0200 Subject: [PATCH 01/46] Include BCD length macros in test --- test/test_bcd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_bcd.c b/test/test_bcd.c index 82b7598..96dc6fb 100644 --- a/test/test_bcd.c +++ b/test/test_bcd.c @@ -40,6 +40,7 @@ START_TEST (test_2bcd_2_filler) fail_if(len != 2, "len is %zu", len); fail_if(bcd[0] != 0x21,"was 0x%x",bcd[0]); fail_if(bcd[1] != 0xf3,"was 0x%x",bcd[1]); + fail_if(UC_BCD_LEN(strlen(ascii)) != len); END_TEST START_TEST (test_2bcd_3_filler) @@ -98,6 +99,7 @@ START_TEST (test_2hex_1) fail_if(ascii[2] != '3',"2 was %c",ascii[2]); fail_if(ascii[3] != '4',"3 was %c",ascii[3]); fail_if(ascii[4] != 0, "4 was %c",ascii[4]); + fail_if(UC_ASCII_LEN(sizeof bcd) != len); END_TEST START_TEST (test_2hex_2) From 4310414b49fe27791031b0199c749774b7757e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 2 Oct 2013 22:17:15 +0200 Subject: [PATCH 02/46] More comments in string.h --- include/ucore/string.h | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/include/ucore/string.h b/include/ucore/string.h index 9b09103..60955a1 100644 --- a/include/ucore/string.h +++ b/include/ucore/string.h @@ -17,16 +17,48 @@ uc_hex_decode(const char *in, size_t maxlen,uint8_t *out); char* uc_hex_encode_delim(const uint8_t *in, size_t inlen, char *out, int outlen, char *delim); +/** + * Convert the string to lowercase, calls tolower() on each char. + * @param s string to lowercase + */ void uc_str_tolower(char *s); +/** + * Convert the string to uppercase, calls toupper() on each char. + * @param s string to uppercase + */ void uc_str_toupper(char *s); +/** Macro to calculate the number of bytes required to BCD encode + * the number of ascii digits + */ #define UC_BCD_LEN(ascii_len) ((ascii_len) / 2 + ((ascii_len) % 2)) -#define UC_ASCII_LEN(bcd_len) ((bcd_len) * 2 + 1) +/** Macro to calculate the number of bytes required to ASCII encode + * the number of BCD digits + */ +#define UC_ASCII_LEN(bcd_len) ((bcd_len) * 2) + +/** Convert the ascii digits to BCD. Digits not in 0..9 are skipped. + * + * @param ascii string to BCD encode. + * @param bcdout resulting BCD data. Must be at least UC_BCD_LEN(strlen(asciii)) big + * @param filler filler byte(not the ascii digit) to write in the last BCD nibble + * if the number of ASCII digits is odd. This would usually be 0x0 or 0xf. + * @return the number of bytes written to bcdout + */ size_t uc_ascii2bcd(const char *ascii, unsigned char *bcdout, unsigned char filler); + +/** Convert the BCD digits to ascii. + * The resulting ascii string is 0 terminated. + * + * @param bcd Buffer with BCD data to convert to ascii + * @param bcdlen number of bytes in @bcd to convert + * @param asciiout buffer to place ASCII in, must be UC_ASCII_LEN(bcdlen)+1 big + * @return the number of characters written to bcdout (this will always be UC_ASCII_LEN(bcdlen) + */ size_t uc_bcd2ascii(const unsigned char *bcd, size_t bcdlen, char *asciiout); @@ -39,10 +71,13 @@ uc_sprintbll(char *result, unsigned long long value); char* uc_sprintbs(char *result, unsigned short value); +/** Like http://swtch.com/plan9port/man/man3/getfields.html + */ int getfields(char *str, char **args, int max, int mflag, const char *set); -//ocnvert bytes to a human representable test, (i.e. 12kB, 160 MB 1.78 GB etc. +//Converts bytes to a human representable test, (i.e. 12kB, 160 MB 1.78 GB etc.) +//Conversion is done in decimal (base 10 ,1 kB == 1000 bytes) ///result should be at least of size 12, ///returns the result argument char * From d9517ad868a10d7fbf84db180f6af41a59aeaf2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 3 Oct 2013 22:04:33 +0200 Subject: [PATCH 03/46] Make the thread queue walk function take a cookie to the callback and implement a _clear() function to clear out the queue --- include/ucore/threadqueue.h | 25 +++++++++++++++--- src/threadqueue.c | 52 ++++++++++++++++++++++++++++++++----- test/test_threadqueue.c | 19 +++++++------- 3 files changed, 77 insertions(+), 19 deletions(-) diff --git a/include/ucore/threadqueue.h b/include/ucore/threadqueue.h index 383621d..61cfbb4 100644 --- a/include/ucore/threadqueue.h +++ b/include/ucore/threadqueue.h @@ -44,7 +44,7 @@ struct uc_threadmsg{ /** * Callback function for the walk and destroy functions. */ -typedef void (*uc_thread_queue_walk_func) (struct uc_threadmsg *msg); +typedef void (*uc_thread_queue_walk_func) (struct uc_threadmsg *msg, void *cookie); /** @@ -240,23 +240,40 @@ long uc_thread_queue_length( struct uc_threadqueue *queue ); * @param queue Pointer to the queue that should be cleaned * @param free_func pointer to function that will be called for each item. * The function must not in anyway interact with the queue. + * @param cookie pointer passed back to the uc_thread_queue_walk_func * @return 0 on success EINVAL if queue is NULL EBUSY if someone is holding any locks on the queue */ -int uc_thread_queue_destroy(struct uc_threadqueue *queue, uc_thread_queue_walk_func free_func); +int uc_thread_queue_destroy(struct uc_threadqueue *queue, uc_thread_queue_walk_func free_func, void *cookie); /** * Walk the elements of the queue, intended for debugging * * The supplied function will be called for each item in the queue, internal queue * locks are held while the function is called, so the supplied function must not - * interact with the queue, else deadlock occors. + * interact with the queue, else deadlock occurs. * * @param queue Pointer to the queue to walk * @param free_func pointer to function that will be called for each item. + * @param cookie pointer passed back to the uc_thread_queue_walk_func * The function must not in anyway interact with the queue. * @return 0 on success */ -int uc_thread_queue_walk(struct uc_threadqueue *queue, uc_thread_queue_walk_func walk_func); +int uc_thread_queue_walk(struct uc_threadqueue *queue, uc_thread_queue_walk_func walk_func, void *cookie); + + +/** + * Clear the queue. + * The free_func unless NULL, will be called for each element + * locks are held while the function is called, so the supplied function must not + * interact with the queue, else deadlock occurs. + * + * @param queue Pointer to the queue to clear + * @param free_func pointer to function that will be called for each item, intended to e.g. release memory + * @param cookie pointer passed back to the uc_thread_queue_walk_func + * @return 0 on success +*/ +int uc_thread_queue_clear(struct uc_threadqueue *queue, uc_thread_queue_walk_func free_func, void *cookie); + /** * @}*/ diff --git a/src/threadqueue.c b/src/threadqueue.c index 47308df..c5da95f 100644 --- a/src/threadqueue.c +++ b/src/threadqueue.c @@ -199,19 +199,21 @@ int uc_thread_queue_tryget(struct uc_threadqueue *queue, } static void uc_thread_queue_walk_unlocked(struct uc_threadqueue *queue, - uc_thread_queue_walk_func walk_func) + uc_thread_queue_walk_func walk_func, + void *cookie) { struct uc_threadmsg *p; struct uc_threadmsg *next; for(p = queue->first; p; p = next) { next = p->next; - walk_func(p); + walk_func(p, cookie); } } int uc_thread_queue_walk(struct uc_threadqueue *queue, - uc_thread_queue_walk_func walk_func) + uc_thread_queue_walk_func walk_func, + void *cookie) { if (queue == NULL || !walk_func) { return EINVAL; @@ -219,7 +221,7 @@ int uc_thread_queue_walk(struct uc_threadqueue *queue, pthread_mutex_lock(&queue->mutex); - uc_thread_queue_walk_unlocked(queue, walk_func); + uc_thread_queue_walk_unlocked(queue, walk_func, cookie); pthread_mutex_unlock(&queue->mutex); @@ -227,7 +229,8 @@ int uc_thread_queue_walk(struct uc_threadqueue *queue, } int uc_thread_queue_destroy(struct uc_threadqueue *queue, - uc_thread_queue_walk_func free_func) + uc_thread_queue_walk_func free_func, + void *cookie) { int rc; @@ -250,7 +253,7 @@ int uc_thread_queue_destroy(struct uc_threadqueue *queue, } if (free_func) { - uc_thread_queue_walk_unlocked(queue, free_func); + uc_thread_queue_walk_unlocked(queue, free_func, cookie); } pthread_mutex_unlock(&queue->mutex); @@ -276,3 +279,40 @@ long uc_thread_queue_length(struct uc_threadqueue *queue) return length; } + +int uc_thread_queue_clear(struct uc_threadqueue *queue, + uc_thread_queue_walk_func free_func, + void *cookie) +{ + if (queue == NULL) { + return -EINVAL; + } + + pthread_mutex_lock(&queue->mutex); + + //Let the caller free the data + if (free_func != NULL) { + uc_thread_queue_walk_unlocked(queue, free_func, cookie); + } + + //reset the elements + queue->first = NULL; + queue->num_elements = 0; + queue->last = &queue->first; + + //notify writes, there should be space now + if (queue->num_write_waiters == 1) { + //if there's just one thread waiting to push elements + //use _cond_signal. _cond_broadcast can be more expensive (os dependent) + pthread_cond_signal(&queue->write_cond); + } else if (queue->num_write_waiters > 1) { + //signall all threads that there's items available. + pthread_cond_broadcast(&queue->write_cond); + } + + pthread_mutex_unlock(&queue->mutex); + + return 0; +} + + diff --git a/test/test_threadqueue.c b/test/test_threadqueue.c index 933645a..c0f0cb3 100644 --- a/test/test_threadqueue.c +++ b/test/test_threadqueue.c @@ -50,7 +50,7 @@ START_TEST (test_thread_queue_one_thread) fail_if(my_msg->a != i); free(my_msg); } - fail_if(uc_thread_queue_destroy(&queue, NULL) != 0); + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); END_TEST @@ -59,8 +59,9 @@ END_TEST //pretty ugly.. static int test_thread_queue_walk_global; -static void test_thread_queue_walk_func(struct uc_threadmsg *msg) +static void test_thread_queue_walk_func(struct uc_threadmsg *msg, void *cookie) { + (void)cookie; struct thr_msg *my_msg = (struct thr_msg *)msg; fail_if(my_msg->a != test_thread_queue_walk_global); test_thread_queue_walk_global++; @@ -82,7 +83,7 @@ START_TEST (test_thread_queue_walk) test_thread_queue_walk_global = 0; - uc_thread_queue_walk(&queue, test_thread_queue_walk_func); + uc_thread_queue_walk(&queue, test_thread_queue_walk_func, NULL); END_TEST @@ -125,7 +126,7 @@ START_TEST (test_thread_queue_reader_writer) pthread_join(tid, NULL); - fail_if(uc_thread_queue_destroy(&queue, NULL) != 0); + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); END_TEST @@ -216,7 +217,7 @@ START_TEST (test_thread_queue_timeout1) fail_if(uc_thread_queue_init(&queue, 100) != 0); fail_if(uc_thread_queue_tryget(&queue, &timeout, &msg) != ETIMEDOUT); - uc_thread_queue_destroy(&queue, NULL); + uc_thread_queue_destroy(&queue, NULL, NULL); END_TEST @@ -251,7 +252,7 @@ START_TEST (test_thread_queue_timeout2) pthread_join(tid1, NULL); - uc_thread_queue_destroy(&queue, NULL); + uc_thread_queue_destroy(&queue, NULL, NULL); END_TEST @@ -270,7 +271,7 @@ START_TEST (test_thread_queue_tryadd_timeout_0_0) fail_if(uc_thread_queue_tryadd(&queue, &timeout, &msg[2].msg) != ETIMEDOUT); - fail_if(uc_thread_queue_destroy(&queue, NULL) != 0); + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); END_TEST @@ -289,7 +290,7 @@ START_TEST (test_thread_queue_tryadd_timeout_0_100) fail_if(uc_thread_queue_tryadd(&queue, &timeout, &msg[2].msg) != ETIMEDOUT); - fail_if(uc_thread_queue_destroy(&queue, NULL) != 0); + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); END_TEST @@ -308,7 +309,7 @@ START_TEST (test_thread_queue_tryadd_no_timeout_0_100) fail_if(uc_thread_queue_tryadd(&queue, &timeout, &msg[2].msg) != 0); - fail_if(uc_thread_queue_destroy(&queue, NULL) != 0); + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); END_TEST From f4b1d81dcf3582ead6d6382e7628acbc0240c4d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 3 Oct 2013 23:42:12 +0200 Subject: [PATCH 04/46] test uc_thread_queue_clear --- test/test_threadqueue.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/test_threadqueue.c b/test/test_threadqueue.c index c0f0cb3..0bac993 100644 --- a/test/test_threadqueue.c +++ b/test/test_threadqueue.c @@ -313,6 +313,35 @@ START_TEST (test_thread_queue_tryadd_no_timeout_0_100) END_TEST +void clear_cb(struct uc_threadmsg *msg, void *cookie) +{ + int *i = cookie; + (void)msg; + ++*i; +} + +START_TEST (test_thread_queue_clear) + + struct uc_threadqueue queue; + int i; + struct thr_msg msg[3]; + memset(msg, 0, sizeof msg); + int cnt = 0; + + fail_if(uc_thread_queue_init(&queue, 3) != 0); + for (i = 0; i < 3; i++) { + fail_if(uc_thread_queue_add(&queue, &msg[i].msg) != 0); + } + + fail_if(uc_thread_queue_length(&queue) != 3); + fail_if(uc_thread_queue_clear(&queue, clear_cb, &cnt)); + ck_assert_int_eq(cnt , 3); + fail_if(uc_thread_queue_length(&queue) != 0); + + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); + +END_TEST + Suite *threadqueue_suite(void) { @@ -331,6 +360,7 @@ Suite *threadqueue_suite(void) tcase_add_test(tc1, test_thread_queue_tryadd_timeout_0_0); tcase_add_test(tc1, test_thread_queue_tryadd_timeout_0_100); tcase_add_test(tc1, test_thread_queue_tryadd_no_timeout_0_100); + tcase_add_test(tc1, test_thread_queue_clear); //lets start with this: tcase_set_timeout(tc1, 15); From f2f2ed247f55b5998efabcb8508a0f1f83796c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Sat, 5 Oct 2013 00:55:21 +0200 Subject: [PATCH 05/46] Add assert for correct state when removing timer --- src/timers.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/timers.c b/src/timers.c index 872a21b..8cb57b3 100644 --- a/src/timers.c +++ b/src/timers.c @@ -76,6 +76,8 @@ void uc_timers_remove(struct UCTimers *timers, struct UCTimer *timer) timer->ready_entry.tqe_prev = (struct UCTimer **)105; return; } + assert(timer->state == UC_TIMER_ACTIVE || + timer->state == UC_TIMER_PENDING); uc_rb_remove(&timers->timer_tree, &timer->rb_node); //remove it, safe for the //callback to re_add it From 3da0591cc3f7baab95849074f7a4b53b5a5ca229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Sat, 5 Oct 2013 00:55:51 +0200 Subject: [PATCH 06/46] introduce --with-assertions to build a release with assertions enabled --- SConstruct | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/SConstruct b/SConstruct index 7b7f1c4..94e438b 100644 --- a/SConstruct +++ b/SConstruct @@ -42,6 +42,12 @@ AddOption('--coverage', help='Build with coverage (gcov) support' ) +AddOption('--with-assertions', + dest='assertions', + action='store_true', + help='Build with assertions enabled (Default for a debug build)' +) + AddOption('--stack-protection', dest = 'stack_protection', action='store_true', @@ -51,6 +57,7 @@ AddOption('--stack-protection', build_type = GetOption('build_type') +assertions = GetOption('assertions') test_xml = GetOption('test_xml') prefix = GetOption('prefix') if not (build_type in ['debug', 'release']): @@ -81,8 +88,9 @@ env.Append(CPPPATH = ['#include']) if build_type == 'release': env.Append(CFLAGS = ['-O2']) - #this will turn off assert() - env.Append(CPPDEFINES = ['NDEBUG']) + if not assertions: + #this will turn off assert() + env.Append(CPPDEFINES = ['NDEBUG']) elif build_type == 'debug': env.Append(CPPDEFINES = ['DEBUG']) From 1f4e41df3acb9f78e200ee724456d24015975893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Sat, 5 Oct 2013 00:56:41 +0200 Subject: [PATCH 07/46] Test inserting priority messages --- test/test_threadqueue.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/test_threadqueue.c b/test/test_threadqueue.c index 0bac993..2cd7d6d 100644 --- a/test/test_threadqueue.c +++ b/test/test_threadqueue.c @@ -54,6 +54,32 @@ START_TEST (test_thread_queue_one_thread) END_TEST +START_TEST (test_thread_queue_priority_msg) + + struct uc_threadqueue queue; + int i; + + struct uc_threadmsg msg[3]; + memset(&msg, 0, sizeof msg); + + fail_if(uc_thread_queue_init(&queue, 3) != 0); + + + for (i = 0; i < 3; i++) { + msg[i].msgtype = -1; //negative == add at head + fail_if(uc_thread_queue_add(&queue, &msg[i]) != 0); + } + + for (i = 2; i >= 0; i--) { + struct uc_threadmsg *my_msg; + fail_if(uc_thread_queue_get(&queue, &my_msg) != 0); + + fail_if(my_msg != &msg[i]); + } + + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); + +END_TEST @@ -350,6 +376,7 @@ Suite *threadqueue_suite(void) tcase_add_test(tc1, test_thread_queue_length); tcase_add_test(tc1, test_thread_queue_one_thread); + tcase_add_test(tc1, test_thread_queue_priority_msg); tcase_add_test(tc1, test_thread_queue_walk); tcase_add_test(tc1, test_thread_queue_reader_writer); tcase_add_test(tc1, test_thread_queue_reader_writer_len_1); From 425062ad18ceec305a7fd57ca0e2e15beb8a8991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 8 Oct 2013 17:34:58 +0200 Subject: [PATCH 08/46] Add needed include files --- include/ucore/utils.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/ucore/utils.h b/include/ucore/utils.h index 26d706e..7253f04 100644 --- a/include/ucore/utils.h +++ b/include/ucore/utils.h @@ -1,6 +1,9 @@ #ifndef UCORE_UTILS_H_ #define UCORE_UTILS_H_ #include +#include +#include +#include "backtrace.h" //Gnerate a compiler error if the compile time //constant expression fails From 08a50d8cfe51c180c0efc2a1de2e57bdc09136d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 8 Oct 2013 17:39:44 +0200 Subject: [PATCH 09/46] fix UC_ASSERT writing to wrong fd, and unterminated if log more in UC_NOTREACHED --- include/ucore/utils.h | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/include/ucore/utils.h b/include/ucore/utils.h index 7253f04..5f8b0af 100644 --- a/include/ucore/utils.h +++ b/include/ucore/utils.h @@ -84,19 +84,26 @@ const typeof( ((const type *)0)->member ) *__mptr = (ptr); \ /** * assert() that is not affected by NDEBUG define */ -#define UC_ASSERT(expr) do {\ -if (!(expr) ) {\ - fprintf(stderr,\ - "UC_ASSERT failed '%s' %s:%d\n", UC_STRINGIFY(expr), __FILE__, __LINE__);\ - uc_backtrace_fd(3);\ - abort();\ +#define UC_ASSERT(expr)\ + do {\ + if (!(expr) ) {\ + fprintf(stderr,\ + "UC_ASSERT failed '%s' %s:%d\n", UC_STRINGIFY(expr), __FILE__, __LINE__);\ + uc_backtrace_fd(2);\ + abort();\ + }\ } while (0) /** * Macro for asserting code that should not be reached, * terminates the program if executed */ -#define UC_NOT_REACED() abort() +#define UC_NOT_REACED()\ +do {\ + fprintf(stderr, "UC_NOT_REACED at %s:%d\n", __FILE__, __LINE__);\ + uc_backtrace_fd(2);\ + abort();\ +while (0) /** * Round up X / y From 06083a054e58b511eb2fd2ffac91e99024a99aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 9 Oct 2013 22:55:29 +0200 Subject: [PATCH 10/46] Add rate limit API --- include/ucore/rate_limit.h | 70 ++++++++++++++++++++++++++++++++ src/rate_limit.c | 81 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 include/ucore/rate_limit.h create mode 100644 src/rate_limit.c diff --git a/include/ucore/rate_limit.h b/include/ucore/rate_limit.h new file mode 100644 index 0000000..fa7ec01 --- /dev/null +++ b/include/ucore/rate_limit.h @@ -0,0 +1,70 @@ +#ifndef UC_RATE_LIMIT_H_ +#define UC_RATE_LIMIT_H_ + +/** This is a rate limiter, based on the token bucket principle. + * We have X amount of tickets(=our rate) per Y amount of time, + * + * The allowed tickets increases with X per Y amount of time. + * + * Bursts will be allowed up till the number of tickets initially + * specified. + * e.g. if we are limited to 50 tickets * per 2 second, 50 tickets + * can be immedietely handed out in the + * first second. At the 2. second, 25 more tickets become available + * handed out, at the 3. second 25 more, and so on. + * (So this is throttleed, so no more than the initial tickets, 50 in + * this case, can be handed out in any given period) + * + * Another example is if we rate 1000 tickets per 60 seconds, + * 1000 tickets can be handed out the 1. second, 16/17 more the + * next second, 16/17 more the 3. second and so on. + * + * Specify how many tickes we have available per period of time. + * when work is performed, call uc_ratelimit_cllow() with the current + * time. + * The unit of time passed to uc_ratelimit_allow must be the same as + * for uc_ratelimit_init. + * + * Example for rate limiting accepting connections: + * struct RateLimit r; + * //Limit to 20 per 1 second, which also limits us to max 20 + * per second. (1 second since we will pass in time that have 1 second resolution) + * 40 per 2 second would even out to the same rate, but allow a burst of 40 + * (if there's 40 tickets available) + * + * uc_ratelimit_init(&r,20, 1, time(NULL)); + * + * for (;;) { + * int fd = accept(..); + * if (uc_ratelimit_allow(&r, time(NULL)) { + * //handle new connection + * } else { + * //exceeded limit + * close(fd); + * } + * } + */ +struct RateLimit { + //how many tickets we want + long tickets; + //per this period of time + long period; + + //internal fields: + + //scaled up number of tickets, to avoid floating point + //math + long scaled_tickets; + + //number of tickets we have (scaled up) + long available_tickets; + //time of the previos period + long last_ts; +}; + +void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts); +void uc_ratelimit_reset(struct RateLimit *r, long current_ts); +int uc_ratelimit_allow(struct RateLimit *r, long current_ts); + +#endif + diff --git a/src/rate_limit.c b/src/rate_limit.c new file mode 100644 index 0000000..f7c33b4 --- /dev/null +++ b/src/rate_limit.c @@ -0,0 +1,81 @@ +#include "ucore/rate_limit.h" + +#define SCALE_FACTOR 1000 + + +void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts) +{ + r->tickets = tickets; + r->scaled_tickets = tickets * SCALE_FACTOR; + r->period = period; + r->available_tickets = r->scaled_tickets; + r->last_ts = current_ts; +} + +void uc_ratelimit_reset(struct RateLimit *r, long current_ts) +{ + r->available_tickets = r->scaled_tickets; + + if (current_ts) + r->last_ts = current_ts; +} + +int uc_ratelimit_allow(struct RateLimit *r, long current_ts) +{ + int allowed; + long diff_period; + + //Find elapsed time + diff_period = current_ts - r->last_ts; + + //If time went backwards, we halt the time for this round + if (diff_period < 0) + diff_period = 0; + + //Calculate the number of tickets that became available since + //the last time. (+ ScALE_FACTOR/2 is to round up at halfway points for + //integer arithmetics) + r->available_tickets += diff_period * + ((r->scaled_tickets + SCALE_FACTOR/2) / r->period); + + //throttle handing out tickets + // + if (r->available_tickets > r->scaled_tickets) + r->available_tickets = r->scaled_tickets; + + //If we have at least onen ticket, we can allow + if (r->available_tickets >= 1 * SCALE_FACTOR) { + r->available_tickets -= 1 * SCALE_FACTOR; + allowed = 1; + } else { + //no more tickets. + allowed = 0; + } + + //save this period. + r->last_ts = current_ts; + + return allowed; +} + +#if 0 +int main() +{ + int i; + struct RateLimit r; + time_t start; + + uc_ratelimit_init(&r, 10, 60, time(NULL)); + + for(i = 0; i < 120; i++) { + int k; + long allowed = 0; + sleep(1); + for (k = 0; k < 500; k++) + allowed += uc_ratelimit_allow(&r, time(NULL)); + printf("%ld of 500 can pass\n", allowed); + } + + return 0; +} +#endif From 212fecae08cc6e645ff795a4c858f82ae8c7a31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 9 Oct 2013 22:59:47 +0200 Subject: [PATCH 11/46] Move rate limit example to test/ --- src/rate_limit.c | 21 --------------------- test/rate_limit.c | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 21 deletions(-) create mode 100644 test/rate_limit.c diff --git a/src/rate_limit.c b/src/rate_limit.c index f7c33b4..3ed94bf 100644 --- a/src/rate_limit.c +++ b/src/rate_limit.c @@ -58,24 +58,3 @@ int uc_ratelimit_allow(struct RateLimit *r, long current_ts) return allowed; } -#if 0 -int main() -{ - int i; - struct RateLimit r; - time_t start; - - uc_ratelimit_init(&r, 10, 60, time(NULL)); - - for(i = 0; i < 120; i++) { - int k; - long allowed = 0; - sleep(1); - for (k = 0; k < 500; k++) - allowed += uc_ratelimit_allow(&r, time(NULL)); - printf("%ld of 500 can pass\n", allowed); - } - - return 0; -} -#endif diff --git a/test/rate_limit.c b/test/rate_limit.c new file mode 100644 index 0000000..1b5c714 --- /dev/null +++ b/test/rate_limit.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include "ucore/rate_limit.h" + + +int main() +{ + int i; + struct RateLimit r; + time_t start; + + uc_ratelimit_init(&r, 10, 60, time(NULL)); + + for(i = 0; i < 120; i++) { + int k; + long allowed = 0; + sleep(1); + for (k = 0; k < 500; k++) + allowed += uc_ratelimit_allow(&r, time(NULL)); + printf("%ld of 500 can pass\n", allowed); + } + + return 0; +} From 9ca4a354eede406a4eee5bfbfcf1283a8e6dce90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 10 Oct 2013 01:06:49 +0200 Subject: [PATCH 12/46] Fix calculating available tickets. Help prevent overflow when there's long periods between calls --- src/rate_limit.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/rate_limit.c b/src/rate_limit.c index 3ed94bf..fec4c79 100644 --- a/src/rate_limit.c +++ b/src/rate_limit.c @@ -1,6 +1,6 @@ #include "ucore/rate_limit.h" -#define SCALE_FACTOR 1000 +#define SCALE_FACTOR 100 void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts) @@ -28,22 +28,25 @@ int uc_ratelimit_allow(struct RateLimit *r, long current_ts) //Find elapsed time diff_period = current_ts - r->last_ts; - //If time went backwards, we halt the time for this round - if (diff_period < 0) + if (diff_period > r->period + 1) { + //help prevent overflow when calculating available_tickets below + diff_period = r->period + 1; + } else if (diff_period < 0) { + //If time went backwards, we halt the time for this round diff_period = 0; + } //Calculate the number of tickets that became available since - //the last time. (+ ScALE_FACTOR/2 is to round up at halfway points for - //integer arithmetics) + //the last time. r->available_tickets += diff_period * - ((r->scaled_tickets + SCALE_FACTOR/2) / r->period); + (r->scaled_tickets / r->period); //throttle handing out tickets // if (r->available_tickets > r->scaled_tickets) r->available_tickets = r->scaled_tickets; - //If we have at least onen ticket, we can allow + //If we have at least one ticket, we can allow if (r->available_tickets >= 1 * SCALE_FACTOR) { r->available_tickets -= 1 * SCALE_FACTOR; allowed = 1; From 8a76210661702035825e159d5d84ab784d2cbd57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Fri, 11 Oct 2013 23:54:37 +0200 Subject: [PATCH 13/46] Comment rate_limit.h --- include/ucore/rate_limit.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/include/ucore/rate_limit.h b/include/ucore/rate_limit.h index fa7ec01..51c70b4 100644 --- a/include/ucore/rate_limit.h +++ b/include/ucore/rate_limit.h @@ -62,8 +62,35 @@ struct RateLimit { long last_ts; }; +/** + * Initialize a struct RateLimit, which can hand out @tickets per @period + * the perioid must be in the same units as the current_ts + * + * @param r RateLimit to initialize + * @param tickets number of tickets (bucket depth) + * @param period per this period + * @param current_ts the current timestamp + */ void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts); + +/** + * Reset a RateLimit, filling up the tickets again. + * + * @param r RateLimit to reset + * @param current ts, if non-zero, re-sets the last_ts to the current_ts + */ void uc_ratelimit_reset(struct RateLimit *r, long current_ts); + +/** + * Check if we have 1 ticket available, and can therefore allow + * the work. Removes 1 ticket if we can, and replenishes the tickets + * based on the elapsed time since last time the function was called. + * + * @param r the RateLimit + * @param current_ts the current timestamp. Used to calculate the + * replenisment of tichets since the previous check. + * @return 1 if we can allow, 0 if we don't. + */ int uc_ratelimit_allow(struct RateLimit *r, long current_ts); #endif From 3d93ec60910df1914cca58d23b60aca97629998d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Sat, 12 Oct 2013 01:28:20 +0200 Subject: [PATCH 14/46] Bump scaling to 1000. Note the error in integer arithmetic --- include/ucore/rate_limit.h | 7 +++++++ src/rate_limit.c | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/ucore/rate_limit.h b/include/ucore/rate_limit.h index 51c70b4..e7bc651 100644 --- a/include/ucore/rate_limit.h +++ b/include/ucore/rate_limit.h @@ -34,6 +34,13 @@ * * uc_ratelimit_init(&r,20, 1, time(NULL)); * + * NOTE. The the internals uses integer arithmetic to replenish the tickets, + * this can lead to greater errors estimating the available tickets the smaller + * the number of tickets is in relation to the period. The arithmetic is scaled + * by 1000 currently, so if ticket = 1 and period = 60, we get 1000/60 = 16. This + * should be 16.6667, which is an error of about 3.6%, which means we really just + * rate limit to 1 per 62 seconds. + * * for (;;) { * int fd = accept(..); * if (uc_ratelimit_allow(&r, time(NULL)) { diff --git a/src/rate_limit.c b/src/rate_limit.c index fec4c79..6bebef0 100644 --- a/src/rate_limit.c +++ b/src/rate_limit.c @@ -1,6 +1,6 @@ #include "ucore/rate_limit.h" -#define SCALE_FACTOR 100 +#define SCALE_FACTOR 1000 void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts) From 52eb710db7d3ab18d5cd3da7d83612529391e31f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Sat, 12 Oct 2013 01:29:00 +0200 Subject: [PATCH 15/46] Add rate limit test --- test/test_ratelimit.c | 148 ++++++++++++++++++++++++++++++++++++++++++ test/test_runner.c | 2 + 2 files changed, 150 insertions(+) create mode 100644 test/test_ratelimit.c diff --git a/test/test_ratelimit.c b/test/test_ratelimit.c new file mode 100644 index 0000000..072c9ea --- /dev/null +++ b/test/test_ratelimit.c @@ -0,0 +1,148 @@ +#include +#include + + +START_TEST (test_ratelimit_1) + struct RateLimit r; + long now = 10; + int i; + + uc_ratelimit_init(&r, 30, 2, now); + for (i = 0; i < 30; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + fail_if(uc_ratelimit_allow(&r, now)); +END_TEST + +START_TEST (test_ratelimit_2) + struct RateLimit r; + long now = 10; + + uc_ratelimit_init(&r, 1, 1, now); + fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(uc_ratelimit_allow(&r, now)); + + now += 1; + + fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(uc_ratelimit_allow(&r, now)); + +END_TEST + +START_TEST (test_ratelimit_3) + struct RateLimit r; + long now = 10; + int i; + + uc_ratelimit_init(&r, 10, 5, now); + + for (i = 0; i < 10; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + + now += 1; + + for (i = 0; i < 2; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + + fail_if(uc_ratelimit_allow(&r, now)); + + now += 1; + + for (i = 0; i < 2; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + fail_if(uc_ratelimit_allow(&r, now)); + + now += 1; + + for (i = 0; i < 2; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + fail_if(uc_ratelimit_allow(&r, now)); + + +END_TEST + +START_TEST (test_ratelimit_4) + struct RateLimit r; + long now = 10; + int i; + + uc_ratelimit_init(&r, 10, 5, now); + + for (i = 0; i < 10; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + + + fail_if(uc_ratelimit_allow(&r, now)); + + uc_ratelimit_reset(&r, now); + + for (i = 0; i < 10; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + fail_if(uc_ratelimit_allow(&r, now)); + +END_TEST + +START_TEST (test_ratelimit_5) + struct RateLimit r; + long now = 1400000000; + + uc_ratelimit_init(&r, 1, 10, now); + + fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(uc_ratelimit_allow(&r, now)); + + now += 5; + + fail_if(uc_ratelimit_allow(&r, now)); + + now += 5; + + fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(uc_ratelimit_allow(&r, now)); + + now += 200; + + fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(uc_ratelimit_allow(&r, now)); + +END_TEST + +START_TEST (test_ratelimit_time_backward) + struct RateLimit r; + long now = 1400000000; + + uc_ratelimit_init(&r, 1, 10, now); + + fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(uc_ratelimit_allow(&r, now)); + + now -= 5; + + fail_if(uc_ratelimit_allow(&r, now)); + +END_TEST + + +Suite *ratelimit_suite(void) +{ + Suite *s = suite_create("ratelimit"); + TCase *tc = tcase_create("ratelimit tests"); + + tcase_add_test(tc, test_ratelimit_1); + tcase_add_test(tc, test_ratelimit_2); + tcase_add_test(tc, test_ratelimit_3); + tcase_add_test(tc, test_ratelimit_4); + tcase_add_test(tc, test_ratelimit_5); + tcase_add_test(tc, test_ratelimit_time_backward); + + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test_runner.c b/test/test_runner.c index 73cf4c1..a21b4e9 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -24,6 +24,7 @@ extern Suite *human_bytesz_suite(void); extern Suite *dbuf_suite(void); extern Suite *sprintb_suite(void); extern Suite *strv_suite(void); +extern Suite *ratelimit_suite(void); static suite_func suites[] = { bitvec_suite, @@ -42,6 +43,7 @@ static suite_func suites[] = { dbuf_suite, sprintb_suite, strv_suite, + ratelimit_suite, clock_suite, threadqueue_suite }; From 48678a98581e47464087545441a5bb37bf7045b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Sun, 13 Oct 2013 17:37:28 +0200 Subject: [PATCH 16/46] Use consisten header guard, prefixed with UC_ --- include/ucore/bitvec.h | 4 ++-- include/ucore/buffer.h | 4 ++-- include/ucore/dbuf.h | 4 ++-- include/ucore/fd_utils.h | 4 ++-- include/ucore/hash.h | 4 ++-- include/ucore/heapsort.h | 4 ++-- include/ucore/iomux.h | 4 ++-- include/ucore/logging.h | 4 ++-- include/ucore/math.h | 4 ++-- include/ucore/mersenne_twister.h | 4 ++-- include/ucore/pack.h | 4 ++-- include/ucore/rbtree.h | 4 ++-- include/ucore/read_file.h | 4 ++-- include/ucore/salloc.h | 4 ++-- include/ucore/saturating_math.h | 3 +++ include/ucore/seq.h | 4 ++-- include/ucore/string.h | 4 ++-- include/ucore/strvec.h | 5 +++-- include/ucore/supervisor.h | 4 ++-- include/ucore/threadqueue.h | 4 ++-- include/ucore/timers.h | 4 ++-- include/ucore/utils.h | 5 +++-- include/ucore/version.h | 4 ++-- 23 files changed, 49 insertions(+), 44 deletions(-) diff --git a/include/ucore/bitvec.h b/include/ucore/bitvec.h index acbedaa..be06f4f 100644 --- a/include/ucore/bitvec.h +++ b/include/ucore/bitvec.h @@ -1,5 +1,5 @@ -#ifndef BITEVEC_H_ -#define BITEVEC_H_ +#ifndef UC_BITEVEC_H_ +#define UC_BITEVEC_H_ #ifdef __cplusplus extern "C" { diff --git a/include/ucore/buffer.h b/include/ucore/buffer.h index f49f339..87b0016 100644 --- a/include/ucore/buffer.h +++ b/include/ucore/buffer.h @@ -1,5 +1,5 @@ -#ifndef UCORE_BUFFER_H_ -#define UCORE_BUFFER_H_ +#ifndef UC_BUFFER_H_ +#define uc_BUFFER_H_ #include diff --git a/include/ucore/dbuf.h b/include/ucore/dbuf.h index c3c841c..f564c3e 100644 --- a/include/ucore/dbuf.h +++ b/include/ucore/dbuf.h @@ -1,5 +1,5 @@ -#ifndef dbuf_H_ -#define dbuf_H_ +#ifndef UC_DBUF_H_ +#define UC_DBUF_H_ #include #ifdef __cplusplus diff --git a/include/ucore/fd_utils.h b/include/ucore/fd_utils.h index 9c8d178..b696e3a 100644 --- a/include/ucore/fd_utils.h +++ b/include/ucore/fd_utils.h @@ -1,5 +1,5 @@ -#ifndef FD_UTILS_H_ -#define FD_UTILS_H_ +#ifndef UC_FD_UTILS_H_ +#define UC_FD_UTILS_H_ #ifdef __cplusplus extern "C" { #endif diff --git a/include/ucore/hash.h b/include/ucore/hash.h index 6c90469..750b446 100644 --- a/include/ucore/hash.h +++ b/include/ucore/hash.h @@ -1,5 +1,5 @@ -#ifndef UCORE_HASH_H_ -#define UCORE_HASH_H_ +#ifndef UC_HASH_H_ +#define UC_HASH_H_ #include #include diff --git a/include/ucore/heapsort.h b/include/ucore/heapsort.h index 2391e84..38947c3 100644 --- a/include/ucore/heapsort.h +++ b/include/ucore/heapsort.h @@ -1,5 +1,5 @@ -#ifndef UCORE_HEAPSORT_H_ -#define UCORE_HEAPSORT_H_ +#ifndef UC_HEAPSORT_H_ +#define UC_HEAPSORT_H_ #include diff --git a/include/ucore/iomux.h b/include/ucore/iomux.h index 78e4ccd..afc746d 100644 --- a/include/ucore/iomux.h +++ b/include/ucore/iomux.h @@ -1,5 +1,5 @@ -#ifndef IO_MUX_H_ -#define IO_MUX_H_ +#ifndef UC_IO_MUX_H_ +#define UC_IO_MUX_H_ #include "timers.h" /** @addtogroup IOMux diff --git a/include/ucore/logging.h b/include/ucore/logging.h index 9efa2d6..6bb0956 100644 --- a/include/ucore/logging.h +++ b/include/ucore/logging.h @@ -1,5 +1,5 @@ -#ifndef UCORE_LOGGING_H_ -#define UCORE_LOGGING_H_ +#ifndef UC_LOGGING_H_ +#define UC_LOGGING_H_ /** Logging functions. * All logging functions except uc_log_init are thread safe, diff --git a/include/ucore/math.h b/include/ucore/math.h index 8af110a..4d9d6fc 100644 --- a/include/ucore/math.h +++ b/include/ucore/math.h @@ -1,5 +1,5 @@ -#ifndef UCORE_MATH_H_ -#define UCORE_MATH_H_ +#ifndef UC_MATH_H_ +#define UC_MATH_H_ #include diff --git a/include/ucore/mersenne_twister.h b/include/ucore/mersenne_twister.h index f3fb166..b2604e1 100644 --- a/include/ucore/mersenne_twister.h +++ b/include/ucore/mersenne_twister.h @@ -1,5 +1,5 @@ -#ifndef UCORE_MERSENNE_TWISTER_H -#define UCORE_MERSENNE_TWISTER_H +#ifndef UC_MERSENNE_TWISTER_H +#define UC_MERSENNE_TWISTER_H #ifdef __cplusplus extern "C" { diff --git a/include/ucore/pack.h b/include/ucore/pack.h index ed36852..9c9b742 100644 --- a/include/ucore/pack.h +++ b/include/ucore/pack.h @@ -1,5 +1,5 @@ -#ifndef UCORE_PACK_H_ -#define UCORE_PACK_H_ +#ifndef UC_PACK_H_ +#define UC_PACK_H_ #include #ifdef __GNUC__ diff --git a/include/ucore/rbtree.h b/include/ucore/rbtree.h index edc271d..7b6e16e 100644 --- a/include/ucore/rbtree.h +++ b/include/ucore/rbtree.h @@ -1,5 +1,5 @@ -#ifndef RBTREE_H_ -#define RBTREE_H_ +#ifndef UC_RBTREE_H_ +#define UC_RBTREE_H_ #ifdef __cplusplus extern "C" { diff --git a/include/ucore/read_file.h b/include/ucore/read_file.h index 173adc8..9e1ea10 100644 --- a/include/ucore/read_file.h +++ b/include/ucore/read_file.h @@ -1,5 +1,5 @@ -#ifndef UCORE_READ_FILE_H_ -#define UCORE_READ_FILE_H_y +#ifndef UC_READ_FILE_H_ +#define UC_READ_FILE_H_y #include diff --git a/include/ucore/salloc.h b/include/ucore/salloc.h index 4632316..d9f5d44 100644 --- a/include/ucore/salloc.h +++ b/include/ucore/salloc.h @@ -1,5 +1,5 @@ -#ifndef SALLOC_H_ -#define SALLOC_H_ +#ifndef UC_SALLOC_H_ +#define UC_SALLOC_H_ #include diff --git a/include/ucore/saturating_math.h b/include/ucore/saturating_math.h index e10539d..4ef5ff0 100644 --- a/include/ucore/saturating_math.h +++ b/include/ucore/saturating_math.h @@ -1,3 +1,5 @@ +#ifndef UC_SATMATH_H_ +#define UC_SATMATH_H_ #include /** Implementation of saturated operation on uintxx_t. @@ -79,3 +81,4 @@ static inline uint64_t uc_sat_multu64(uint64_t a, uint64_t b) return UC_SAT_MULT(a, b, uint64_t, UINT64_MAX); } +#endif diff --git a/include/ucore/seq.h b/include/ucore/seq.h index 72f33fb..7d5867b 100644 --- a/include/ucore/seq.h +++ b/include/ucore/seq.h @@ -1,5 +1,5 @@ -#ifndef SEQ_H_ -#define SEQ_H_ +#ifndef UC_SEQ_H_ +#define UC_SEQ_H_ #include // Check if a is less than b, taking care of wrap arounds diff --git a/include/ucore/string.h b/include/ucore/string.h index 60955a1..fa586bb 100644 --- a/include/ucore/string.h +++ b/include/ucore/string.h @@ -1,5 +1,5 @@ -#ifndef UCORE_STRING_H_ -#define UCORE_STRING_H_ +#ifndef UC_STRING_H_ +#define UC_STRING_H_ #include #include diff --git a/include/ucore/strvec.h b/include/ucore/strvec.h index 79fd641..e5ce69e 100644 --- a/include/ucore/strvec.h +++ b/include/ucore/strvec.h @@ -1,8 +1,9 @@ -#include - #ifndef UC_STRVEC_H_ #define UC_STRVEC_H_ +#include + + #ifdef __cplusplus extern "C" { #endif diff --git a/include/ucore/supervisor.h b/include/ucore/supervisor.h index b33aa2b..d3e4d13 100644 --- a/include/ucore/supervisor.h +++ b/include/ucore/supervisor.h @@ -1,5 +1,5 @@ -#ifndef UCORE_SUPERVISOR_H_ -#define UCORE_SUPERVISOR_H_ +#ifndef UC_SUPERVISOR_H_ +#define UC_SUPERVISOR_H_ /** Supervises this process - restart if it dies. * diff --git a/include/ucore/threadqueue.h b/include/ucore/threadqueue.h index 61cfbb4..c6f546e 100644 --- a/include/ucore/threadqueue.h +++ b/include/ucore/threadqueue.h @@ -1,5 +1,5 @@ -#ifndef _THREADQUEUE_H_ -#define _THREADQUEUE_H_ 1 +#ifndef UC_THREADQUEUE_H_ +#define UC_THREADQUEUE_H_ #include diff --git a/include/ucore/timers.h b/include/ucore/timers.h index 6aa34b5..c202cb0 100644 --- a/include/ucore/timers.h +++ b/include/ucore/timers.h @@ -1,5 +1,5 @@ -#ifndef UCORE_TIMERS_H_ -#define UCORE_TIMERS_H_ +#ifndef UC_TIMERS_H_ +#define UC_TIMERS_H_ #include #include diff --git a/include/ucore/utils.h b/include/ucore/utils.h index 5f8b0af..0720b46 100644 --- a/include/ucore/utils.h +++ b/include/ucore/utils.h @@ -1,5 +1,6 @@ -#ifndef UCORE_UTILS_H_ -#define UCORE_UTILS_H_ +#ifndef UC_UTILS_H_ +#define UC_UTILS_H_ + #include #include #include diff --git a/include/ucore/version.h b/include/ucore/version.h index 9f9d0b2..23803a1 100644 --- a/include/ucore/version.h +++ b/include/ucore/version.h @@ -1,5 +1,5 @@ -#ifndef UCORE_VERSION_H_ -#define UCORE_VERSION_H_ +#ifndef UC_VERSION_H_ +#define UC_VERSION_H_ /** * @mainpage * @htmlonly From 6058be1c07acfb33ee161e78f5755d2af13573c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Mon, 14 Oct 2013 18:03:17 +0200 Subject: [PATCH 17/46] Fix UC_STATIC_ASSERT --- include/ucore/utils.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/ucore/utils.h b/include/ucore/utils.h index 0720b46..cc1b0cf 100644 --- a/include/ucore/utils.h +++ b/include/ucore/utils.h @@ -9,9 +9,7 @@ //Gnerate a compiler error if the compile time //constant expression fails #define UC_STATIC_ASSERT(expr) \ - do { \ - enum { assert_static__ = 1/(expr) }; \ - } while (0) + enum { assert_static__ = 1/(expr) }; #ifdef DEBUG #define TRACEF(fmt, ...)\ From 41bb24bb617937027b81385e39866975844e6422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Mon, 14 Oct 2013 19:01:56 +0200 Subject: [PATCH 18/46] Delete the silly bitvec _s functions --- include/ucore/bitvec.h | 8 -------- src/bitvec.c | 35 ----------------------------------- 2 files changed, 43 deletions(-) diff --git a/include/ucore/bitvec.h b/include/ucore/bitvec.h index be06f4f..5eec670 100644 --- a/include/ucore/bitvec.h +++ b/include/ucore/bitvec.h @@ -65,14 +65,6 @@ void uc_bv_set_all(struct UCBitVec *v); * */ void uc_bv_set_bits_from_array(struct UCBitVec *v,char *array,size_t array_len); -// The _s ("secure") versions does boundary checking and assert() if they -// try to access a bit out of bounds. - -void uc_bv_set_bit_s(struct UCBitVec *v,int b); - -void uc_bv_clear_bit_s(struct UCBitVec *v,int b); - -int uc_bv_get_bit_s(const struct UCBitVec *v,int b); #ifdef __cplusplus } diff --git a/src/bitvec.c b/src/bitvec.c index fff5d31..abab68b 100644 --- a/src/bitvec.c +++ b/src/bitvec.c @@ -48,7 +48,6 @@ void uc_bv_free(struct UCBitVec *v) } } - void uc_bv_clr_bit(struct UCBitVec *v,int b) { v->vec[bit_index(b)] &= ~(1UL << (bit_offset(b))); @@ -64,40 +63,6 @@ void uc_bv_set_bit(struct UCBitVec *v,int b) v->vec[bit_index(b)] |= 1UL << (bit_offset(b)); } -void uc_bv_set_bit_s(struct UCBitVec *v,int b) -{ - size_t idx = (size_t)bit_index(b); - assert(b >= 0); - assert(v->vec_len > (size_t)idx); - - if (likely(idx < v->vec_len)) - v->vec[idx] |= 1UL << (bit_offset(b)); -} - -void uc_bv_clear_bit_s(struct UCBitVec *v,int b) -{ - size_t idx = (size_t)bit_index(b); - - assert(b >= 0); - assert(v->vec_len > (size_t)idx); - - if (likely(idx < v->vec_len)) - v->vec[idx] &= ~(1UL << (bit_offset(b))); -} - -int uc_bv_get_bit_s(const struct UCBitVec *v,int b) -{ - size_t idx = (size_t)bit_index(b); - - assert(b >= 0); - assert(v->vec_len > (size_t)idx); - - if (likely(idx < v->vec_len)) - return !!(v->vec[idx] & (1UL << bit_offset(b))); - else - return 0; -} - void uc_bv_set_bits_from_array(struct UCBitVec *v,char *array,size_t array_len) { size_t i; From 763032fb04c27f5cd3fb249299091663c4b91b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 15 Oct 2013 15:03:27 +0200 Subject: [PATCH 19/46] Greatly improve the rate limit algorithm, so it is correct for a low ticket to period ratio. --- include/ucore/rate_limit.h | 18 +++++++----------- src/rate_limit.c | 28 ++++++++++++---------------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/include/ucore/rate_limit.h b/include/ucore/rate_limit.h index e7bc651..000dff1 100644 --- a/include/ucore/rate_limit.h +++ b/include/ucore/rate_limit.h @@ -34,12 +34,7 @@ * * uc_ratelimit_init(&r,20, 1, time(NULL)); * - * NOTE. The the internals uses integer arithmetic to replenish the tickets, - * this can lead to greater errors estimating the available tickets the smaller - * the number of tickets is in relation to the period. The arithmetic is scaled - * by 1000 currently, so if ticket = 1 and period = 60, we get 1000/60 = 16. This - * should be 16.6667, which is an error of about 3.6%, which means we really just - * rate limit to 1 per 62 seconds. + * NOTE. period * tickets * 2 must not exceed the value of a long * * for (;;) { * int fd = accept(..); @@ -59,12 +54,11 @@ struct RateLimit { //internal fields: - //scaled up number of tickets, to avoid floating point - //math - long scaled_tickets; + //how much each ticket is worth. + long ticket_cost; - //number of tickets we have (scaled up) - long available_tickets; + //money we have to "buy" tickets + long funds; //time of the previos period long last_ts; }; @@ -72,6 +66,8 @@ struct RateLimit { /** * Initialize a struct RateLimit, which can hand out @tickets per @period * the perioid must be in the same units as the current_ts + * + * NOTE. period * tickets * 2 must not exceed the range of the RateLimit.funds * * @param r RateLimit to initialize * @param tickets number of tickets (bucket depth) diff --git a/src/rate_limit.c b/src/rate_limit.c index 6bebef0..8b9261a 100644 --- a/src/rate_limit.c +++ b/src/rate_limit.c @@ -1,20 +1,17 @@ #include "ucore/rate_limit.h" -#define SCALE_FACTOR 1000 - - void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts) { r->tickets = tickets; - r->scaled_tickets = tickets * SCALE_FACTOR; + r->ticket_cost = tickets * period; r->period = period; - r->available_tickets = r->scaled_tickets; + r->funds = r->ticket_cost * tickets; r->last_ts = current_ts; } void uc_ratelimit_reset(struct RateLimit *r, long current_ts) { - r->available_tickets = r->scaled_tickets; + r->funds = r->ticket_cost * r->tickets; if (current_ts) r->last_ts = current_ts; @@ -36,22 +33,21 @@ int uc_ratelimit_allow(struct RateLimit *r, long current_ts) diff_period = 0; } - //Calculate the number of tickets that became available since + //Calculate the cost of tickets that became available since //the last time. - r->available_tickets += diff_period * - (r->scaled_tickets / r->period); + r->funds += diff_period * + ((r->tickets * r->ticket_cost) / r->period); //throttle handing out tickets - // - if (r->available_tickets > r->scaled_tickets) - r->available_tickets = r->scaled_tickets; + if (r->funds > r->ticket_cost * r->tickets) + r->funds = r->ticket_cost * r->tickets; - //If we have at least one ticket, we can allow - if (r->available_tickets >= 1 * SCALE_FACTOR) { - r->available_tickets -= 1 * SCALE_FACTOR; + //If we have enough to buy atleast one ticket, we can allow + if (r->funds >= r->ticket_cost) { + r->funds -= r->ticket_cost; allowed = 1; } else { - //no more tickets. + //not enough to buy a ticket allowed = 0; } From f4e7ad0fe6bee52bc79648c2e88037b07e42f2cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 15 Oct 2013 15:23:32 +0200 Subject: [PATCH 20/46] Note the limits on tickets and period --- include/ucore/rate_limit.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ucore/rate_limit.h b/include/ucore/rate_limit.h index 000dff1..c5f8d9a 100644 --- a/include/ucore/rate_limit.h +++ b/include/ucore/rate_limit.h @@ -34,7 +34,7 @@ * * uc_ratelimit_init(&r,20, 1, time(NULL)); * - * NOTE. period * tickets * 2 must not exceed the value of a long + * NOTE. period * tickets * tickets must not exceed the value of a long * * for (;;) { * int fd = accept(..); @@ -67,7 +67,7 @@ struct RateLimit { * Initialize a struct RateLimit, which can hand out @tickets per @period * the perioid must be in the same units as the current_ts * - * NOTE. period * tickets * 2 must not exceed the range of the RateLimit.funds + * NOTE. period * tickets * tickets must not exceed the range of the RateLimit.funds * * @param r RateLimit to initialize * @param tickets number of tickets (bucket depth) From c84d903b4fa3e701420ab8b8dcd4f75ed035fde5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 15 Oct 2013 15:27:25 +0200 Subject: [PATCH 21/46] Assert that rate limit values are ok --- src/rate_limit.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rate_limit.c b/src/rate_limit.c index 8b9261a..b829b50 100644 --- a/src/rate_limit.c +++ b/src/rate_limit.c @@ -1,3 +1,4 @@ +#include #include "ucore/rate_limit.h" void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts) @@ -7,6 +8,11 @@ void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long curr r->period = period; r->funds = r->ticket_cost * tickets; r->last_ts = current_ts; + + assert(tickets > 0); + assert(period > 0); + assert(r->ticket_cost > 0); + assert(r->funds > 0); } void uc_ratelimit_reset(struct RateLimit *r, long current_ts) From 28514c0a57b2feba07a6598fc33e0cb70021d925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 15 Oct 2013 15:44:12 +0200 Subject: [PATCH 22/46] More limits clarification --- include/ucore/rate_limit.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ucore/rate_limit.h b/include/ucore/rate_limit.h index c5f8d9a..10bd00f 100644 --- a/include/ucore/rate_limit.h +++ b/include/ucore/rate_limit.h @@ -34,7 +34,7 @@ * * uc_ratelimit_init(&r,20, 1, time(NULL)); * - * NOTE. period * tickets * tickets must not exceed the value of a long + * NOTE. 2 * (period + 1) * tickets * tickets must not exceed the range of the RateLimit.funds * * for (;;) { * int fd = accept(..); @@ -67,7 +67,7 @@ struct RateLimit { * Initialize a struct RateLimit, which can hand out @tickets per @period * the perioid must be in the same units as the current_ts * - * NOTE. period * tickets * tickets must not exceed the range of the RateLimit.funds + * NOTE. 2 * (period + 1) * tickets * tickets must not exceed the range of the RateLimit.funds * * @param r RateLimit to initialize * @param tickets number of tickets (bucket depth) From 9c15b73774ce528f95f117947f434f10541ade69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 15 Oct 2013 20:14:15 +0200 Subject: [PATCH 23/46] Small optimization change when tickets < period --- src/rate_limit.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/rate_limit.c b/src/rate_limit.c index b829b50..2ca92ae 100644 --- a/src/rate_limit.c +++ b/src/rate_limit.c @@ -4,7 +4,13 @@ void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts) { r->tickets = tickets; - r->ticket_cost = tickets * period; + + if (tickets > period) { + r->ticket_cost = tickets * period; + } else { + r->ticket_cost = period; + } + r->period = period; r->funds = r->ticket_cost * tickets; r->last_ts = current_ts; @@ -19,8 +25,9 @@ void uc_ratelimit_reset(struct RateLimit *r, long current_ts) { r->funds = r->ticket_cost * r->tickets; - if (current_ts) + if (current_ts) { r->last_ts = current_ts; + } } int uc_ratelimit_allow(struct RateLimit *r, long current_ts) @@ -45,8 +52,9 @@ int uc_ratelimit_allow(struct RateLimit *r, long current_ts) ((r->tickets * r->ticket_cost) / r->period); //throttle handing out tickets - if (r->funds > r->ticket_cost * r->tickets) + if (r->funds > r->ticket_cost * r->tickets) { r->funds = r->ticket_cost * r->tickets; + } //If we have enough to buy atleast one ticket, we can allow if (r->funds >= r->ticket_cost) { From c1ef4af4ad5e3ff80b76da5a93a8a6360ada88d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 15 Oct 2013 20:14:35 +0200 Subject: [PATCH 24/46] Changed test program --- test/rate_limit.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/rate_limit.c b/test/rate_limit.c index 1b5c714..bb6408c 100644 --- a/test/rate_limit.c +++ b/test/rate_limit.c @@ -8,17 +8,18 @@ int main() { int i; struct RateLimit r; - time_t start; + time_t start = time(NULL); - uc_ratelimit_init(&r, 10, 60, time(NULL)); + uc_ratelimit_init(&r, 3, 10, start); for(i = 0; i < 120; i++) { int k; long allowed = 0; sleep(1); - for (k = 0; k < 500; k++) - allowed += uc_ratelimit_allow(&r, time(NULL)); - printf("%ld of 500 can pass\n", allowed); + time_t now = time(NULL); + for (k = 0; k < 5000; k++) + allowed += uc_ratelimit_allow(&r, now); + printf("%3ld of 500 can pass i=%3d, time = %3ld\n", allowed,i , (long)(now - start)); } return 0; From 0a36c669c6cbac2ffdd1a47543e0fad8532294c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 15 Oct 2013 21:35:10 +0200 Subject: [PATCH 25/46] More ratelimit optimizations --- include/ucore/rate_limit.h | 4 ++-- src/rate_limit.c | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/ucore/rate_limit.h b/include/ucore/rate_limit.h index 10bd00f..6e9ff42 100644 --- a/include/ucore/rate_limit.h +++ b/include/ucore/rate_limit.h @@ -34,7 +34,7 @@ * * uc_ratelimit_init(&r,20, 1, time(NULL)); * - * NOTE. 2 * (period + 1) * tickets * tickets must not exceed the range of the RateLimit.funds + * NOTE. (period + 1) * tickets must not exceed the range of the RateLimit.funds * * for (;;) { * int fd = accept(..); @@ -67,7 +67,7 @@ struct RateLimit { * Initialize a struct RateLimit, which can hand out @tickets per @period * the perioid must be in the same units as the current_ts * - * NOTE. 2 * (period + 1) * tickets * tickets must not exceed the range of the RateLimit.funds + * NOTE. 2 * (period + 1) * tickets must not exceed the range of the RateLimit.funds * * @param r RateLimit to initialize * @param tickets number of tickets (bucket depth) diff --git a/src/rate_limit.c b/src/rate_limit.c index 2ca92ae..9bb51d0 100644 --- a/src/rate_limit.c +++ b/src/rate_limit.c @@ -5,11 +5,7 @@ void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long curr { r->tickets = tickets; - if (tickets > period) { - r->ticket_cost = tickets * period; - } else { - r->ticket_cost = period; - } + r->ticket_cost = period; r->period = period; r->funds = r->ticket_cost * tickets; @@ -48,8 +44,13 @@ int uc_ratelimit_allow(struct RateLimit *r, long current_ts) //Calculate the cost of tickets that became available since //the last time. - r->funds += diff_period * - ((r->tickets * r->ticket_cost) / r->period); + r->funds += diff_period * r->tickets; + + //the below is the real algorithm, with ticket_cost = + //tickets * period. + //we have optimized this to the expression used above + //r->funds += diff_period * + // ((r->tickets * r->ticket_cost) / r->period); //throttle handing out tickets if (r->funds > r->ticket_cost * r->tickets) { From 934088ca4af2c3af9d42028702fd9bd59ad93b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 15 Oct 2013 22:31:57 +0200 Subject: [PATCH 26/46] Remove ticket_cost member as it is no longer needed --- include/ucore/rate_limit.h | 13 ++++++++++--- src/rate_limit.c | 20 +++++++++----------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/include/ucore/rate_limit.h b/include/ucore/rate_limit.h index 6e9ff42..0437674 100644 --- a/include/ucore/rate_limit.h +++ b/include/ucore/rate_limit.h @@ -54,15 +54,22 @@ struct RateLimit { //internal fields: - //how much each ticket is worth. - long ticket_cost; - //money we have to "buy" tickets long funds; //time of the previos period long last_ts; }; +/** Static initializer for a struct RateLimit + * + * @param tickets number of tickets (bucket depth) + * @param period per this period + */ +#define UC_RATELIMIT_INITIALIZER(tickets, period)\ +{ tickets, period, tickets * period, 0} + + + /** * Initialize a struct RateLimit, which can hand out @tickets per @period * the perioid must be in the same units as the current_ts diff --git a/src/rate_limit.c b/src/rate_limit.c index 9bb51d0..5805967 100644 --- a/src/rate_limit.c +++ b/src/rate_limit.c @@ -5,21 +5,19 @@ void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long curr { r->tickets = tickets; - r->ticket_cost = period; - r->period = period; - r->funds = r->ticket_cost * tickets; + r->funds = r->period * tickets; r->last_ts = current_ts; assert(tickets > 0); assert(period > 0); - assert(r->ticket_cost > 0); + assert(r->period > 0); assert(r->funds > 0); } void uc_ratelimit_reset(struct RateLimit *r, long current_ts) { - r->funds = r->ticket_cost * r->tickets; + r->funds = r->period * r->tickets; if (current_ts) { r->last_ts = current_ts; @@ -46,20 +44,20 @@ int uc_ratelimit_allow(struct RateLimit *r, long current_ts) //the last time. r->funds += diff_period * r->tickets; - //the below is the real algorithm, with ticket_cost = + //the below is the real algorithm, with period = //tickets * period. //we have optimized this to the expression used above //r->funds += diff_period * - // ((r->tickets * r->ticket_cost) / r->period); + // ((r->tickets * r->period) / r->period); //throttle handing out tickets - if (r->funds > r->ticket_cost * r->tickets) { - r->funds = r->ticket_cost * r->tickets; + if (r->funds > r->period * r->tickets) { + r->funds = r->period * r->tickets; } //If we have enough to buy atleast one ticket, we can allow - if (r->funds >= r->ticket_cost) { - r->funds -= r->ticket_cost; + if (r->funds >= r->period) { + r->funds -= r->period; allowed = 1; } else { //not enough to buy a ticket From 13567e3cd94a927030522be16d2c1063cb449fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 16 Oct 2013 01:37:20 +0200 Subject: [PATCH 27/46] Add macro for stringifying __FILE__:__LINE__ --- include/ucore/utils.h | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/include/ucore/utils.h b/include/ucore/utils.h index cc1b0cf..cc23bef 100644 --- a/include/ucore/utils.h +++ b/include/ucore/utils.h @@ -11,15 +11,6 @@ #define UC_STATIC_ASSERT(expr) \ enum { assert_static__ = 1/(expr) }; -#ifdef DEBUG - #define TRACEF(fmt, ...)\ - do { \ - fprintf(stdout, "%s:%d (%s)\t" fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__);\ - fflush(stdout);\ - } while(0) -#else - #define TRACEF(fmt, ...) -#endif //MAX of a and b #define UC_MAX(a,b) \ @@ -80,6 +71,9 @@ const typeof( ((const type *)0)->member ) *__mptr = (ptr); \ #define UC_STRINGIFY(x) UC_STRINGIFY_HLP(x) #define UC_STRINGIFY_HLP(x) #x +/** Macro that expands to a string for the current file:line + */ +#define UC_SRC_LOCATION __FILE__ ":" UC_STRINGIFY(__LINE__) /** * assert() that is not affected by NDEBUG define */ @@ -87,19 +81,29 @@ const typeof( ((const type *)0)->member ) *__mptr = (ptr); \ do {\ if (!(expr) ) {\ fprintf(stderr,\ - "UC_ASSERT failed '%s' %s:%d\n", UC_STRINGIFY(expr), __FILE__, __LINE__);\ + "UC_ASSERT failed '%s' %s\n", UC_STRINGIFY(expr), UC_SRC_LOCATION);\ uc_backtrace_fd(2);\ abort();\ }\ } while (0) +#ifdef DEBUG + #define TRACEF(fmt, ...)\ + do { \ + fprintf(stdout, "%s (%s)\t" fmt, UC_SRC_LOCATION, __FUNCTION__, ##__VA_ARGS__);\ + fflush(stdout);\ + } while(0) +#else + #define TRACEF(fmt, ...) +#endif + /** * Macro for asserting code that should not be reached, * terminates the program if executed */ #define UC_NOT_REACED()\ do {\ - fprintf(stderr, "UC_NOT_REACED at %s:%d\n", __FILE__, __LINE__);\ + fprintf(stderr, "UC_NOT_REACED at %s\n", UC_SRC_LOCATION);\ uc_backtrace_fd(2);\ abort();\ while (0) From ee3dd22b0e5fb415280407af5dedd3f30cfd107e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 16 Oct 2013 01:44:36 +0200 Subject: [PATCH 28/46] Convert logging to use UC_SRC_LOCATION --- include/ucore/logging.h | 13 +++++++------ src/logging.c | 16 +++++++--------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/include/ucore/logging.h b/include/ucore/logging.h index 6bb0956..501a45d 100644 --- a/include/ucore/logging.h +++ b/include/ucore/logging.h @@ -1,5 +1,6 @@ #ifndef UC_LOGGING_H_ #define UC_LOGGING_H_ +#include "utils.h" /** Logging functions. * All logging functions except uc_log_init are thread safe, @@ -268,14 +269,14 @@ int uc_log_reopen_files(void); void uc_log_init(struct uc_log_modules *user_modules); void uc_logf(int log_level, int module, int raw, - const char *file, int line, const char *fmt, ...) - __attribute__((format(printf, 6, 7))); + const char *location, const char *fmt, ...) + __attribute__((format(printf, 5, 6))); #ifdef DEBUG # define UC_DEBUGF(mod, fmt, ...)\ - uc_logf(UC_LL_DEBUG, mod,0 , __FILE__, __LINE__, fmt, ## __VA_ARGS__) + uc_logf(UC_LL_DEBUG, mod,0 , UC_SRC_LOCATION fmt, ## __VA_ARGS__) # define UC_DEBUGFR(mod, fmt, ...)\ - uc_logf(UC_LL_DEBUG, mod,1 , __FILE__, __LINE__, fmt, ## __VA_ARGS__) + uc_logf(UC_LL_DEBUG, mod,1 , UC_SRC_LOCATION, fmt, ## __VA_ARGS__) #else # define UC_DEBUGF(mod, fmt, ...) # define UC_DEBUGFR(mod, fmt, ...) @@ -292,14 +293,14 @@ void uc_logf(int log_level, int module, int raw, * @param ... printf style arguments */ #define UC_LOGF(lvl, mod, fmt, ...) \ - uc_logf(lvl, mod, 0, __FILE__, __LINE__, fmt, ## __VA_ARGS__) + uc_logf(lvl, mod, 0, UC_SRC_LOCATION, fmt, ## __VA_ARGS__) /** Raw logging ,Like UC_LOGF, but only prints the supplied * data, not timestamp, log level etc. */ #define UC_LOGFR(lvl, mod, fmt, ...) \ - uc_logf(lvl, mod, 1, __FILE__, __LINE__, fmt, ## __VA_ARGS__) + uc_logf(lvl, mod, 1, UC_SRC_LOCATION, fmt, ## __VA_ARGS__) #ifdef __cplusplus } diff --git a/src/logging.c b/src/logging.c index 85146e8..248e548 100644 --- a/src/logging.c +++ b/src/logging.c @@ -42,8 +42,7 @@ struct uc_log_args { struct uc_log_destination *dest; const struct uc_log_module *module; int log_level; - const char *file; - int line; + const char *location; int raw; }; @@ -415,9 +414,9 @@ static inline void uc_vlog_file(const struct uc_log_args *args, const char *fmt, time_buf[sizeof time_buf -1] = 0; if (dest->log_location) { - fprintf(dest->type.file.file, "[%-7s] %s %s:%d [%s] : ", - uc_ll_2_str(args->log_level), time_buf, args->file, - args->line, args->module->short_name); + fprintf(dest->type.file.file, "[%-7s] %s %s [%s] : ", + uc_ll_2_str(args->log_level), time_buf, + args->location, args->module->short_name); } else { fprintf(dest->type.file.file, "[%-7s] %s [%s]: ", uc_ll_2_str(args->log_level), time_buf, args->module->short_name); @@ -446,7 +445,7 @@ static inline void uc_vlog_syslog(const struct uc_log_args *args, const char *fm buf[0] = 0; if (dest->log_location) { - rc = snprintf(buf, sizeof buf, "%s:%d ", args->file, args->line); + rc = snprintf(buf, sizeof buf, "%s ", args->location); if (rc < 0) goto log; offset += rc; @@ -543,7 +542,7 @@ void uc_log_destination_set_mask( } void uc_logf(int log_level, int module, int raw, - const char *file, int line, const char *fmt, ...) + const char *location, const char *fmt, ...) { va_list ap; struct uc_log_destination *dest; @@ -581,8 +580,7 @@ void uc_logf(int log_level, int module, int raw, .dest = dest, .module = log_module, .log_level = log_level, - .file = file, - .line = line, + .location = location, .raw = raw }; From 2d680052b2bccc5eb5533dc2b00cab7f0062a0b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 16 Oct 2013 01:46:50 +0200 Subject: [PATCH 29/46] Fix UC_DEBUGF --- include/ucore/logging.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ucore/logging.h b/include/ucore/logging.h index 501a45d..b7bc443 100644 --- a/include/ucore/logging.h +++ b/include/ucore/logging.h @@ -274,7 +274,7 @@ void uc_logf(int log_level, int module, int raw, #ifdef DEBUG # define UC_DEBUGF(mod, fmt, ...)\ - uc_logf(UC_LL_DEBUG, mod,0 , UC_SRC_LOCATION fmt, ## __VA_ARGS__) + uc_logf(UC_LL_DEBUG, mod,0 , UC_SRC_LOCATION, fmt, ## __VA_ARGS__) # define UC_DEBUGFR(mod, fmt, ...)\ uc_logf(UC_LL_DEBUG, mod,1 , UC_SRC_LOCATION, fmt, ## __VA_ARGS__) #else From 262958b8a01e594e3d9e2da75b1e56425a3c5cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 16 Oct 2013 19:22:53 +0200 Subject: [PATCH 30/46] Rework rate limit to only refill when needed --- include/ucore/rate_limit.h | 7 ++-- src/rate_limit.c | 77 +++++++++++++++++++------------------- test/test_ratelimit.c | 14 +++---- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/include/ucore/rate_limit.h b/include/ucore/rate_limit.h index 0437674..fedfd55 100644 --- a/include/ucore/rate_limit.h +++ b/include/ucore/rate_limit.h @@ -65,8 +65,8 @@ struct RateLimit { * @param tickets number of tickets (bucket depth) * @param period per this period */ -#define UC_RATELIMIT_INITIALIZER(tickets, period)\ -{ tickets, period, tickets * period, 0} +#define UC_RATELIMIT_INITIALIZER(tickets)\ +{ tickets, period, 0, -period} @@ -79,9 +79,8 @@ struct RateLimit { * @param r RateLimit to initialize * @param tickets number of tickets (bucket depth) * @param period per this period - * @param current_ts the current timestamp */ -void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts); +void uc_ratelimit_init(struct RateLimit *r, long tickets, long period); /** * Reset a RateLimit, filling up the tickets again. diff --git a/src/rate_limit.c b/src/rate_limit.c index 5805967..14299fe 100644 --- a/src/rate_limit.c +++ b/src/rate_limit.c @@ -1,18 +1,18 @@ #include +#include #include "ucore/rate_limit.h" -void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts) +void uc_ratelimit_init(struct RateLimit *r, long tickets, long period) { r->tickets = tickets; r->period = period; - r->funds = r->period * tickets; - r->last_ts = current_ts; + r->funds = 0; //we fill it on first _allow call + r->last_ts = -period; //-period ensures the first tick can start + //at >= 0 and still refill the bicket assert(tickets > 0); - assert(period > 0); assert(r->period > 0); - assert(r->funds > 0); } void uc_ratelimit_reset(struct RateLimit *r, long current_ts) @@ -29,43 +29,44 @@ int uc_ratelimit_allow(struct RateLimit *r, long current_ts) int allowed; long diff_period; - //Find elapsed time - diff_period = current_ts - r->last_ts; - - if (diff_period > r->period + 1) { - //help prevent overflow when calculating available_tickets below - diff_period = r->period + 1; - } else if (diff_period < 0) { - //If time went backwards, we halt the time for this round - diff_period = 0; - } - - //Calculate the cost of tickets that became available since - //the last time. - r->funds += diff_period * r->tickets; - - //the below is the real algorithm, with period = - //tickets * period. - //we have optimized this to the expression used above - //r->funds += diff_period * - // ((r->tickets * r->period) / r->period); - - //throttle handing out tickets - if (r->funds > r->period * r->tickets) { - r->funds = r->period * r->tickets; - } - - //If we have enough to buy atleast one ticket, we can allow - if (r->funds >= r->period) { + if (r->funds >= r->period) { + //we have enough r->funds -= r->period; allowed = 1; - } else { - //not enough to buy a ticket - allowed = 0; + } else { //refill + //Find elapsed time + diff_period = current_ts - r->last_ts; + + if (diff_period > r->period + 1) { + //help prevent overflow when calculating available_tickets below + diff_period = r->period + 1; + } else if (diff_period < 0) { + //time went backwards, skip this round + diff_period = 0; + } + + //Calculate the cost of tickets that became available + r->funds += diff_period * r->tickets; + + + //throttle handing out tickets + if (r->funds > r->period * r->tickets) { + r->funds = r->period * r->tickets; + } + + //If we have enough to buy atleast one ticket, we can allow + if (r->funds >= r->period) { + r->funds -= r->period; + allowed = 1; + } else { + //not enough to buy a ticket + allowed = 0; + } + + //save this period. + r->last_ts = current_ts; } - //save this period. - r->last_ts = current_ts; return allowed; } diff --git a/test/test_ratelimit.c b/test/test_ratelimit.c index 072c9ea..0b09753 100644 --- a/test/test_ratelimit.c +++ b/test/test_ratelimit.c @@ -7,9 +7,9 @@ START_TEST (test_ratelimit_1) long now = 10; int i; - uc_ratelimit_init(&r, 30, 2, now); + uc_ratelimit_init(&r, 30, 2); for (i = 0; i < 30; i++) { - fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(!uc_ratelimit_allow(&r, now), "i = %d", i); } fail_if(uc_ratelimit_allow(&r, now)); END_TEST @@ -18,7 +18,7 @@ START_TEST (test_ratelimit_2) struct RateLimit r; long now = 10; - uc_ratelimit_init(&r, 1, 1, now); + uc_ratelimit_init(&r, 1, 1); fail_if(!uc_ratelimit_allow(&r, now)); fail_if(uc_ratelimit_allow(&r, now)); @@ -34,7 +34,7 @@ START_TEST (test_ratelimit_3) long now = 10; int i; - uc_ratelimit_init(&r, 10, 5, now); + uc_ratelimit_init(&r, 10, 5); for (i = 0; i < 10; i++) { fail_if(!uc_ratelimit_allow(&r, now)); @@ -70,7 +70,7 @@ START_TEST (test_ratelimit_4) long now = 10; int i; - uc_ratelimit_init(&r, 10, 5, now); + uc_ratelimit_init(&r, 10, 5); for (i = 0; i < 10; i++) { fail_if(!uc_ratelimit_allow(&r, now)); @@ -92,7 +92,7 @@ START_TEST (test_ratelimit_5) struct RateLimit r; long now = 1400000000; - uc_ratelimit_init(&r, 1, 10, now); + uc_ratelimit_init(&r, 1, 10); fail_if(!uc_ratelimit_allow(&r, now)); fail_if(uc_ratelimit_allow(&r, now)); @@ -117,7 +117,7 @@ START_TEST (test_ratelimit_time_backward) struct RateLimit r; long now = 1400000000; - uc_ratelimit_init(&r, 1, 10, now); + uc_ratelimit_init(&r, 1, 10); fail_if(!uc_ratelimit_allow(&r, now)); fail_if(uc_ratelimit_allow(&r, now)); From b91d5870b80c00a8af85ea5fdfecd66b13185fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 16 Oct 2013 19:23:16 +0200 Subject: [PATCH 31/46] Update rate_limit example --- test/rate_limit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rate_limit.c b/test/rate_limit.c index bb6408c..d3d8bea 100644 --- a/test/rate_limit.c +++ b/test/rate_limit.c @@ -10,7 +10,7 @@ int main() struct RateLimit r; time_t start = time(NULL); - uc_ratelimit_init(&r, 3, 10, start); + uc_ratelimit_init(&r, 30, 2); for(i = 0; i < 120; i++) { int k; From 987db2d09661256cab58f4ffe6b5f33f56ef5a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 17 Oct 2013 21:00:05 +0200 Subject: [PATCH 32/46] First iteration of hash table --- include/ucore/htable.h | 214 +++++++++++++++++++++++++++++++++++++++++ src/htable.c | 149 ++++++++++++++++++++++++++++ test/test_runner.c | 2 + 3 files changed, 365 insertions(+) create mode 100644 include/ucore/htable.h create mode 100644 src/htable.c diff --git a/include/ucore/htable.h b/include/ucore/htable.h new file mode 100644 index 0000000..29d935e --- /dev/null +++ b/include/ucore/htable.h @@ -0,0 +1,214 @@ +#ifndef UC_HTABLE_H_ +#define UC_HTABLE_H_ + +#include + +/** Building block for hash table. + * The hash table is stored using a chained list of buckets. + * Nodes with different hash value can be stored in the same bucket. + * + * + * The hash table manages only the hash value, not the equality + * between nodes. + * + * It's up to the user to check for duplicate values + * before adding nodes if dublicates are not desired. + * + * Finding a node must be done in a user specific way, + * normally by iterating over all nodes with the desidred + * hash, and deciding in some other way whether it is the desidred + * node. + */ + +/** Node the hashtable tracks. + * Will normally be embedded inside another struct + */ +struct UHNode { + //cached value of the hash + size_t hash; + //pointer to next in same bucket + struct UHNode *next; +}; + +/** + * The hash table struct + */ +struct UHTable { + size_t cnt_elements; + size_t cnt_buckets; + struct UHNode **buckets; +}; + +/** + * Initialize a hash table. + * @param table hash table to initialize + * @param buckets number of buckets + * @return 0 on success, errno if malloc fails or buckets == 0 + */ +int uc_htable_init(struct UHTable *table, size_t buckets); + +/** + * Frees resources of the hash table. + * @param table hash table to free (does not free @table itself + */ +void uc_htable_destroy(struct UHTable *table); + + +/** + * @param table + * @param hash find the bucket from this hash value + * @return the bucket number for the given hash + */ +static inline size_t uc_htable_bucket(const struct UHTable *table, size_t hash) +{ + return hash % table->cnt_buckets; +} + +/** + * Get the first bucket node for the given hash. + * The returned node might not have the same hash, since different + * nodes can share the same bucket. + * + * @param table table + * @param hash hash + */ +static inline struct UHNode *uc_htable_first_bucket(const struct UHTable *table, size_t hash) +{ + size_t bucket = uc_htable_bucket(table, hash); + + return table->buckets[bucket]; +} + +/** + * Get the next node in the bucket. + * @return next node or NULL + */ +static inline struct UHNode *uc_htable_next_bucket(const struct UHNode *node) +{ + return node->next; +} + +/** + * Get the first node that have the given hash + * @param table table + * @param hash hash + * @return first node with the given hash or NULL + */ +struct UHNode *uc_htable_first_hash(const struct UHTable *table, size_t hash); + + +//internal helper +struct UHNode *uc_htable_next_hash_hlp(struct UHNode *next, size_t hash); +/** + * Get the next node with the same hash + * @param node previous node + * @return next node with the same hash as node or NULL + */ +static inline struct UHNode *uc_htable_next_hash(const struct UHNode *node) +{ + return uc_htable_next_hash_hlp(node->next, node->hash); +} + + +//internal helper +struct UHNode *uc_htable_next_hlp(struct UHTable *table, size_t bucket); + +/** Get the first node in the hash table + * + * @param table table + * @return first node or NULL + */ +static inline struct UHNode *uc_htable_first(struct UHTable *table) +{ + return uc_htable_next_hlp(table, 0); +} + +/** Get the next node in the hash table + * + * @param table table + * @param node previous node + * @return first node or NULL + */ +struct UHNode *uc_htable_next(struct UHTable *table, struct UHNode *node); + +/** + * Swap 2 hash tables. + * + * @param a first table + * @param b second table + */ +void uc_htable_swap(struct UHTable *a, struct UHTable *b); + +#define UC_HTABLE_FOREACH_BUCKET(table, node_iter, hash)\ + for ((node_iter) = uc_htable_first_bucket((table), (hash));\ + (node_iter) != NULL;\ + (node_iter) = uc_htable_next_bucket((node_iter))) + +#define UC_HTABLE_FOREACH_HASH(table, node_iter, hash)\ + for ((node_iter) = uc_htable_first_hash((table), (hash));\ + (node_iter) != NULL;\ + (node_iter) = uc_htable_next_hash((node_iter))) + +#define UC_HTABLE_FOREACH(table, node_iter, hash)\ + for ((node_iter) = uc_htable_first((table));\ + (node_iter) != NULL;\ + (node_iter) = uc_htable_next((table), (node_iter))) + + +/** + * Check if the given node pointer exists in the hash table + * + * @param table table + * @param node node to check for + * @return 0 if the node does not exist in table non-0 if it does + */ +int uc_htable_has_node(struct UHTable *table, struct UHNode *node); + + +/** + * + * @return 0 if table is empty non-0 if it contains any nodes + */ +static inline int uc_htable_isempty(const struct UHTable *table) +{ + return table->cnt_elements == 0; +} + +/** + * Get number of elements in the hash table. + * + * @rparam table table + * @return number of elements + */ +static inline size_t uc_htable_count(const struct UHTable *table) +{ + return table->cnt_elements; +} + +/** Clears all nodes from the hash table, making it empty. + * No memory is released. + * @param table table to clear + */ +void uc_htable_clear(struct UHTable *table); + +/** Insert a new node. + * The new node pointer must exist as long as it is part of the + * hash table. The hash table does not check for duplicates in any way. + * + * @param table table to insert into + * @param node new node to insert. + * @param hash hash value of the new node + */ +void uc_htable_insert(struct UHTable *table, struct UHNode *node, size_t hash); + +/** Resize the hash table. This changes the number of buckets, and re-inserts + * all nodes. + * + * @param table table to resize + * @param buckets new number of buckets, must be > 0 + * @return 0 on success, errno if malloc fails or buckets == 0 + */ +int uc_htable_resize(struct UHTable *table, size_t buckets); + + +#endif diff --git a/src/htable.c b/src/htable.c new file mode 100644 index 0000000..fbc2c4e --- /dev/null +++ b/src/htable.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include "ucore/htable.h" + +int uc_htable_init(struct UHTable *table, size_t buckets) +{ + if (buckets == 0) { + return EINVAL; + } + + table->buckets = calloc(buckets, sizeof *table->buckets); + if (table->buckets == NULL) { + return ENOMEM; + } + + table->cnt_buckets = buckets; + table->cnt_elements = 0; + + return 0; +} + +void uc_htable_destroy(struct UHTable *table) +{ + free(table->buckets); + table->buckets = NULL; + table->cnt_elements = table->cnt_buckets = -1; +} + +struct UHNode *uc_htable_first_hash(const struct UHTable *table, size_t hash) +{ + size_t bucket = uc_htable_bucket(table, hash); + struct UHNode *it = table->buckets[bucket]; + + while (it && it->hash != hash) { + it = it->next; + } + + return it; +} + +struct UHNode *uc_htable_next_hash_hlp(struct UHNode *next, size_t hash) +{ + while (next && next->hash != hash) { + next = next->next; + } + + return next; +} + +struct UHNode *uc_htable_next_hlp(struct UHTable *table, size_t bucket) +{ + size_t i; + struct UHNode *found = NULL; + + for (i = bucket; i < table->cnt_buckets; i++) { + found = uc_htable_first_bucket(table, i); + if (found != NULL) { + break; + } + } + return found; +} + +struct UHNode *uc_htable_next(struct UHTable *table, struct UHNode *node) +{ + struct UHNode *found; + + found = uc_htable_next_bucket(node); + if (found == NULL) { + found = uc_htable_next_hlp(table, node->hash + 1); + } + + return found; +} + +void uc_htable_swap(struct UHTable *a, struct UHTable *b) +{ + struct UHTable tmp; + + tmp = *a; + *a = *b; + *b = tmp; +} + +int uc_htable_has_node(struct UHTable *table, struct UHNode *node) +{ + struct UHNode *iter; + + UC_HTABLE_FOREACH_BUCKET(table, iter, node->hash) { + if (node == iter) { + return 1; + } + } + + return 0; +} + +void uc_htable_clear(struct UHTable *table) +{ + memset(table->buckets, 0 , sizeof *table->buckets * table->cnt_buckets); + table->cnt_elements = 0; +} + +void uc_htable_insert(struct UHTable *table, struct UHNode *node, size_t hash) +{ + size_t bucket = uc_htable_bucket(table, hash); + + node->next = table->buckets[bucket]; + table->buckets[bucket] = node; + node->hash = hash; + + table->cnt_elements++; +} + +int uc_htable_resize(struct UHTable *table, size_t buckets) +{ + struct UHTable tmp_table; + int rc; + size_t h; + + if (buckets == 0) { + return EINVAL; + } + + rc = uc_htable_init(&tmp_table, buckets); + if (rc != 0) { + return rc; + } + + for (h = 0; h < table->cnt_buckets; h++) { + struct UHNode *next; + struct UHNode *node; + + for (node = uc_htable_first_bucket(table, h); + node != NULL; + node = next) { + next = node->next; + uc_htable_insert(&tmp_table, node, node->hash); + } + } + + uc_htable_swap(&tmp_table, table); + uc_htable_destroy(&tmp_table); + + return 0; +} + + diff --git a/test/test_runner.c b/test/test_runner.c index a21b4e9..3bd0a05 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -25,6 +25,7 @@ extern Suite *dbuf_suite(void); extern Suite *sprintb_suite(void); extern Suite *strv_suite(void); extern Suite *ratelimit_suite(void); +extern Suite *htable_suite(void); static suite_func suites[] = { bitvec_suite, @@ -44,6 +45,7 @@ static suite_func suites[] = { sprintb_suite, strv_suite, ratelimit_suite, + htable_suite, clock_suite, threadqueue_suite }; From 9f41543fb1a6e958b519154f47bac4dc823d4a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 17 Oct 2013 21:00:57 +0200 Subject: [PATCH 33/46] hash table test --- test/test_htable.c | 125 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 test/test_htable.c diff --git a/test/test_htable.c b/test/test_htable.c new file mode 100644 index 0000000..46c1436 --- /dev/null +++ b/test/test_htable.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include + +struct MyString { + char str[32]; + struct UHNode node; +}; + +size_t hash_str(const char *str) +{ + size_t hash = 31; + while (*str) { + hash *= *str++; + } + + return hash; +} + +struct MyString *my_find(struct UHTable *table, const char *str) +{ + struct UHNode *it; + size_t hash = hash_str(str); + + UC_HTABLE_FOREACH_HASH(table, it, hash) { + struct MyString *my_str = UC_CONTAINER_OF(it, struct MyString, node); + if (strcmp(my_str->str, str) == 0) { + return my_str; + } + } + + return NULL; +} + + + +START_TEST (test_htable1) + struct MyString str[5] = { + {"One", {0,NULL}}, + {"Two", {0,NULL}}, + {"Three",{0,NULL}}, + {"Four", {0,NULL}}, + {"Five", {0,NULL}} + }; + struct UHTable table; + int i; + + uc_htable_init(&table, 3); + + for (i = 0; i < 5; i++) { + uc_htable_insert(&table, &str[i].node, hash_str(str[i].str)); + } + + ck_assert_int_eq(uc_htable_count(&table), 5); + + fail_if(my_find(&table, "One") != &str[0]); + fail_if(my_find(&table, "Two") != &str[1]); + fail_if(my_find(&table, "Three") != &str[2]); + fail_if(my_find(&table, "Four") != &str[3]); + fail_if(my_find(&table, "Five") != &str[4]); + +END_TEST + +START_TEST (test_htable_resize) + struct MyString str[5] = { + {"One", {0,NULL}}, + {"Two", {0,NULL}}, + {"Three",{0,NULL}}, + {"Four", {0,NULL}}, + {"Five", {0,NULL}} + }; + struct UHTable table; + int i; + int rc; + + rc = uc_htable_init(&table, 3); + ck_assert_int_eq(rc, 0); + + for (i = 0; i < 5; i++) { + uc_htable_insert(&table, &str[i].node, hash_str(str[i].str)); + } + ck_assert_int_eq(uc_htable_count(&table), 5); + + rc = uc_htable_resize(&table, 1024); + ck_assert_int_eq(rc, 0); + + ck_assert_int_eq(uc_htable_count(&table), 5); + + fail_if(my_find(&table, "One") != &str[0]); + fail_if(my_find(&table, "Two") != &str[1]); + fail_if(my_find(&table, "Three") != &str[2]); + fail_if(my_find(&table, "Four") != &str[3]); + fail_if(my_find(&table, "Five") != &str[4]); + +END_TEST + +START_TEST (test_htable_fail_init_resize) + int rc; + struct UHTable table; + rc = uc_htable_init(&table, 0); + ck_assert_int_ne(rc, 0); + + rc = uc_htable_init(&table, 1); + ck_assert_int_eq(rc, 0); + + rc = uc_htable_resize(&table, 0); + ck_assert_int_ne(rc, 0); + +END_TEST +Suite *htable_suite(void) +{ + Suite *s = suite_create("htable"); + TCase *tc = tcase_create("htable tests"); + + tcase_add_test(tc, test_htable1); + tcase_add_test(tc, test_htable_resize); + tcase_add_test(tc, test_htable_fail_init_resize); + + + suite_add_tcase(s, tc); + + return s; +} + From 6c76759355023bec3648725024464b14067e8755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 17 Oct 2013 21:29:55 +0200 Subject: [PATCH 34/46] Add more htable tests --- test/test_htable.c | 81 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/test/test_htable.c b/test/test_htable.c index 46c1436..ad3814c 100644 --- a/test/test_htable.c +++ b/test/test_htable.c @@ -18,10 +18,14 @@ size_t hash_str(const char *str) return hash; } -struct MyString *my_find(struct UHTable *table, const char *str) +size_t bad_hash(const char *str) +{ + return str[0]; +} + +struct MyString *my_find(struct UHTable *table, const char *str, size_t hash) { struct UHNode *it; - size_t hash = hash_str(str); UC_HTABLE_FOREACH_HASH(table, it, hash) { struct MyString *my_str = UC_CONTAINER_OF(it, struct MyString, node); @@ -54,11 +58,39 @@ START_TEST (test_htable1) ck_assert_int_eq(uc_htable_count(&table), 5); - fail_if(my_find(&table, "One") != &str[0]); - fail_if(my_find(&table, "Two") != &str[1]); - fail_if(my_find(&table, "Three") != &str[2]); - fail_if(my_find(&table, "Four") != &str[3]); - fail_if(my_find(&table, "Five") != &str[4]); + fail_if(my_find(&table, "One", hash_str("One")) != &str[0]); + fail_if(my_find(&table, "Two", hash_str("Two")) != &str[1]); + fail_if(my_find(&table, "Three",hash_str("Three")) != &str[2]); + fail_if(my_find(&table, "Four", hash_str("Four")) != &str[3]); + fail_if(my_find(&table, "Five", hash_str("Five")) != &str[4]); + +END_TEST + +START_TEST (test_htable_hash_collision) + //same as test1 but with a bad hash to force equal hash + struct MyString str[5] = { + {"...", {0,NULL}}, + {"", {0,NULL}}, + {".", {0,NULL}}, + {"..", {0,NULL}}, + {"....", {0,NULL}} + }; + struct UHTable table; + int i; + + uc_htable_init(&table, 1); + + for (i = 0; i < 5; i++) { + uc_htable_insert(&table, &str[i].node, bad_hash(str[i].str)); + } + + ck_assert_int_eq(uc_htable_count(&table), 5); + + fail_if(my_find(&table, "....", bad_hash("....")) != &str[4]); + fail_if(my_find(&table, ".", bad_hash(".")) != &str[2]); + fail_if(my_find(&table, "", bad_hash("")) != &str[1]); + fail_if(my_find(&table, "..", bad_hash("..")) != &str[3]); + fail_if(my_find(&table, "...", bad_hash("...")) != &str[0]); END_TEST @@ -87,11 +119,12 @@ START_TEST (test_htable_resize) ck_assert_int_eq(uc_htable_count(&table), 5); - fail_if(my_find(&table, "One") != &str[0]); - fail_if(my_find(&table, "Two") != &str[1]); - fail_if(my_find(&table, "Three") != &str[2]); - fail_if(my_find(&table, "Four") != &str[3]); - fail_if(my_find(&table, "Five") != &str[4]); + fail_if(my_find(&table, "One", hash_str("One")) != &str[0]); + fail_if(my_find(&table, "Two", hash_str("Two")) != &str[1]); + fail_if(my_find(&table, "Three",hash_str("Three")) != &str[2]); + fail_if(my_find(&table, "Four", hash_str("Four")) != &str[3]); + fail_if(my_find(&table, "Five", hash_str("Five")) != &str[4]); + END_TEST @@ -108,14 +141,38 @@ START_TEST (test_htable_fail_init_resize) ck_assert_int_ne(rc, 0); END_TEST + +START_TEST (test_htable_has_node) + struct MyString str1 = {"One", {0,NULL}}; + struct MyString str2 = {"Two", {0,NULL}}; + + struct UHTable table; + int rc; + + rc = uc_htable_init(&table, 3); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + ck_assert_int_eq(uc_htable_count(&table), 1); + + rc = uc_htable_has_node(&table, &str1.node); + ck_assert_int_ne(rc, 0); + + rc = uc_htable_has_node(&table, &str2.node); + ck_assert_int_eq(rc, 0); + +END_TEST + Suite *htable_suite(void) { Suite *s = suite_create("htable"); TCase *tc = tcase_create("htable tests"); tcase_add_test(tc, test_htable1); + tcase_add_test(tc, test_htable_hash_collision); tcase_add_test(tc, test_htable_resize); tcase_add_test(tc, test_htable_fail_init_resize); + tcase_add_test(tc, test_htable_has_node); suite_add_tcase(s, tc); From 0f0ae6dd1eeba5853fb92fb04b48aaa851a01bfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 17 Oct 2013 22:12:48 +0200 Subject: [PATCH 35/46] Add uc_htable_remove + test --- include/ucore/htable.h | 11 ++++++++++- src/htable.c | 16 +++++++++++++++ test/test_htable.c | 44 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/include/ucore/htable.h b/include/ucore/htable.h index 29d935e..5c73563 100644 --- a/include/ucore/htable.h +++ b/include/ucore/htable.h @@ -48,7 +48,7 @@ struct UHTable { int uc_htable_init(struct UHTable *table, size_t buckets); /** - * Frees resources of the hash table. + * Free resources of the hash table. * @param table hash table to free (does not free @table itself */ void uc_htable_destroy(struct UHTable *table); @@ -201,6 +201,15 @@ void uc_htable_clear(struct UHTable *table); */ void uc_htable_insert(struct UHTable *table, struct UHNode *node, size_t hash); +/** Remove a node. + * If the node pointer is not part of the table, + * no action is taken + * + * @param table table to remove from. + * @param node node to remove. + */ +void uc_htable_remove(struct UHTable *table, struct UHNode *node); + /** Resize the hash table. This changes the number of buckets, and re-inserts * all nodes. * diff --git a/src/htable.c b/src/htable.c index fbc2c4e..3fab8e6 100644 --- a/src/htable.c +++ b/src/htable.c @@ -113,6 +113,22 @@ void uc_htable_insert(struct UHTable *table, struct UHNode *node, size_t hash) table->cnt_elements++; } +void uc_htable_remove(struct UHTable *table, struct UHNode *node) +{ + struct UHNode **it = &table->buckets[uc_htable_bucket(table, node->hash)]; + + while (*it) { + + if (*it == node) { + *it = (*it)->next; + table->cnt_elements--; + break; + } + + it = &(*it)->next; + } +} + int uc_htable_resize(struct UHTable *table, size_t buckets) { struct UHTable tmp_table; diff --git a/test/test_htable.c b/test/test_htable.c index ad3814c..523a34e 100644 --- a/test/test_htable.c +++ b/test/test_htable.c @@ -163,6 +163,49 @@ START_TEST (test_htable_has_node) END_TEST +START_TEST (test_htable_remove) + struct MyString str1 = {"One", {0,NULL}}; + struct MyString str2 = {"One", {0,NULL}}; + struct MyString str3 = {"Other", {0,NULL}}; + + struct UHTable table; + int rc; + + rc = uc_htable_init(&table, 1); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + uc_htable_insert(&table, &str2.node, hash_str(str2.str)); + uc_htable_insert(&table, &str3.node, hash_str(str3.str)); + ck_assert_int_eq(uc_htable_count(&table), 3); + + uc_htable_remove(&table, &str2.node); + ck_assert_int_eq(uc_htable_count(&table), 2); + + //this one should still be here + fail_if(my_find(&table, "One", hash_str("One")) != &str1); + // + //thisone should be gone + rc = uc_htable_has_node(&table, &str2.node); + ck_assert_int_eq(rc, 0); + + + uc_htable_remove(&table, &str1.node); + ck_assert_int_eq(uc_htable_count(&table), 1); + + //now this one should be gone + rc = uc_htable_has_node(&table, &str1.node); + ck_assert_int_eq(rc, 0); + + //double removal should be ok + uc_htable_remove(&table, &str1.node); + ck_assert_int_eq(uc_htable_count(&table), 1); + + //this one should still be here + fail_if(my_find(&table, "Other", hash_str("Other")) != &str3); + +END_TEST + Suite *htable_suite(void) { Suite *s = suite_create("htable"); @@ -173,6 +216,7 @@ Suite *htable_suite(void) tcase_add_test(tc, test_htable_resize); tcase_add_test(tc, test_htable_fail_init_resize); tcase_add_test(tc, test_htable_has_node); + tcase_add_test(tc, test_htable_remove); suite_add_tcase(s, tc); From b0172a365e63c6afec8e96f478fae4a001c3e3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 17 Oct 2013 22:28:32 +0200 Subject: [PATCH 36/46] test uc_htable_clear. Add comments, fix uc_htable_next --- src/htable.c | 12 ++++++++++-- test/test_htable.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/htable.c b/src/htable.c index 3fab8e6..02fc876 100644 --- a/src/htable.c +++ b/src/htable.c @@ -39,6 +39,7 @@ struct UHNode *uc_htable_first_hash(const struct UHTable *table, size_t hash) return it; } +//given a node, find the next one in the chain with the given hash struct UHNode *uc_htable_next_hash_hlp(struct UHNode *next, size_t hash) { while (next && next->hash != hash) { @@ -48,11 +49,13 @@ struct UHNode *uc_htable_next_hash_hlp(struct UHNode *next, size_t hash) return next; } -struct UHNode *uc_htable_next_hlp(struct UHTable *table, size_t bucket) +//find a node, starting at bucket +struct UHNode *uc_htable_iter_hlp(struct UHTable *table, size_t bucket) { size_t i; struct UHNode *found = NULL; + //search through buckets until a node is found for (i = bucket; i < table->cnt_buckets; i++) { found = uc_htable_first_bucket(table, i); if (found != NULL) { @@ -68,7 +71,8 @@ struct UHNode *uc_htable_next(struct UHTable *table, struct UHNode *node) found = uc_htable_next_bucket(node); if (found == NULL) { - found = uc_htable_next_hlp(table, node->hash + 1); + size_t current_bucket = uc_htable_bucket(table, node->hash); + found = uc_htable_iter_hlp(table, current_bucket + 1); } return found; @@ -139,11 +143,14 @@ int uc_htable_resize(struct UHTable *table, size_t buckets) return EINVAL; } + //create new temp table rc = uc_htable_init(&tmp_table, buckets); if (rc != 0) { return rc; } + + //move all the nodes to the new temp table for (h = 0; h < table->cnt_buckets; h++) { struct UHNode *next; struct UHNode *node; @@ -156,6 +163,7 @@ int uc_htable_resize(struct UHTable *table, size_t buckets) } } + //replace the existing table with the resized temp table uc_htable_swap(&tmp_table, table); uc_htable_destroy(&tmp_table); diff --git a/test/test_htable.c b/test/test_htable.c index 523a34e..32e363b 100644 --- a/test/test_htable.c +++ b/test/test_htable.c @@ -64,6 +64,8 @@ START_TEST (test_htable1) fail_if(my_find(&table, "Four", hash_str("Four")) != &str[3]); fail_if(my_find(&table, "Five", hash_str("Five")) != &str[4]); + uc_htable_destroy(&table); + END_TEST START_TEST (test_htable_hash_collision) @@ -92,6 +94,8 @@ START_TEST (test_htable_hash_collision) fail_if(my_find(&table, "..", bad_hash("..")) != &str[3]); fail_if(my_find(&table, "...", bad_hash("...")) != &str[0]); + uc_htable_destroy(&table); + END_TEST START_TEST (test_htable_resize) @@ -125,6 +129,7 @@ START_TEST (test_htable_resize) fail_if(my_find(&table, "Four", hash_str("Four")) != &str[3]); fail_if(my_find(&table, "Five", hash_str("Five")) != &str[4]); + uc_htable_destroy(&table); END_TEST @@ -140,6 +145,8 @@ START_TEST (test_htable_fail_init_resize) rc = uc_htable_resize(&table, 0); ck_assert_int_ne(rc, 0); + uc_htable_destroy(&table); + END_TEST START_TEST (test_htable_has_node) @@ -161,6 +168,8 @@ START_TEST (test_htable_has_node) rc = uc_htable_has_node(&table, &str2.node); ck_assert_int_eq(rc, 0); + uc_htable_destroy(&table); + END_TEST START_TEST (test_htable_remove) @@ -204,6 +213,30 @@ START_TEST (test_htable_remove) //this one should still be here fail_if(my_find(&table, "Other", hash_str("Other")) != &str3); + uc_htable_destroy(&table); + +END_TEST + +START_TEST (test_htable_clear) + struct MyString str1 = {"One", {0,NULL}}; + + struct UHTable table; + int rc; + + rc = uc_htable_init(&table, 100); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + ck_assert_int_eq(uc_htable_count(&table), 1); + + fail_if(my_find(&table, "One", hash_str("One")) != &str1); + + uc_htable_clear(&table); + ck_assert_int_eq(uc_htable_count(&table), 0); + fail_if(my_find(&table, "One", hash_str("One")) != NULL); + + uc_htable_destroy(&table); + END_TEST Suite *htable_suite(void) @@ -217,6 +250,7 @@ Suite *htable_suite(void) tcase_add_test(tc, test_htable_fail_init_resize); tcase_add_test(tc, test_htable_has_node); tcase_add_test(tc, test_htable_remove); + tcase_add_test(tc, test_htable_clear); suite_add_tcase(s, tc); From 7ff33c113b9fdb9eebd28533d973b632cf95dacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 17 Oct 2013 22:38:00 +0200 Subject: [PATCH 37/46] Test UC_HTABLE_FOR_EACH --- include/ucore/htable.h | 7 ++++--- test/test_htable.c | 43 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/include/ucore/htable.h b/include/ucore/htable.h index 5c73563..f202662 100644 --- a/include/ucore/htable.h +++ b/include/ucore/htable.h @@ -99,6 +99,7 @@ struct UHNode *uc_htable_first_hash(const struct UHTable *table, size_t hash); //internal helper struct UHNode *uc_htable_next_hash_hlp(struct UHNode *next, size_t hash); + /** * Get the next node with the same hash * @param node previous node @@ -111,7 +112,7 @@ static inline struct UHNode *uc_htable_next_hash(const struct UHNode *node) //internal helper -struct UHNode *uc_htable_next_hlp(struct UHTable *table, size_t bucket); +struct UHNode *uc_htable_iter_hlp(struct UHTable *table, size_t bucket); /** Get the first node in the hash table * @@ -120,7 +121,7 @@ struct UHNode *uc_htable_next_hlp(struct UHTable *table, size_t bucket); */ static inline struct UHNode *uc_htable_first(struct UHTable *table) { - return uc_htable_next_hlp(table, 0); + return uc_htable_iter_hlp(table, 0); } /** Get the next node in the hash table @@ -149,7 +150,7 @@ void uc_htable_swap(struct UHTable *a, struct UHTable *b); (node_iter) != NULL;\ (node_iter) = uc_htable_next_hash((node_iter))) -#define UC_HTABLE_FOREACH(table, node_iter, hash)\ +#define UC_HTABLE_FOREACH(table, node_iter)\ for ((node_iter) = uc_htable_first((table));\ (node_iter) != NULL;\ (node_iter) = uc_htable_next((table), (node_iter))) diff --git a/test/test_htable.c b/test/test_htable.c index 32e363b..925d273 100644 --- a/test/test_htable.c +++ b/test/test_htable.c @@ -239,6 +239,48 @@ START_TEST (test_htable_clear) END_TEST +START_TEST (test_htable_foreach) + struct MyString str1 = {"One", {0,NULL}}; + struct MyString str2 = {"One", {0,NULL}}; + struct MyString str3 = {"Other",{0,NULL}}; + + struct UHTable table; + struct UHNode *it; + int rc; + int found1 = 0; + int found2 = 0; + int found3 = 0; + + rc = uc_htable_init(&table, 1000); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + uc_htable_insert(&table, &str2.node, hash_str(str2.str)); + uc_htable_insert(&table, &str3.node, hash_str(str3.str)); + ck_assert_int_eq(uc_htable_count(&table), 3); + + UC_HTABLE_FOREACH(&table, it) { + struct MyString *my_str = UC_CONTAINER_OF(it, struct MyString, node); + if (my_str == &str1) { + found1++; + } + if (my_str == &str2) { + found2++; + } + if (my_str == &str3) { + found3++; + } + } + + ck_assert_int_eq(found1, 1); + ck_assert_int_eq(found2, 1); + ck_assert_int_eq(found3, 1); + + + uc_htable_destroy(&table); + +END_TEST + Suite *htable_suite(void) { Suite *s = suite_create("htable"); @@ -251,6 +293,7 @@ Suite *htable_suite(void) tcase_add_test(tc, test_htable_has_node); tcase_add_test(tc, test_htable_remove); tcase_add_test(tc, test_htable_clear); + tcase_add_test(tc, test_htable_foreach); suite_add_tcase(s, tc); From 2cd5d9d7a9afef9583982daf6e0d747f8afc14c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Fri, 18 Oct 2013 19:43:05 +0200 Subject: [PATCH 38/46] Comment the FOREACH macros --- include/ucore/htable.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/ucore/htable.h b/include/ucore/htable.h index f202662..3ae18f6 100644 --- a/include/ucore/htable.h +++ b/include/ucore/htable.h @@ -140,16 +140,41 @@ struct UHNode *uc_htable_next(struct UHTable *table, struct UHNode *node); */ void uc_htable_swap(struct UHTable *a, struct UHTable *b); +/** + * Iterate over all nodes in a bucket. + * The table must not be changed while iterating. + * + * @param table the struct UHTable* + * @param node_iter struct UHNode* for the current node being iterated + * @param hash size_t hash for the bucket to iterate + */ #define UC_HTABLE_FOREACH_BUCKET(table, node_iter, hash)\ for ((node_iter) = uc_htable_first_bucket((table), (hash));\ (node_iter) != NULL;\ (node_iter) = uc_htable_next_bucket((node_iter))) +/** + * Iterate over all nodes that has the given hash. + * The table must not be changed while iterating. + * Normally the user would check in a user specific way + * if the node_iter is equal to the node one wants to find. + * + * @param table the struct UHTable* + * @param node_iter struct UHNode* for the current node being iterated + * @param hash size_t hash for the node(s) to iterate over + */ #define UC_HTABLE_FOREACH_HASH(table, node_iter, hash)\ for ((node_iter) = uc_htable_first_hash((table), (hash));\ (node_iter) != NULL;\ (node_iter) = uc_htable_next_hash((node_iter))) + /** + * Iterate over all nodes in a table + * The table must not be changed while iterating. + * + * @param table the struct UHTable* + * @param node_iter struct UHNode* for the current node being iterated + */ #define UC_HTABLE_FOREACH(table, node_iter)\ for ((node_iter) = uc_htable_first((table));\ (node_iter) != NULL;\ From 49503804ddcd71552fee5b8d5bd506c6beb632ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 23 Oct 2013 22:07:58 +0200 Subject: [PATCH 39/46] Allow user to always (not just in debug mode) compile in UC_DEBUGXXX log calls by defining UC_DEBUG_ALWAYS --- include/ucore/logging.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/ucore/logging.h b/include/ucore/logging.h index b7bc443..bbaebe0 100644 --- a/include/ucore/logging.h +++ b/include/ucore/logging.h @@ -272,7 +272,9 @@ void uc_logf(int log_level, int module, int raw, const char *location, const char *fmt, ...) __attribute__((format(printf, 5, 6))); -#ifdef DEBUG +// only compile in UC_DEBUG log calls in debug mode, but allow the user +// to override that by defining UC_DEBUG_ALWAYS +#if defined(DEBUG) || defined(UC_DEBUG_ALWAYS) # define UC_DEBUGF(mod, fmt, ...)\ uc_logf(UC_LL_DEBUG, mod,0 , UC_SRC_LOCATION, fmt, ## __VA_ARGS__) # define UC_DEBUGFR(mod, fmt, ...)\ From 03124601d8fa3ef4f8a6aee32e3c83ce311b0341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 24 Oct 2013 21:18:27 +0200 Subject: [PATCH 40/46] Test failing to find a hash node --- test/test_htable.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_htable.c b/test/test_htable.c index 925d273..dc3e70b 100644 --- a/test/test_htable.c +++ b/test/test_htable.c @@ -63,6 +63,7 @@ START_TEST (test_htable1) fail_if(my_find(&table, "Three",hash_str("Three")) != &str[2]); fail_if(my_find(&table, "Four", hash_str("Four")) != &str[3]); fail_if(my_find(&table, "Five", hash_str("Five")) != &str[4]); + fail_if(my_find(&table, "NotHere", hash_str("NotHere")) != NULL); uc_htable_destroy(&table); From 821399a61b38b1d31ce94d00ed27986d13832b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 24 Oct 2013 21:27:31 +0200 Subject: [PATCH 41/46] Add iterator API --- include/ucore/htable.h | 37 +++++++++++++++++++++++++++++++++++ src/htable.c | 44 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/include/ucore/htable.h b/include/ucore/htable.h index 3ae18f6..3fea240 100644 --- a/include/ucore/htable.h +++ b/include/ucore/htable.h @@ -39,6 +39,15 @@ struct UHTable { struct UHNode **buckets; }; +/** Iterator for the iterator API */ +struct UHIter { + //internal fields + struct UHNode **it; + struct UHNode **next; + size_t cur_bucket; + size_t cur_hash; +}; + /** * Initialize a hash table. * @param table hash table to initialize @@ -246,4 +255,32 @@ void uc_htable_remove(struct UHTable *table, struct UHNode *node); int uc_htable_resize(struct UHTable *table, size_t buckets); +/** Get the first node with the given hash. + * + * @param table table + * @param iter opaque iterator + * @param hash hash to find + * @return the first node found or NULL + */ +struct UHNode *uc_htable_iter_first_hash(struct UHTable *table, + struct UHIter *iter, size_t hash); +/** Get the next node with the given hash, the iterator + * must previously have been passed to uc_htable_iter_first_hash + * which returned a non-NULL value + * + * @param iter opaque iterator + * @return the next node found or NULL + */ +struct UHNode *uc_htable_iter_next_hash(struct UHIter *iter); + +/** Remove the node pointed to by the current iterator, + * uc_htable_iter_first_hash or uc_htable_iter_next_hash + * must previously have returned non-NULL. The node + * returned by the previous call is the one removed. + * It is safe to continue iterating after this + * + * @param iter indicated node to remove + */ +void uc_htable_iter_remove(struct UHIter *iter); + #endif diff --git a/src/htable.c b/src/htable.c index 02fc876..839d50f 100644 --- a/src/htable.c +++ b/src/htable.c @@ -171,3 +171,47 @@ int uc_htable_resize(struct UHTable *table, size_t buckets) } +struct UHNode *uc_htable_iter_first_hash(struct UHTable *table, + struct UHIter *iter, size_t hash) +{ + size_t bucket = uc_htable_bucket(table, hash); + struct UHNode **it= &table->buckets[bucket]; + + while (*it) { + if ((*it)->hash == hash) { + iter->it = it; + iter->cur_bucket = bucket; + iter->cur_hash = (*it)->hash; + iter->next = &(*it)->next; + return *it; + } + + it = &(*it)->next; + } + + return NULL; +} + +struct UHNode *uc_htable_iter_next_hash(struct UHIter *iter) +{ + struct UHNode **it = iter->next; + + while (*it) { + if ((*it)->hash == iter->cur_hash) { + iter->it = it; + iter->next = &(*it)->next; + return *it; + } + it = &(*it)->next; + } + + return NULL; +} + +void uc_htable_iter_remove(struct UHIter *iter) +{ + if (*iter->it) { + *iter->it = *iter->next; + iter->it = NULL; + } +} From 45ecb3c1e068ae793f9ee52d645b8087eaddda59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 24 Oct 2013 21:28:35 +0200 Subject: [PATCH 42/46] Revert "Add iterator API". Will try to find a better API This reverts commit 821399a61b38b1d31ce94d00ed27986d13832b06. --- include/ucore/htable.h | 37 ----------------------------------- src/htable.c | 44 ------------------------------------------ 2 files changed, 81 deletions(-) diff --git a/include/ucore/htable.h b/include/ucore/htable.h index 3fea240..3ae18f6 100644 --- a/include/ucore/htable.h +++ b/include/ucore/htable.h @@ -39,15 +39,6 @@ struct UHTable { struct UHNode **buckets; }; -/** Iterator for the iterator API */ -struct UHIter { - //internal fields - struct UHNode **it; - struct UHNode **next; - size_t cur_bucket; - size_t cur_hash; -}; - /** * Initialize a hash table. * @param table hash table to initialize @@ -255,32 +246,4 @@ void uc_htable_remove(struct UHTable *table, struct UHNode *node); int uc_htable_resize(struct UHTable *table, size_t buckets); -/** Get the first node with the given hash. - * - * @param table table - * @param iter opaque iterator - * @param hash hash to find - * @return the first node found or NULL - */ -struct UHNode *uc_htable_iter_first_hash(struct UHTable *table, - struct UHIter *iter, size_t hash); -/** Get the next node with the given hash, the iterator - * must previously have been passed to uc_htable_iter_first_hash - * which returned a non-NULL value - * - * @param iter opaque iterator - * @return the next node found or NULL - */ -struct UHNode *uc_htable_iter_next_hash(struct UHIter *iter); - -/** Remove the node pointed to by the current iterator, - * uc_htable_iter_first_hash or uc_htable_iter_next_hash - * must previously have returned non-NULL. The node - * returned by the previous call is the one removed. - * It is safe to continue iterating after this - * - * @param iter indicated node to remove - */ -void uc_htable_iter_remove(struct UHIter *iter); - #endif diff --git a/src/htable.c b/src/htable.c index 839d50f..02fc876 100644 --- a/src/htable.c +++ b/src/htable.c @@ -171,47 +171,3 @@ int uc_htable_resize(struct UHTable *table, size_t buckets) } -struct UHNode *uc_htable_iter_first_hash(struct UHTable *table, - struct UHIter *iter, size_t hash) -{ - size_t bucket = uc_htable_bucket(table, hash); - struct UHNode **it= &table->buckets[bucket]; - - while (*it) { - if ((*it)->hash == hash) { - iter->it = it; - iter->cur_bucket = bucket; - iter->cur_hash = (*it)->hash; - iter->next = &(*it)->next; - return *it; - } - - it = &(*it)->next; - } - - return NULL; -} - -struct UHNode *uc_htable_iter_next_hash(struct UHIter *iter) -{ - struct UHNode **it = iter->next; - - while (*it) { - if ((*it)->hash == iter->cur_hash) { - iter->it = it; - iter->next = &(*it)->next; - return *it; - } - it = &(*it)->next; - } - - return NULL; -} - -void uc_htable_iter_remove(struct UHIter *iter) -{ - if (*iter->it) { - *iter->it = *iter->next; - iter->it = NULL; - } -} From 0acb4789b07e937dbac619c99407cc61c81388b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 24 Oct 2013 22:13:16 +0200 Subject: [PATCH 43/46] Add FOREACH_SAFE macros + tests --- include/ucore/htable.h | 32 +++++++++++++++++- test/test_htable.c | 73 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/include/ucore/htable.h b/include/ucore/htable.h index 3ae18f6..e5dc9d1 100644 --- a/include/ucore/htable.h +++ b/include/ucore/htable.h @@ -168,7 +168,22 @@ void uc_htable_swap(struct UHTable *a, struct UHTable *b); (node_iter) != NULL;\ (node_iter) = uc_htable_next_hash((node_iter))) - /** +/** + * "Safe" variant of UC_HTABLE_FOREACH_HASH, the iterated node + * (or any previously iterated nodes) can be freed while iterating + * + * @param table the struct UHTable* + * @param node_iter struct UHNode* for the current node being iterated + * @param next_iter struct UHNode* for the next node, required by the + * iteration, next_iter must not be altered while iterating. + * @param hash size_t hash for the node(s) to iterate over + */ +#define UC_HTABLE_FOREACH_HASH_SAFE(table, node_iter, next_iter, hash)\ + for ((node_iter) = uc_htable_first_hash((table), (hash));\ + (node_iter) != NULL && (((next_iter) = (node_iter->next)), 1);\ + (node_iter) = (next)) + +/** * Iterate over all nodes in a table * The table must not be changed while iterating. * @@ -180,6 +195,21 @@ void uc_htable_swap(struct UHTable *a, struct UHTable *b); (node_iter) != NULL;\ (node_iter) = uc_htable_next((table), (node_iter))) +/** + * "Safe" variant of UC_HTABLE_FOREACH, the iterated node + * (or any previously iterated nodes) can be freed while iterating + * + * @param table the struct UHTable* + * @param node_iter struct UHNode* for the current node being iterated + * @param next_iter struct UHNode* for the next node, required by the + * iteration, next_iter must not be altered while iterating. +*/ +#define UC_HTABLE_FOREACH_SAFE(table, node_iter, next_iter)\ + for ((node_iter) = uc_htable_first((table));\ + (node_iter) != NULL &&\ + ((next_iter) = uc_htable_next((table), (node_iter)), 1);\ + (node_iter) = (next)) + /** * Check if the given node pointer exists in the hash table diff --git a/test/test_htable.c b/test/test_htable.c index dc3e70b..29e8bca 100644 --- a/test/test_htable.c +++ b/test/test_htable.c @@ -218,6 +218,43 @@ START_TEST (test_htable_remove) END_TEST +START_TEST (test_htable_remove_foreach) + struct MyString str1 = {"One", {0,NULL}}; + struct MyString str2 = {"One", {0,NULL}}; + struct MyString str3 = {"Other", {0,NULL}}; + + struct UHTable table; + struct UHNode *it; + struct UHNode *next; + int rc; + int removed = 0; + + rc = uc_htable_init(&table, 1); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + uc_htable_insert(&table, &str2.node, hash_str(str2.str)); + uc_htable_insert(&table, &str3.node, hash_str(str3.str)); + ck_assert_int_eq(uc_htable_count(&table), 3); + + fail_if(my_find(&table, "One", hash_str("One")) == NULL); + + UC_HTABLE_FOREACH_HASH_SAFE(&table, it, next, hash_str("One")) { + uc_htable_remove(&table, it); + removed++; + + } + + ck_assert_int_eq(removed, 2); + + fail_if(my_find(&table, "One", hash_str("One")) != NULL); + fail_if(my_find(&table, "Other", hash_str("Other")) != &str3); + ck_assert_int_eq(uc_htable_count(&table), 1); + + uc_htable_destroy(&table); + +END_TEST + START_TEST (test_htable_clear) struct MyString str1 = {"One", {0,NULL}}; @@ -282,6 +319,40 @@ START_TEST (test_htable_foreach) END_TEST +START_TEST (test_htable_foreach_safe) + struct MyString str1 = {"One", {0,NULL}}; + struct MyString str2 = {"One", {0,NULL}}; + struct MyString str3 = {"Other",{0,NULL}}; + + struct UHTable table; + struct UHNode *it; + struct UHNode *next; + int rc; + + rc = uc_htable_init(&table, 1000); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + uc_htable_insert(&table, &str2.node, hash_str(str2.str)); + uc_htable_insert(&table, &str3.node, hash_str(str3.str)); + + fail_if(my_find(&table, "Other", hash_str("Other")) != &str3); + + ck_assert_int_eq(uc_htable_count(&table), 3); + + UC_HTABLE_FOREACH_SAFE(&table, it, next) { + uc_htable_remove(&table, it); + } + + fail_if(my_find(&table, "One", hash_str("One")) != NULL); + fail_if(my_find(&table, "Other", hash_str("Other")) != NULL); + + ck_assert_int_eq(uc_htable_count(&table), 0); + + uc_htable_destroy(&table); + +END_TEST + Suite *htable_suite(void) { Suite *s = suite_create("htable"); @@ -293,8 +364,10 @@ Suite *htable_suite(void) tcase_add_test(tc, test_htable_fail_init_resize); tcase_add_test(tc, test_htable_has_node); tcase_add_test(tc, test_htable_remove); + tcase_add_test(tc, test_htable_remove_foreach); tcase_add_test(tc, test_htable_clear); tcase_add_test(tc, test_htable_foreach); + tcase_add_test(tc, test_htable_foreach_safe); suite_add_tcase(s, tc); From c1609c2a57e682f2e1e81ce5f4a418a77cb8bdb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 24 Oct 2013 22:14:41 +0200 Subject: [PATCH 44/46] Poison the removed node in tests, to help ensure nothing fishy is going on --- test/test_htable.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_htable.c b/test/test_htable.c index 29e8bca..61a6889 100644 --- a/test/test_htable.c +++ b/test/test_htable.c @@ -241,6 +241,7 @@ START_TEST (test_htable_remove_foreach) UC_HTABLE_FOREACH_HASH_SAFE(&table, it, next, hash_str("One")) { uc_htable_remove(&table, it); + memset(it, 0xff, sizeof *it); removed++; } @@ -342,6 +343,7 @@ START_TEST (test_htable_foreach_safe) UC_HTABLE_FOREACH_SAFE(&table, it, next) { uc_htable_remove(&table, it); + memset(it, 0xff, sizeof *it); } fail_if(my_find(&table, "One", hash_str("One")) != NULL); From 1b563e797bbf89867588d448c3ff95fade23cead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 24 Oct 2013 22:28:17 +0200 Subject: [PATCH 45/46] Have check get the fork status from env. variable (set CK_FORK_GETENV flag) --- test/test_runner.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_runner.c b/test/test_runner.c index 3bd0a05..c485f6f 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -62,6 +62,8 @@ main (int argc, char *argv[]) if (xml_output) srunner_set_xml(sr, "result.xml"); + srunner_set_fork_status(sr, CK_FORK_GETENV); + for(i = 0; i < sizeof suites/sizeof suites[0]; i++) { srunner_add_suite(sr, suites[i]()); From 8a44b591de1d85c3275a5b9f771d2839c6c0b7bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Sat, 26 Oct 2013 23:00:32 +0200 Subject: [PATCH 46/46] Docs for mbuf.flags --- include/ucore/mbuf.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/ucore/mbuf.h b/include/ucore/mbuf.h index 256354a..1b92b81 100644 --- a/include/ucore/mbuf.h +++ b/include/ucore/mbuf.h @@ -30,6 +30,7 @@ struct MBuf { /**Arbitary pointer user can associate with the mbuf*/ void *cookie; + /*UC_MBUF_FLAGS. The upper 16 bits can be used by the application */ uint32_t flags; /** Total no of bytes from bufstart*/ uint32_t allocated;