From 8c34786ea9d4b879bf5c5cf7ad811836f0d1f37f Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Tue, 2 Oct 2007 16:52:14 -0700 Subject: [PATCH] Getting everything ready for non-blocking IO. Everything in library now goes through memcached_response() New memcached_behavior_set() was written so that you can adjust how the library works. --- include/memcached.h | 13 +++++++++++++ lib/Makefile.am | 1 + lib/memcached_auto.c | 10 +++++++++- lib/memcached_behavior.c | 13 +++++++++++++ lib/memcached_connect.c | 27 ++++++++++++++++++++++----- lib/memcached_response.c | 5 ++++- lib/memcached_storage.c | 22 +++++----------------- lib/memcached_strerror.c | 2 ++ tests/output.res | 17 +++++++++-------- 9 files changed, 78 insertions(+), 32 deletions(-) create mode 100644 lib/memcached_behavior.c diff --git a/include/memcached.h b/include/memcached.h index 3733b2f1..e9a61f28 100644 --- a/include/memcached.h +++ b/include/memcached.h @@ -57,6 +57,7 @@ typedef enum { MEMCACHED_DATA_EXISTS, MEMCACHED_DATA_DOES_NOT_EXIST, MEMCACHED_NOTSTORED, + MEMCACHED_STORED, MEMCACHED_NOTFOUND, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_PARTIAL_READ, @@ -68,6 +69,10 @@ typedef enum { MEMCACHED_MAXIMUM_RETURN, /* Always add new error code before */ } memcached_return; +typedef enum { + MEMCACHED_BEHAVIOR_NO_BLOCK, +} memcached_behavior; + typedef enum { MEMCACHED_NOT_ALLOCATED= 0, MEMCACHED_ALLOCATED= 1, @@ -108,6 +113,8 @@ struct memcached_stat_st { unsigned int limit_maxbytes; }; +#define MEM_NO_BLOCK (1 << 0) + struct memcached_string_st { char *string; char *end; @@ -127,6 +134,10 @@ struct memcached_st { size_t write_buffer_offset; size_t write_between_flush; char connected; + int my_errno; + unsigned int stack_responses; + unsigned long long flags; + memcached_return warning; /* Future Use */ }; /* Public API */ @@ -150,6 +161,7 @@ memcached_return memcached_flush(memcached_st *ptr, time_t expiration); memcached_return memcached_verbosity(memcached_st *ptr, unsigned int verbosity); void memcached_quit(memcached_st *ptr); char *memcached_strerror(memcached_st *ptr, memcached_return rc); +memcached_return memcached_behavior_set(memcached_st *ptr, memcached_behavior flag); /* All of the functions for adding data to the server */ memcached_return memcached_set(memcached_st *ptr, char *key, size_t key_length, @@ -225,6 +237,7 @@ void memcached_string_free(memcached_st *ptr, memcached_string_st *string); #define WATCHPOINT_ERROR(A) printf("WATCHPOINT %s:%d %s\n", __FILE__, __LINE__, memcached_strerror(NULL, A));fflush(stdout); #define WATCHPOINT_STRING(A) printf("WATCHPOINT %s:%d %s\n", __FILE__, __LINE__, A);fflush(stdout); #define WATCHPOINT_NUMBER(A) printf("WATCHPOINT %s:%d %d\n", __FILE__, __LINE__, A);fflush(stdout); +#define WATCHPOINT_ERRNO(A) printf("WATCHPOINT %s:%d %s\n", __FILE__, __LINE__, strerror(A));A= 0;fflush(stdout); #ifdef __cplusplus diff --git a/lib/Makefile.am b/lib/Makefile.am index 4ba350fb..8ff7b139 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -27,6 +27,7 @@ noinst_HEADERS = libmemcached_probes.h \ lib_LTLIBRARIES = libmemcached.la libmemcached_la_SOURCES = memcached.c \ memcached_auto.c \ + memcached_behavior.c \ memcached_connect.c \ memcached_delete.c \ memcached_flush.c \ diff --git a/lib/memcached_auto.c b/lib/memcached_auto.c index e779f279..0bb91fd0 100644 --- a/lib/memcached_auto.c +++ b/lib/memcached_auto.c @@ -30,8 +30,16 @@ static memcached_return memcached_auto(memcached_st *ptr, return MEMCACHED_WRITE_FAILURE; memset(buffer, 0, MEMCACHED_DEFAULT_COMMAND_SIZE); - send_length= read(ptr->hosts[server_key].fd, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE); + rc= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, server_key); + + /* + So why recheck responce? Because the protocol is brain dead :) + The number returned might end up equaling one of the string + values. Less chance of a mistake with memcmp() so we will + use it. We still called memcached_response() though since it + worked its magic for non-blocking IO. + */ if (!memcmp(buffer, "ERROR\r\n", MEMCACHED_DEFAULT_COMMAND_SIZE)) { *value= 0; diff --git a/lib/memcached_behavior.c b/lib/memcached_behavior.c new file mode 100644 index 00000000..1095168c --- /dev/null +++ b/lib/memcached_behavior.c @@ -0,0 +1,13 @@ +#include + +memcached_return memcached_behavior_set(memcached_st *ptr, memcached_behavior flag) +{ + switch (flag) + { + case MEMCACHED_BEHAVIOR_NO_BLOCK: + ptr->flags|= MEM_NO_BLOCK; + break; + } + + return MEMCACHED_SUCCESS; +} diff --git a/lib/memcached_connect.c b/lib/memcached_connect.c index 2120db5c..5b024749 100644 --- a/lib/memcached_connect.c +++ b/lib/memcached_connect.c @@ -31,7 +31,10 @@ memcached_return memcached_connect(memcached_st *ptr) /* Create the socket */ if ((ptr->hosts[0].fd= socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + ptr->my_errno= errno; return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE; + } /* bind any port number */ @@ -39,18 +42,32 @@ memcached_return memcached_connect(memcached_st *ptr) localAddr.sin_addr.s_addr = htonl(INADDR_ANY); localAddr.sin_port = htons(0); -#ifdef NOT_YET /* For the moment, not getting a nonblocking mode will note be fatal */ - flags= fcntl(ptr->hosts[0].fd, F_GETFL, 0); - if (flags != -1) - (void)fcntl(ptr->hosts[0].fd, F_SETFL, flags | O_NONBLOCK); -#endif + if (ptr->flags & MEM_NO_BLOCK) + { + flags= fcntl(ptr->hosts[0].fd, F_GETFL, 0); + if (flags != -1) + (void)fcntl(ptr->hosts[0].fd, F_SETFL, flags | O_NONBLOCK); + } /* connect to server */ +test_connect: if (connect(ptr->hosts[0].fd, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0) + { + switch (errno) { + /* We are spinning waiting on connect */ + case EINPROGRESS: + case EINTR: + goto test_connect; + case EISCONN: /* We were spinning waiting on connect */ + break; + default: + ptr->my_errno= errno; return MEMCACHED_HOST_LOCKUP_FAILURE; + } ptr->connected++; + } } } LIBMEMCACHED_MEMCACHED_CONNECT_END(); diff --git a/lib/memcached_response.c b/lib/memcached_response.c index 24ee832f..42a06411 100644 --- a/lib/memcached_response.c +++ b/lib/memcached_response.c @@ -6,6 +6,7 @@ */ #include "common.h" +#include "memcached_io.h" memcached_return memcached_response(memcached_st *ptr, char *buffer, size_t buffer_length, @@ -42,10 +43,12 @@ memcached_return memcached_response(memcached_st *ptr, return MEMCACHED_SUCCESS; case 'S': /* STORED STATS SERVER_ERROR */ { - if (buffer[1] == 'T') /* STORED STATS */ + if (buffer[2] == 'A') /* STORED STATS */ return MEMCACHED_SUCCESS; else if (buffer[1] == 'E') return MEMCACHED_SERVER_ERROR; + else if (buffer[1] == 'T') + return MEMCACHED_STORED; else return MEMCACHED_UNKNOWN_READ_FAILURE; } diff --git a/lib/memcached_storage.c b/lib/memcached_storage.c index 0c27c766..1e607c24 100644 --- a/lib/memcached_storage.c +++ b/lib/memcached_storage.c @@ -32,6 +32,7 @@ static memcached_return memcached_send(memcached_st *ptr, if (rc != MEMCACHED_SUCCESS) return rc; + /* Leaveing this assert in since only a library fubar could blow this */ assert(ptr->write_buffer_offset == 0); server_key= memcached_generate_hash(key, key_length) % ptr->number_of_hosts; @@ -51,7 +52,6 @@ static memcached_return memcached_send(memcached_st *ptr, rc= MEMCACHED_WRITE_FAILURE; goto error; } - assert(write_length == sent_length); /* We have to flush after sending the command. Memcached is not smart enough @@ -65,7 +65,6 @@ static memcached_return memcached_send(memcached_st *ptr, rc= MEMCACHED_WRITE_FAILURE; goto error; } - assert(value_length == sent_length); if ((sent_length= memcached_io_write(ptr, server_key, "\r\n", 2)) == -1) { @@ -73,26 +72,15 @@ static memcached_return memcached_send(memcached_st *ptr, goto error; } - assert(2 == sent_length); - if ((sent_length= memcached_io_flush(ptr, server_key)) == -1) return MEMCACHED_WRITE_FAILURE; - //assert(sent_length == write_length + value_length + 2); - - sent_length= recv(ptr->hosts[server_key].fd, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, 0); + rc= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, server_key); - if (sent_length && buffer[0] == 'S') /* STORED */ + if (rc == MEMCACHED_STORED) return MEMCACHED_SUCCESS; - else if (write_length && buffer[0] == 'N') /* NOT_STORED */ - return MEMCACHED_NOTSTORED; - else if (write_length && buffer[0] == 'E') /* ERROR */ - { - printf("BUFFER :%s:\n", buffer); - return MEMCACHED_PROTOCOL_ERROR; - } - else - return MEMCACHED_READ_FAILURE; + else + return rc; error: memcached_io_reset(ptr, server_key); diff --git a/lib/memcached_strerror.c b/lib/memcached_strerror.c index c917e96e..43c36a48 100644 --- a/lib/memcached_strerror.c +++ b/lib/memcached_strerror.c @@ -34,6 +34,8 @@ char *memcached_strerror(memcached_st *ptr, memcached_return rc) return "CONNECTION DATA DOES NOT EXIST"; case MEMCACHED_NOTSTORED: return "NOT STORED"; + case MEMCACHED_STORED: + return "STORED"; case MEMCACHED_NOTFOUND: return "NOT FOUND"; case MEMCACHED_MEMORY_ALLOCATION_FAILURE: diff --git a/tests/output.res b/tests/output.res index 50dd00ba..974aa22d 100644 --- a/tests/output.res +++ b/tests/output.res @@ -13,14 +13,15 @@ Error 11 -> CONNECTION SOCKET CREATE FAILURE Error 12 -> CONNECTION DATA EXISTS Error 13 -> CONNECTION DATA DOES NOT EXIST Error 14 -> NOT STORED -Error 15 -> NOT FOUND -Error 16 -> MEMORY ALLOCATION FAILURE -Error 17 -> PARTIAL READ -Error 18 -> SOME ERRORS WERE REPORTED -Error 19 -> NO SERVERS DEFINED -Error 20 -> SERVER END -Error 21 -> SERVER DELETE -Error 22 -> SERVER VALUE +Error 15 -> STORED +Error 16 -> NOT FOUND +Error 17 -> MEMORY ALLOCATION FAILURE +Error 18 -> PARTIAL READ +Error 19 -> SOME ERRORS WERE REPORTED +Error 20 -> NO SERVERS DEFINED +Error 21 -> SERVER END +Error 22 -> SERVER DELETE +Error 23 -> SERVER VALUE Found key pid Found key uptime Found key time -- 2.30.2