X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;f=libmemcached%2Fio.cc;h=e246f3ec50d4165eb812e25829bff1b24055c3c1;hb=7fc1ce1d52601c1ba1d8e7ff0035a7243eaf14c3;hp=8b297a7acaed71215d0c9624f632f91c4909bf7f;hpb=387c65c94bec4333da574771fc82a40deabbc3e2;p=awesomized%2Flibmemcached diff --git a/libmemcached/io.cc b/libmemcached/io.cc index 8b297a7a..e246f3ec 100644 --- a/libmemcached/io.cc +++ b/libmemcached/io.cc @@ -39,6 +39,17 @@ #include +#ifdef HAVE_SYS_SOCKET_H +# include +#endif + +void initialize_binary_request(memcached_instance_st* server, protocol_binary_request_header& header) +{ + server->request_id++; + header.request.magic= PROTOCOL_BINARY_REQ; + header.request.opaque= htons(server->request_id); +} + enum memc_read_or_write { MEM_READ, MEM_WRITE @@ -48,70 +59,68 @@ enum memc_read_or_write { * Try to fill the input buffer for a server with as much * data as possible. * - * @param ptr the server to pack + * @param instance the server to pack */ -static bool repack_input_buffer(memcached_server_write_instance_st ptr) +static bool repack_input_buffer(memcached_instance_st* instance) { - if (ptr->read_ptr != ptr->read_buffer) + if (instance->read_ptr != instance->read_buffer) { /* Move all of the data to the beginning of the buffer so ** that we can fit more data into the buffer... */ - memmove(ptr->read_buffer, ptr->read_ptr, ptr->read_buffer_length); - ptr->read_ptr= ptr->read_buffer; - ptr->read_data_length= ptr->read_buffer_length; + memmove(instance->read_buffer, instance->read_ptr, instance->read_buffer_length); + instance->read_ptr= instance->read_buffer; + instance->read_data_length= instance->read_buffer_length; } /* There is room in the buffer, try to fill it! */ - if (ptr->read_buffer_length != MEMCACHED_MAX_BUFFER) + if (instance->read_buffer_length != MEMCACHED_MAX_BUFFER) { do { /* Just try a single read to grab what's available */ - ssize_t nr= recv(ptr->fd, - ptr->read_ptr + ptr->read_data_length, - MEMCACHED_MAX_BUFFER - ptr->read_data_length, - MSG_DONTWAIT); - - switch (nr) + ssize_t nr; + if ((nr= ::recv(instance->fd, + instance->read_ptr + instance->read_data_length, + MEMCACHED_MAX_BUFFER - instance->read_data_length, + MSG_NOSIGNAL)) <= 0) { - case SOCKET_ERROR: + if (nr == 0) + { + memcached_set_error(*instance, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT); + } + else { switch (get_socket_errno()) { case EINTR: continue; +#if EWOULDBLOCK != EAGAIN case EWOULDBLOCK: -#ifdef USE_EAGAIN - case EAGAIN: #endif -#ifdef TARGET_OS_LINUX + case EAGAIN: +#ifdef __linux case ERESTART: #endif break; // No IO is fine, we can just move on default: - memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); + memcached_set_errno(*instance, get_socket_errno(), MEMCACHED_AT); } } - break; - case 0: // Shutdown on the socket has occurred - { - memcached_set_error(*ptr, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT); - } break; + } + else // We read data, append to our read buffer + { + instance->read_data_length+= size_t(nr); + instance->read_buffer_length+= size_t(nr); - default: - { - ptr->read_data_length+= size_t(nr); - ptr->read_buffer_length+= size_t(nr); - return true; - } - break; + return true; } - } while (0); + } while (false); } + return false; } @@ -122,28 +131,28 @@ static bool repack_input_buffer(memcached_server_write_instance_st ptr) * when the input buffer is full, so that we _know_ that we have * at least _one_ message to process. * - * @param ptr the server to star processing iput messages for + * @param instance the server to star processing iput messages for * @return true if we processed anything, false otherwise */ -static bool process_input_buffer(memcached_server_write_instance_st ptr) +static bool process_input_buffer(memcached_instance_st* instance) { /* ** We might be able to process some of the response messages if we ** have a callback set up */ - if (ptr->root->callbacks != NULL) + if (instance->root->callbacks != NULL) { /* * We might have responses... try to read them out and fire * callbacks */ - memcached_callback_st cb= *ptr->root->callbacks; + memcached_callback_st cb= *instance->root->callbacks; - memcached_set_processing_input((memcached_st *)ptr->root, true); + memcached_set_processing_input((Memcached *)instance->root, true); char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - memcached_st *root= (memcached_st *)ptr->root; - memcached_return_t error= memcached_response(ptr, buffer, sizeof(buffer), &root->result); + Memcached *root= (Memcached *)instance->root; + memcached_return_t error= memcached_response(instance, buffer, sizeof(buffer), &root->result); memcached_set_processing_input(root, false); @@ -151,9 +160,11 @@ static bool process_input_buffer(memcached_server_write_instance_st ptr) { for (unsigned int x= 0; x < cb.number_of_callback; x++) { - error= (*cb.callback[x])(ptr->root, &root->result, cb.context); + error= (*cb.callback[x])(instance->root, &root->result, cb.context); if (error != MEMCACHED_SUCCESS) + { break; + } } /* @todo what should I do with the error message??? */ @@ -165,23 +176,9 @@ static bool process_input_buffer(memcached_server_write_instance_st ptr) return false; } -static memcached_return_t io_wait(memcached_server_write_instance_st ptr, - const memc_read_or_write read_or_write) +static memcached_return_t io_wait(memcached_instance_st* instance, + const short events) { - struct pollfd fds; - fds.fd= ptr->fd; - fds.events= POLLIN; - - if (read_or_write == MEM_WRITE) /* write */ - { - fds.events= POLLOUT; - WATCHPOINT_SET(ptr->io_wait_count.write++); - } - else - { - WATCHPOINT_SET(ptr->io_wait_count.read++); - } - /* ** We are going to block on write, but at least on Solaris we might block ** on write if we haven't read anything from our input buffer.. @@ -190,85 +187,117 @@ static memcached_return_t io_wait(memcached_server_write_instance_st ptr, ** The test is moved down in the purge function to avoid duplication of ** the test. */ - if (read_or_write == MEM_WRITE) + if (events & POLLOUT) { - memcached_return_t rc= memcached_purge(ptr); - if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED) + if (memcached_purge(instance) == false) { return MEMCACHED_FAILURE; } } - if (ptr->root->poll_timeout == 0) // Mimic 0 causes timeout behavior (not all platforms do this) + struct pollfd fds; + fds.fd= instance->fd; + fds.events= events; + fds.revents= 0; + + if (fds.events & POLLOUT) /* write */ + { + instance->io_wait_count.write++; + } + else { - return memcached_set_error(*ptr, MEMCACHED_TIMEOUT, MEMCACHED_AT); + instance->io_wait_count.read++; + } + + if (instance->root->poll_timeout == 0) // Mimic 0 causes timeout behavior (not all platforms do this) + { + return memcached_set_error(*instance, MEMCACHED_TIMEOUT, MEMCACHED_AT, memcached_literal_param("poll_timeout() was set to zero")); } size_t loop_max= 5; while (--loop_max) // While loop is for ERESTART or EINTR { + int active_fd= poll(&fds, 1, instance->root->poll_timeout); - int error= poll(&fds, 1, ptr->root->poll_timeout); - switch (error) + if (active_fd >= 1) { - case 1: // Success! - WATCHPOINT_IF_LABELED_NUMBER(read_or_write && loop_max < 4, "read() times we had to loop, decremented down from 5", loop_max); - WATCHPOINT_IF_LABELED_NUMBER(!read_or_write && loop_max < 4, "write() times we had to loop, decremented down from 5", loop_max); - - return MEMCACHED_SUCCESS; - - case 0: // Timeout occured, we let the while() loop do its thing. - return memcached_set_error(*ptr, MEMCACHED_TIMEOUT, MEMCACHED_AT); - - default: - WATCHPOINT_ERRNO(get_socket_errno()); - switch (get_socket_errno()) + assert_msg(active_fd == 1 , "poll() returned an unexpected number of active file descriptors"); + if (fds.revents & POLLIN or fds.revents & POLLOUT) { -#ifdef TARGET_OS_LINUX - case ERESTART: -#endif - case EINTR: - break; - - case EFAULT: - case ENOMEM: - return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT); + return MEMCACHED_SUCCESS; + } - case EINVAL: - return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT, memcached_literal_param("RLIMIT_NOFILE exceeded, or if OSX the timeout value was invalid")); + if (fds.revents & POLLHUP) + { + return memcached_set_error(*instance, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT, + memcached_literal_param("poll() detected hang up")); + } - default: - if (fds.revents & POLLERR) + if (fds.revents & POLLERR) + { + int local_errno= EINVAL; + int err; + socklen_t len= sizeof (err); + if (getsockopt(instance->fd, SOL_SOCKET, SO_ERROR, (char*)&err, &len) == 0) { - int err; - socklen_t len= sizeof (err); - if (getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len) == 0) + if (err == 0) // treat this as EINTR { - if (err == 0) - { - continue; - } - errno= err; + continue; } + local_errno= err; } - else - { - memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); - } - int local_errno= get_socket_errno(); // We cache in case memcached_quit_server() modifies errno - memcached_quit_server(ptr, true); - - return memcached_set_errno(*ptr, local_errno, MEMCACHED_AT); + memcached_quit_server(instance, true); + return memcached_set_errno(*instance, local_errno, MEMCACHED_AT, + memcached_literal_param("poll() returned POLLHUP")); } + + return memcached_set_error(*instance, MEMCACHED_FAILURE, MEMCACHED_AT, memcached_literal_param("poll() returned a value that was not dealt with")); + } + + if (active_fd == 0) + { + return memcached_set_error(*instance, MEMCACHED_TIMEOUT, MEMCACHED_AT, memcached_literal_param("No active_fd were found")); } + + // Only an error should result in this code being called. + int local_errno= get_socket_errno(); // We cache in case memcached_quit_server() modifies errno + assert_msg(active_fd == -1 , "poll() returned an unexpected value"); + switch (local_errno) + { +#ifdef __linux + case ERESTART: +#endif + case EINTR: + continue; + + case EFAULT: + case ENOMEM: + memcached_set_error(*instance, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT); + break; + + case EINVAL: + memcached_set_error(*instance, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT, memcached_literal_param("RLIMIT_NOFILE exceeded, or if OSX the timeout value was invalid")); + break; + + default: + memcached_set_errno(*instance, local_errno, MEMCACHED_AT, memcached_literal_param("poll")); + } + + break; } - memcached_quit_server(ptr, true); + memcached_quit_server(instance, true); + + if (memcached_has_error(instance)) + { + return memcached_instance_error_return(instance); + } - return memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); + return memcached_set_error(*instance, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT, + memcached_literal_param("number of attempts to call io_wait() failed")); } -static bool io_flush(memcached_server_write_instance_st ptr, +static bool io_flush(memcached_instance_st* instance, const bool with_flush, memcached_return_t& error) { @@ -278,45 +307,45 @@ static bool io_flush(memcached_server_write_instance_st ptr, ** in the purge function to avoid duplicating the logic.. */ { - memcached_return_t rc; - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); - rc= memcached_purge(ptr); + WATCHPOINT_ASSERT(instance->fd != INVALID_SOCKET); - if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED) + if (memcached_purge(instance) == false) { return false; } } - char *local_write_ptr= ptr->write_buffer; - size_t write_length= ptr->write_buffer_offset; + char *local_write_ptr= instance->write_buffer; + size_t write_length= instance->write_buffer_offset; error= MEMCACHED_SUCCESS; - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); + WATCHPOINT_ASSERT(instance->fd != INVALID_SOCKET); /* Looking for memory overflows */ #if defined(DEBUG) if (write_length == MEMCACHED_MAX_BUFFER) - WATCHPOINT_ASSERT(ptr->write_buffer == local_write_ptr); - WATCHPOINT_ASSERT((ptr->write_buffer + MEMCACHED_MAX_BUFFER) >= (local_write_ptr + write_length)); + WATCHPOINT_ASSERT(instance->write_buffer == local_write_ptr); + WATCHPOINT_ASSERT((instance->write_buffer + MEMCACHED_MAX_BUFFER) >= (local_write_ptr + write_length)); #endif while (write_length) { - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); + WATCHPOINT_ASSERT(instance->fd != INVALID_SOCKET); WATCHPOINT_ASSERT(write_length > 0); - ssize_t sent_length= 0; - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); + int flags; if (with_flush) { - sent_length= ::send(ptr->fd, local_write_ptr, write_length, MSG_NOSIGNAL|MSG_DONTWAIT); + flags= MSG_NOSIGNAL; } else { - sent_length= ::send(ptr->fd, local_write_ptr, write_length, MSG_NOSIGNAL|MSG_DONTWAIT|MSG_MORE); + flags= MSG_NOSIGNAL|MSG_MORE; } + ssize_t sent_length= ::send(instance->fd, local_write_ptr, write_length, flags); + int local_errno= get_socket_errno(); // We cache in case memcached_quit_server() modifies errno + if (sent_length == SOCKET_ERROR) { #if 0 // @todo I should look at why we hit this bit of code hard frequently @@ -327,10 +356,11 @@ static bool io_flush(memcached_server_write_instance_st ptr, { case ENOBUFS: continue; + +#if EWOULDBLOCK != EAGAIN case EWOULDBLOCK: -#ifdef USE_EAGAIN - case EAGAIN: #endif + case EAGAIN: { /* * We may be blocked on write because the input buffer @@ -338,158 +368,178 @@ static bool io_flush(memcached_server_write_instance_st ptr, * buffer for more data and retry the write before * waiting.. */ - if (repack_input_buffer(ptr) or process_input_buffer(ptr)) + if (repack_input_buffer(instance) or process_input_buffer(instance)) { continue; } - memcached_return_t rc= io_wait(ptr, MEM_WRITE); + memcached_return_t rc= io_wait(instance, POLLOUT); if (memcached_success(rc)) { continue; } else if (rc == MEMCACHED_TIMEOUT) { - error= memcached_set_error(*ptr, MEMCACHED_TIMEOUT, MEMCACHED_AT); return false; } - memcached_quit_server(ptr, true); - error= memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); + memcached_quit_server(instance, true); + error= memcached_set_errno(*instance, local_errno, MEMCACHED_AT); return false; } case ENOTCONN: case EPIPE: default: - memcached_quit_server(ptr, true); - error= memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); - WATCHPOINT_ASSERT(ptr->fd == INVALID_SOCKET); + memcached_quit_server(instance, true); + error= memcached_set_errno(*instance, local_errno, MEMCACHED_AT); + WATCHPOINT_ASSERT(instance->fd == INVALID_SOCKET); return false; } } - ptr->io_bytes_sent+= uint32_t(sent_length); + instance->io_bytes_sent+= uint32_t(sent_length); local_write_ptr+= sent_length; write_length-= uint32_t(sent_length); } WATCHPOINT_ASSERT(write_length == 0); - ptr->write_buffer_offset= 0; + instance->write_buffer_offset= 0; return true; } -memcached_return_t memcached_io_wait_for_write(memcached_server_write_instance_st ptr) +memcached_return_t memcached_io_wait_for_write(memcached_instance_st* instance) +{ + return io_wait(instance, POLLOUT); +} + +memcached_return_t memcached_io_wait_for_read(memcached_instance_st* instance) { - return io_wait(ptr, MEM_WRITE); + return io_wait(instance, POLLIN); } -memcached_return_t memcached_io_read(memcached_server_write_instance_st ptr, +static memcached_return_t _io_fill(memcached_instance_st* instance) +{ + ssize_t data_read; + do + { + data_read= ::recv(instance->fd, instance->read_buffer, MEMCACHED_MAX_BUFFER, MSG_NOSIGNAL); + int local_errno= get_socket_errno(); // We cache in case memcached_quit_server() modifies errno + + if (data_read == SOCKET_ERROR) + { + switch (get_socket_errno()) + { + case EINTR: // We just retry + continue; + + case ETIMEDOUT: // OSX +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + case EAGAIN: +#ifdef __linux + case ERESTART: +#endif + { + memcached_return_t io_wait_ret; + if (memcached_success(io_wait_ret= io_wait(instance, POLLIN))) + { + continue; + } + + return io_wait_ret; + } + + /* fall through */ + + case ENOTCONN: // Programmer Error + WATCHPOINT_ASSERT(0); + case ENOTSOCK: + WATCHPOINT_ASSERT(0); + case EBADF: + assert_msg(instance->fd != INVALID_SOCKET, "Programmer error, invalid socket"); + /* fall through */ + case EINVAL: + case EFAULT: + case ECONNREFUSED: + default: + memcached_quit_server(instance, true); + memcached_set_errno(*instance, local_errno, MEMCACHED_AT); + break; + } + + return memcached_instance_error_return(instance); + } + else if (data_read == 0) + { + /* + EOF. Any data received so far is incomplete + so discard it. This always reads by byte in case of TCP + and protocol enforcement happens at memcached_response() + looking for '\n'. We do not care for UDB which requests 8 bytes + at once. Generally, this means that connection went away. Since + for blocking I/O we do not return 0 and for non-blocking case + it will return EGAIN if data is not immediatly available. + */ + memcached_quit_server(instance, true); + return memcached_set_error(*instance, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT, + memcached_literal_param("::rec() returned zero, server has disconnected")); + } + instance->io_wait_count._bytes_read+= data_read; + } while (data_read <= 0); + + instance->io_bytes_sent= 0; + instance->read_data_length= (size_t) data_read; + instance->read_buffer_length= (size_t) data_read; + instance->read_ptr= instance->read_buffer; + + return MEMCACHED_SUCCESS; +} + +memcached_return_t memcached_io_read(memcached_instance_st* instance, void *buffer, size_t length, ssize_t& nread) { - assert(memcached_is_udp(ptr->root) == false); - assert_msg(ptr, "Programmer error, memcached_io_read() recieved an invalid memcached_server_write_instance_st"); // Programmer error + assert(memcached_is_udp(instance->root) == false); + assert_msg(instance, "Programmer error, memcached_io_read() recieved an invalid Instance"); // Programmer error char *buffer_ptr= static_cast(buffer); - if (ptr->fd == INVALID_SOCKET) + if (instance->fd == INVALID_SOCKET) { #if 0 - assert_msg(int(ptr->state) <= int(MEMCACHED_SERVER_STATE_ADDRINFO), "Programmer error, invalid socket state"); + assert_msg(int(instance->state) <= int(MEMCACHED_SERVER_STATE_ADDRINFO), "Programmer error, invalid socket state"); #endif return MEMCACHED_CONNECTION_FAILURE; } while (length) { - if (not ptr->read_buffer_length) + if (instance->read_buffer_length == 0) { - ssize_t data_read; - do + memcached_return_t io_fill_ret; + if (memcached_fatal(io_fill_ret= _io_fill(instance))) { - data_read= ::recv(ptr->fd, ptr->read_buffer, MEMCACHED_MAX_BUFFER, MSG_DONTWAIT); - if (data_read == SOCKET_ERROR) - { - switch (get_socket_errno()) - { - case EINTR: // We just retry - continue; - - case ETIMEDOUT: // OSX - case EWOULDBLOCK: -#ifdef USE_EAGAIN - case EAGAIN: -#endif -#ifdef TARGET_OS_LINUX - case ERESTART: -#endif - if (memcached_success(io_wait(ptr, MEM_READ))) - { - continue; - } - return MEMCACHED_IN_PROGRESS; - - /* fall through */ - - case ENOTCONN: // Programmer Error - WATCHPOINT_ASSERT(0); - case ENOTSOCK: - WATCHPOINT_ASSERT(0); - case EBADF: - assert_msg(ptr->fd != INVALID_SOCKET, "Programmer error, invalid socket"); - case EINVAL: - case EFAULT: - case ECONNREFUSED: - default: - { - memcached_quit_server(ptr, true); - nread= -1; - return memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); - } - } - } - else if (data_read == 0) - { - /* - EOF. Any data received so far is incomplete - so discard it. This always reads by byte in case of TCP - and protocol enforcement happens at memcached_response() - looking for '\n'. We do not care for UDB which requests 8 bytes - at once. Generally, this means that connection went away. Since - for blocking I/O we do not return 0 and for non-blocking case - it will return EGAIN if data is not immediatly available. - */ - WATCHPOINT_STRING("We had a zero length recv()"); - memcached_quit_server(ptr, true); - nread= -1; - return memcached_set_error(*ptr, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT, - memcached_literal_param("::rec() returned zero, server has disconnected")); - } - } while (data_read <= 0); - - ptr->io_bytes_sent = 0; - ptr->read_data_length= (size_t) data_read; - ptr->read_buffer_length= (size_t) data_read; - ptr->read_ptr= ptr->read_buffer; + nread= -1; + return io_fill_ret; + } } if (length > 1) { - size_t difference; - - difference= (length > ptr->read_buffer_length) ? ptr->read_buffer_length : length; + size_t difference= (length > instance->read_buffer_length) ? instance->read_buffer_length : length; - memcpy(buffer_ptr, ptr->read_ptr, difference); + memcpy(buffer_ptr, instance->read_ptr, difference); length -= difference; - ptr->read_ptr+= difference; - ptr->read_buffer_length-= difference; + instance->read_ptr+= difference; + instance->read_buffer_length-= difference; buffer_ptr+= difference; } else { - *buffer_ptr= *ptr->read_ptr; - ptr->read_ptr++; - ptr->read_buffer_length--; + *buffer_ptr= *instance->read_ptr; + instance->read_ptr++; + instance->read_buffer_length--; buffer_ptr++; break; } @@ -500,14 +550,14 @@ memcached_return_t memcached_io_read(memcached_server_write_instance_st ptr, return MEMCACHED_SUCCESS; } -memcached_return_t memcached_io_slurp(memcached_server_write_instance_st ptr) +memcached_return_t memcached_io_slurp(memcached_instance_st* instance) { - assert_msg(ptr, "Programmer error, invalid memcached_server_write_instance_st"); - assert(memcached_is_udp(ptr->root) == false); + assert_msg(instance, "Programmer error, invalid Instance"); + assert(memcached_is_udp(instance->root) == false); - if (ptr->fd == INVALID_SOCKET) + if (instance->fd == INVALID_SOCKET) { - assert_msg(int(ptr->state) <= int(MEMCACHED_SERVER_STATE_ADDRINFO), "Invalid socket state"); + assert_msg(int(instance->state) <= int(MEMCACHED_SERVER_STATE_ADDRINFO), "Invalid socket state"); return MEMCACHED_CONNECTION_FAILURE; } @@ -515,7 +565,7 @@ memcached_return_t memcached_io_slurp(memcached_server_write_instance_st ptr) char buffer[MEMCACHED_MAX_BUFFER]; do { - data_read= recv(ptr->fd, ptr->read_buffer, sizeof(buffer), MSG_DONTWAIT); + data_read= ::recv(instance->fd, instance->read_buffer, sizeof(buffer), MSG_NOSIGNAL); if (data_read == SOCKET_ERROR) { switch (get_socket_errno()) @@ -524,14 +574,14 @@ memcached_return_t memcached_io_slurp(memcached_server_write_instance_st ptr) continue; case ETIMEDOUT: // OSX +#if EWOULDBLOCK != EAGAIN case EWOULDBLOCK: -#ifdef USE_EAGAIN - case EAGAIN: #endif -#ifdef TARGET_OS_LINUX + case EAGAIN: +#ifdef __linux case ERESTART: #endif - if (memcached_success(io_wait(ptr, MEM_READ))) + if (memcached_success(io_wait(instance, POLLIN))) { continue; } @@ -540,11 +590,12 @@ memcached_return_t memcached_io_slurp(memcached_server_write_instance_st ptr) /* fall through */ case ENOTCONN: // Programmer Error - WATCHPOINT_ASSERT(0); + assert(0); case ENOTSOCK: - WATCHPOINT_ASSERT(0); + assert(0); case EBADF: - assert_msg(ptr->fd != INVALID_SOCKET, "Invalid socket state"); + assert_msg(instance->fd != INVALID_SOCKET, "Invalid socket state"); + /* fall through */ case EINVAL: case EFAULT: case ECONNREFUSED: @@ -557,36 +608,39 @@ memcached_return_t memcached_io_slurp(memcached_server_write_instance_st ptr) return MEMCACHED_CONNECTION_FAILURE; } -static ssize_t _io_write(memcached_server_write_instance_st ptr, - const void *buffer, size_t length, bool with_flush) +static bool _io_write(memcached_instance_st* instance, + const void *buffer, size_t length, bool with_flush, + size_t& written) { - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); - assert(memcached_is_udp(ptr->root) == false); + assert(instance->fd != INVALID_SOCKET); + assert(memcached_is_udp(instance->root) == false); - size_t original_length= length; const char *buffer_ptr= static_cast(buffer); + const size_t original_length= length; + while (length) { char *write_ptr; size_t buffer_end= MEMCACHED_MAX_BUFFER; - size_t should_write= buffer_end -ptr->write_buffer_offset; + size_t should_write= buffer_end -instance->write_buffer_offset; should_write= (should_write < length) ? should_write : length; - write_ptr= ptr->write_buffer + ptr->write_buffer_offset; + write_ptr= instance->write_buffer + instance->write_buffer_offset; memcpy(write_ptr, buffer_ptr, should_write); - ptr->write_buffer_offset+= should_write; + instance->write_buffer_offset+= should_write; buffer_ptr+= should_write; length-= should_write; - if (ptr->write_buffer_offset == buffer_end) + if (instance->write_buffer_offset == buffer_end) { - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); + WATCHPOINT_ASSERT(instance->fd != INVALID_SOCKET); memcached_return_t rc; - if (io_flush(ptr, with_flush, rc) == false) + if (io_flush(instance, with_flush, rc) == false) { - return -1; + written= original_length -length; + return false; } } } @@ -594,102 +648,144 @@ static ssize_t _io_write(memcached_server_write_instance_st ptr, if (with_flush) { memcached_return_t rc; - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); - if (io_flush(ptr, with_flush, rc) == false) + WATCHPOINT_ASSERT(instance->fd != INVALID_SOCKET); + if (io_flush(instance, with_flush, rc) == false) { - return -1; + written= original_length -length; + return false; } } - return (ssize_t) original_length; + written= original_length -length; + + return true; } -bool memcached_io_write(memcached_server_write_instance_st ptr) +bool memcached_io_write(memcached_instance_st* instance) { - return (_io_write(ptr, NULL, 0, true) >= 0); + size_t written; + return _io_write(instance, NULL, 0, true, written); } -ssize_t memcached_io_write(memcached_server_write_instance_st ptr, +ssize_t memcached_io_write(memcached_instance_st* instance, const void *buffer, const size_t length, const bool with_flush) { - return _io_write(ptr, buffer, length, with_flush); + size_t written; + + if (_io_write(instance, buffer, length, with_flush, written) == false) + { + return -1; + } + + return ssize_t(written); } -ssize_t memcached_io_writev(memcached_server_write_instance_st ptr, - libmemcached_io_vector_st vector[], - const size_t number_of, const bool with_flush) +bool memcached_io_writev(memcached_instance_st* instance, + libmemcached_io_vector_st vector[], + const size_t number_of, const bool with_flush) { + ssize_t complete_total= 0; ssize_t total= 0; for (size_t x= 0; x < number_of; x++, vector++) { - ssize_t returnable; - + complete_total+= vector->length; if (vector->length) { - if ((returnable= _io_write(ptr, vector->buffer, vector->length, false)) == -1) + size_t written; + if ((_io_write(instance, vector->buffer, vector->length, false, written)) == false) { - return -1; + return false; } - total+= returnable; + total+= written; } } if (with_flush) { - if (memcached_io_write(ptr) == false) + if (memcached_io_write(instance) == false) { - return -1; + return false; } } - return total; + return (complete_total == total); } - -void memcached_io_close(memcached_server_write_instance_st ptr) +void memcached_instance_st::start_close_socket() { - if (ptr->fd == INVALID_SOCKET) + if (fd != INVALID_SOCKET) { - return; + shutdown(fd, SHUT_WR); + options.is_shutting_down= true; } +} - /* in case of death shutdown to avoid blocking at close() */ - if (shutdown(ptr->fd, SHUT_RDWR) == SOCKET_ERROR && get_socket_errno() != ENOTCONN) +void memcached_instance_st::reset_socket() +{ + if (fd != INVALID_SOCKET) { - WATCHPOINT_NUMBER(ptr->fd); - WATCHPOINT_ERRNO(get_socket_errno()); - WATCHPOINT_ASSERT(get_socket_errno()); + (void)closesocket(fd); + fd= INVALID_SOCKET; } +} - if (closesocket(ptr->fd) == SOCKET_ERROR) +void memcached_instance_st::close_socket() +{ + if (fd != INVALID_SOCKET) { - WATCHPOINT_ERRNO(get_socket_errno()); + int shutdown_options= SHUT_RD; + if (options.is_shutting_down == false) + { + shutdown_options= SHUT_RDWR; + } + + /* in case of death shutdown to avoid blocking at close() */ + if (shutdown(fd, shutdown_options) == SOCKET_ERROR and get_socket_errno() != ENOTCONN) + { + WATCHPOINT_NUMBER(fd); + WATCHPOINT_ERRNO(get_socket_errno()); + WATCHPOINT_ASSERT(get_socket_errno()); + } + + reset_socket(); + state= MEMCACHED_SERVER_STATE_NEW; } - ptr->state= MEMCACHED_SERVER_STATE_NEW; - ptr->fd= INVALID_SOCKET; + + state= MEMCACHED_SERVER_STATE_NEW; + cursor_active_= 0; + io_bytes_sent= 0; + write_buffer_offset= size_t(root and memcached_is_udp(root) ? UDP_DATAGRAM_HEADER_LENGTH : 0); + read_buffer_length= 0; + read_ptr= read_buffer; + options.is_shutting_down= false; + memcached_server_response_reset(this); + + // We reset the version so that if we end up talking to a different server + // we don't have stale server version information. + major_version= minor_version= micro_version= UINT8_MAX; } -memcached_server_write_instance_st memcached_io_get_readable_server(memcached_st *memc) +memcached_instance_st* memcached_io_get_readable_server(Memcached *memc, memcached_return_t&) { #define MAX_SERVERS_TO_POLL 100 struct pollfd fds[MAX_SERVERS_TO_POLL]; - unsigned int host_index= 0; + nfds_t host_index= 0; - for (uint32_t x= 0; x < memcached_server_count(memc) && host_index < MAX_SERVERS_TO_POLL; ++x) + for (uint32_t x= 0; x < memcached_server_count(memc) and host_index < MAX_SERVERS_TO_POLL; ++x) { - memcached_server_write_instance_st instance= memcached_server_instance_fetch(memc, x); + memcached_instance_st* instance= memcached_instance_fetch(memc, x); if (instance->read_buffer_length > 0) /* I have data in the buffer */ { return instance; } - if (memcached_server_response_count(instance) > 0) + if (instance->response_count() > 0) { - fds[host_index].events = POLLIN; - fds[host_index].revents = 0; - fds[host_index].fd = instance->fd; + fds[host_index].events= POLLIN; + fds[host_index].revents= 0; + fds[host_index].fd= instance->fd; ++host_index; } } @@ -699,10 +795,9 @@ memcached_server_write_instance_st memcached_io_get_readable_server(memcached_st /* We have 0 or 1 server with pending events.. */ for (uint32_t x= 0; x< memcached_server_count(memc); ++x) { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(memc, x); + memcached_instance_st* instance= memcached_instance_fetch(memc, x); - if (memcached_server_response_count(instance) > 0) + if (instance->response_count() > 0) { return instance; } @@ -721,17 +816,18 @@ memcached_server_write_instance_st memcached_io_get_readable_server(memcached_st break; default: - for (size_t x= 0; x < host_index; ++x) + for (nfds_t x= 0; x < host_index; ++x) { if (fds[x].revents & POLLIN) { for (uint32_t y= 0; y < memcached_server_count(memc); ++y) { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(memc, y); + memcached_instance_st* instance= memcached_instance_fetch(memc, y); if (instance->fd == fds[x].fd) + { return instance; + } } } } @@ -743,16 +839,16 @@ memcached_server_write_instance_st memcached_io_get_readable_server(memcached_st /* Eventually we will just kill off the server with the problem. */ -void memcached_io_reset(memcached_server_write_instance_st ptr) +void memcached_io_reset(memcached_instance_st* instance) { - memcached_quit_server(ptr, true); + memcached_quit_server(instance, true); } /** * Read a given number of bytes from the server and place it into a specific * buffer. Reset the IO channel on this server if an error occurs. */ -memcached_return_t memcached_safe_read(memcached_server_write_instance_st ptr, +memcached_return_t memcached_safe_read(memcached_instance_st* instance, void *dta, const size_t size) { @@ -764,7 +860,7 @@ memcached_return_t memcached_safe_read(memcached_server_write_instance_st ptr, ssize_t nread; memcached_return_t rc; - while (memcached_continue(rc= memcached_io_read(ptr, data + offset, size - offset, nread))) { }; + while (memcached_continue(rc= memcached_io_read(instance, data + offset, size - offset, nread))) { }; if (memcached_failed(rc)) { @@ -777,7 +873,7 @@ memcached_return_t memcached_safe_read(memcached_server_write_instance_st ptr, return MEMCACHED_SUCCESS; } -memcached_return_t memcached_io_readline(memcached_server_write_instance_st ptr, +memcached_return_t memcached_io_readline(memcached_instance_st* instance, char *buffer_ptr, size_t size, size_t& total_nr) @@ -787,7 +883,7 @@ memcached_return_t memcached_io_readline(memcached_server_write_instance_st ptr, while (line_complete == false) { - if (ptr->read_buffer_length == 0) + if (instance->read_buffer_length == 0) { /* * We don't have any data in the buffer, so let's fill the read @@ -795,11 +891,11 @@ memcached_return_t memcached_io_readline(memcached_server_write_instance_st ptr, * the logic. */ ssize_t nread; - memcached_return_t rc= memcached_io_read(ptr, buffer_ptr, 1, nread); + memcached_return_t rc= memcached_io_read(instance, buffer_ptr, 1, nread); if (memcached_failed(rc) and rc == MEMCACHED_IN_PROGRESS) { - memcached_quit_server(ptr, true); - return memcached_set_error(*ptr, rc, MEMCACHED_AT); + memcached_quit_server(instance, true); + return memcached_set_error(*instance, rc, MEMCACHED_AT); } else if (memcached_failed(rc)) { @@ -816,15 +912,15 @@ memcached_return_t memcached_io_readline(memcached_server_write_instance_st ptr, } /* Now let's look in the buffer and copy as we go! */ - while (ptr->read_buffer_length && total_nr < size && !line_complete) + while (instance->read_buffer_length and total_nr < size and line_complete == false) { - *buffer_ptr = *ptr->read_ptr; + *buffer_ptr = *instance->read_ptr; if (*buffer_ptr == '\n') { line_complete = true; } - --ptr->read_buffer_length; - ++ptr->read_ptr; + --instance->read_buffer_length; + ++instance->read_ptr; ++total_nr; ++buffer_ptr; }