C++ベースのLightweightなHTTPサーバー
| Revision | 2a7cb64db6481cc02ffb97efbe3100b01faed698 (tree) |
|---|---|
| Time | 2013-01-20 10:51:35 |
| Author | Michio Hirai <smg_ykz@user...> |
| Commiter | Michio Hirai |
[Function] New introduction of cm::VectorSocket class to realize gather-write operation.
| @@ -22,6 +22,7 @@ cxx_library_static(cm_socket "" | ||
| 22 | 22 | cm_socket_server.cpp |
| 23 | 23 | cm_socket_client.cpp |
| 24 | 24 | cm_socket.cpp |
| 25 | + cm_vector_socket.cpp | |
| 25 | 26 | ) |
| 26 | 27 | |
| 27 | 28 | gmock_executable(cm_thread_test "cm_thread" |
| @@ -38,5 +39,9 @@ gmock_executable(cm_socket_server_test "cm_socket;cm_event" | ||
| 38 | 39 | test/cm_socket_server_client_test.cpp |
| 39 | 40 | ) |
| 40 | 41 | |
| 42 | +gmock_executable(cm_vector_socket_test "cm_socket;" | |
| 43 | + test/cm_vector_socket_test.cpp | |
| 44 | +) | |
| 45 | + | |
| 41 | 46 | enable_coverage() |
| 42 | 47 |
| @@ -7,16 +7,16 @@ | ||
| 7 | 7 | namespace cm { |
| 8 | 8 | |
| 9 | 9 | Socket::Socket() |
| 10 | - : fd_(-1), need_open_(true) | |
| 10 | + : fd_(-1) | |
| 11 | 11 | {} |
| 12 | 12 | |
| 13 | 13 | Socket::Socket(int fd) |
| 14 | - : fd_(fd), need_open_(false) | |
| 14 | + : fd_(fd) | |
| 15 | 15 | {} |
| 16 | 16 | |
| 17 | 17 | Socket::~Socket() |
| 18 | 18 | { |
| 19 | - if ((!need_open_) && (fd_ >= 0)) { | |
| 19 | + if (fd_ >= 0) { | |
| 20 | 20 | close(fd_); |
| 21 | 21 | fd_ = -1; |
| 22 | 22 | } |
| @@ -71,6 +71,13 @@ int Socket::getFD() const | ||
| 71 | 71 | return fd_; |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | +int Socket::release() | |
| 75 | +{ | |
| 76 | + int fd_to_return = fd_; | |
| 77 | + fd_ = -1; | |
| 78 | + return fd_to_return; | |
| 79 | +} | |
| 80 | + | |
| 74 | 81 | SocketIf* Socket::doClone(int fd) const |
| 75 | 82 | { |
| 76 | 83 | return new Socket(fd); |
| @@ -0,0 +1,29 @@ | ||
| 1 | + | |
| 2 | +#include <cassert> | |
| 3 | + | |
| 4 | +#include "mt_shim_clear_memory.h" | |
| 5 | +#include "cm_vector_socket.h" | |
| 6 | + | |
| 7 | +namespace cm { | |
| 8 | + | |
| 9 | +VectorSocket::VectorSocket(int fd) | |
| 10 | + : fd_(fd) | |
| 11 | +{} | |
| 12 | + | |
| 13 | +bool VectorSocket::writev(size_t& bytes_written, const IOVectorBase& vector, size_t skip_bytes) | |
| 14 | +{ | |
| 15 | + assert(fd_ >= 0); | |
| 16 | + | |
| 17 | + struct iovec iovector[MAX_IOVEC_COUNT]; | |
| 18 | + | |
| 19 | + mt::clearMemory(iovector); | |
| 20 | + | |
| 21 | + vector.setToVector(iovector); | |
| 22 | + | |
| 23 | + bytes_written = ::writev(fd_, iovector, vector.getCount()); | |
| 24 | + | |
| 25 | + return true; | |
| 26 | +} | |
| 27 | + | |
| 28 | + | |
| 29 | +} // namespace cm |
| @@ -71,6 +71,7 @@ public: | ||
| 71 | 71 | |
| 72 | 72 | MOCK_METHOD3(read, bool(size_t& bytes_read, void* buf, size_t size_to_read)); |
| 73 | 73 | MOCK_METHOD3(write, bool(size_t& bytes_read, const void* buf, size_t size_to_write)); |
| 74 | + MOCK_METHOD0(release, int()); | |
| 74 | 75 | |
| 75 | 76 | virtual int getFD() const |
| 76 | 77 | { |
| @@ -0,0 +1,79 @@ | ||
| 1 | + | |
| 2 | +#include <unistd.h> | |
| 3 | + | |
| 4 | +#include "gmock/gmock.h" | |
| 5 | + | |
| 6 | +#include "mt_typelist_algo_utility.h" | |
| 7 | +#include "mt_shim_clear_memory.h" | |
| 8 | +#include "cm_vector_socket.h" | |
| 9 | +#include "cm_socket.h" | |
| 10 | + | |
| 11 | +namespace { | |
| 12 | + | |
| 13 | +#pragma GCC diagnostic ignored "-Weffc++" | |
| 14 | +struct StringBufferMock | |
| 15 | +{ | |
| 16 | + MOCK_CONST_METHOD0(getString, const char*()); | |
| 17 | +}; | |
| 18 | +#pragma GCC diagnostic warning "-Weffc++" | |
| 19 | + | |
| 20 | +struct RFRelease | |
| 21 | +{ | |
| 22 | + RFRelease(StringBufferMock* mock_ptr) | |
| 23 | + : mock_ptr_(mock_ptr) | |
| 24 | + {} | |
| 25 | + | |
| 26 | + void operator()() | |
| 27 | + { | |
| 28 | + delete mock_ptr_; | |
| 29 | + } | |
| 30 | + | |
| 31 | + StringBufferMock* mock_ptr_; | |
| 32 | +}; | |
| 33 | + | |
| 34 | +static const char string1[] = "hello"; | |
| 35 | +static const char string2[] = " "; | |
| 36 | +static const char string3[] = "world "; | |
| 37 | +static const char string4[] = "!!!!"; | |
| 38 | + | |
| 39 | +static const char string_for_mock[] = "[good by cruel world --]"; | |
| 40 | + | |
| 41 | +TEST(CmVectorSocketTest, simple) | |
| 42 | +{ | |
| 43 | + StringBufferMock* mock = new StringBufferMock; | |
| 44 | + | |
| 45 | + EXPECT_CALL(*mock, getString()) | |
| 46 | + .WillOnce(::testing::Return(string_for_mock)); | |
| 47 | + | |
| 48 | + const cm::IOVectorBase& iov | |
| 49 | + = cm::IOVec()(string1, sizeof(string1) - 1) | |
| 50 | + (string2, sizeof(string2) - 1) | |
| 51 | + (string3, sizeof(string3) - 1) | |
| 52 | + (string4, sizeof(string4) - 1) | |
| 53 | + (mock->getString(), sizeof(string_for_mock) - 1, RFRelease(mock)); | |
| 54 | + | |
| 55 | + int pipe_fds[2]; | |
| 56 | + int retval = pipe(pipe_fds); | |
| 57 | + EXPECT_EQ(retval, 0); | |
| 58 | + | |
| 59 | + cm::VectorSocket write_socket(pipe_fds[1]); | |
| 60 | + cm::Socket read_socket(pipe_fds[0]); | |
| 61 | + | |
| 62 | + size_t bytes_written = 0u; | |
| 63 | + bool ret = write_socket.writev(bytes_written, iov); | |
| 64 | + | |
| 65 | + EXPECT_EQ(ret, true); | |
| 66 | + | |
| 67 | + size_t total_bytes_written = sizeof(string1) - 1 + sizeof(string2) - 1 + sizeof(string3) - 1 + sizeof(string4) - 1 + sizeof(string_for_mock) - 1; | |
| 68 | + EXPECT_EQ(bytes_written, total_bytes_written); | |
| 69 | + | |
| 70 | + size_t bytes_read = 0u; | |
| 71 | + char recv_buffer[1024]; | |
| 72 | + mt::clearMemory(recv_buffer); | |
| 73 | + ret = read_socket.read(bytes_read, recv_buffer, sizeof(recv_buffer)); | |
| 74 | + | |
| 75 | + EXPECT_EQ(ret, true); | |
| 76 | + EXPECT_EQ(bytes_read, total_bytes_written); | |
| 77 | +} | |
| 78 | + | |
| 79 | +} // namespace |
| @@ -0,0 +1,139 @@ | ||
| 1 | + | |
| 2 | +#ifndef INC_CM_IO_VECTOR_H_ | |
| 3 | +#define INC_CM_IO_VECTOR_H_ | |
| 4 | + | |
| 5 | +#include "mt_static_assert.h" | |
| 6 | +#include "mt_member_detector.h" | |
| 7 | +#include "mt_typelist.h" | |
| 8 | +#include "mt_typelist_algo_utility.h" | |
| 9 | + | |
| 10 | +namespace cm { | |
| 11 | + | |
| 12 | +struct IOVectorBase | |
| 13 | +{ | |
| 14 | + virtual ~IOVectorBase() | |
| 15 | + {} | |
| 16 | + | |
| 17 | + virtual void setToVector(struct iovec*) const = 0; | |
| 18 | + | |
| 19 | + virtual size_t getCount() const = 0; | |
| 20 | +}; | |
| 21 | + | |
| 22 | +template <typename TL, typename TLList> | |
| 23 | +struct IOVectorChain : IOVectorBase | |
| 24 | +{ | |
| 25 | +public: | |
| 26 | + typedef typename TL::Head T; | |
| 27 | + typedef typename TL::Tail RF; | |
| 28 | + | |
| 29 | + static const size_t LAYERED_COUNT = IOVectorChain<typename TLList::Head, typename TLList::Tail>::LAYERED_COUNT + 1; | |
| 30 | + | |
| 31 | + IOVectorChain(const IOVectorChain<TL, TLList>& rhs) | |
| 32 | + : ptr_(rhs.ptr_), size_(rhs.size_), release_functor_(rhs.release_functor_), tail_(rhs.tail_), should_release_(rhs.should_release_) | |
| 33 | + { | |
| 34 | + rhs.should_release_ = false; | |
| 35 | + } | |
| 36 | + | |
| 37 | + IOVectorChain(const T* ptr, size_t size, RF release_functor, const IOVectorChain<typename TLList::Head, typename TLList::Tail>& obj) | |
| 38 | + : ptr_(ptr), size_(size), release_functor_(release_functor), tail_(obj), should_release_(true) | |
| 39 | + {} | |
| 40 | + | |
| 41 | + template <typename Functor> | |
| 42 | + void performReleaseFunctor(Functor& func, typename mt::EnableIf<mt::HasFunctorOperator<Functor>::value>::Result* = 0) | |
| 43 | + { | |
| 44 | + release_functor_(); | |
| 45 | + } | |
| 46 | + | |
| 47 | + template <typename Functor> | |
| 48 | + void performReleaseFunctor(Functor&, typename mt::EnableIf<!mt::HasFunctorOperator<Functor>::value>::Result* = 0) | |
| 49 | + { | |
| 50 | + } | |
| 51 | + | |
| 52 | + virtual ~IOVectorChain() | |
| 53 | + { | |
| 54 | + if (should_release_) { | |
| 55 | + performReleaseFunctor(release_functor_); | |
| 56 | + } | |
| 57 | + } | |
| 58 | + | |
| 59 | + void setToVector(struct iovec* iovec_ptr) const | |
| 60 | + { | |
| 61 | + STATIC_ASSERT(LAYERED_COUNT >= 1); | |
| 62 | + const size_t offset = LAYERED_COUNT - 1; | |
| 63 | + iovec_ptr[offset].iov_base = const_cast<void*>(reinterpret_cast<const void*>(ptr_)); | |
| 64 | + iovec_ptr[offset].iov_len = size_; | |
| 65 | + tail_.setToVector(iovec_ptr); | |
| 66 | + } | |
| 67 | + | |
| 68 | + virtual size_t getCount() const | |
| 69 | + { | |
| 70 | + return LAYERED_COUNT; | |
| 71 | + } | |
| 72 | + | |
| 73 | + template <typename X, typename XRF> | |
| 74 | + IOVectorChain< mt::Typelist<X, XRF>, mt::Typelist<TL, TLList> > | |
| 75 | + operator()(const X* ptr, size_t size, XRF x_release_functor) | |
| 76 | + { | |
| 77 | + return IOVectorChain< mt::Typelist<X, XRF>, mt::Typelist<TL, TLList> >(ptr, size, x_release_functor, *this); | |
| 78 | + } | |
| 79 | + | |
| 80 | + template <typename X> | |
| 81 | + IOVectorChain< mt::Typelist<X, mt::NullType>, mt::Typelist<TL, TLList> > operator()(const X* ptr, size_t size) | |
| 82 | + { | |
| 83 | + return IOVectorChain< mt::Typelist<X, mt::NullType>, mt::Typelist<TL, TLList> >(ptr, size, mt::getNullType(), *this); | |
| 84 | + } | |
| 85 | + | |
| 86 | +private: | |
| 87 | + const T* ptr_; | |
| 88 | + size_t size_; | |
| 89 | + RF release_functor_; | |
| 90 | + const IOVectorChain<typename TLList::Head, typename TLList::Tail> tail_; | |
| 91 | + mutable bool should_release_; | |
| 92 | +}; | |
| 93 | + | |
| 94 | +template <> | |
| 95 | +struct IOVectorChain<mt::NullType, mt::NullType> | |
| 96 | +{ | |
| 97 | +public: | |
| 98 | + static const size_t LAYERED_COUNT = 0; | |
| 99 | + | |
| 100 | + IOVectorChain() | |
| 101 | + : should_release_(true) | |
| 102 | + {} | |
| 103 | + | |
| 104 | + IOVectorChain(const IOVectorChain<mt::NullType, mt::NullType>& rhs) | |
| 105 | + : should_release_(rhs.should_release_) | |
| 106 | + { | |
| 107 | + rhs.should_release_ = false; | |
| 108 | + } | |
| 109 | + | |
| 110 | + virtual ~IOVectorChain() | |
| 111 | + {} | |
| 112 | + | |
| 113 | + void setToVector(struct iovec*) const | |
| 114 | + { | |
| 115 | + return; | |
| 116 | + } | |
| 117 | + | |
| 118 | + template <typename T, typename RF> | |
| 119 | + IOVectorChain< mt::Typelist<T, RF>, mt::Typelist<mt::NullType, mt::NullType> > | |
| 120 | + operator()(const T* ptr, size_t size, RF release_functor) | |
| 121 | + { | |
| 122 | + return IOVectorChain< mt::Typelist<T, RF>, mt::Typelist<mt::NullType, mt::NullType> >(ptr, size, release_functor, *this); | |
| 123 | + } | |
| 124 | + | |
| 125 | + template <typename T> | |
| 126 | + IOVectorChain< mt::Typelist<T, mt::NullType>, mt::Typelist<mt::NullType, mt::NullType> > | |
| 127 | + operator()(const T* ptr, size_t size) | |
| 128 | + { | |
| 129 | + return IOVectorChain< mt::Typelist<T, mt::NullType>, mt::Typelist<mt::NullType, mt::NullType> >(ptr, size, mt::getNullType(), *this); | |
| 130 | + } | |
| 131 | + | |
| 132 | + mutable bool should_release_; | |
| 133 | +}; | |
| 134 | + | |
| 135 | +typedef IOVectorChain<mt::NullType, mt::NullType> IOVec; | |
| 136 | + | |
| 137 | + | |
| 138 | +} // namespace cm | |
| 139 | +#endif // INC_CM_IO_VECTOR_H_ |
| @@ -3,6 +3,8 @@ | ||
| 3 | 3 | #define INC_CM_SOCKET_H_ |
| 4 | 4 | |
| 5 | 5 | #include "cm_socket_if.h" |
| 6 | +#include "mt_mpl.h" | |
| 7 | +#include "mt_member_detector.h" | |
| 6 | 8 | |
| 7 | 9 | namespace cm { |
| 8 | 10 |
| @@ -12,6 +14,11 @@ public: | ||
| 12 | 14 | Socket(); |
| 13 | 15 | Socket(int fd); |
| 14 | 16 | |
| 17 | + template <typename SocketType> | |
| 18 | + Socket(SocketType& socket, typename mt::EnableIf<mt::HasReleaseMethod<SocketType>::value>::Result* = 0) | |
| 19 | + : fd_(socket.release()) | |
| 20 | + {} | |
| 21 | + | |
| 15 | 22 | virtual ~Socket(); |
| 16 | 23 | |
| 17 | 24 | virtual bool read(size_t& bytes_read, void* buf, size_t size_to_read); |
| @@ -19,11 +26,11 @@ public: | ||
| 19 | 26 | |
| 20 | 27 | virtual int getFD() const; |
| 21 | 28 | |
| 29 | + virtual int release(); | |
| 22 | 30 | private: |
| 23 | 31 | virtual SocketIf* doClone(int fd) const; |
| 24 | 32 | |
| 25 | 33 | int fd_; |
| 26 | - bool need_open_; | |
| 27 | 34 | }; |
| 28 | 35 | |
| 29 | 36 | } // namespace cm |
| @@ -22,6 +22,8 @@ public: | ||
| 22 | 22 | return mt::AutoPtr<SocketIf>(this->doClone(fd)); |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | + virtual int release() = 0; | |
| 26 | + | |
| 25 | 27 | protected: |
| 26 | 28 | virtual SocketIf* doClone(int fd) const = 0; |
| 27 | 29 |
| @@ -63,6 +63,8 @@ public: | ||
| 63 | 63 | return false; |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | + MOCK_METHOD0(release, int()); | |
| 67 | + | |
| 66 | 68 | MOCK_METHOD3(write, bool(size_t& bytes_written, const void* buf, size_t size_to_write)); |
| 67 | 69 | MOCK_CONST_METHOD1(doClone, SocketIf*(int fd)); |
| 68 | 70 |
| @@ -0,0 +1,36 @@ | ||
| 1 | +#ifndef INC_CM_VECTOR_SOCKET_H_ | |
| 2 | +#define INC_CM_VECTOR_SOCKET_H_ | |
| 3 | + | |
| 4 | +#include <unistd.h> | |
| 5 | +#include <sys/uio.h> | |
| 6 | +#include <vector> | |
| 7 | +#include <iostream> | |
| 8 | + | |
| 9 | +#include "mt_member_detector.h" | |
| 10 | +#include "mt_mpl.h" | |
| 11 | +#include "cm_io_vector.h" | |
| 12 | + | |
| 13 | +namespace cm { | |
| 14 | + | |
| 15 | +class VectorSocket | |
| 16 | +{ | |
| 17 | +public: | |
| 18 | + VectorSocket(int fd); | |
| 19 | + | |
| 20 | + template <typename SocketType> | |
| 21 | + VectorSocket(SocketType& socket, typename mt::EnableIf<mt::HasReleaseMethod<SocketType>::value>::Result* = 0) | |
| 22 | + : fd_(socket.release()) | |
| 23 | + {} | |
| 24 | + | |
| 25 | + static const int MAX_IOVEC_COUNT = 64; | |
| 26 | + | |
| 27 | + bool writev(size_t& bytes_written, const IOVectorBase& vector, size_t skip_bytes = 0u); | |
| 28 | + // bool readv(size_t& bytes_read, IOVector<T>& vector); | |
| 29 | + | |
| 30 | +private: | |
| 31 | + int fd_; | |
| 32 | +}; | |
| 33 | + | |
| 34 | +} // namespace cm | |
| 35 | + | |
| 36 | +#endif // INC_CM_VECTOR_SOCKET_H_ |
| @@ -30,9 +30,8 @@ public: | ||
| 30 | 30 | {} |
| 31 | 31 | |
| 32 | 32 | AutoPtr(AutoPtr<T>& rhs) |
| 33 | - : raw_ptr_(rhs.get()) | |
| 33 | + : raw_ptr_(rhs.release()) | |
| 34 | 34 | { |
| 35 | - rhs.release(); | |
| 36 | 35 | } |
| 37 | 36 | |
| 38 | 37 | AutoPtr(AutoPtrRef<T> ref) |
| @@ -49,9 +48,7 @@ public: | ||
| 49 | 48 | |
| 50 | 49 | operator AutoPtrRef<T> () |
| 51 | 50 | { |
| 52 | - T* ptr = this->get(); | |
| 53 | - this->release(); | |
| 54 | - return AutoPtrRef<T>(ptr); | |
| 51 | + return AutoPtrRef<T>(release()); | |
| 55 | 52 | } |
| 56 | 53 | |
| 57 | 54 | ~AutoPtr() |
| @@ -59,9 +56,11 @@ public: | ||
| 59 | 56 | reset(); |
| 60 | 57 | } |
| 61 | 58 | |
| 62 | - void release() | |
| 59 | + T* release() | |
| 63 | 60 | { |
| 61 | + T* ptr_to_return = raw_ptr_; | |
| 64 | 62 | raw_ptr_ = 0; |
| 63 | + return ptr_to_return; | |
| 65 | 64 | } |
| 66 | 65 | |
| 67 | 66 | void set(T* new_ptr) |
| @@ -117,9 +116,6 @@ public: | ||
| 117 | 116 | } |
| 118 | 117 | |
| 119 | 118 | private: |
| 120 | - // Won't allow conventional copy semantics from another const AutoPtr object. | |
| 121 | -// AutoPtr(const AutoPtr& rhs); | |
| 122 | -// AutoPtr& operator=(const AutoPtr& rhs); | |
| 123 | 119 | |
| 124 | 120 | T* raw_ptr_; |
| 125 | 121 | }; |
| @@ -0,0 +1,37 @@ | ||
| 1 | + | |
| 2 | +#ifndef INC_MT_MEMBER_DETECTOR_H_ | |
| 3 | +#define INC_MT_MEMBER_DETECTOR_H_ | |
| 4 | + | |
| 5 | +#include "mt_sfinae.h" | |
| 6 | + | |
| 7 | +namespace mt { | |
| 8 | + | |
| 9 | +// Meta function to detect whether parameterized type T has void operator() member function. | |
| 10 | +template <typename T> | |
| 11 | +struct HasFunctorOperator | |
| 12 | +{ | |
| 13 | + template <typename X> | |
| 14 | + static mt::YesTypeWithNonConstMemberMethodNoArg<X, void, &X::operator()> test(X*); | |
| 15 | + | |
| 16 | + template <typename X> | |
| 17 | + static mt::NoType test(...); | |
| 18 | + | |
| 19 | + static const bool value = sizeof(test<T>(0)) != sizeof(mt::NoType); | |
| 20 | +}; | |
| 21 | + | |
| 22 | +// Meta function to detect whether parameterized type T has int relase() member function | |
| 23 | +template <typename T> | |
| 24 | +struct HasReleaseMethod | |
| 25 | +{ | |
| 26 | + template <typename X> | |
| 27 | + static mt::YesTypeWithNonConstMemberMethodNoArg<X, int, &X::release> test(T*); | |
| 28 | + | |
| 29 | + template <typename X> | |
| 30 | + static mt::NoType test(...); | |
| 31 | + | |
| 32 | + static const bool value = sizeof(test<T>(0)) != sizeof(mt::NoType); | |
| 33 | +}; | |
| 34 | + | |
| 35 | +} // namespace mt | |
| 36 | + | |
| 37 | +#endif // INC_MT_MEMBER_DETECTOR_H_ |
| @@ -16,6 +16,6 @@ struct StaticAssert<true> | ||
| 16 | 16 | |
| 17 | 17 | } // namespace mt |
| 18 | 18 | |
| 19 | -#define STATIC_ASSERT(cond) mt::unuseWithMessage(StaticAssert< (cond) >(), #cond) | |
| 19 | +#define STATIC_ASSERT(cond) mt::unuseWithMessage(mt::StaticAssert< (cond) >(), #cond) | |
| 20 | 20 | |
| 21 | 21 | #endif // INC_MT_STATIC_ASSERT_H_ |