diff --git a/src/atomicvar.h b/src/atomicvar.h index 4aa8fa17..84a5bbc5 100644 --- a/src/atomicvar.h +++ b/src/atomicvar.h @@ -3,18 +3,29 @@ * * The exported interaface is composed of three macros: * - * atomicIncr(var,count,mutex) -- Increment the atomic counter - * atomicDecr(var,count,mutex) -- Decrement the atomic counter - * atomicGet(var,dstvar,mutex) -- Fetch the atomic counter value + * atomicIncr(var,count) -- Increment the atomic counter + * atomicGetIncr(var,oldvalue_var,count) -- Get and increment the atomic counter + * atomicDecr(var,count) -- Decrement the atomic counter + * atomicGet(var,dstvar) -- Fetch the atomic counter value + * atomicSet(var,value) -- Set the atomic counter value + * + * The variable 'var' should also have a declared mutex with the same + * name and the "_mutex" postfix, for instance: + * + * long myvar; + * pthread_mutex_t myvar_mutex; + * atomicSet(myvar,12345); * * If atomic primitives are availble (tested in config.h) the mutex * is not used. * - * Never use return value from the macros. To update and get use instead: + * Never use return value from the macros, instead use the AtomicGetIncr() + * if you need to get the current value and increment it atomically, like + * in the followign example: * - * atomicIncr(mycounter,...); - * atomicGet(mycounter,newvalue); - * doSomethingWith(newvalue); + * long oldvalue; + * atomicGetIncr(myvar,oldvalue,1); + * doSomethingWith(oldvalue); * * ---------------------------------------------------------------------------- * @@ -51,44 +62,72 @@ #ifndef __ATOMIC_VAR_H #define __ATOMIC_VAR_H -#if defined(__ATOMIC_RELAXED) && (!defined(__clang__) || !defined(__APPLE__) || __apple_build_version__ > 4210057) +/* To test Redis with Helgrind (a Valgrind tool) it is useful to define + * the following macro, so that __sync macros are used: those can be detected + * by Helgrind (even if they are less efficient) so that no false positive + * is reported. */ +// #define __ATOMIC_VAR_FORCE_SYNC_MACROS + +#if !defined(__ATOMIC_VAR_FORCE_SYNC_MACROS) && defined(__ATOMIC_RELAXED) && !defined(__sun) && (!defined(__clang__) || !defined(__APPLE__) || __apple_build_version__ > 4210057) /* Implementation using __atomic macros. */ -#define atomicIncr(var,count,mutex) __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED) -#define atomicDecr(var,count,mutex) __atomic_sub_fetch(&var,(count),__ATOMIC_RELAXED) -#define atomicGet(var,dstvar,mutex) do { \ +#define atomicIncr(var,count) __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED) +#define atomicGetIncr(var,oldvalue_var,count) do { \ + oldvalue_var = __atomic_fetch_add(&var,(count),__ATOMIC_RELAXED); \ +} while(0) +#define atomicDecr(var,count) __atomic_sub_fetch(&var,(count),__ATOMIC_RELAXED) +#define atomicGet(var,dstvar) do { \ dstvar = __atomic_load_n(&var,__ATOMIC_RELAXED); \ } while(0) +#define atomicSet(var,value) __atomic_store_n(&var,value,__ATOMIC_RELAXED) +#define REDIS_ATOMIC_API "atomic-builtin" #elif defined(HAVE_ATOMIC) /* Implementation using __sync macros. */ -#define atomicIncr(var,count,mutex) __sync_add_and_fetch(&var,(count)) -#define atomicDecr(var,count,mutex) __sync_sub_and_fetch(&var,(count)) -#define atomicGet(var,dstvar,mutex) do { \ +#define atomicIncr(var,count) __sync_add_and_fetch(&var,(count)) +#define atomicGetIncr(var,oldvalue_var,count) do { \ + oldvalue_var = __sync_fetch_and_add(&var,(count)); \ +} while(0) +#define atomicDecr(var,count) __sync_sub_and_fetch(&var,(count)) +#define atomicGet(var,dstvar) do { \ dstvar = __sync_sub_and_fetch(&var,0); \ } while(0) +#define atomicSet(var,value) do { \ + while(!__sync_bool_compare_and_swap(&var,var,value)); \ +} while(0) +#define REDIS_ATOMIC_API "sync-builtin" #else /* Implementation using pthread mutex. */ -#define atomicIncr(var,count,mutex) do { \ - pthread_mutex_lock(&mutex); \ +#define atomicIncr(var,count) do { \ + pthread_mutex_lock(&var ## _mutex); \ var += (count); \ - pthread_mutex_unlock(&mutex); \ + pthread_mutex_unlock(&var ## _mutex); \ } while(0) - -#define atomicDecr(var,count,mutex) do { \ - pthread_mutex_lock(&mutex); \ +#define atomicGetIncr(var,oldvalue_var,count) do { \ + pthread_mutex_lock(&var ## _mutex); \ + oldvalue_var = var; \ + var += (count); \ + pthread_mutex_unlock(&var ## _mutex); \ +} while(0) +#define atomicDecr(var,count) do { \ + pthread_mutex_lock(&var ## _mutex); \ var -= (count); \ - pthread_mutex_unlock(&mutex); \ + pthread_mutex_unlock(&var ## _mutex); \ } while(0) - -#define atomicGet(var,dstvar,mutex) do { \ - pthread_mutex_lock(&mutex); \ +#define atomicGet(var,dstvar) do { \ + pthread_mutex_lock(&var ## _mutex); \ dstvar = var; \ - pthread_mutex_unlock(&mutex); \ + pthread_mutex_unlock(&var ## _mutex); \ } while(0) -#endif +#define atomicSet(var,value) do { \ + pthread_mutex_lock(&var ## _mutex); \ + var = value; \ + pthread_mutex_unlock(&var ## _mutex); \ +} while(0) +#define REDIS_ATOMIC_API "pthread-mutex" +#endif #endif /* __ATOMIC_VAR_H */ diff --git a/src/http_parser.c b/src/http_parser.c index 895bf0c7..8d851245 100644 --- a/src/http_parser.c +++ b/src/http_parser.c @@ -1,7 +1,4 @@ -/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev - * - * Additional changes are licensed under the same terms as NGINX and - * copyright Joyent, Inc. and other Node contributors. All rights reserved. +/* Copyright Joyent, Inc. and other Node contributors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -286,10 +283,10 @@ enum state , s_res_HT , s_res_HTT , s_res_HTTP - , s_res_first_http_major , s_res_http_major - , s_res_first_http_minor + , s_res_http_dot , s_res_http_minor + , s_res_http_end , s_res_first_status_code , s_res_status_code , s_res_status_start @@ -316,10 +313,10 @@ enum state , s_req_http_HT , s_req_http_HTT , s_req_http_HTTP - , s_req_first_http_major , s_req_http_major - , s_req_first_http_minor + , s_req_http_dot , s_req_http_minor + , s_req_http_end , s_req_line_almost_done , s_header_field_start @@ -795,75 +792,48 @@ size_t http_parser_execute (http_parser *parser, case s_res_HTTP: STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_res_first_http_major); + UPDATE_STATE(s_res_http_major); break; - case s_res_first_http_major: - if (UNLIKELY(ch < '0' || ch > '9')) { + case s_res_http_major: + if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major = ch - '0'; - UPDATE_STATE(s_res_http_major); + UPDATE_STATE(s_res_http_dot); break; - /* major HTTP version or dot */ - case s_res_http_major: + case s_res_http_dot: { - if (ch == '.') { - UPDATE_STATE(s_res_first_http_minor); - break; - } - - if (!IS_NUM(ch)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major *= 10; - parser->http_major += ch - '0'; - - if (UNLIKELY(parser->http_major > 999)) { + if (UNLIKELY(ch != '.')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } + UPDATE_STATE(s_res_http_minor); break; } - /* first digit of minor HTTP version */ - case s_res_first_http_minor: + case s_res_http_minor: if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; - UPDATE_STATE(s_res_http_minor); + UPDATE_STATE(s_res_http_end); break; - /* minor HTTP version or end of request line */ - case s_res_http_minor: + case s_res_http_end: { - if (ch == ' ') { - UPDATE_STATE(s_res_first_status_code); - break; - } - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor *= 10; - parser->http_minor += ch - '0'; - - if (UNLIKELY(parser->http_minor > 999)) { + if (UNLIKELY(ch != ' ')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } + UPDATE_STATE(s_res_first_status_code); break; } @@ -890,10 +860,9 @@ size_t http_parser_execute (http_parser *parser, UPDATE_STATE(s_res_status_start); break; case CR: - UPDATE_STATE(s_res_line_almost_done); - break; case LF: - UPDATE_STATE(s_header_field_start); + UPDATE_STATE(s_res_status_start); + REEXECUTE(); break; default: SET_ERRNO(HPE_INVALID_STATUS); @@ -915,19 +884,13 @@ size_t http_parser_execute (http_parser *parser, case s_res_status_start: { - if (ch == CR) { - UPDATE_STATE(s_res_line_almost_done); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_field_start); - break; - } - MARK(status); UPDATE_STATE(s_res_status); parser->index = 0; + + if (ch == CR || ch == LF) + REEXECUTE(); + break; } @@ -1007,7 +970,7 @@ size_t http_parser_execute (http_parser *parser, UPDATE_STATE(s_req_spaces_before_url); } else if (ch == matcher[parser->index]) { ; /* nada */ - } else if (IS_ALPHA(ch)) { + } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') { switch (parser->method << 16 | parser->index << 8 | ch) { #define XX(meth, pos, ch, new_meth) \ @@ -1016,31 +979,27 @@ size_t http_parser_execute (http_parser *parser, XX(POST, 1, 'U', PUT) XX(POST, 1, 'A', PATCH) + XX(POST, 1, 'R', PROPFIND) + XX(PUT, 2, 'R', PURGE) XX(CONNECT, 1, 'H', CHECKOUT) XX(CONNECT, 2, 'P', COPY) XX(MKCOL, 1, 'O', MOVE) XX(MKCOL, 1, 'E', MERGE) + XX(MKCOL, 1, '-', MSEARCH) XX(MKCOL, 2, 'A', MKACTIVITY) XX(MKCOL, 3, 'A', MKCALENDAR) XX(SUBSCRIBE, 1, 'E', SEARCH) XX(REPORT, 2, 'B', REBIND) - XX(POST, 1, 'R', PROPFIND) XX(PROPFIND, 4, 'P', PROPPATCH) - XX(PUT, 2, 'R', PURGE) XX(LOCK, 1, 'I', LINK) XX(UNLOCK, 2, 'S', UNSUBSCRIBE) XX(UNLOCK, 2, 'B', UNBIND) XX(UNLOCK, 3, 'I', UNLINK) #undef XX - default: SET_ERRNO(HPE_INVALID_METHOD); goto error; } - } else if (ch == '-' && - parser->index == 1 && - parser->method == HTTP_MKCOL) { - parser->method = HTTP_MSEARCH; } else { SET_ERRNO(HPE_INVALID_METHOD); goto error; @@ -1153,57 +1112,41 @@ size_t http_parser_execute (http_parser *parser, case s_req_http_HTTP: STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_req_first_http_major); - break; - - /* first digit of major HTTP version */ - case s_req_first_http_major: - if (UNLIKELY(ch < '1' || ch > '9')) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major = ch - '0'; UPDATE_STATE(s_req_http_major); break; - /* major HTTP version or dot */ case s_req_http_major: - { - if (ch == '.') { - UPDATE_STATE(s_req_first_http_minor); - break; - } - if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } - parser->http_major *= 10; - parser->http_major += ch - '0'; + parser->http_major = ch - '0'; + UPDATE_STATE(s_req_http_dot); + break; - if (UNLIKELY(parser->http_major > 999)) { + case s_req_http_dot: + { + if (UNLIKELY(ch != '.')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } + UPDATE_STATE(s_req_http_minor); break; } - /* first digit of minor HTTP version */ - case s_req_first_http_minor: + case s_req_http_minor: if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; - UPDATE_STATE(s_req_http_minor); + UPDATE_STATE(s_req_http_end); break; - /* minor HTTP version or end of request line */ - case s_req_http_minor: + case s_req_http_end: { if (ch == CR) { UPDATE_STATE(s_req_line_almost_done); @@ -1215,21 +1158,8 @@ size_t http_parser_execute (http_parser *parser, break; } - /* XXX allow spaces after digit? */ - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor *= 10; - parser->http_minor += ch - '0'; - - if (UNLIKELY(parser->http_minor > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - + SET_ERRNO(HPE_INVALID_VERSION); + goto error; break; } @@ -1794,10 +1724,17 @@ size_t http_parser_execute (http_parser *parser, UPDATE_STATE(s_headers_done); /* Set this here so that on_headers_complete() callbacks can see it */ - parser->upgrade = - ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == - (F_UPGRADE | F_CONNECTION_UPGRADE) || - parser->method == HTTP_CONNECT); + if ((parser->flags & F_UPGRADE) && + (parser->flags & F_CONNECTION_UPGRADE)) { + /* For responses, "Upgrade: foo" and "Connection: upgrade" are + * mandatory only when it is a 101 Switching Protocols response, + * otherwise it is purely informational, to announce support. + */ + parser->upgrade = + (parser->type == HTTP_REQUEST || parser->status_code == 101); + } else { + parser->upgrade = (parser->method == HTTP_CONNECT); + } /* Here we call the headers_complete callback. This is somewhat * different than other callbacks because if the user returns 1, we @@ -1816,6 +1753,7 @@ size_t http_parser_execute (http_parser *parser, case 2: parser->upgrade = 1; + /* FALLTHROUGH */ case 1: parser->flags |= F_SKIPBODY; break; @@ -2375,7 +2313,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, case s_req_server_with_at: found_at = 1; - /* FALLTROUGH */ + /* FALLTHROUGH */ case s_req_server: uf = UF_HOST; break; diff --git a/src/http_parser.h b/src/http_parser.h index 45c72a07..fd2a5641 100644 --- a/src/http_parser.h +++ b/src/http_parser.h @@ -29,11 +29,10 @@ extern "C" { #define HTTP_PARSER_VERSION_MINOR 7 #define HTTP_PARSER_VERSION_PATCH 1 -#include +#include #if defined(_WIN32) && !defined(__MINGW32__) && \ (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) #include -#include typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; diff --git a/src/zmalloc.c b/src/zmalloc.c index f71ce2c9..094dd80f 100644 --- a/src/zmalloc.c +++ b/src/zmalloc.c @@ -73,25 +73,16 @@ void zlibc_free(void *ptr) { #define update_zmalloc_stat_alloc(__n) do { \ size_t _n = (__n); \ if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ - if (zmalloc_thread_safe) { \ - atomicIncr(used_memory,__n,used_memory_mutex); \ - } else { \ - used_memory += _n; \ - } \ + atomicIncr(used_memory,__n); \ } while(0) #define update_zmalloc_stat_free(__n) do { \ size_t _n = (__n); \ if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ - if (zmalloc_thread_safe) { \ - atomicDecr(used_memory,__n,used_memory_mutex); \ - } else { \ - used_memory -= _n; \ - } \ + atomicDecr(used_memory,__n); \ } while(0) static size_t used_memory = 0; -static int zmalloc_thread_safe = 0; pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER; static void zmalloc_default_oom(size_t size) { @@ -220,19 +211,10 @@ char *zstrdup(const char *s) { size_t zmalloc_used_memory(void) { size_t um; - - if (zmalloc_thread_safe) { - atomicGet(used_memory,um,used_memory_mutex); - } else { - um = used_memory; - } + atomicGet(used_memory,um); return um; } -void zmalloc_enable_thread_safeness(void) { - zmalloc_thread_safe = 1; -} - void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) { zmalloc_oom_handler = oom_handler; } @@ -418,8 +400,9 @@ size_t zmalloc_get_memory_size(void) { if (sysctl(mib, 2, &size, &len, NULL, 0) == 0) return (size_t)size; return 0L; /* Failed? */ -#endif /* sysctl and sysconf variants */ - +#else + return 0L; /* Unknown method to get the data. */ +#endif #else return 0L; /* Unknown OS. */ #endif diff --git a/src/zmalloc.h b/src/zmalloc.h index b6d4e1d9..64f2f36a 100644 --- a/src/zmalloc.h +++ b/src/zmalloc.h @@ -78,7 +78,6 @@ void *zrealloc(void *ptr, size_t size); void zfree(void *ptr); char *zstrdup(const char *s); size_t zmalloc_used_memory(void); -void zmalloc_enable_thread_safeness(void); void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); float zmalloc_get_fragmentation_ratio(size_t rss); size_t zmalloc_get_rss(void);