mirror of https://github.com/facebook/rocksdb.git
Create leveldb server via Thrift.
Summary: First draft. Unit tests pass. Test Plan: unit tests attached Reviewers: heyongqiang Reviewed By: heyongqiang Differential Revision: https://reviews.facebook.net/D3969
This commit is contained in:
parent
22ee777f68
commit
80c663882a
16
Makefile
16
Makefile
|
@ -27,6 +27,7 @@ CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT)
|
|||
LDFLAGS += $(PLATFORM_LDFLAGS)
|
||||
|
||||
LIBOBJECTS = $(SOURCES:.cc=.o)
|
||||
LIBOBJECTS += $(SOURCESCPP:.cpp=.o)
|
||||
MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o)
|
||||
|
||||
TESTUTIL = ./util/testutil.o
|
||||
|
@ -51,7 +52,8 @@ TESTS = \
|
|||
table_test \
|
||||
version_edit_test \
|
||||
version_set_test \
|
||||
write_batch_test
|
||||
write_batch_test \
|
||||
leveldb_server_test
|
||||
|
||||
PROGRAMS = db_bench $(TESTS)
|
||||
BENCHMARKS = db_bench_sqlite3 db_bench_tree_db
|
||||
|
@ -71,20 +73,20 @@ SHARED2 = $(SHARED1).$(SHARED_MAJOR)
|
|||
SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR)
|
||||
SHARED = $(SHARED1) $(SHARED2) $(SHARED3)
|
||||
$(SHARED3):
|
||||
$(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(INSTALL_PATH)/$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3) $(EXEC_LDFLAGS_SHARED)
|
||||
$(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(INSTALL_PATH)/$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) $(SOURCESCPP) -o $(SHARED3) $(EXEC_LDFLAGS_SHARED)
|
||||
$(SHARED2): $(SHARED3)
|
||||
ln -fs $(SHARED3) $(SHARED2)
|
||||
$(SHARED1): $(SHARED3)
|
||||
ln -fs $(SHARED3) $(SHARED1)
|
||||
endif
|
||||
|
||||
all: $(SHARED) $(LIBRARY)
|
||||
all: $(SHARED) $(LIBRARY) $(THRIFTSERVER)
|
||||
|
||||
check: all $(PROGRAMS) $(TESTS)
|
||||
for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done
|
||||
|
||||
clean:
|
||||
-rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk
|
||||
-rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) $(THRIFTSERVER) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk
|
||||
-rm -rf ios-x86/* ios-arm/*
|
||||
|
||||
$(LIBRARY): $(LIBOBJECTS)
|
||||
|
@ -161,6 +163,12 @@ $(MEMENVLIBRARY) : $(MEMENVOBJECTS)
|
|||
memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS)
|
||||
$(CXX) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LDFLAGS)
|
||||
|
||||
leveldb_server: thrift/server.o $(LIBRARY)
|
||||
$(CXX) thrift/server.o $(LIBRARY) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
|
||||
|
||||
leveldb_server_test: thrift/test/simpletest.o $(LIBRARY)
|
||||
$(CXX) thrift/test/simpletest.o $(LIBRARY) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
|
||||
|
||||
ifeq ($(PLATFORM), IOS)
|
||||
# For iOS, create universal object files to be used on both the simulator and
|
||||
# a device.
|
||||
|
|
|
@ -101,15 +101,21 @@ esac
|
|||
# of all files matching either rule, so we need to append -print to make the
|
||||
# prune take effect.
|
||||
DIRS="util db table"
|
||||
if test "$USE_THRIFT"; then
|
||||
DIRS+=" thrift/gen-cpp thrift/server_utils.cpp"
|
||||
THRIFTSERVER=leveldb_server
|
||||
fi
|
||||
set -f # temporarily disable globbing so that our patterns aren't expanded
|
||||
PRUNE_TEST="-name *test*.cc -prune"
|
||||
PRUNE_BENCH="-name *_bench.cc -prune"
|
||||
PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o -name '*.cc' -print | sort | tr "\n" " "`
|
||||
PORTABLE_CPP=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o -name '*.cpp' -print | sort | tr "\n" " "`
|
||||
set +f # re-enable globbing
|
||||
|
||||
# The sources consist of the portable files, plus the platform-specific port
|
||||
# file.
|
||||
echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT
|
||||
echo "SOURCESCPP=$PORTABLE_CPP" >> $OUTPUT
|
||||
echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT
|
||||
|
||||
if [ "$PLATFORM" = "OS_ANDROID_CROSSCOMPILE" ]; then
|
||||
|
@ -136,7 +142,7 @@ EOF
|
|||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DSNAPPY"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lsnappy"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lsnappy -L./snappy/libs"
|
||||
fi
|
||||
|
||||
# Test whether zlib library is installed
|
||||
|
@ -183,6 +189,14 @@ if test "$USE_HDFS"; then
|
|||
PLATFORM_LDFLAGS+=$HDFS_LDFLAGS
|
||||
fi
|
||||
|
||||
# shall we build thrift server
|
||||
if test "$USE_THRIFT"; then
|
||||
THRIFT_CCFLAGS=" -I./thrift -I./thrift/gen-cpp -I./thrift/lib/cpp -I/usr/include -std=gnu++0x"
|
||||
THRIFT_LDFLAGS=" -lserver -lthrift_base -ltransport -lthrift_exception -lutil -L./thrift/libs "
|
||||
COMMON_FLAGS+=$THRIFT_CCFLAGS
|
||||
PLATFORM_LDFLAGS+=$THRIFT_LDFLAGS
|
||||
fi
|
||||
|
||||
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
|
||||
|
||||
|
@ -193,3 +207,4 @@ echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT
|
|||
echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT
|
||||
echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT
|
||||
echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT
|
||||
echo "THRIFTSERVER=$THRIFTSERVER" >> $OUTPUT
|
||||
|
|
|
@ -22,8 +22,13 @@ fi
|
|||
SNAPPY_INCLUDE=" -I ./snappy"
|
||||
SNAPPY_LIBS=" -L./snappy/libs"
|
||||
|
||||
# location of boost headers and libraries
|
||||
THRIFT_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/boost/default/eed002c/include -std=gnu++0x"
|
||||
THRIFT_INCLUDE+=" -I./thrift -I./thrift/gen-cpp -I./thrift/lib/cpp"
|
||||
THRIFT_LIBS=" -L $TOOLCHAIN_LIB_BASE/boost/default/eed002c/lib"
|
||||
|
||||
CC="$TOOLCHAIN_EXECUTABLES/gcc/gcc-4.6.2-glibc-2.13/bin/gcc"
|
||||
CXX="$TOOLCHAIN_EXECUTABLES/gcc/gcc-4.6.2-glibc-2.13/bin/g++ $JINCLUDE $SNAPPY_INCLUDE"
|
||||
CXX="$TOOLCHAIN_EXECUTABLES/gcc/gcc-4.6.2-glibc-2.13/bin/g++ $JINCLUDE $SNAPPY_INCLUDE $THRIFT_INCLUDE"
|
||||
AR=$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/ar
|
||||
RANLIB=$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/ranlib
|
||||
|
||||
|
@ -32,7 +37,7 @@ CFLAGS+=" -I $TOOLCHAIN_LIB_BASE/jemalloc/jemalloc-2.2.5/96de4f9/include -DHAVE_
|
|||
|
||||
EXEC_LDFLAGS=" -Wl,--whole-archive $TOOLCHAIN_LIB_BASE/jemalloc/jemalloc-2.2.4/96de4f9/lib/libjemalloc.a "
|
||||
EXEC_LDFLAGS+="-Wl,--no-whole-archive $TOOLCHAIN_LIB_BASE/libunwind/libunwind-20100810/4bc2c16/lib/libunwind.a"
|
||||
EXEC_LDFLAGS+="$HDFSLIB $SNAPPY_LIBS"
|
||||
EXEC_LDFLAGS+="$HDFSLIB $SNAPPY_LIBS $THRIFT_LIBS "
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $TOOLCHAIN_LIB_BASE/jemalloc/jemalloc-2.2.4/96de4f9/lib/libjemalloc.so"
|
||||
|
||||
export CC CXX AR RANLIB CFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED
|
||||
|
|
|
@ -16,7 +16,7 @@ If you want to compile leveldb with hdfs support, please set the following
|
|||
enviroment variables appropriately:
|
||||
USE_HDFS=1
|
||||
JAVA_HOME=/usr/local/jdk-6u22-64
|
||||
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/amd64/server:$JAVA_HOME/jre/lib/amd64/:./snappy/libs
|
||||
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/jdk-6u22-64/jre/lib/amd64/server:/usr/local/jdk-6u22-64/jre/lib/amd64/:./snappy/libs
|
||||
make clean all db_bench
|
||||
|
||||
To run dbbench,
|
||||
|
|
|
@ -2,5 +2,5 @@ This is a pre-compiled version of snappy 1.0.5 from
|
|||
http://code.google.com/p/snappy/downloads/detail?name=snappy-1.0.5.tar.gz
|
||||
|
||||
This is compiled using gcc-4.6.2-glibc-2.13 on centos5.2
|
||||
and uses jemalloc. This is here so that one can compie leveldb to use
|
||||
and uses jemalloc. This is here so that one can compile leveldb to use
|
||||
snappy easily.
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
This directory has the thrift server code that exposes leveldb apis.
|
||||
|
||||
The thrift header files are in ./thrift/lib. These are part of
|
||||
Apache Thrift code base and are needed for compilation of the leveldb
|
||||
thrift server. The thrift libraries are copied into ./thrift/libs.
|
||||
If you want to use a different version of thrift, please update these
|
||||
directories with the corresponding thrift header files and the
|
||||
compiled thrift libraries.
|
||||
|
||||
If you want to compile leveldb with thrift-server support, please set the following
|
||||
enviroment variables appropriately:
|
||||
USE_THRIFT=1
|
||||
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./thrift/libs
|
||||
make clean leveldb_server leveldb_server_test
|
||||
|
||||
You can run the leveldb server unit tests by
|
||||
./leveldb_server_test
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compiler hints to indicate the fast path of an "if" branch: whether
|
||||
* the if condition is likely to be true or false.
|
||||
*
|
||||
* @author Tudor Bosman (tudorb@fb.com)
|
||||
*/
|
||||
|
||||
#ifndef FOLLY_BASE_LIKELY_H_
|
||||
#define FOLLY_BASE_LIKELY_H_
|
||||
|
||||
#undef LIKELY
|
||||
#undef UNLIKELY
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
#define LIKELY(x) (__builtin_expect((x), 1))
|
||||
#define UNLIKELY(x) (__builtin_expect((x), 0))
|
||||
#else
|
||||
#define LIKELY(x) (x)
|
||||
#define UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
#endif /* FOLLY_BASE_LIKELY_H_ */
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FOLLY_IO_IOBUF_QUEUE_H
|
||||
#define FOLLY_IO_IOBUF_QUEUE_H
|
||||
|
||||
#include "folly/experimental/io/IOBuf.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace folly {
|
||||
|
||||
/**
|
||||
* An IOBufQueue encapsulates a chain of IOBufs and provides
|
||||
* convenience functions to append data to the back of the chain
|
||||
* and remove data from the front.
|
||||
*/
|
||||
class IOBufQueue {
|
||||
public:
|
||||
struct Options {
|
||||
Options() : cacheChainLength(false) { }
|
||||
bool cacheChainLength;
|
||||
};
|
||||
|
||||
explicit IOBufQueue(const Options& options = Options());
|
||||
|
||||
/**
|
||||
* Add a buffer or buffer chain to the end of this queue. The
|
||||
* queue takes ownership of buf.
|
||||
*/
|
||||
void append(std::unique_ptr<folly::IOBuf>&& buf);
|
||||
|
||||
/**
|
||||
* Add a queue to the end of this queue. The queue takes ownership of
|
||||
* all buffers from the other queue.
|
||||
*/
|
||||
void append(IOBufQueue& other);
|
||||
void append(IOBufQueue&& other) {
|
||||
append(other); // call lvalue reference overload, above
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy len bytes, starting at buf, to the end of this queue.
|
||||
* The caller retains ownership of the source data.
|
||||
*/
|
||||
void append(const void* buf, size_t len);
|
||||
|
||||
/**
|
||||
* Copy a string to the end of this queue.
|
||||
* The caller retains ownership of the source data.
|
||||
*/
|
||||
void append(const std::string& buf) {
|
||||
append(buf.data(), buf.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a chain of IOBuf objects that point to consecutive regions
|
||||
* within buf.
|
||||
*
|
||||
* Just like IOBuf::wrapBuffer, this should only be used when the caller
|
||||
* knows ahead of time and can ensure that all IOBuf objects that will point
|
||||
* to this buffer will be destroyed before the buffer itself is destroyed;
|
||||
* all other caveats from wrapBuffer also apply.
|
||||
*
|
||||
* Every buffer except for the last will wrap exactly blockSize bytes.
|
||||
* Importantly, this method may be used to wrap buffers larger than 4GB.
|
||||
*/
|
||||
void wrapBuffer(const void* buf, size_t len,
|
||||
uint32_t blockSize=(1U << 31)); // default block size: 2GB
|
||||
|
||||
/**
|
||||
* Obtain a writable block of contiguous bytes at the end of this
|
||||
* queue, allocating more space if necessary. The amount of space
|
||||
* reserved will be between min and max, inclusive; the IOBufQueue
|
||||
* implementation may pick a value in that range that makes efficient
|
||||
* use of already-allocated internal space.
|
||||
*
|
||||
* If the caller subsequently writes anything into the returned space,
|
||||
* it must call the postallocate() method.
|
||||
*
|
||||
* @return The starting address of the block and the length in bytes.
|
||||
*
|
||||
* @note The point of the preallocate()/postallocate() mechanism is
|
||||
* to support I/O APIs such as Thrift's TAsyncSocket::ReadCallback
|
||||
* that request a buffer from the application and then, in a later
|
||||
* callback, tell the application how much of the buffer they've
|
||||
* filled with data.
|
||||
*/
|
||||
std::pair<void*,uint32_t> preallocate(uint32_t min, uint32_t max);
|
||||
|
||||
/**
|
||||
* Tell the queue that the caller has written data into the first n
|
||||
* bytes provided by the previous preallocate() call.
|
||||
*
|
||||
* @note n should be less than or equal to the size returned by
|
||||
* preallocate(). If n is zero, the caller may skip the call
|
||||
* to postallocate(). If n is nonzero, the caller must not
|
||||
* invoke any other non-const methods on this IOBufQueue between
|
||||
* the call to preallocate and the call to postallocate().
|
||||
*/
|
||||
void postallocate(uint32_t n);
|
||||
|
||||
/**
|
||||
* Obtain a writable block of n contiguous bytes, allocating more space
|
||||
* if necessary, and mark it as used. The caller can fill it later.
|
||||
*/
|
||||
void* allocate(uint32_t n) {
|
||||
void* p = preallocate(n, n).first;
|
||||
postallocate(n);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split off the first n bytes of the queue into a separate IOBuf chain,
|
||||
* and transfer ownership of the new chain to the caller. The IOBufQueue
|
||||
* retains ownership of everything after the split point.
|
||||
*
|
||||
* @warning If the split point lies in the middle of some IOBuf within
|
||||
* the chain, this function may, as an implementation detail,
|
||||
* clone that IOBuf.
|
||||
*
|
||||
* @throws std::underflow_error if n exceeds the number of bytes
|
||||
* in the queue.
|
||||
*/
|
||||
std::unique_ptr<folly::IOBuf> split(size_t n);
|
||||
|
||||
/**
|
||||
* Similar to IOBuf::trimStart, but works on the whole queue. Will
|
||||
* pop off buffers that have been completely trimmed.
|
||||
*/
|
||||
void trimStart(size_t amount);
|
||||
|
||||
/**
|
||||
* Similar to IOBuf::trimEnd, but works on the whole queue. Will
|
||||
* pop off buffers that have been completely trimmed.
|
||||
*/
|
||||
void trimEnd(size_t amount);
|
||||
|
||||
/**
|
||||
* Transfer ownership of the queue's entire IOBuf chain to the caller.
|
||||
*/
|
||||
std::unique_ptr<folly::IOBuf>&& move() {
|
||||
chainLength_ = 0;
|
||||
return std::move(head_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access
|
||||
*/
|
||||
const folly::IOBuf* front() const {
|
||||
return head_.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Total chain length, only valid if cacheLength was specified in the
|
||||
* constructor.
|
||||
*/
|
||||
size_t chainLength() const {
|
||||
if (!options_.cacheChainLength) {
|
||||
throw std::invalid_argument("IOBufQueue: chain length not cached");
|
||||
}
|
||||
return chainLength_;
|
||||
}
|
||||
|
||||
const Options& options() const {
|
||||
return options_;
|
||||
}
|
||||
|
||||
/** Movable */
|
||||
IOBufQueue(IOBufQueue&&);
|
||||
IOBufQueue& operator=(IOBufQueue&&);
|
||||
|
||||
private:
|
||||
static const size_t kChainLengthNotCached = (size_t)-1;
|
||||
/** Not copyable */
|
||||
IOBufQueue(const IOBufQueue&) = delete;
|
||||
IOBufQueue& operator=(const IOBufQueue&) = delete;
|
||||
|
||||
Options options_;
|
||||
size_t chainLength_;
|
||||
/** Everything that has been appended but not yet discarded or moved out */
|
||||
std::unique_ptr<folly::IOBuf> head_;
|
||||
};
|
||||
|
||||
} // folly
|
||||
|
||||
#endif // FOLLY_IO_IOBUF_QUEUE_H
|
|
@ -0,0 +1,2 @@
|
|||
#define CHECK(a)
|
||||
#define CHECK_LT(a, b)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Autogenerated by Thrift
|
||||
*
|
||||
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
* @generated
|
||||
*/
|
||||
#include "leveldb_constants.h"
|
||||
|
||||
namespace Tleveldb {
|
||||
|
||||
const leveldbConstants g_leveldb_constants;
|
||||
|
||||
leveldbConstants::leveldbConstants() {
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* Autogenerated by Thrift
|
||||
*
|
||||
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
* @generated
|
||||
*/
|
||||
#ifndef leveldb_CONSTANTS_H
|
||||
#define leveldb_CONSTANTS_H
|
||||
|
||||
#include "leveldb_types.h"
|
||||
|
||||
namespace Tleveldb {
|
||||
|
||||
class leveldbConstants {
|
||||
public:
|
||||
leveldbConstants();
|
||||
|
||||
};
|
||||
|
||||
extern const leveldbConstants g_leveldb_constants;
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,867 @@
|
|||
/**
|
||||
* Autogenerated by Thrift
|
||||
*
|
||||
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
* @generated
|
||||
*/
|
||||
#ifndef leveldb_TYPES_H
|
||||
#define leveldb_TYPES_H
|
||||
|
||||
#include <Thrift.h>
|
||||
#include <TApplicationException.h>
|
||||
#include <protocol/TProtocol.h>
|
||||
#include <transport/TTransport.h>
|
||||
|
||||
namespace apache { namespace thrift { namespace reflection {
|
||||
class Schema;
|
||||
}}}
|
||||
|
||||
|
||||
namespace Tleveldb {
|
||||
|
||||
enum CompressionType {
|
||||
kNoCompression = 0,
|
||||
kSnappyCompression = 1
|
||||
};
|
||||
|
||||
extern const std::map<int, const char*> _CompressionType_VALUES_TO_NAMES;
|
||||
|
||||
extern const std::map<const char*, int, apache::thrift::ltstr> _CompressionType_NAMES_TO_VALUES;
|
||||
|
||||
enum Code {
|
||||
kOk = 0,
|
||||
kNotFound = 1,
|
||||
kCorruption = 2,
|
||||
kNotSupported = 3,
|
||||
kInvalidArgument = 4,
|
||||
kIOError = 5,
|
||||
kEnd = 6
|
||||
};
|
||||
|
||||
extern const std::map<int, const char*> _Code_VALUES_TO_NAMES;
|
||||
|
||||
extern const std::map<const char*, int, apache::thrift::ltstr> _Code_NAMES_TO_VALUES;
|
||||
|
||||
enum IteratorType {
|
||||
seekToFirst = 0,
|
||||
seekToLast = 1,
|
||||
seekToKey = 2
|
||||
};
|
||||
|
||||
extern const std::map<int, const char*> _IteratorType_VALUES_TO_NAMES;
|
||||
|
||||
extern const std::map<const char*, int, apache::thrift::ltstr> _IteratorType_NAMES_TO_VALUES;
|
||||
|
||||
typedef std::string Text;
|
||||
|
||||
typedef std::string Bytes;
|
||||
|
||||
class Slice {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 2199896239461470156U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
Slice() : data(""), size(0) {
|
||||
}
|
||||
|
||||
Slice(const Slice&) = default;
|
||||
Slice& operator=(const Slice&) = default;
|
||||
Slice(Slice&&) = default;
|
||||
Slice& operator=(Slice&&) = default;
|
||||
|
||||
void __clear() {
|
||||
data = "";
|
||||
size = 0;
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~Slice() throw() {}
|
||||
|
||||
Text data;
|
||||
int32_t size;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
data = false;
|
||||
size = false;
|
||||
}
|
||||
bool data;
|
||||
bool size;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const Slice & rhs) const
|
||||
{
|
||||
if (!(this->data == rhs.data))
|
||||
return false;
|
||||
if (!(this->size == rhs.size))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const Slice &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const Slice & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class Slice;
|
||||
void swap(Slice &a, Slice &b);
|
||||
|
||||
class Range {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 6170219570187881516U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
Range() {
|
||||
}
|
||||
|
||||
Range(const Range&) = default;
|
||||
Range& operator=(const Range&) = default;
|
||||
Range(Range&&) = default;
|
||||
Range& operator=(Range&&) = default;
|
||||
|
||||
void __clear() {
|
||||
start.__clear();
|
||||
limit.__clear();
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~Range() throw() {}
|
||||
|
||||
Slice start;
|
||||
Slice limit;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
start = false;
|
||||
limit = false;
|
||||
}
|
||||
bool start;
|
||||
bool limit;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const Range & rhs) const
|
||||
{
|
||||
if (!(this->start == rhs.start))
|
||||
return false;
|
||||
if (!(this->limit == rhs.limit))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const Range &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const Range & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class Range;
|
||||
void swap(Range &a, Range &b);
|
||||
|
||||
class DBOptions {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 6731746507948871532U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
DBOptions() : create_if_missing(0), error_if_exists(0), write_buffer_size(0), max_open_files(0), block_size(0), block_restart_interval(0), compression(static_cast<CompressionType>(0)) {
|
||||
}
|
||||
|
||||
DBOptions(const DBOptions&) = default;
|
||||
DBOptions& operator=(const DBOptions&) = default;
|
||||
DBOptions(DBOptions&&) = default;
|
||||
DBOptions& operator=(DBOptions&&) = default;
|
||||
|
||||
void __clear() {
|
||||
create_if_missing = 0;
|
||||
error_if_exists = 0;
|
||||
write_buffer_size = 0;
|
||||
max_open_files = 0;
|
||||
block_size = 0;
|
||||
block_restart_interval = 0;
|
||||
compression = static_cast<CompressionType>(0);
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~DBOptions() throw() {}
|
||||
|
||||
bool create_if_missing;
|
||||
bool error_if_exists;
|
||||
int32_t write_buffer_size;
|
||||
int32_t max_open_files;
|
||||
int32_t block_size;
|
||||
int32_t block_restart_interval;
|
||||
CompressionType compression;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
create_if_missing = false;
|
||||
error_if_exists = false;
|
||||
write_buffer_size = false;
|
||||
max_open_files = false;
|
||||
block_size = false;
|
||||
block_restart_interval = false;
|
||||
compression = false;
|
||||
}
|
||||
bool create_if_missing;
|
||||
bool error_if_exists;
|
||||
bool write_buffer_size;
|
||||
bool max_open_files;
|
||||
bool block_size;
|
||||
bool block_restart_interval;
|
||||
bool compression;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const DBOptions & rhs) const
|
||||
{
|
||||
if (!(this->create_if_missing == rhs.create_if_missing))
|
||||
return false;
|
||||
if (!(this->error_if_exists == rhs.error_if_exists))
|
||||
return false;
|
||||
if (!(this->write_buffer_size == rhs.write_buffer_size))
|
||||
return false;
|
||||
if (!(this->max_open_files == rhs.max_open_files))
|
||||
return false;
|
||||
if (!(this->block_size == rhs.block_size))
|
||||
return false;
|
||||
if (!(this->block_restart_interval == rhs.block_restart_interval))
|
||||
return false;
|
||||
if (!(this->compression == rhs.compression))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const DBOptions &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const DBOptions & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class DBOptions;
|
||||
void swap(DBOptions &a, DBOptions &b);
|
||||
|
||||
class WriteOptions {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 8830325115029814540U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
WriteOptions() : sync(0) {
|
||||
}
|
||||
|
||||
WriteOptions(const WriteOptions&) = default;
|
||||
WriteOptions& operator=(const WriteOptions&) = default;
|
||||
WriteOptions(WriteOptions&&) = default;
|
||||
WriteOptions& operator=(WriteOptions&&) = default;
|
||||
|
||||
void __clear() {
|
||||
sync = 0;
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~WriteOptions() throw() {}
|
||||
|
||||
bool sync;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
sync = false;
|
||||
}
|
||||
bool sync;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const WriteOptions & rhs) const
|
||||
{
|
||||
if (!(this->sync == rhs.sync))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const WriteOptions &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const WriteOptions & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class WriteOptions;
|
||||
void swap(WriteOptions &a, WriteOptions &b);
|
||||
|
||||
class Snapshot {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 15670548806217660204U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
Snapshot() : snapshotid(0) {
|
||||
}
|
||||
|
||||
Snapshot(const Snapshot&) = default;
|
||||
Snapshot& operator=(const Snapshot&) = default;
|
||||
Snapshot(Snapshot&&) = default;
|
||||
Snapshot& operator=(Snapshot&&) = default;
|
||||
|
||||
void __clear() {
|
||||
snapshotid = 0;
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~Snapshot() throw() {}
|
||||
|
||||
int64_t snapshotid;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
snapshotid = false;
|
||||
}
|
||||
bool snapshotid;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const Snapshot & rhs) const
|
||||
{
|
||||
if (!(this->snapshotid == rhs.snapshotid))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const Snapshot &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const Snapshot & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class Snapshot;
|
||||
void swap(Snapshot &a, Snapshot &b);
|
||||
|
||||
class ReadOptions {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 1092669993626789804U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
ReadOptions() : verify_checksums(0), fill_cache(0) {
|
||||
}
|
||||
|
||||
ReadOptions(const ReadOptions&) = default;
|
||||
ReadOptions& operator=(const ReadOptions&) = default;
|
||||
ReadOptions(ReadOptions&&) = default;
|
||||
ReadOptions& operator=(ReadOptions&&) = default;
|
||||
|
||||
void __clear() {
|
||||
verify_checksums = 0;
|
||||
fill_cache = 0;
|
||||
snapshot.__clear();
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~ReadOptions() throw() {}
|
||||
|
||||
bool verify_checksums;
|
||||
bool fill_cache;
|
||||
Snapshot snapshot;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
verify_checksums = false;
|
||||
fill_cache = false;
|
||||
snapshot = false;
|
||||
}
|
||||
bool verify_checksums;
|
||||
bool fill_cache;
|
||||
bool snapshot;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const ReadOptions & rhs) const
|
||||
{
|
||||
if (!(this->verify_checksums == rhs.verify_checksums))
|
||||
return false;
|
||||
if (!(this->fill_cache == rhs.fill_cache))
|
||||
return false;
|
||||
if (!(this->snapshot == rhs.snapshot))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const ReadOptions &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const ReadOptions & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class ReadOptions;
|
||||
void swap(ReadOptions &a, ReadOptions &b);
|
||||
|
||||
class DBHandle {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 8973827971994157004U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
DBHandle() : dbname(""), handleid(0) {
|
||||
}
|
||||
|
||||
DBHandle(const DBHandle&) = default;
|
||||
DBHandle& operator=(const DBHandle&) = default;
|
||||
DBHandle(DBHandle&&) = default;
|
||||
DBHandle& operator=(DBHandle&&) = default;
|
||||
|
||||
void __clear() {
|
||||
dbname = "";
|
||||
handleid = 0;
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~DBHandle() throw() {}
|
||||
|
||||
Text dbname;
|
||||
int64_t handleid;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
dbname = false;
|
||||
handleid = false;
|
||||
}
|
||||
bool dbname;
|
||||
bool handleid;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const DBHandle & rhs) const
|
||||
{
|
||||
if (!(this->dbname == rhs.dbname))
|
||||
return false;
|
||||
if (!(this->handleid == rhs.handleid))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const DBHandle &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const DBHandle & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class DBHandle;
|
||||
void swap(DBHandle &a, DBHandle &b);
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 11184146435197093740U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
Iterator() : iteratorid(0) {
|
||||
}
|
||||
|
||||
Iterator(const Iterator&) = default;
|
||||
Iterator& operator=(const Iterator&) = default;
|
||||
Iterator(Iterator&&) = default;
|
||||
Iterator& operator=(Iterator&&) = default;
|
||||
|
||||
void __clear() {
|
||||
iteratorid = 0;
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~Iterator() throw() {}
|
||||
|
||||
int64_t iteratorid;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
iteratorid = false;
|
||||
}
|
||||
bool iteratorid;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const Iterator & rhs) const
|
||||
{
|
||||
if (!(this->iteratorid == rhs.iteratorid))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const Iterator &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const Iterator & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class Iterator;
|
||||
void swap(Iterator &a, Iterator &b);
|
||||
|
||||
class kv {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 16082992224095104076U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
kv() {
|
||||
}
|
||||
|
||||
kv(const kv&) = default;
|
||||
kv& operator=(const kv&) = default;
|
||||
kv(kv&&) = default;
|
||||
kv& operator=(kv&&) = default;
|
||||
|
||||
void __clear() {
|
||||
key.__clear();
|
||||
value.__clear();
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~kv() throw() {}
|
||||
|
||||
Slice key;
|
||||
Slice value;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
key = false;
|
||||
value = false;
|
||||
}
|
||||
bool key;
|
||||
bool value;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const kv & rhs) const
|
||||
{
|
||||
if (!(this->key == rhs.key))
|
||||
return false;
|
||||
if (!(this->value == rhs.value))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const kv &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const kv & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class kv;
|
||||
void swap(kv &a, kv &b);
|
||||
|
||||
class ResultItem {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 13211316281207238796U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
ResultItem() : status(static_cast<Code>(0)) {
|
||||
}
|
||||
|
||||
ResultItem(const ResultItem&) = default;
|
||||
ResultItem& operator=(const ResultItem&) = default;
|
||||
ResultItem(ResultItem&&) = default;
|
||||
ResultItem& operator=(ResultItem&&) = default;
|
||||
|
||||
void __clear() {
|
||||
status = static_cast<Code>(0);
|
||||
value.__clear();
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~ResultItem() throw() {}
|
||||
|
||||
Code status;
|
||||
Slice value;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
status = false;
|
||||
value = false;
|
||||
}
|
||||
bool status;
|
||||
bool value;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const ResultItem & rhs) const
|
||||
{
|
||||
if (!(this->status == rhs.status))
|
||||
return false;
|
||||
if (!(this->value == rhs.value))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const ResultItem &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const ResultItem & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class ResultItem;
|
||||
void swap(ResultItem &a, ResultItem &b);
|
||||
|
||||
class ResultPair {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 14875242256166808460U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
ResultPair() : status(static_cast<Code>(0)) {
|
||||
}
|
||||
|
||||
ResultPair(const ResultPair&) = default;
|
||||
ResultPair& operator=(const ResultPair&) = default;
|
||||
ResultPair(ResultPair&&) = default;
|
||||
ResultPair& operator=(ResultPair&&) = default;
|
||||
|
||||
void __clear() {
|
||||
status = static_cast<Code>(0);
|
||||
keyvalue.__clear();
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~ResultPair() throw() {}
|
||||
|
||||
Code status;
|
||||
kv keyvalue;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
status = false;
|
||||
keyvalue = false;
|
||||
}
|
||||
bool status;
|
||||
bool keyvalue;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const ResultPair & rhs) const
|
||||
{
|
||||
if (!(this->status == rhs.status))
|
||||
return false;
|
||||
if (!(this->keyvalue == rhs.keyvalue))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const ResultPair &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const ResultPair & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class ResultPair;
|
||||
void swap(ResultPair &a, ResultPair &b);
|
||||
|
||||
class ResultSnapshot {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 16627180600575569004U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
ResultSnapshot() : status(static_cast<Code>(0)) {
|
||||
}
|
||||
|
||||
ResultSnapshot(const ResultSnapshot&) = default;
|
||||
ResultSnapshot& operator=(const ResultSnapshot&) = default;
|
||||
ResultSnapshot(ResultSnapshot&&) = default;
|
||||
ResultSnapshot& operator=(ResultSnapshot&&) = default;
|
||||
|
||||
void __clear() {
|
||||
status = static_cast<Code>(0);
|
||||
snapshot.__clear();
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~ResultSnapshot() throw() {}
|
||||
|
||||
Code status;
|
||||
Snapshot snapshot;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
status = false;
|
||||
snapshot = false;
|
||||
}
|
||||
bool status;
|
||||
bool snapshot;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const ResultSnapshot & rhs) const
|
||||
{
|
||||
if (!(this->status == rhs.status))
|
||||
return false;
|
||||
if (!(this->snapshot == rhs.snapshot))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const ResultSnapshot &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const ResultSnapshot & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class ResultSnapshot;
|
||||
void swap(ResultSnapshot &a, ResultSnapshot &b);
|
||||
|
||||
class ResultIterator {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 595886977232564460U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
ResultIterator() : status(static_cast<Code>(0)) {
|
||||
}
|
||||
|
||||
ResultIterator(const ResultIterator&) = default;
|
||||
ResultIterator& operator=(const ResultIterator&) = default;
|
||||
ResultIterator(ResultIterator&&) = default;
|
||||
ResultIterator& operator=(ResultIterator&&) = default;
|
||||
|
||||
void __clear() {
|
||||
status = static_cast<Code>(0);
|
||||
iterator.__clear();
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~ResultIterator() throw() {}
|
||||
|
||||
Code status;
|
||||
Iterator iterator;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
status = false;
|
||||
iterator = false;
|
||||
}
|
||||
bool status;
|
||||
bool iterator;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const ResultIterator & rhs) const
|
||||
{
|
||||
if (!(this->status == rhs.status))
|
||||
return false;
|
||||
if (!(this->iterator == rhs.iterator))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const ResultIterator &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const ResultIterator & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class ResultIterator;
|
||||
void swap(ResultIterator &a, ResultIterator &b);
|
||||
|
||||
class LeveldbException : public apache::thrift::TException {
|
||||
public:
|
||||
|
||||
static const uint64_t _reflection_id = 2551220192341843436U;
|
||||
static void _reflection_register(::apache::thrift::reflection::Schema&);
|
||||
LeveldbException() : message(""), errorCode(static_cast<Code>(0)) {
|
||||
}
|
||||
|
||||
LeveldbException(const LeveldbException&) = default;
|
||||
LeveldbException& operator=(const LeveldbException&) = default;
|
||||
LeveldbException(LeveldbException&&) = default;
|
||||
LeveldbException& operator=(LeveldbException&&) = default;
|
||||
|
||||
void __clear() {
|
||||
message = "";
|
||||
errorCode = static_cast<Code>(0);
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~LeveldbException() throw() {}
|
||||
|
||||
Text message;
|
||||
Code errorCode;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
message = false;
|
||||
errorCode = false;
|
||||
}
|
||||
bool message;
|
||||
bool errorCode;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const LeveldbException & rhs) const
|
||||
{
|
||||
if (!(this->message == rhs.message))
|
||||
return false;
|
||||
if (!(this->errorCode == rhs.errorCode))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const LeveldbException &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const LeveldbException & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
return "LeveldbException";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class LeveldbException;
|
||||
void swap(LeveldbException &a, LeveldbException &b);
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,247 @@
|
|||
/**
|
||||
* Autogenerated by Thrift
|
||||
*
|
||||
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
* @generated SignedSource<<b50937b866513442f6f2690c8af84db0>>
|
||||
*/
|
||||
#ifndef reflection_TYPES_H
|
||||
#define reflection_TYPES_H
|
||||
|
||||
#include <Thrift.h>
|
||||
#include <TApplicationException.h>
|
||||
#include <protocol/TProtocol.h>
|
||||
#include <transport/TTransport.h>
|
||||
|
||||
|
||||
#include <unordered_map>
|
||||
#include <boost/interprocess/containers/flat_map.hpp>
|
||||
|
||||
namespace apache { namespace thrift { namespace reflection {
|
||||
|
||||
enum Type {
|
||||
TYPE_VOID = 0,
|
||||
TYPE_STRING = 1,
|
||||
TYPE_BOOL = 2,
|
||||
TYPE_BYTE = 3,
|
||||
TYPE_I16 = 4,
|
||||
TYPE_I32 = 5,
|
||||
TYPE_I64 = 6,
|
||||
TYPE_DOUBLE = 7,
|
||||
TYPE_ENUM = 8,
|
||||
TYPE_LIST = 9,
|
||||
TYPE_SET = 10,
|
||||
TYPE_MAP = 11,
|
||||
TYPE_STRUCT = 12,
|
||||
TYPE_SERVICE = 13,
|
||||
TYPE_PROGRAM = 14
|
||||
};
|
||||
|
||||
extern const std::map<int, const char*> _Type_VALUES_TO_NAMES;
|
||||
|
||||
extern const std::map<const char*, int, apache::thrift::ltstr> _Type_NAMES_TO_VALUES;
|
||||
|
||||
class StructField {
|
||||
public:
|
||||
|
||||
StructField() : isRequired(0), type(0), name("") {
|
||||
}
|
||||
|
||||
StructField(const StructField&) = default;
|
||||
StructField& operator=(const StructField&) = default;
|
||||
StructField(StructField&&) = default;
|
||||
StructField& operator=(StructField&&) = default;
|
||||
|
||||
void __clear() {
|
||||
isRequired = 0;
|
||||
type = 0;
|
||||
name = "";
|
||||
annotations.clear();
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~StructField() throw() {}
|
||||
|
||||
bool isRequired;
|
||||
int64_t type;
|
||||
std::string name;
|
||||
boost::container::flat_map<std::string, std::string> annotations;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
isRequired = false;
|
||||
type = false;
|
||||
name = false;
|
||||
annotations = false;
|
||||
}
|
||||
bool isRequired;
|
||||
bool type;
|
||||
bool name;
|
||||
bool annotations;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const StructField & rhs) const
|
||||
{
|
||||
if (!(this->isRequired == rhs.isRequired))
|
||||
return false;
|
||||
if (!(this->type == rhs.type))
|
||||
return false;
|
||||
if (!(this->name == rhs.name))
|
||||
return false;
|
||||
if (__isset.annotations != rhs.__isset.annotations)
|
||||
return false;
|
||||
else if (__isset.annotations && !(annotations == rhs.annotations))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const StructField &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const StructField & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class StructField;
|
||||
void swap(StructField &a, StructField &b);
|
||||
|
||||
class DataType {
|
||||
public:
|
||||
|
||||
DataType() : name(""), mapKeyType(0), valueType(0) {
|
||||
}
|
||||
|
||||
DataType(const DataType&) = default;
|
||||
DataType& operator=(const DataType&) = default;
|
||||
DataType(DataType&&) = default;
|
||||
DataType& operator=(DataType&&) = default;
|
||||
|
||||
void __clear() {
|
||||
name = "";
|
||||
fields.clear();
|
||||
mapKeyType = 0;
|
||||
valueType = 0;
|
||||
enumValues.clear();
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~DataType() throw() {}
|
||||
|
||||
std::string name;
|
||||
boost::container::flat_map<int16_t, StructField> fields;
|
||||
int64_t mapKeyType;
|
||||
int64_t valueType;
|
||||
boost::container::flat_map<std::string, int32_t> enumValues;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
name = false;
|
||||
fields = false;
|
||||
mapKeyType = false;
|
||||
valueType = false;
|
||||
enumValues = false;
|
||||
}
|
||||
bool name;
|
||||
bool fields;
|
||||
bool mapKeyType;
|
||||
bool valueType;
|
||||
bool enumValues;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const DataType & rhs) const
|
||||
{
|
||||
if (!(this->name == rhs.name))
|
||||
return false;
|
||||
if (__isset.fields != rhs.__isset.fields)
|
||||
return false;
|
||||
else if (__isset.fields && !(fields == rhs.fields))
|
||||
return false;
|
||||
if (__isset.mapKeyType != rhs.__isset.mapKeyType)
|
||||
return false;
|
||||
else if (__isset.mapKeyType && !(mapKeyType == rhs.mapKeyType))
|
||||
return false;
|
||||
if (__isset.valueType != rhs.__isset.valueType)
|
||||
return false;
|
||||
else if (__isset.valueType && !(valueType == rhs.valueType))
|
||||
return false;
|
||||
if (__isset.enumValues != rhs.__isset.enumValues)
|
||||
return false;
|
||||
else if (__isset.enumValues && !(enumValues == rhs.enumValues))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const DataType &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const DataType & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class DataType;
|
||||
void swap(DataType &a, DataType &b);
|
||||
|
||||
class Schema {
|
||||
public:
|
||||
|
||||
Schema() {
|
||||
}
|
||||
|
||||
Schema(const Schema&) = default;
|
||||
Schema& operator=(const Schema&) = default;
|
||||
Schema(Schema&&) = default;
|
||||
Schema& operator=(Schema&&) = default;
|
||||
|
||||
void __clear() {
|
||||
dataTypes.clear();
|
||||
names.clear();
|
||||
__isset.__clear();
|
||||
}
|
||||
|
||||
virtual ~Schema() throw() {}
|
||||
|
||||
std::unordered_map<int64_t, DataType> dataTypes;
|
||||
std::unordered_map<std::string, int64_t> names;
|
||||
|
||||
struct __isset {
|
||||
__isset() { __clear(); }
|
||||
void __clear() {
|
||||
dataTypes = false;
|
||||
names = false;
|
||||
}
|
||||
bool dataTypes;
|
||||
bool names;
|
||||
} __isset;
|
||||
|
||||
bool operator == (const Schema & rhs) const
|
||||
{
|
||||
if (!(this->dataTypes == rhs.dataTypes))
|
||||
return false;
|
||||
if (!(this->names == rhs.names))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator != (const Schema &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool operator < (const Schema & ) const;
|
||||
|
||||
uint32_t read(apache::thrift::protocol::TProtocol* iprot);
|
||||
uint32_t write(apache::thrift::protocol::TProtocol* oprot) const;
|
||||
|
||||
};
|
||||
|
||||
class Schema;
|
||||
void swap(Schema &a, Schema &b);
|
||||
|
||||
}}} // namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,180 @@
|
|||
#!/usr/local/bin/thrift --gen cpp
|
||||
|
||||
namespace java Tleveldb
|
||||
namespace cpp Tleveldb
|
||||
namespace rb Tleveldb
|
||||
namespace py Tleveldb
|
||||
namespace perl Tleveldb
|
||||
|
||||
// Types
|
||||
typedef binary Text
|
||||
typedef binary Bytes
|
||||
|
||||
// A basic object needed for storing keys and values
|
||||
struct Slice {
|
||||
1:Text data;
|
||||
2:i32 size
|
||||
}
|
||||
|
||||
// Different compression types supported
|
||||
enum CompressionType {
|
||||
kNoCompression = 0x0,
|
||||
kSnappyCompression = 0x1
|
||||
}
|
||||
|
||||
// Error codes
|
||||
enum Code {
|
||||
kOk = 0,
|
||||
kNotFound = 1,
|
||||
kCorruption = 2,
|
||||
kNotSupported = 3,
|
||||
kInvalidArgument = 4,
|
||||
kIOError = 5,
|
||||
kEnd = 6
|
||||
}
|
||||
|
||||
// A range object
|
||||
struct Range {
|
||||
1:Slice start; // Included in the range
|
||||
2:Slice limit // Not included in the range
|
||||
}
|
||||
|
||||
// Options to creating a database
|
||||
struct DBOptions {
|
||||
1:bool create_if_missing;
|
||||
2:bool error_if_exists;
|
||||
3:i32 write_buffer_size;
|
||||
4:i32 max_open_files;
|
||||
5:i32 block_size;
|
||||
6:i32 block_restart_interval;
|
||||
7:CompressionType compression
|
||||
}
|
||||
|
||||
// Options for writing
|
||||
struct WriteOptions {
|
||||
1:bool sync
|
||||
}
|
||||
|
||||
struct Snapshot {
|
||||
1:i64 snapshotid // server generated
|
||||
}
|
||||
|
||||
// Options for reading. If you do not have a
|
||||
// snapshot, set snapshot.snapshotid = 0
|
||||
struct ReadOptions {
|
||||
1:bool verify_checksums;
|
||||
2:bool fill_cache,
|
||||
3:Snapshot snapshot
|
||||
}
|
||||
|
||||
// Represents a open database object
|
||||
struct DBHandle {
|
||||
1:Text dbname; //name of the database
|
||||
2:i64 handleid // server generated
|
||||
}
|
||||
|
||||
struct Iterator {
|
||||
1:i64 iteratorid // server generated
|
||||
}
|
||||
|
||||
// flags for the iterator
|
||||
enum IteratorType {
|
||||
seekToFirst = 0,
|
||||
seekToLast = 1,
|
||||
seekToKey = 2
|
||||
}
|
||||
|
||||
struct kv {
|
||||
1:Slice key;
|
||||
2:Slice value
|
||||
}
|
||||
|
||||
// Return a single value from the Get call
|
||||
struct ResultItem {
|
||||
1:Code status;
|
||||
2:Slice value
|
||||
}
|
||||
|
||||
// Return a key,value from a Scan call
|
||||
struct ResultPair {
|
||||
1:Code status;
|
||||
2:kv keyvalue
|
||||
}
|
||||
|
||||
// Snapshot result
|
||||
struct ResultSnapshot {
|
||||
1:Code status;
|
||||
2:Snapshot snapshot
|
||||
}
|
||||
|
||||
// Iterator result
|
||||
struct ResultIterator {
|
||||
1:Code status;
|
||||
2:Iterator iterator
|
||||
}
|
||||
|
||||
exception LeveldbException {
|
||||
1:Text message,
|
||||
2:Code errorCode
|
||||
}
|
||||
|
||||
// The Database service
|
||||
service DB {
|
||||
|
||||
// opens the database
|
||||
DBHandle Open(1:Text dbname, 2:DBOptions dboptions)
|
||||
throws (1:LeveldbException se),
|
||||
|
||||
// closes the database
|
||||
Code Close(1:DBHandle dbhandle, 2:Text dbname),
|
||||
|
||||
// puts a key in the database
|
||||
Code Put(1:DBHandle dbhandle, 2:kv keyvalue, 3:WriteOptions options),
|
||||
|
||||
// deletes a key from the database
|
||||
Code Delete(1:DBHandle dbhandle, 2:Slice key, 3:WriteOptions options),
|
||||
|
||||
// writes batch of keys into the database
|
||||
Code Write(1:DBHandle dbhandle, 2:list<kv> batch, 3:WriteOptions options),
|
||||
|
||||
// fetch a key from the DB.
|
||||
// ResultItem.status == kNotFound means key is non existant
|
||||
// ResultItem.status == kOk means key is found
|
||||
ResultItem Get(1:DBHandle dbhandle, 2:Slice inputkey,
|
||||
3:ReadOptions options),
|
||||
|
||||
// start iteration over a set of keys. If iteratorType.seekToFirst
|
||||
// is set, then position the iterator at the first key in the source.
|
||||
// If iteratorType.seekToLast is set, then position at the last key in the
|
||||
// source. If iteratorType.seekToKey is set, then position at the first
|
||||
// key in the source that is at or past target.
|
||||
// If any two of iteratorType.seekToFirst & iteratorType.seekToLast
|
||||
// and iteratorType.seekToKey are set, then error.
|
||||
// If either iteratorType.seekToFirst or iteratorType.seekToLast is set,
|
||||
// then target is not used.
|
||||
ResultIterator NewIterator(1:DBHandle dbhandle, 2:ReadOptions options,
|
||||
3:IteratorType iteratorType,
|
||||
4:Slice target),
|
||||
|
||||
// Release resources associated with an iterator allocated previously
|
||||
// via a call to NewIterator. The call to this method may be skipped
|
||||
// if the iterator had already traversed all the keys in the specified
|
||||
// range. If the application wants to terminate a scan before processing
|
||||
// all the resulting keys, then it is essential to invoke this method.
|
||||
Code DeleteIterator(1:DBHandle dbhandle, 2:Iterator iterator),
|
||||
|
||||
// Return the previous/next from this iteration
|
||||
ResultPair GetNext(1:DBHandle dbhandle, 2:Iterator iterator),
|
||||
ResultPair GetPrev(1:DBHandle dbhandle, 2:Iterator iterator),
|
||||
|
||||
// Create snapshot.
|
||||
ResultSnapshot GetSnapshot(1:DBHandle dbhandle),
|
||||
|
||||
// Release snapshots
|
||||
Code ReleaseSnapshot(1:DBHandle dbhandle, 2:Snapshot snapshot),
|
||||
|
||||
// compact a range of keys
|
||||
// begin.size == 0 to start at a range earlier than the first existing key
|
||||
// end.size == 0 to end at a range later than the last existing key
|
||||
Code CompactRange(1:DBHandle dbhandle, 2:Slice begin, 3:Slice end),
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_CLIENTUTIL_H_
|
||||
#define THRIFT_CLIENTUTIL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/transport/TBufferTransports.h"
|
||||
#include "thrift/lib/cpp/transport/TSocket.h"
|
||||
#include "thrift/lib/cpp/protocol/TBinaryProtocol.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace util {
|
||||
|
||||
/*
|
||||
* Versions that accept a host and port
|
||||
*/
|
||||
|
||||
template<typename ClientT, typename ProtocolT, typename TransportT>
|
||||
ClientT* createClient(const std::string& host, uint16_t port) {
|
||||
boost::shared_ptr<transport::TSocket> socket(
|
||||
new transport::TSocket(host, port));
|
||||
// We could specialize this to not create a wrapper transport when
|
||||
// TransportT is TTransport or TSocket. However, everyone should always
|
||||
// use a TFramedTransport or TBufferedTransport wrapper for performance
|
||||
// reasons.
|
||||
boost::shared_ptr<TransportT> transport(new TransportT(socket));
|
||||
boost::shared_ptr<ProtocolT> protocol( new ProtocolT(transport));
|
||||
transport->open();
|
||||
|
||||
return new ClientT(protocol);
|
||||
}
|
||||
|
||||
template<typename ClientT, typename ProtocolT, typename TransportT>
|
||||
boost::shared_ptr<ClientT> createClientPtr(const std::string& host,
|
||||
uint16_t port) {
|
||||
return boost::shared_ptr<ClientT>(
|
||||
createClient<ClientT, ProtocolT, TransportT>(host, port));
|
||||
}
|
||||
|
||||
template<typename ClientT, typename ProtocolT>
|
||||
ClientT* createClient(const std::string& host,
|
||||
uint16_t port,
|
||||
bool useFramed = true) {
|
||||
if (useFramed) {
|
||||
return createClient<ClientT, ProtocolT, transport::TFramedTransport>(
|
||||
host, port);
|
||||
} else {
|
||||
return createClient<ClientT, ProtocolT, transport::TBufferedTransport>(
|
||||
host, port);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ClientT>
|
||||
ClientT* createClient(const std::string& host,
|
||||
uint16_t port,
|
||||
bool useFramed = true) {
|
||||
return createClient<ClientT,
|
||||
protocol::TBinaryProtocolT<transport::TBufferBase> >(
|
||||
host, port, useFramed);
|
||||
}
|
||||
|
||||
template<typename ClientT, typename ProtocolT>
|
||||
boost::shared_ptr<ClientT> createClientPtr(const std::string& host,
|
||||
uint16_t port,
|
||||
bool useFramed = true) {
|
||||
return boost::shared_ptr<ClientT>(
|
||||
createClient<ClientT, ProtocolT>(host, port, useFramed));
|
||||
}
|
||||
|
||||
template<typename ClientT>
|
||||
boost::shared_ptr<ClientT> createClientPtr(const std::string& host,
|
||||
uint16_t port,
|
||||
bool useFramed = true) {
|
||||
return boost::shared_ptr<ClientT>(
|
||||
createClient<ClientT>(host, port, useFramed));
|
||||
}
|
||||
|
||||
/*
|
||||
* Versions that accept TSocketAddress
|
||||
*/
|
||||
|
||||
template<typename ClientT, typename ProtocolT, typename TransportT>
|
||||
ClientT* createClient(const transport::TSocketAddress* address) {
|
||||
boost::shared_ptr<transport::TSocket> socket(
|
||||
new transport::TSocket(address));
|
||||
// We could specialize this to not create a wrapper transport when
|
||||
// TransportT is TTransport or TSocket. However, everyone should always
|
||||
// use a TFramedTransport or TBufferedTransport wrapper for performance
|
||||
// reasons.
|
||||
boost::shared_ptr<TransportT> transport(new TransportT(socket));
|
||||
boost::shared_ptr<ProtocolT> protocol( new ProtocolT(transport));
|
||||
transport->open();
|
||||
|
||||
return new ClientT(protocol);
|
||||
}
|
||||
|
||||
template<typename ClientT, typename ProtocolT, typename TransportT>
|
||||
boost::shared_ptr<ClientT> createClientPtr(
|
||||
const transport::TSocketAddress* address) {
|
||||
return boost::shared_ptr<ClientT>(
|
||||
createClient<ClientT, ProtocolT, TransportT>(address));
|
||||
}
|
||||
|
||||
template<typename ClientT, typename ProtocolT>
|
||||
ClientT* createClient(const transport::TSocketAddress* address,
|
||||
bool useFramed = true) {
|
||||
if (useFramed) {
|
||||
return createClient<ClientT, ProtocolT, transport::TFramedTransport>(
|
||||
address);
|
||||
} else {
|
||||
return createClient<ClientT, ProtocolT, transport::TBufferedTransport>(
|
||||
address);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ClientT>
|
||||
ClientT* createClient(const transport::TSocketAddress* address,
|
||||
bool useFramed = true) {
|
||||
return createClient<ClientT,
|
||||
protocol::TBinaryProtocolT<transport::TBufferBase> >(
|
||||
address, useFramed);
|
||||
}
|
||||
|
||||
template<typename ClientT, typename ProtocolT>
|
||||
boost::shared_ptr<ClientT> createClientPtr(
|
||||
const transport::TSocketAddress* address,
|
||||
bool useFramed = true) {
|
||||
return boost::shared_ptr<ClientT>(
|
||||
createClient<ClientT, ProtocolT>(address, useFramed));
|
||||
}
|
||||
|
||||
template<typename ClientT>
|
||||
boost::shared_ptr<ClientT> createClientPtr(
|
||||
const transport::TSocketAddress* address,
|
||||
bool useFramed = true) {
|
||||
return boost::shared_ptr<ClientT>(
|
||||
createClient<ClientT>(address, useFramed));
|
||||
}
|
||||
|
||||
/*
|
||||
* Versions that accept TSocketAddress and socket options
|
||||
*/
|
||||
|
||||
template<typename ClientT, typename ProtocolT, typename TransportT>
|
||||
ClientT* createClient(const transport::TSocketAddress* address,
|
||||
const transport::TSocket::Options& options) {
|
||||
boost::shared_ptr<transport::TSocket> socket(
|
||||
new transport::TSocket(address));
|
||||
socket->setSocketOptions(options);
|
||||
|
||||
// We could specialize this to not create a wrapper transport when
|
||||
// TransportT is TTransport or TSocket. However, everyone should always
|
||||
// use a TFramedTransport or TBufferedTransport wrapper for performance
|
||||
// reasons.
|
||||
boost::shared_ptr<TransportT> transport(new TransportT(socket));
|
||||
boost::shared_ptr<ProtocolT> protocol( new ProtocolT(transport));
|
||||
transport->open();
|
||||
|
||||
return new ClientT(protocol);
|
||||
}
|
||||
|
||||
template<typename ClientT, typename ProtocolT, typename TransportT>
|
||||
boost::shared_ptr<ClientT> createClientPtr(
|
||||
const transport::TSocketAddress* address,
|
||||
const transport::TSocket::Options& options) {
|
||||
|
||||
return boost::shared_ptr<ClientT>(
|
||||
createClient<ClientT, ProtocolT, TransportT>(address), options);
|
||||
}
|
||||
|
||||
template<typename ClientT, typename ProtocolT>
|
||||
ClientT* createClient(const transport::TSocketAddress* address,
|
||||
const transport::TSocket::Options& options,
|
||||
bool useFramed = true) {
|
||||
if (useFramed) {
|
||||
return createClient<ClientT, ProtocolT, transport::TFramedTransport>(
|
||||
address, options);
|
||||
} else {
|
||||
return createClient<ClientT, ProtocolT, transport::TBufferedTransport>(
|
||||
address, options);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ClientT>
|
||||
ClientT* createClient(const transport::TSocketAddress* address,
|
||||
const transport::TSocket::Options& options,
|
||||
bool useFramed = true
|
||||
) {
|
||||
return createClient<ClientT,
|
||||
protocol::TBinaryProtocolT<transport::TBufferBase> >(
|
||||
address, options, useFramed);
|
||||
}
|
||||
|
||||
template<typename ClientT, typename ProtocolT>
|
||||
boost::shared_ptr<ClientT> createClientPtr(
|
||||
const transport::TSocketAddress* address,
|
||||
const transport::TSocket::Options& options,
|
||||
bool useFramed = true) {
|
||||
return boost::shared_ptr<ClientT>(
|
||||
createClient<ClientT, ProtocolT>(address, options, useFramed));
|
||||
}
|
||||
|
||||
template<typename ClientT>
|
||||
boost::shared_ptr<ClientT> createClientPtr(
|
||||
const transport::TSocketAddress* address,
|
||||
const transport::TSocket::Options& options,
|
||||
bool useFramed = true) {
|
||||
return boost::shared_ptr<ClientT>(
|
||||
createClient<ClientT>(address, options, useFramed));
|
||||
}
|
||||
|
||||
|
||||
}}} // apache::thrift::util
|
||||
|
||||
#endif // THRIFT_CLIENTUTIL_H_
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef THRIFT_EVENTHANDLERBASE_H_
|
||||
#define THRIFT_EVENTHANDLERBASE_H_ 1
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "thrift/lib/cpp/server/TConnectionContext.h"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace apache { namespace thrift {
|
||||
|
||||
using server::TConnectionContext;
|
||||
|
||||
/**
|
||||
* Virtual interface class that can handle events from the processor. To
|
||||
* use this you should subclass it and implement the methods that you care
|
||||
* about. Your subclass can also store local data that you may care about,
|
||||
* such as additional "arguments" to these methods (stored in the object
|
||||
* instance's state).
|
||||
*/
|
||||
class TProcessorEventHandler {
|
||||
public:
|
||||
|
||||
virtual ~TProcessorEventHandler() {}
|
||||
|
||||
/**
|
||||
* Called before calling other callback methods.
|
||||
* Expected to return some sort of context object.
|
||||
* The return value is passed to all other callbacks
|
||||
* for that function invocation.
|
||||
*/
|
||||
virtual void* getContext(const char* fn_name,
|
||||
TConnectionContext* connectionContext) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expected to free resources associated with a context.
|
||||
*/
|
||||
virtual void freeContext(void* ctx, const char* fn_name) { }
|
||||
|
||||
/**
|
||||
* Called before reading arguments.
|
||||
*/
|
||||
virtual void preRead(void* ctx, const char* fn_name) {}
|
||||
|
||||
/**
|
||||
* Called between reading arguments and calling the handler.
|
||||
*/
|
||||
virtual void postRead(void* ctx, const char* fn_name, uint32_t bytes) {}
|
||||
|
||||
/**
|
||||
* Called between calling the handler and writing the response.
|
||||
*/
|
||||
virtual void preWrite(void* ctx, const char* fn_name) {}
|
||||
|
||||
/**
|
||||
* Called after writing the response.
|
||||
*/
|
||||
virtual void postWrite(void* ctx, const char* fn_name, uint32_t bytes) {}
|
||||
|
||||
/**
|
||||
* Called when an async function call completes successfully.
|
||||
*/
|
||||
virtual void asyncComplete(void* ctx, const char* fn_name) {}
|
||||
|
||||
/**
|
||||
* Called if the handler throws an undeclared exception.
|
||||
*/
|
||||
virtual void handlerError(void* ctx, const char* fn_name) {}
|
||||
|
||||
protected:
|
||||
TProcessorEventHandler() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper class used by the generated code to free each context.
|
||||
*/
|
||||
class TProcessorContextFreer {
|
||||
public:
|
||||
TProcessorContextFreer(boost::shared_ptr<TProcessorEventHandler> handler,
|
||||
void* context, const char* method) :
|
||||
handler_(handler), context_(context), method_(method) {}
|
||||
~TProcessorContextFreer() {
|
||||
if (handler_ != NULL) {
|
||||
handler_->freeContext(context_, method_);
|
||||
}
|
||||
}
|
||||
|
||||
void unregister() { handler_.reset(); }
|
||||
|
||||
private:
|
||||
boost::shared_ptr<TProcessorEventHandler> handler_;
|
||||
void* context_;
|
||||
const char* method_;
|
||||
};
|
||||
|
||||
class ContextStack {
|
||||
friend class EventHandlerBase;
|
||||
|
||||
public:
|
||||
ContextStack(
|
||||
const std::vector<boost::shared_ptr<TProcessorEventHandler>>& handlers,
|
||||
const char* method,
|
||||
TConnectionContext* connectionContext)
|
||||
: handlers_(handlers)
|
||||
, method_(method) {
|
||||
for (auto handler: handlers) {
|
||||
ctxs.push_back(handler->getContext(method, connectionContext));
|
||||
}
|
||||
}
|
||||
|
||||
~ContextStack() {
|
||||
for (size_t i = 0; i < handlers_.size(); i++) {
|
||||
handlers_[i]->freeContext(ctxs[i], method_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<void*> ctxs;
|
||||
std::vector<boost::shared_ptr<TProcessorEventHandler>> handlers_;
|
||||
const char* method_;
|
||||
};
|
||||
|
||||
class EventHandlerBase {
|
||||
private:
|
||||
int setEventHandlerPos_;
|
||||
ContextStack* s_;
|
||||
|
||||
public:
|
||||
EventHandlerBase()
|
||||
: setEventHandlerPos_(-1)
|
||||
, s_(NULL)
|
||||
{}
|
||||
|
||||
void addEventHandler(
|
||||
const boost::shared_ptr<TProcessorEventHandler>& handler) {
|
||||
handlers_.push_back(handler);
|
||||
}
|
||||
|
||||
void clearEventHandlers() {
|
||||
handlers_.clear();
|
||||
setEventHandlerPos_ = -1;
|
||||
if (eventHandler_) {
|
||||
setEventHandler(eventHandler_);
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<TProcessorEventHandler> getEventHandler() {
|
||||
return eventHandler_;
|
||||
}
|
||||
|
||||
void setEventHandler(boost::shared_ptr<TProcessorEventHandler> eventHandler) {
|
||||
eventHandler_ = eventHandler;
|
||||
if (setEventHandlerPos_ > 0) {
|
||||
handlers_.erase(handlers_.begin() + setEventHandlerPos_);
|
||||
}
|
||||
setEventHandlerPos_ = handlers_.size();
|
||||
handlers_.push_back(eventHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* These functions are only used in the client handler
|
||||
* implementation. The server process functions maintain
|
||||
* ContextStack on the stack and binds ctx in to the async calls.
|
||||
*
|
||||
* Clients are not thread safe, so using a member variable is okay.
|
||||
* Client send_ and recv_ functions contain parameters based off of
|
||||
* the function call, and adding a parameter there would change the
|
||||
* function signature enough that other thrift users might break.
|
||||
*
|
||||
* The generated code should be the ONLY user of s_. All other functions
|
||||
* should just use the ContextStack parameter.
|
||||
*/
|
||||
ContextStack* getContextStack() {
|
||||
return s_;
|
||||
}
|
||||
|
||||
// Context only freed by freer, this is only used across function calls.
|
||||
void setContextStack(ContextStack* s) {
|
||||
s_ = s;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<ContextStack> getContextStack(
|
||||
const char* fn_name,
|
||||
TConnectionContext* connectionContext) {
|
||||
std::unique_ptr<ContextStack> ctx(
|
||||
new ContextStack(handlers_, fn_name, connectionContext));
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void preWrite(ContextStack* s, const char* fn_name) {
|
||||
if (s) {
|
||||
for (size_t i = 0; i < handlers_.size(); i++) {
|
||||
handlers_[i]->preWrite(s->ctxs[i], fn_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void postWrite(ContextStack* s, const char* fn_name,
|
||||
uint32_t bytes) {
|
||||
if (s) {
|
||||
for (size_t i = 0; i < handlers_.size(); i++) {
|
||||
handlers_[i]->postWrite(s->ctxs[i], fn_name, bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void preRead(ContextStack* s, const char* fn_name) {
|
||||
if (s) {
|
||||
for (size_t i = 0; i < handlers_.size(); i++) {
|
||||
handlers_[i]->preRead(s->ctxs[i], fn_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void postRead(ContextStack* s, const char* fn_name,
|
||||
uint32_t bytes) {
|
||||
if (s) {
|
||||
for (size_t i = 0; i < handlers_.size(); i++) {
|
||||
handlers_[i]->postRead(s->ctxs[i], fn_name, bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handlerError(ContextStack* s, const char* fn_name) {
|
||||
if (s) {
|
||||
for (size_t i = 0; i < handlers_.size(); i++) {
|
||||
handlers_[i]->handlerError(s->ctxs[i], fn_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void asyncComplete(ContextStack* s, const char* fn_name) {
|
||||
if (s) {
|
||||
for (size_t i = 0; i < handlers_.size(); i++) {
|
||||
handlers_[i]->asyncComplete(s->ctxs[i], fn_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<boost::shared_ptr<TProcessorEventHandler>> handlers_;
|
||||
boost::shared_ptr<TProcessorEventHandler> eventHandler_;
|
||||
};
|
||||
|
||||
class TProcessorEventHandlerFactory {
|
||||
public:
|
||||
virtual boost::shared_ptr<TProcessorEventHandler> getEventHandler() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for all thrift processors. Used to automatically attach event
|
||||
* handlers to processors at creation time.
|
||||
*/
|
||||
class TProcessorBase : public EventHandlerBase {
|
||||
public:
|
||||
TProcessorBase();
|
||||
|
||||
static void addProcessorEventHandlerFactory(
|
||||
boost::shared_ptr<TProcessorEventHandlerFactory> factory);
|
||||
|
||||
static void removeProcessorEventHandlerFactory(
|
||||
boost::shared_ptr<TProcessorEventHandlerFactory> factory);
|
||||
|
||||
private:
|
||||
static std::vector<boost::shared_ptr<TProcessorEventHandlerFactory>>
|
||||
registeredHandlerFactories_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for all thrift clients. Used to automatically attach event
|
||||
* handlers to clients at creation time.
|
||||
*/
|
||||
class TClientBase : public EventHandlerBase {
|
||||
public:
|
||||
TClientBase();
|
||||
|
||||
static void addClientEventHandlerFactory(
|
||||
boost::shared_ptr<TProcessorEventHandlerFactory> factory);
|
||||
|
||||
static void removeClientEventHandlerFactory(
|
||||
boost::shared_ptr<TProcessorEventHandlerFactory> factory);
|
||||
|
||||
private:
|
||||
static std::vector<boost::shared_ptr<TProcessorEventHandlerFactory>>
|
||||
registeredHandlerFactories_;
|
||||
};
|
||||
|
||||
}} // apache::thrift
|
||||
|
||||
#endif // #ifndef THRIFT_EVENTHANDLERBASE_H_
|
|
@ -0,0 +1,67 @@
|
|||
Thrift C++ Software Library
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Using Thrift with C++
|
||||
=====================
|
||||
|
||||
The Thrift C++ libraries are built using the GNU tools. Follow the instructions
|
||||
in the top-level README, or run bootstrap.sh in this folder to generate the
|
||||
Makefiles.
|
||||
|
||||
In case you do not want to open another README file, do this:
|
||||
./bootstrap.sh
|
||||
./configure (--with-boost=/usr/local)
|
||||
make
|
||||
sudo make install
|
||||
|
||||
Thrift is divided into two libraries.
|
||||
|
||||
libthrift
|
||||
The core Thrift library contains all the core Thrift code. It requires
|
||||
boost shared pointers, pthreads, and librt.
|
||||
|
||||
libthriftnb
|
||||
This library contains the Thrift nonblocking server, which uses libevent.
|
||||
To link this library you will also need to link libevent.
|
||||
|
||||
Linking Against Thrift
|
||||
======================
|
||||
|
||||
After you build and install Thrift the libraries are installed to
|
||||
/usr/local/lib by default. Make sure this is in your LDPATH.
|
||||
|
||||
On Linux, the best way to do this is to ensure that /usr/local/lib is in
|
||||
your /etc/ld.so.conf and then run /sbin/ldconfig.
|
||||
|
||||
Depending upon whether you are linking dynamically or statically and how
|
||||
your build environment it set up, you may need to include additional
|
||||
libraries when linking against thrift, such as librt and/or libpthread. If
|
||||
you are using libthriftnb you will also need libevent.
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
boost shared pointers
|
||||
http://www.boost.org/libs/smart_ptr/smart_ptr.htm
|
||||
|
||||
libevent (for libthriftnb only)
|
||||
http://monkey.org/~provos/libevent/
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* Copyright 2012 Facebook
|
||||
* @author Tudor Bosman (tudorb@fb.com)
|
||||
*/
|
||||
|
||||
#ifndef THRIFT_LIB_CPP_REFLECTION_H_
|
||||
#define THRIFT_LIB_CPP_REFLECTION_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "reflection_types.h"
|
||||
|
||||
namespace apache {
|
||||
namespace thrift {
|
||||
namespace reflection {
|
||||
|
||||
namespace detail {
|
||||
const size_t kTypeBits = 5;
|
||||
const uint64_t kTypeMask = (1ULL << kTypeBits) - 1;
|
||||
} // namespace detail
|
||||
|
||||
inline int64_t makeTypeId(Type type, uint64_t hash) {
|
||||
return static_cast<int64_t>((hash & ~detail::kTypeMask) | type);
|
||||
}
|
||||
|
||||
inline Type getType(int64_t typeId) {
|
||||
return static_cast<Type>(typeId & detail::kTypeMask);
|
||||
}
|
||||
|
||||
inline bool isBaseType(Type type) {
|
||||
return type <= TYPE_DOUBLE;
|
||||
}
|
||||
|
||||
} // namespace reflection
|
||||
} // namespace thrift
|
||||
} // namespace apache
|
||||
|
||||
#endif /* THRIFT_LIB_CPP_REFLECTION_H_ */
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# TARGETS file for thrift/lib/cpp
|
||||
|
||||
cpp_library (
|
||||
name = "thrift_exception",
|
||||
srcs = [
|
||||
"TApplicationException.cpp",
|
||||
],
|
||||
external_deps = [
|
||||
('boost', None),
|
||||
],
|
||||
)
|
||||
|
||||
cpp_library (
|
||||
name = "thrift",
|
||||
srcs = [
|
||||
"VirtualProfiling.cpp",
|
||||
],
|
||||
deps = [
|
||||
"@/thrift/lib/cpp:thrift_base",
|
||||
"@/thrift/lib/cpp/concurrency",
|
||||
"@/thrift/lib/cpp/processor",
|
||||
"@/thrift/lib/cpp/protocol",
|
||||
"@/thrift/lib/cpp/server",
|
||||
"@/thrift/lib/cpp/transport",
|
||||
"@/thrift/lib/cpp:thrift_exception",
|
||||
]
|
||||
)
|
||||
|
||||
cpp_library (
|
||||
name = "thrift_base",
|
||||
srcs = [
|
||||
"Thrift.cpp",
|
||||
"EventHandlerBase.cpp",
|
||||
],
|
||||
external_deps = [
|
||||
('boost', None),
|
||||
],
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
name = "reflection",
|
||||
srcs = [],
|
||||
deps = [
|
||||
"@/thrift/lib/thrift:reflection-cpp",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,108 @@
|
|||
// Copyright (c) 2006- Facebook
|
||||
// Distributed under the Thrift Software License
|
||||
//
|
||||
// See accompanying file LICENSE or visit the Thrift site at:
|
||||
// http://developers.facebook.com/thrift/
|
||||
|
||||
#ifndef _THRIFT_TAPPLICATIONEXCEPTION_H_
|
||||
#define _THRIFT_TAPPLICATIONEXCEPTION_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/Thrift.h"
|
||||
|
||||
|
||||
namespace apache { namespace thrift {
|
||||
|
||||
namespace protocol {
|
||||
class TProtocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is thrown when some high-level communication errors with
|
||||
* the remote peer occur, and also when a server throws an unexpected
|
||||
* exception from a handler method. Because of the latter case, this
|
||||
* class can be serialized.
|
||||
*/
|
||||
class TApplicationException : public TException {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Error codes for the various types of exceptions.
|
||||
*/
|
||||
enum TApplicationExceptionType
|
||||
{ UNKNOWN = 0
|
||||
, UNKNOWN_METHOD = 1
|
||||
, INVALID_MESSAGE_TYPE = 2
|
||||
, WRONG_METHOD_NAME = 3
|
||||
, BAD_SEQUENCE_ID = 4
|
||||
, MISSING_RESULT = 5
|
||||
, INVALID_TRANSFORM = 6
|
||||
, INVALID_PROTOCOL = 7
|
||||
, UNSUPPORTED_CLIENT_TYPE = 8
|
||||
};
|
||||
|
||||
|
||||
TApplicationException() :
|
||||
type_(UNKNOWN) {}
|
||||
|
||||
TApplicationException(TApplicationExceptionType type) :
|
||||
type_(type) {}
|
||||
|
||||
TApplicationException(const std::string& message) :
|
||||
message_(message),
|
||||
type_(UNKNOWN) {}
|
||||
|
||||
TApplicationException(TApplicationExceptionType type,
|
||||
const std::string& message) :
|
||||
message_(message),
|
||||
type_(type) {}
|
||||
|
||||
virtual ~TApplicationException() throw() {}
|
||||
|
||||
/**
|
||||
* Returns an error code that provides information about the type of error
|
||||
* that has occurred.
|
||||
*
|
||||
* @return Error code
|
||||
*/
|
||||
TApplicationExceptionType getType() {
|
||||
return type_;
|
||||
}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
if (message_.empty()) {
|
||||
switch (type_) {
|
||||
case UNKNOWN : return "TApplicationException: Unknown application exception";
|
||||
case UNKNOWN_METHOD : return "TApplicationException: Unknown method";
|
||||
case INVALID_MESSAGE_TYPE : return "TApplicationException: Invalid message type";
|
||||
case WRONG_METHOD_NAME : return "TApplicationException: Wrong method name";
|
||||
case BAD_SEQUENCE_ID : return "TApplicationException: Bad sequence identifier";
|
||||
case MISSING_RESULT : return "TApplicationException: Missing result";
|
||||
case INVALID_TRANSFORM :
|
||||
return "TApplicationException: Invalid transform";
|
||||
case INVALID_PROTOCOL :
|
||||
return "TApplicationException: Invalid protocol";
|
||||
case UNSUPPORTED_CLIENT_TYPE:
|
||||
return "TApplicationException: Unsupported client type";
|
||||
default : return "TApplicationException: (Invalid exception type)";
|
||||
};
|
||||
} else {
|
||||
return message_.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t read(protocol::TProtocol* iprot);
|
||||
uint32_t write(protocol::TProtocol* oprot) const;
|
||||
|
||||
protected:
|
||||
std::string message_;
|
||||
|
||||
/**
|
||||
* Error code
|
||||
*/
|
||||
TApplicationExceptionType type_;
|
||||
|
||||
};
|
||||
|
||||
}} // apache::thrift
|
||||
|
||||
#endif // #ifndef _THRIFT_TAPPLICATIONEXCEPTION_H_
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef _THRIFT_TDISPATCHPROCESSOR_H_
|
||||
#define _THRIFT_TDISPATCHPROCESSOR_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/TProcessor.h"
|
||||
|
||||
namespace apache { namespace thrift {
|
||||
|
||||
using server::TConnectionContext;
|
||||
|
||||
/**
|
||||
* TDispatchProcessor is a helper class to parse the message header then call
|
||||
* another function to dispatch based on the function name.
|
||||
*
|
||||
* Subclasses must implement dispatchCall() to dispatch on the function name.
|
||||
*/
|
||||
template <class Protocol_>
|
||||
class TDispatchProcessorT : public TProcessor {
|
||||
public:
|
||||
virtual bool process(boost::shared_ptr<protocol::TProtocol> in,
|
||||
boost::shared_ptr<protocol::TProtocol> out,
|
||||
TConnectionContext* connectionContext) {
|
||||
protocol::TProtocol* inRaw = in.get();
|
||||
protocol::TProtocol* outRaw = out.get();
|
||||
|
||||
// Try to dynamic cast to the template protocol type
|
||||
Protocol_* specificIn = dynamic_cast<Protocol_*>(inRaw);
|
||||
Protocol_* specificOut = dynamic_cast<Protocol_*>(outRaw);
|
||||
if (specificIn && specificOut) {
|
||||
return processFast(specificIn, specificOut, connectionContext);
|
||||
}
|
||||
|
||||
// Log the fact that we have to use the slow path
|
||||
T_GENERIC_PROTOCOL(this, inRaw, specificIn);
|
||||
T_GENERIC_PROTOCOL(this, outRaw, specificOut);
|
||||
|
||||
std::string fname;
|
||||
protocol::TMessageType mtype;
|
||||
int32_t seqid;
|
||||
inRaw->readMessageBegin(fname, mtype, seqid);
|
||||
|
||||
// If this doesn't look like a valid call, log an error and return false so
|
||||
// that the server will close the connection.
|
||||
//
|
||||
// (The old generated processor code used to try to skip a T_STRUCT and
|
||||
// continue. However, that seems unsafe.)
|
||||
if (mtype != protocol::T_CALL && mtype != protocol::T_ONEWAY) {
|
||||
GlobalOutput.printf("received invalid message type %d from client",
|
||||
mtype);
|
||||
return false;
|
||||
}
|
||||
|
||||
return this->dispatchCall(inRaw, outRaw, fname, seqid, connectionContext);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool processFast(Protocol_* in, Protocol_* out,
|
||||
TConnectionContext* connectionContext) {
|
||||
std::string fname;
|
||||
protocol::TMessageType mtype;
|
||||
int32_t seqid;
|
||||
in->readMessageBegin(fname, mtype, seqid);
|
||||
|
||||
if (mtype != protocol::T_CALL && mtype != protocol::T_ONEWAY) {
|
||||
GlobalOutput.printf("received invalid message type %d from client",
|
||||
mtype);
|
||||
return false;
|
||||
}
|
||||
|
||||
return this->dispatchCallTemplated(in, out, fname,
|
||||
seqid, connectionContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* dispatchCall() methods must be implemented by subclasses
|
||||
*/
|
||||
virtual bool dispatchCall(apache::thrift::protocol::TProtocol* in,
|
||||
apache::thrift::protocol::TProtocol* out,
|
||||
const std::string& fname, int32_t seqid,
|
||||
TConnectionContext* connectionContext) = 0;
|
||||
|
||||
virtual bool dispatchCallTemplated(Protocol_* in, Protocol_* out,
|
||||
const std::string& fname, int32_t seqid,
|
||||
TConnectionContext* connectionContext) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Non-templatized version of TDispatchProcessor, that doesn't bother trying to
|
||||
* perform a dynamic_cast.
|
||||
*/
|
||||
class TDispatchProcessor : public TProcessor {
|
||||
public:
|
||||
virtual bool process(boost::shared_ptr<protocol::TProtocol> in,
|
||||
boost::shared_ptr<protocol::TProtocol> out,
|
||||
TConnectionContext* connectionContext) {
|
||||
std::string fname;
|
||||
protocol::TMessageType mtype;
|
||||
int32_t seqid;
|
||||
in->readMessageBegin(fname, mtype, seqid);
|
||||
|
||||
if (mtype != protocol::T_CALL && mtype != protocol::T_ONEWAY) {
|
||||
GlobalOutput.printf("received invalid message type %d from client",
|
||||
mtype);
|
||||
return false;
|
||||
}
|
||||
|
||||
return dispatchCall(in.get(), out.get(), fname, seqid, connectionContext);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool dispatchCall(apache::thrift::protocol::TProtocol* in,
|
||||
apache::thrift::protocol::TProtocol* out,
|
||||
const std::string& fname, int32_t seqid,
|
||||
TConnectionContext* connectionContext) = 0;
|
||||
};
|
||||
|
||||
// Specialize TDispatchProcessorT for TProtocol and TDummyProtocol just to use
|
||||
// the generic TDispatchProcessor.
|
||||
template <>
|
||||
class TDispatchProcessorT<protocol::TDummyProtocol> :
|
||||
public TDispatchProcessor {};
|
||||
template <>
|
||||
class TDispatchProcessorT<protocol::TProtocol> :
|
||||
public TDispatchProcessor {};
|
||||
|
||||
}} // apache::thrift
|
||||
|
||||
#endif // _THRIFT_TDISPATCHPROCESSOR_H_
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef THRIFT_TLOGGING_H
|
||||
#define THRIFT_TLOGGING_H 1
|
||||
|
||||
#include "thrift/lib/cpp/thrift_config.h"
|
||||
#include "thrift/lib/cpp/concurrency/Util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* Contains utility macros for debugging and logging.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef THRIFT_HAVE_CLOCK_GETTIME
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
|
||||
#ifdef THRIFT_HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* T_GLOBAL_DEBUGGING_LEVEL = 0: all debugging turned off, debug macros undefined
|
||||
* T_GLOBAL_DEBUGGING_LEVEL = 1: all debugging turned on
|
||||
*/
|
||||
#ifndef T_GLOBAL_DEBUGGING_LEVEL
|
||||
#define T_GLOBAL_DEBUGGING_LEVEL 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* T_GLOBAL_LOGGING_LEVEL = 0: all logging turned off, logging macros undefined
|
||||
* T_GLOBAL_LOGGING_LEVEL = 1: all logging turned on
|
||||
*/
|
||||
#define T_GLOBAL_LOGGING_LEVEL 1
|
||||
|
||||
/**
|
||||
* Standard wrapper around fprintf what will prefix the file name and line
|
||||
* number to the line. Uses T_GLOBAL_DEBUGGING_LEVEL to control whether it is
|
||||
* turned on or off.
|
||||
*
|
||||
* @param format_string
|
||||
*/
|
||||
#define T_DEBUG(format_string,...) \
|
||||
T_DEBUG_L(0, format_string, ##__VA_ARGS__)
|
||||
|
||||
#define COMPUTE_TIME \
|
||||
int64_t nowMs = apache::thrift::concurrency::Util::currentTime(); \
|
||||
time_t nowSec = (time_t) (nowMs / 1000); \
|
||||
nowMs -= nowSec * 1000; \
|
||||
int ms = (int)nowMs; \
|
||||
char dbgtime[26]; \
|
||||
ctime_r(&nowSec, dbgtime); \
|
||||
dbgtime[24] = '\0';
|
||||
|
||||
/**
|
||||
* analogous to T_DEBUG but also prints the time
|
||||
*
|
||||
* @param string format_string input: printf style format string
|
||||
*/
|
||||
#define T_DEBUG_T(format_string,...) \
|
||||
do { \
|
||||
if (T_GLOBAL_DEBUGGING_LEVEL > 0) { \
|
||||
COMPUTE_TIME \
|
||||
fprintf(stderr, "[%s,%d] [%s, %d ms] " format_string " \n", \
|
||||
__FILE__, __LINE__, dbgtime, ms, \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
/**
|
||||
* analogous to T_DEBUG but uses input level to determine whether or not the string
|
||||
* should be logged.
|
||||
*
|
||||
* @param int level: specified debug level
|
||||
* @param string format_string input: format string
|
||||
*/
|
||||
#define T_DEBUG_L(level, format_string, ...) \
|
||||
do { \
|
||||
if (T_GLOBAL_DEBUGGING_LEVEL > (level)) { \
|
||||
COMPUTE_TIME \
|
||||
fprintf(stderr, "[%s,%d] [%s, %d ms] " format_string " \n", \
|
||||
__FILE__, __LINE__, dbgtime, ms, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/**
|
||||
* Explicit error logging. Prints time, file name and line number
|
||||
*
|
||||
* @param string format_string input: printf style format string
|
||||
*/
|
||||
#define T_ERROR(format_string,...) \
|
||||
{ \
|
||||
COMPUTE_TIME \
|
||||
fprintf(stderr,"[%s,%d] [%s, %d ms] ERROR: " format_string " \n", \
|
||||
__FILE__, __LINE__,dbgtime, ms, \
|
||||
##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Analogous to T_ERROR, additionally aborting the process.
|
||||
* WARNING: macro calls abort(), ending program execution
|
||||
*
|
||||
* @param string format_string input: printf style format string
|
||||
*/
|
||||
#define T_ERROR_ABORT(format_string,...) \
|
||||
{ \
|
||||
COMPUTE_TIME \
|
||||
fprintf(stderr,"[%s,%d] [%s, %d ms] ERROR: Going to abort " \
|
||||
format_string " \n", \
|
||||
__FILE__, __LINE__,dbgtime, ms, \
|
||||
##__VA_ARGS__); \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log input message
|
||||
*
|
||||
* @param string format_string input: printf style format string
|
||||
*/
|
||||
#if T_GLOBAL_LOGGING_LEVEL > 0
|
||||
#define T_LOG_OPER(format_string,...) \
|
||||
{ \
|
||||
if (T_GLOBAL_LOGGING_LEVEL > 0) { \
|
||||
COMPUTE_TIME \
|
||||
fprintf(stderr,"[%s, %d ms] " format_string " \n", \
|
||||
dbgtime, ms, ##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define T_LOG_OPER(format_string,...)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* T_GLOBAL_DEBUG_VIRTUAL = 0 or unset: normal operation,
|
||||
* virtual call debug messages disabled
|
||||
* T_GLOBAL_DEBUG_VIRTUAL = 1: log a debug messages whenever an
|
||||
* avoidable virtual call is made
|
||||
* T_GLOBAL_DEBUG_VIRTUAL = 2: record detailed info that can be
|
||||
* printed by calling
|
||||
* apache::thrift::profile_print_info()
|
||||
*/
|
||||
#if T_GLOBAL_DEBUG_VIRTUAL > 1
|
||||
#define T_VIRTUAL_CALL() \
|
||||
::apache::thrift::profile_virtual_call(typeid(*this))
|
||||
#define T_GENERIC_PROTOCOL(template_class, generic_prot, specific_prot) \
|
||||
do { \
|
||||
if (!(specific_prot)) { \
|
||||
::apache::thrift::profile_generic_protocol( \
|
||||
typeid(*template_class), typeid(*generic_prot)); \
|
||||
} \
|
||||
} while (0)
|
||||
#elif T_GLOBAL_DEBUG_VIRTUAL == 1
|
||||
#define T_VIRTUAL_CALL() \
|
||||
fprintf(stderr,"[%s,%d] virtual call\n", __FILE__, __LINE__)
|
||||
#define T_GENERIC_PROTOCOL(template_class, generic_prot, specific_prot) \
|
||||
do { \
|
||||
if (!(specific_prot)) { \
|
||||
fprintf(stderr, \
|
||||
"[%s,%d] failed to cast to specific protocol type\n", \
|
||||
__FILE__, __LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define T_VIRTUAL_CALL()
|
||||
#define T_GENERIC_PROTOCOL(template_class, generic_prot, specific_prot)
|
||||
#endif
|
||||
|
||||
#endif // #ifndef THRIFT_TLOGGING_H
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef THRIFT_TPROCESSOR_H_
|
||||
#define THRIFT_TPROCESSOR_H_ 1
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "thrift/lib/cpp/EventHandlerBase.h"
|
||||
#include "thrift/lib/cpp/server/TConnectionContext.h"
|
||||
#include "thrift/lib/cpp/protocol/TProtocol.h"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace apache { namespace thrift {
|
||||
|
||||
/**
|
||||
* A processor is a generic object that acts upon two streams of data, one
|
||||
* an input and the other an output. The definition of this object is loose,
|
||||
* though the typical case is for some sort of server that either generates
|
||||
* responses to an input stream or forwards data from one pipe onto another.
|
||||
*
|
||||
*/
|
||||
class TProcessor : public TProcessorBase {
|
||||
public:
|
||||
virtual ~TProcessor() {}
|
||||
|
||||
virtual bool process(boost::shared_ptr<protocol::TProtocol> in,
|
||||
boost::shared_ptr<protocol::TProtocol> out,
|
||||
TConnectionContext* connectionContext) = 0;
|
||||
|
||||
bool process(boost::shared_ptr<apache::thrift::protocol::TProtocol> io,
|
||||
TConnectionContext* connectionContext) {
|
||||
return process(io, io, connectionContext);
|
||||
}
|
||||
|
||||
protected:
|
||||
TProcessor() {}
|
||||
};
|
||||
|
||||
class TProcessorFactory {
|
||||
public:
|
||||
virtual ~TProcessorFactory() {}
|
||||
|
||||
/**
|
||||
* Get the TProcessor to use for a particular connection.
|
||||
*
|
||||
* This method is always invoked in the same thread that the connection was
|
||||
* accepted on. This generally means that this call does not need to be
|
||||
* thread safe, as it will always be invoked from a single thread.
|
||||
*/
|
||||
virtual boost::shared_ptr<TProcessor> getProcessor(
|
||||
server::TConnectionContext* ctx) = 0;
|
||||
};
|
||||
|
||||
class TSingletonProcessorFactory : public TProcessorFactory {
|
||||
public:
|
||||
explicit TSingletonProcessorFactory(
|
||||
const boost::shared_ptr<TProcessor>& processor) :
|
||||
processor_(processor) {}
|
||||
|
||||
boost::shared_ptr<TProcessor> getProcessor(server::TConnectionContext*) {
|
||||
return processor_;
|
||||
}
|
||||
|
||||
boost::shared_ptr<TProcessor> getProcessor() {
|
||||
return processor_;
|
||||
}
|
||||
|
||||
private:
|
||||
boost::shared_ptr<TProcessor> processor_;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a helper class to allow boost::shared_ptr to be used with handler
|
||||
* pointers returned by the generated handler factories.
|
||||
*
|
||||
* The handler factory classes generated by the thrift compiler return raw
|
||||
* pointers, and factory->releaseHandler() must be called when the handler is
|
||||
* no longer needed.
|
||||
*
|
||||
* A ReleaseHandler object can be instantiated and passed as the second
|
||||
* parameter to a shared_ptr, so that factory->releaseHandler() will be called
|
||||
* when the object is no longer needed, instead of deleting the pointer.
|
||||
*/
|
||||
template<typename HandlerFactory_>
|
||||
class ReleaseHandler {
|
||||
public:
|
||||
explicit ReleaseHandler(
|
||||
const boost::shared_ptr<HandlerFactory_>& handlerFactory) :
|
||||
handlerFactory_(handlerFactory) {}
|
||||
|
||||
void operator()(typename HandlerFactory_::Handler* handler) {
|
||||
if (handler) {
|
||||
handlerFactory_->releaseHandler(handler);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
boost::shared_ptr<HandlerFactory_> handlerFactory_;
|
||||
};
|
||||
|
||||
}} // apache::thrift
|
||||
|
||||
#endif // #ifndef THRIFT_TPROCESSOR_H_
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef _THRIFT_TREFLECTIONLOCAL_H_
|
||||
#define _THRIFT_TREFLECTIONLOCAL_H_ 1
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstring>
|
||||
#include "thrift/lib/cpp/protocol/TProtocol.h"
|
||||
|
||||
/**
|
||||
* Local Reflection is a blanket term referring to the the structure
|
||||
* and generation of this particular representation of Thrift types.
|
||||
* (It is called local because it cannot be serialized by Thrift).
|
||||
*
|
||||
*/
|
||||
|
||||
namespace apache { namespace thrift { namespace reflection { namespace local {
|
||||
|
||||
using apache::thrift::protocol::TType;
|
||||
|
||||
// We include this many bytes of the structure's fingerprint when serializing
|
||||
// a top-level structure. Long enough to make collisions unlikely, short
|
||||
// enough to not significantly affect the amount of memory used.
|
||||
const int FP_PREFIX_LEN = 4;
|
||||
|
||||
struct FieldMeta {
|
||||
int16_t tag;
|
||||
bool is_optional;
|
||||
};
|
||||
|
||||
struct TypeSpec {
|
||||
TType ttype;
|
||||
uint8_t fp_prefix[FP_PREFIX_LEN];
|
||||
|
||||
// Use an anonymous union here so we can fit two TypeSpecs in one cache line.
|
||||
union {
|
||||
struct {
|
||||
// Use parallel arrays here for denser packing (of the arrays).
|
||||
FieldMeta* metas;
|
||||
TypeSpec** specs;
|
||||
} tstruct;
|
||||
struct {
|
||||
TypeSpec *subtype1;
|
||||
TypeSpec *subtype2;
|
||||
} tcontainer;
|
||||
};
|
||||
|
||||
// Static initialization of unions isn't really possible,
|
||||
// so take the plunge and use constructors.
|
||||
// Hopefully they'll be evaluated at compile time.
|
||||
|
||||
TypeSpec(TType ttype) : ttype(ttype) {
|
||||
std::memset(fp_prefix, 0, FP_PREFIX_LEN);
|
||||
}
|
||||
|
||||
TypeSpec(TType ttype,
|
||||
const uint8_t* fingerprint,
|
||||
FieldMeta* metas,
|
||||
TypeSpec** specs) :
|
||||
ttype(ttype)
|
||||
{
|
||||
std::memcpy(fp_prefix, fingerprint, FP_PREFIX_LEN);
|
||||
tstruct.metas = metas;
|
||||
tstruct.specs = specs;
|
||||
}
|
||||
|
||||
TypeSpec(TType ttype, TypeSpec* subtype1, TypeSpec* subtype2) :
|
||||
ttype(ttype)
|
||||
{
|
||||
std::memset(fp_prefix, 0, FP_PREFIX_LEN);
|
||||
tcontainer.subtype1 = subtype1;
|
||||
tcontainer.subtype2 = subtype2;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}}}} // apache::thrift::reflection::local
|
||||
|
||||
#endif // #ifndef _THRIFT_TREFLECTIONLOCAL_H_
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef THRIFT_THRIFT_H_
|
||||
#define THRIFT_THRIFT_H_
|
||||
|
||||
#include "thrift/lib/cpp/thrift_config.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#ifdef THRIFT_HAVE_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <exception>
|
||||
#include <typeinfo>
|
||||
#include <string.h>
|
||||
#include "thrift/lib/cpp/TLogging.h"
|
||||
|
||||
namespace apache { namespace thrift {
|
||||
|
||||
struct ltstr {
|
||||
bool operator()(const char* s1, const char* s2) const {
|
||||
return strcmp(s1, s2) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper template class for enum<->string conversion.
|
||||
*/
|
||||
template<typename T>
|
||||
struct TEnumTraits {
|
||||
/**
|
||||
* Finds the name of a given enum value, returning it or NULL on failure.
|
||||
* Specialized implementations will be emitted as part of enum codegen.
|
||||
*
|
||||
* Example specialization:
|
||||
* template<>
|
||||
* const char* TEnumTraits<MyEnum>::findName(MyEnum value) {
|
||||
* return findName(_MyEnum_VALUES_TO_NAMES, value);
|
||||
* }
|
||||
* Note the use of helper function 'findName(...)', below.
|
||||
*/
|
||||
static const char* findName(T value);
|
||||
/**
|
||||
* Attempts to find a value for a given name.
|
||||
* Specialized implementations will be emitted as part of enum codegen.
|
||||
*
|
||||
* Example implementation:
|
||||
* template<>
|
||||
* bool TEnumTraits<MyEnum>::findValue(const char* name,
|
||||
* MyEnum* outValue) {
|
||||
* return findValue(_MyEnum_NAMES_TO_VALUES, name, outValue);
|
||||
* }
|
||||
* Note the use of helper function 'findValue(...)', below.
|
||||
*/
|
||||
static bool findValue(const char* name, T* outValue);
|
||||
private:
|
||||
/**
|
||||
* Helper method used by codegen implementation of findName, Supports
|
||||
* use with strict and non-strict enums by way of template parameter
|
||||
* 'ValueType'.
|
||||
*/
|
||||
template<typename ValueType>
|
||||
static const char* findName(const std::map<ValueType, const char*>& map,
|
||||
T value) {
|
||||
auto found = map.find(value);
|
||||
if (found == map.end()) {
|
||||
return NULL;
|
||||
} else {
|
||||
return found->second;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method used by codegen implementation of findValue, Supports
|
||||
* use with strict and non-strict enums by way of template parameter
|
||||
* 'ValueType'.
|
||||
*/
|
||||
template<typename ValueType>
|
||||
static bool findValue(const std::map<const char*, ValueType, ltstr>& map,
|
||||
const char* name, T* out) {
|
||||
auto found = map.find(name);
|
||||
if (found == map.end()) {
|
||||
return false;
|
||||
} else {
|
||||
*out = static_cast<T>(found->second);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class TEnumIterator : public std::map<T, char*>::iterator {
|
||||
public:
|
||||
TEnumIterator(int n,
|
||||
T* enums,
|
||||
const char** names) :
|
||||
ii_(0), n_(n), enums_(enums), names_(names) {
|
||||
}
|
||||
|
||||
int operator ++() {
|
||||
return ++ii_;
|
||||
}
|
||||
|
||||
bool operator !=(const TEnumIterator<T>& end) {
|
||||
assert(end.n_ == -1);
|
||||
return (ii_ != n_);
|
||||
}
|
||||
|
||||
std::pair<T, const char*> operator*() const {
|
||||
return std::make_pair(enums_[ii_], names_[ii_]);
|
||||
}
|
||||
|
||||
private:
|
||||
int ii_;
|
||||
const int n_;
|
||||
T* enums_;
|
||||
const char** names_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class TEnumInverseIterator : public std::map<T, char*>::iterator {
|
||||
public:
|
||||
TEnumInverseIterator(int n,
|
||||
T* enums,
|
||||
const char** names) :
|
||||
ii_(0), n_(n), enums_(enums), names_(names) {
|
||||
}
|
||||
|
||||
int operator ++() {
|
||||
return ++ii_;
|
||||
}
|
||||
|
||||
bool operator !=(const TEnumInverseIterator<T>& end) {
|
||||
assert(end.n_ == -1);
|
||||
return (ii_ != n_);
|
||||
}
|
||||
|
||||
std::pair<const char*, T> operator*() const {
|
||||
return std::make_pair(names_[ii_], enums_[ii_]);
|
||||
}
|
||||
|
||||
private:
|
||||
int ii_;
|
||||
const int n_;
|
||||
T* enums_;
|
||||
const char** names_;
|
||||
};
|
||||
|
||||
class TOutput {
|
||||
public:
|
||||
TOutput() : f_(&errorTimeWrapper) {}
|
||||
|
||||
inline void setOutputFunction(void (*function)(const char *)){
|
||||
f_ = function;
|
||||
}
|
||||
|
||||
inline void operator()(const char *message){
|
||||
f_(message);
|
||||
}
|
||||
|
||||
// It is important to have a const char* overload here instead of
|
||||
// just the string version, otherwise errno could be corrupted
|
||||
// if there is some problem allocating memory when constructing
|
||||
// the string.
|
||||
void perror(const char *message, int errno_copy);
|
||||
inline void perror(const std::string &message, int errno_copy) {
|
||||
perror(message.c_str(), errno_copy);
|
||||
}
|
||||
|
||||
void printf(const char *message, ...);
|
||||
|
||||
inline static void errorTimeWrapper(const char* msg) {
|
||||
time_t now;
|
||||
char dbgtime[26];
|
||||
time(&now);
|
||||
ctime_r(&now, dbgtime);
|
||||
dbgtime[24] = 0;
|
||||
fprintf(stderr, "Thrift: %s %s\n", dbgtime, msg);
|
||||
}
|
||||
|
||||
/** Just like strerror_r but returns a C++ string object. */
|
||||
static std::string strerror_s(int errno_copy);
|
||||
|
||||
private:
|
||||
void (*f_)(const char *);
|
||||
};
|
||||
|
||||
extern TOutput GlobalOutput;
|
||||
|
||||
/**
|
||||
* Base class for all Thrift exceptions.
|
||||
* Should never be instantiated, only caught.
|
||||
*/
|
||||
class TException : public std::exception {
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for exceptions from the Thrift library, and occasionally
|
||||
* from the generated code. This class should not be thrown by user code.
|
||||
* Instances of this class are not meant to be serialized.
|
||||
*/
|
||||
class TLibraryException : public TException {
|
||||
public:
|
||||
TLibraryException() {}
|
||||
|
||||
explicit TLibraryException(const std::string& message) :
|
||||
message_(message) {}
|
||||
|
||||
TLibraryException(const char* message, int errnoValue);
|
||||
|
||||
virtual ~TLibraryException() throw() {}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
if (message_.empty()) {
|
||||
return "Default TLibraryException.";
|
||||
} else {
|
||||
return message_.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string message_;
|
||||
|
||||
};
|
||||
|
||||
#if T_GLOBAL_DEBUG_VIRTUAL > 1
|
||||
void profile_virtual_call(const std::type_info& info);
|
||||
void profile_generic_protocol(const std::type_info& template_type,
|
||||
const std::type_info& prot_type);
|
||||
void profile_print_info(FILE *f);
|
||||
void profile_print_info();
|
||||
void profile_write_pprof(FILE* gen_calls_f, FILE* virtual_calls_f);
|
||||
#endif
|
||||
|
||||
}} // apache::thrift
|
||||
|
||||
#endif // #ifndef THRIFT_THRIFT_H_
|
|
@ -0,0 +1,98 @@
|
|||
#ifndef _THRIFT_ASYNC_SIMPLECALLBACK_H_
|
||||
#define _THRIFT_ASYNC_SIMPLECALLBACK_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/Thrift.h"
|
||||
namespace apache { namespace thrift {
|
||||
|
||||
/**
|
||||
* A template class for forming simple method callbacks with either an empty
|
||||
* argument list or one argument of known type.
|
||||
*
|
||||
* For more efficiency where tr1::function is overkill.
|
||||
*/
|
||||
|
||||
template<typename C, ///< class whose method we wish to wrap
|
||||
typename A = void, ///< type of argument
|
||||
typename R = void> ///< type of return value
|
||||
class SimpleCallback {
|
||||
typedef R (C::*cfptr_t)(A); ///< pointer-to-member-function type
|
||||
cfptr_t fptr_; ///< the embedded function pointer
|
||||
C* obj_; ///< object whose function we're wrapping
|
||||
public:
|
||||
/**
|
||||
* Constructor for empty callback object.
|
||||
*/
|
||||
SimpleCallback() :
|
||||
fptr_(NULL), obj_(NULL) {}
|
||||
/**
|
||||
* Construct callback wrapper for member function.
|
||||
*
|
||||
* @param fptr pointer-to-member-function
|
||||
* @param "this" for object associated with callback
|
||||
*/
|
||||
SimpleCallback(cfptr_t fptr, const C* obj) :
|
||||
fptr_(fptr), obj_(const_cast<C*>(obj))
|
||||
{}
|
||||
|
||||
/**
|
||||
* Make a call to the member function we've wrapped.
|
||||
*
|
||||
* @param i argument for the wrapped member function
|
||||
* @return value from that function
|
||||
*/
|
||||
R operator()(A i) const {
|
||||
(obj_->*fptr_)(i);
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return obj_ != NULL && fptr_ != NULL;
|
||||
}
|
||||
|
||||
~SimpleCallback() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Specialization of SimpleCallback for empty argument list.
|
||||
*/
|
||||
template<typename C, ///< class whose method we wish to wrap
|
||||
typename R> ///< type of return value
|
||||
class SimpleCallback<C, void, R> {
|
||||
typedef R (C::*cfptr_t)(); ///< pointer-to-member-function type
|
||||
cfptr_t fptr_; ///< the embedded function pointer
|
||||
C* obj_; ///< object whose function we're wrapping
|
||||
public:
|
||||
/**
|
||||
* Constructor for empty callback object.
|
||||
*/
|
||||
SimpleCallback() :
|
||||
fptr_(NULL), obj_(NULL) {}
|
||||
|
||||
/**
|
||||
* Construct callback wrapper for member function.
|
||||
*
|
||||
* @param fptr pointer-to-member-function
|
||||
* @param obj "this" for object associated with callback
|
||||
*/
|
||||
SimpleCallback(cfptr_t fptr, const C* obj) :
|
||||
fptr_(fptr), obj_(const_cast<C*>(obj))
|
||||
{}
|
||||
|
||||
/**
|
||||
* Make a call to the member function we've wrapped.
|
||||
*
|
||||
* @return value from that function
|
||||
*/
|
||||
R operator()() const {
|
||||
(obj_->*fptr_)();
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return obj_ != NULL && fptr_ != NULL;
|
||||
}
|
||||
|
||||
~SimpleCallback() {}
|
||||
};
|
||||
|
||||
}} // apache::thrift
|
||||
|
||||
#endif /* !_THRIFT_ASYNC_SIMPLECALLBACK_H_ */
|
|
@ -0,0 +1,103 @@
|
|||
# The base async library
|
||||
cpp_library(
|
||||
name = 'async_base',
|
||||
srcs = [
|
||||
'TAsyncServerSocket.cpp',
|
||||
'TAsyncSignalHandler.cpp',
|
||||
'TAsyncSocket.cpp',
|
||||
'TAsyncTimeout.cpp',
|
||||
'TBinaryAsyncChannel.cpp',
|
||||
'THeaderAsyncChannel.cpp',
|
||||
'TEventBase.cpp',
|
||||
'TEventBaseManager.cpp',
|
||||
'TEventHandler.cpp',
|
||||
'TFramedAsyncChannel.cpp',
|
||||
'TNotificationPipe.cpp',
|
||||
'TUnframedAsyncChannel.cpp',
|
||||
'THttpAsyncChannel.cpp',
|
||||
],
|
||||
deps = [
|
||||
'@/folly/experimental/io',
|
||||
'@/thrift/lib/cpp:thrift_base',
|
||||
'@/thrift/lib/cpp/transport',
|
||||
'@/thrift/lib/cpp/transport:header',
|
||||
'@/thrift/lib/cpp/protocol',
|
||||
'@/thrift/lib/cpp/server',
|
||||
'@/thrift/lib/cpp/util:httpparser',
|
||||
],
|
||||
external_deps = [ ('libevent', None) ],
|
||||
)
|
||||
|
||||
# TEventServer library (async name is used all over), now depends on ssl
|
||||
cpp_library(
|
||||
name = 'async',
|
||||
srcs = [
|
||||
'TEventConnection.cpp',
|
||||
'TEventServer.cpp',
|
||||
'TEventTask.cpp',
|
||||
'TEventWorker.cpp',
|
||||
],
|
||||
deps = [
|
||||
':async_ssl',
|
||||
],
|
||||
)
|
||||
|
||||
cpp_library (
|
||||
name = "async_ssl",
|
||||
srcs = [
|
||||
'TAsyncSSLServerSocket.cpp',
|
||||
'TAsyncSSLSocket.cpp',
|
||||
],
|
||||
deps = [
|
||||
":async_base",
|
||||
"@/thrift/lib/cpp/transport:transport_ssl",
|
||||
],
|
||||
external_deps = [ ("openssl", None, "ssl"),
|
||||
("openssl", None, "crypto") ],
|
||||
)
|
||||
|
||||
# This library is the same as async_ssl, except that it has the debug
|
||||
# logging level set to 4, whereas async_ssl has debug logging disabled.
|
||||
cpp_library (
|
||||
name = "async_ssl_log",
|
||||
srcs = [
|
||||
'TAsyncSSLServerSocket.cpp',
|
||||
'TAsyncSSLSocket.cpp',
|
||||
],
|
||||
deps = [
|
||||
":async_base",
|
||||
"@/thrift/lib/cpp/transport:transport_ssl",
|
||||
],
|
||||
external_deps = [ ("openssl", None, "ssl"),
|
||||
("openssl", None, "crypto") ],
|
||||
preprocessor_flags = [
|
||||
"-DT_GLOBAL_DEBUGGING_LEVEL=4"
|
||||
],
|
||||
output_subdir = "async_ssl_log",
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
name = 'zlib',
|
||||
srcs = [
|
||||
'TZlibAsyncChannel.cpp',
|
||||
],
|
||||
deps = [
|
||||
':async',
|
||||
'@/thrift/lib/cpp/transport:zlib',
|
||||
],
|
||||
)
|
||||
|
||||
# Parts of the async library that depend on C++11 features.
|
||||
# This is separate from the main async library for now so that users who cannot
|
||||
# depend on C++11 features yet will still be able to use the bulk of the async
|
||||
# library.
|
||||
cpp_library(
|
||||
name = 'async_cxx11',
|
||||
srcs = [
|
||||
'TAsyncTimeoutSet.cpp',
|
||||
],
|
||||
deps = [
|
||||
':async_base',
|
||||
'@/folly',
|
||||
],
|
||||
)
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright (c) 2006- Facebook
|
||||
// Distributed under the Thrift Software License
|
||||
//
|
||||
// See accompanying file LICENSE or visit the Thrift site at:
|
||||
// http://developers.facebook.com/thrift/
|
||||
|
||||
#ifndef THRIFT_ASYNC_TASYNCCHANNEL_H_
|
||||
#define THRIFT_ASYNC_TASYNCCHANNEL_H_ 1
|
||||
|
||||
#include <tr1/functional>
|
||||
#include "thrift/lib/cpp/Thrift.h"
|
||||
#include "thrift/lib/cpp/transport/TTransportUtils.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
class TAsyncTransport;
|
||||
|
||||
/**
|
||||
* TAsyncChannel defines an asynchronous API for message-based I/O.
|
||||
*/
|
||||
class TAsyncChannel {
|
||||
public:
|
||||
typedef std::tr1::function<void()> VoidCallback;
|
||||
|
||||
virtual ~TAsyncChannel() {}
|
||||
|
||||
// is the channel readable (possibly closed by the remote site)?
|
||||
virtual bool readable() const = 0;
|
||||
// is the channel in a good state?
|
||||
virtual bool good() const = 0;
|
||||
virtual bool error() const = 0;
|
||||
virtual bool timedOut() const = 0;
|
||||
|
||||
/**
|
||||
* Send a message over the channel.
|
||||
*
|
||||
* @return call "cob" on success, "errorCob" on fail. Caller must be ready
|
||||
* for either cob to be called before return. Only one cob will be
|
||||
* called and it will be called exactly once per invocation.
|
||||
*/
|
||||
virtual void sendMessage(
|
||||
const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
apache::thrift::transport::TMemoryBuffer* message) = 0;
|
||||
|
||||
/**
|
||||
* Receive a message from the channel.
|
||||
*
|
||||
* @return call "cob" on success, "errorCob" on fail. Caller must be ready
|
||||
* for either cob to be called before return. Only one cob will be
|
||||
* called and it will be called exactly once per invocation.
|
||||
*/
|
||||
virtual void recvMessage(
|
||||
const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
apache::thrift::transport::TMemoryBuffer* message) = 0;
|
||||
|
||||
/**
|
||||
* Send a message over the channel and receive a response.
|
||||
*
|
||||
* @return call "cob" on success, "errorCob" on fail. Caller must be ready
|
||||
* for either cob to be called before return. Only one cob will be
|
||||
* called and it will be called exactly once per invocation.
|
||||
*/
|
||||
virtual void sendAndRecvMessage(
|
||||
const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
apache::thrift::transport::TMemoryBuffer* sendBuf,
|
||||
apache::thrift::transport::TMemoryBuffer* recvBuf) = 0;
|
||||
|
||||
/**
|
||||
* Send a message over the channel, single cob version. (See above.)
|
||||
*
|
||||
* @return call "cob" on success or fail; channel status must be queried
|
||||
* by the cob.
|
||||
*/
|
||||
void sendMessage(const VoidCallback& cob,
|
||||
apache::thrift::transport::TMemoryBuffer* message) {
|
||||
return sendMessage(cob, cob, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive a message from the channel, single cob version. (See above.)
|
||||
*
|
||||
* @return call "cob" on success or fail; channel status must be queried
|
||||
* by the cob.
|
||||
*/
|
||||
void recvMessage(const VoidCallback& cob,
|
||||
apache::thrift::transport::TMemoryBuffer* message) {
|
||||
return recvMessage(cob, cob, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message over the channel and receive response, single cob version.
|
||||
* (See above.)
|
||||
*
|
||||
* @return call "cob" on success or fail; channel status must be queried
|
||||
* by the cob.
|
||||
*/
|
||||
void sendAndRecvMessage(const VoidCallback& cob,
|
||||
apache::thrift::transport::TMemoryBuffer* sendBuf,
|
||||
apache::thrift::transport::TMemoryBuffer* recvBuf) {
|
||||
return sendAndRecvMessage(cob, cob, sendBuf, recvBuf);
|
||||
}
|
||||
|
||||
// TODO(dreiss): Make this nonvirtual when TFramedSocketAsyncChannel gets
|
||||
// renamed to TFramedAsyncChannel.
|
||||
virtual boost::shared_ptr<TAsyncTransport> getTransport() = 0;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // #ifndef THRIFT_ASYNC_TASYNCCHANNEL_H_
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TASYNCDISPATCHPROCESSOR_H_
|
||||
#define THRIFT_ASYNC_TASYNCDISPATCHPROCESSOR_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TAsyncProcessor.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
/**
|
||||
* TAsyncDispatchProcessor is a helper class to parse the message header then
|
||||
* call another function to dispatch based on the function name.
|
||||
*
|
||||
* Subclasses must implement dispatchCall() to dispatch on the function name.
|
||||
*/
|
||||
template <class Protocol_>
|
||||
class TAsyncDispatchProcessorT : public TAsyncProcessor {
|
||||
public:
|
||||
virtual void process(std::tr1::function<void(bool success)> _return,
|
||||
boost::shared_ptr<protocol::TProtocol> in,
|
||||
boost::shared_ptr<protocol::TProtocol> out,
|
||||
TConnectionContext* context) {
|
||||
protocol::TProtocol* inRaw = in.get();
|
||||
protocol::TProtocol* outRaw = out.get();
|
||||
|
||||
// Try to dynamic cast to the template protocol type
|
||||
Protocol_* specificIn = dynamic_cast<Protocol_*>(inRaw);
|
||||
Protocol_* specificOut = dynamic_cast<Protocol_*>(outRaw);
|
||||
if (specificIn && specificOut) {
|
||||
return processFast(_return, specificIn, specificOut, context);
|
||||
}
|
||||
|
||||
// Log the fact that we have to use the slow path
|
||||
T_GENERIC_PROTOCOL(this, inRaw, specificIn);
|
||||
T_GENERIC_PROTOCOL(this, outRaw, specificOut);
|
||||
|
||||
std::string fname;
|
||||
protocol::TMessageType mtype;
|
||||
int32_t seqid;
|
||||
try {
|
||||
inRaw->readMessageBegin(fname, mtype, seqid);
|
||||
} catch (const TException &ex) {
|
||||
GlobalOutput.printf("received invalid message from client: %s",
|
||||
ex.what());
|
||||
_return(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// If this doesn't look like a valid call, log an error and return false so
|
||||
// that the server will close the connection.
|
||||
//
|
||||
// (The old generated processor code used to try to skip a T_STRUCT and
|
||||
// continue. However, that seems unsafe.)
|
||||
if (mtype != protocol::T_CALL && mtype != protocol::T_ONEWAY) {
|
||||
GlobalOutput.printf("received invalid message type %d from client",
|
||||
mtype);
|
||||
_return(false);
|
||||
return;
|
||||
}
|
||||
|
||||
return this->dispatchCall(_return, inRaw, outRaw, fname, seqid, context);
|
||||
}
|
||||
|
||||
void processFast(std::tr1::function<void(bool success)> _return,
|
||||
Protocol_* in, Protocol_* out,
|
||||
TConnectionContext* context) {
|
||||
std::string fname;
|
||||
protocol::TMessageType mtype;
|
||||
int32_t seqid;
|
||||
try {
|
||||
in->readMessageBegin(fname, mtype, seqid);
|
||||
} catch (const TException &ex) {
|
||||
GlobalOutput.printf("received invalid message from client: %s",
|
||||
ex.what());
|
||||
_return(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mtype != protocol::T_CALL && mtype != protocol::T_ONEWAY) {
|
||||
GlobalOutput.printf("received invalid message type %d from client",
|
||||
mtype);
|
||||
_return(false);
|
||||
return;
|
||||
}
|
||||
|
||||
return this->dispatchCallTemplated(_return, in, out, fname, seqid, context);
|
||||
}
|
||||
|
||||
virtual void dispatchCall(std::tr1::function<void(bool ok)> _return,
|
||||
apache::thrift::protocol::TProtocol* in,
|
||||
apache::thrift::protocol::TProtocol* out,
|
||||
const std::string& fname, int32_t seqid,
|
||||
TConnectionContext* context) = 0;
|
||||
|
||||
virtual void dispatchCallTemplated(std::tr1::function<void(bool ok)> _return,
|
||||
Protocol_* in, Protocol_* out,
|
||||
const std::string& fname, int32_t seqid,
|
||||
TConnectionContext* context) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Non-templatized version of TAsyncDispatchProcessor,
|
||||
* that doesn't bother trying to perform a dynamic_cast.
|
||||
*/
|
||||
class TAsyncDispatchProcessor : public TAsyncProcessor {
|
||||
public:
|
||||
virtual void process(std::tr1::function<void(bool success)> _return,
|
||||
boost::shared_ptr<protocol::TProtocol> in,
|
||||
boost::shared_ptr<protocol::TProtocol> out,
|
||||
TConnectionContext* context) {
|
||||
protocol::TProtocol* inRaw = in.get();
|
||||
protocol::TProtocol* outRaw = out.get();
|
||||
|
||||
std::string fname;
|
||||
protocol::TMessageType mtype;
|
||||
int32_t seqid;
|
||||
try {
|
||||
inRaw->readMessageBegin(fname, mtype, seqid);
|
||||
} catch (const TException &ex) {
|
||||
GlobalOutput.printf("received invalid message from client: %s",
|
||||
ex.what());
|
||||
_return(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// If this doesn't look like a valid call, log an error and return false so
|
||||
// that the server will close the connection.
|
||||
//
|
||||
// (The old generated processor code used to try to skip a T_STRUCT and
|
||||
// continue. However, that seems unsafe.)
|
||||
if (mtype != protocol::T_CALL && mtype != protocol::T_ONEWAY) {
|
||||
GlobalOutput.printf("received invalid message type %d from client",
|
||||
mtype);
|
||||
_return(false);
|
||||
return;
|
||||
}
|
||||
|
||||
return dispatchCall(_return, inRaw, outRaw, fname, seqid, context);
|
||||
}
|
||||
|
||||
virtual void dispatchCall(std::tr1::function<void(bool ok)> _return,
|
||||
apache::thrift::protocol::TProtocol* in,
|
||||
apache::thrift::protocol::TProtocol* out,
|
||||
const std::string& fname, int32_t seqid,
|
||||
TConnectionContext* context) = 0;
|
||||
};
|
||||
|
||||
// Specialize TAsyncDispatchProcessorT for TProtocol and TDummyProtocol just to
|
||||
// use the generic TDispatchProcessor.
|
||||
template <>
|
||||
class TAsyncDispatchProcessorT<protocol::TDummyProtocol> :
|
||||
public TAsyncDispatchProcessor {};
|
||||
template <>
|
||||
class TAsyncDispatchProcessorT<protocol::TProtocol> :
|
||||
public TAsyncDispatchProcessor {};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TASYNCDISPATCHPROCESSOR_H_
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TASYNCEVENTCHANNEL_H_
|
||||
#define THRIFT_ASYNC_TASYNCEVENTCHANNEL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TAsyncChannel.h"
|
||||
#include "thrift/lib/cpp/async/TDelayedDestruction.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
class TEventBase;
|
||||
|
||||
/**
|
||||
* TAsyncEventChannel defines an API for TAsyncChannel objects that are driven
|
||||
* by TEventBase.
|
||||
*/
|
||||
class TAsyncEventChannel : public TAsyncChannel,
|
||||
public TDelayedDestruction {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Determine if this channel is idle (i.e., has no outstanding reads or
|
||||
* writes).
|
||||
*/
|
||||
virtual bool isIdle() const = 0;
|
||||
|
||||
/**
|
||||
* Attach the channel to a TEventBase.
|
||||
*
|
||||
* This may only be called if the channel is not currently attached to a
|
||||
* TEventBase (by an earlier call to detachEventBase()).
|
||||
*
|
||||
* This method must be invoked in the TEventBase's thread.
|
||||
*/
|
||||
virtual void attachEventBase(TEventBase* eventBase) = 0;
|
||||
|
||||
/**
|
||||
* Detach the channel from its TEventBase.
|
||||
*
|
||||
* This may only be called when the channel is idle and has no reads or
|
||||
* writes pending. Once detached, the channel may not be used again until it
|
||||
* is re-attached to a TEventBase by calling attachEventBase().
|
||||
*
|
||||
* This method must be called from the current TEventBase's thread.
|
||||
*/
|
||||
virtual void detachEventBase() = 0;
|
||||
|
||||
/**
|
||||
* Get the receive timeout.
|
||||
*
|
||||
* @return Returns the current receive timeout, in milliseconds. A return
|
||||
* value of 0 indicates that no timeout is set.
|
||||
*/
|
||||
virtual uint32_t getRecvTimeout() const = 0;
|
||||
|
||||
/**
|
||||
* Set the timeout for receiving messages.
|
||||
*
|
||||
* When set to a non-zero value, the entire message must be received within
|
||||
* the specified number of milliseconds, or the receive will fail and the
|
||||
* channel will be closed.
|
||||
*/
|
||||
virtual void setRecvTimeout(uint32_t milliseconds) = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Users of TAsyncEventChannel must never delete it directly. Instead, invoke
|
||||
* destroy() instead. (See the documentation in TDelayedDestruction.h for
|
||||
* more details.)
|
||||
*/
|
||||
|
||||
virtual ~TAsyncEventChannel() { }
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TASYNCEVENTCHANNEL_H_
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright (c) 2006- Facebook
|
||||
// Distributed under the Thrift Software License
|
||||
//
|
||||
// See accompanying file LICENSE or visit the Thrift site at:
|
||||
// http://developers.facebook.com/thrift/
|
||||
|
||||
#ifndef THRIFT_TASYNCPROCESSOR_H
|
||||
#define THRIFT_TASYNCPROCESSOR_H 1
|
||||
|
||||
#include <tr1/functional>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "thrift/lib/cpp/TProcessor.h"
|
||||
#include "thrift/lib/cpp/server/TConnectionContext.h"
|
||||
#include "thrift/lib/cpp/protocol/TProtocol.h"
|
||||
|
||||
using apache::thrift::server::TConnectionContext;
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
/**
|
||||
* Async version of a TProcessor. It is not expected to complete by the time
|
||||
* the call to process returns. Instead, it calls a cob to signal completion.
|
||||
*
|
||||
* @author David Reiss <dreiss@facebook.com>
|
||||
*/
|
||||
|
||||
class TEventServer; // forward declaration
|
||||
|
||||
class TAsyncProcessor : public TProcessorBase {
|
||||
public:
|
||||
virtual ~TAsyncProcessor() {}
|
||||
|
||||
virtual void process(std::tr1::function<void(bool success)> _return,
|
||||
boost::shared_ptr<protocol::TProtocol> in,
|
||||
boost::shared_ptr<protocol::TProtocol> out,
|
||||
TConnectionContext* context = NULL) = 0;
|
||||
|
||||
void process(std::tr1::function<void(bool success)> _return,
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol> io) {
|
||||
return process(_return, io, io);
|
||||
}
|
||||
|
||||
const TEventServer* getAsyncServer() {
|
||||
return asyncServer_;
|
||||
}
|
||||
protected:
|
||||
TAsyncProcessor() {}
|
||||
|
||||
const TEventServer* asyncServer_;
|
||||
private:
|
||||
friend class TEventServer;
|
||||
void setAsyncServer(const TEventServer* server) {
|
||||
asyncServer_ = server;
|
||||
}
|
||||
};
|
||||
|
||||
class TAsyncProcessorFactory {
|
||||
public:
|
||||
virtual ~TAsyncProcessorFactory() {}
|
||||
|
||||
/**
|
||||
* Get the TAsyncProcessor to use for a particular connection.
|
||||
*/
|
||||
virtual boost::shared_ptr<TAsyncProcessor> getProcessor(
|
||||
server::TConnectionContext* ctx) = 0;
|
||||
};
|
||||
|
||||
class TAsyncSingletonProcessorFactory : public TAsyncProcessorFactory {
|
||||
public:
|
||||
explicit TAsyncSingletonProcessorFactory(
|
||||
const boost::shared_ptr<TAsyncProcessor>& processor) :
|
||||
processor_(processor) {}
|
||||
|
||||
boost::shared_ptr<TAsyncProcessor> getProcessor(server::TConnectionContext*) {
|
||||
return processor_;
|
||||
}
|
||||
|
||||
private:
|
||||
boost::shared_ptr<TAsyncProcessor> processor_;
|
||||
};
|
||||
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
// XXX I'm lazy for now
|
||||
namespace apache { namespace thrift {
|
||||
using apache::thrift::async::TAsyncProcessor;
|
||||
}}
|
||||
|
||||
#endif // #ifndef THRIFT_TASYNCPROCESSOR_H
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TASYNCSSLSERVERSOCKET_H_
|
||||
#define THRIFT_ASYNC_TASYNCSSLSERVERSOCKET_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TAsyncServerSocket.h"
|
||||
#include "thrift/lib/cpp/transport/TSSLSocket.h"
|
||||
|
||||
namespace apache { namespace thrift {
|
||||
|
||||
namespace transport {
|
||||
class TSocketAddress;
|
||||
}
|
||||
|
||||
namespace async {
|
||||
|
||||
class TAsyncSSLSocket;
|
||||
|
||||
class TAsyncSSLServerSocket : public TDelayedDestruction,
|
||||
private TAsyncServerSocket::AcceptCallback {
|
||||
public:
|
||||
class SSLAcceptCallback {
|
||||
public:
|
||||
virtual ~SSLAcceptCallback() {}
|
||||
|
||||
/**
|
||||
* connectionAccepted() is called whenever a new client connection is
|
||||
* received.
|
||||
*
|
||||
* The SSLAcceptCallback will remain installed after connectionAccepted()
|
||||
* returns.
|
||||
*
|
||||
* @param sock The newly accepted client socket. The
|
||||
* SSLAcceptCallback
|
||||
* assumes ownership of this socket, and is responsible
|
||||
* for closing it when done.
|
||||
*/
|
||||
virtual void connectionAccepted(
|
||||
const boost::shared_ptr<TAsyncSSLSocket> &sock)
|
||||
THRIFT_NOEXCEPT = 0;
|
||||
|
||||
/**
|
||||
* acceptError() is called if an error occurs while accepting.
|
||||
*
|
||||
* The SSLAcceptCallback will remain installed even after an accept error.
|
||||
* If the callback wants to uninstall itself and stop trying to accept new
|
||||
* connections, it must explicit call setAcceptCallback(NULL).
|
||||
*
|
||||
* @param ex An exception representing the error.
|
||||
*/
|
||||
virtual void acceptError(const std::exception& ex) THRIFT_NOEXCEPT = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new TAsyncSSLServerSocket with the specified TEventBase.
|
||||
*
|
||||
* @param eventBase The TEventBase to use for driving the asynchronous I/O.
|
||||
* If this parameter is NULL, attachEventBase() must be
|
||||
* called before this socket can begin accepting
|
||||
* connections. All TAsyncSSLSocket objects accepted by
|
||||
* this server socket will be attached to this TEventBase
|
||||
* when they are created.
|
||||
*/
|
||||
explicit TAsyncSSLServerSocket(
|
||||
const boost::shared_ptr<apache::thrift::transport::SSLContext>& ctx,
|
||||
TEventBase* eventBase = NULL);
|
||||
|
||||
/**
|
||||
* Destroy the socket.
|
||||
*
|
||||
* destroy() must be called to destroy the socket. The normal destructor is
|
||||
* private, and should not be invoked directly. This prevents callers from
|
||||
* deleting a TAsyncSSLServerSocket while it is invoking a callback.
|
||||
*/
|
||||
virtual void destroy();
|
||||
|
||||
void bind(const transport::TSocketAddress& address) {
|
||||
serverSocket_->bind(address);
|
||||
}
|
||||
void bind(uint16_t port) {
|
||||
serverSocket_->bind(port);
|
||||
}
|
||||
void getAddress(transport::TSocketAddress* addressReturn) {
|
||||
serverSocket_->getAddress(addressReturn);
|
||||
}
|
||||
void listen(int backlog) {
|
||||
serverSocket_->listen(backlog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TAsyncSSLServerSocket>.
|
||||
*
|
||||
* This passes in the correct destructor object, since TAsyncSSLServerSocket's
|
||||
* destructor is protected and cannot be invoked directly.
|
||||
*/
|
||||
static boost::shared_ptr<TAsyncSSLServerSocket> newSocket(
|
||||
const boost::shared_ptr<apache::thrift::transport::SSLContext>& ctx,
|
||||
TEventBase* evb) {
|
||||
return boost::shared_ptr<TAsyncSSLServerSocket>(
|
||||
new TAsyncSSLServerSocket(ctx, evb),
|
||||
Destructor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the accept callback.
|
||||
*
|
||||
* This method may only be invoked from the TEventBase's loop thread.
|
||||
*
|
||||
* @param callback The callback to invoke when a new socket
|
||||
* connection is accepted and a new TAsyncSSLSocket is
|
||||
* created.
|
||||
*
|
||||
* Throws TTransportException on error.
|
||||
*/
|
||||
void setSSLAcceptCallback(SSLAcceptCallback* callback);
|
||||
|
||||
SSLAcceptCallback *getSSLAcceptCallback() const {
|
||||
return sslCallback_;
|
||||
}
|
||||
|
||||
void attachEventBase(TEventBase* eventBase);
|
||||
void detachEventBase();
|
||||
|
||||
/**
|
||||
* Returns the TEventBase that the handler is currently attached to.
|
||||
*/
|
||||
TEventBase* getEventBase() const {
|
||||
return eventBase_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Invoke destroy() instead to destroy the TAsyncSSLServerSocket.
|
||||
*/
|
||||
virtual ~TAsyncSSLServerSocket();
|
||||
|
||||
private:
|
||||
virtual void connectionAccepted(int fd,
|
||||
const transport::TSocketAddress& clientAddr)
|
||||
THRIFT_NOEXCEPT;
|
||||
virtual void acceptError(const std::exception& ex) THRIFT_NOEXCEPT;
|
||||
|
||||
TEventBase* eventBase_;
|
||||
TAsyncServerSocket* serverSocket_;
|
||||
// SSL context
|
||||
boost::shared_ptr<apache::thrift::transport::SSLContext> ctx_;
|
||||
// The accept callback
|
||||
SSLAcceptCallback* sslCallback_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TASYNCSSLSERVERSOCKET_H_
|
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TASYNCSSLSOCKET_H_
|
||||
#define THRIFT_ASYNC_TASYNCSSLSOCKET_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TAsyncSocket.h"
|
||||
#include "thrift/lib/cpp/async/TAsyncTimeout.h"
|
||||
#include "thrift/lib/cpp/transport/TSSLSocket.h"
|
||||
#include "thrift/lib/cpp/transport/TTransportException.h"
|
||||
|
||||
namespace apache { namespace thrift {
|
||||
|
||||
namespace async {
|
||||
|
||||
class TSSLException: public apache::thrift::transport::TTransportException {
|
||||
public:
|
||||
TSSLException(int sslError, int errno_copy);
|
||||
|
||||
int getSSLError() const { return error_; }
|
||||
|
||||
protected:
|
||||
int error_;
|
||||
};
|
||||
|
||||
/**
|
||||
* A class for performing asynchronous I/O on an SSL connection.
|
||||
*
|
||||
* TAsyncSSLSocket allows users to asynchronously wait for data on an
|
||||
* SSL connection, and to asynchronously send data.
|
||||
*
|
||||
* The APIs for reading and writing are intentionally asymmetric.
|
||||
* Waiting for data to read is a persistent API: a callback is
|
||||
* installed, and is notified whenever new data is available. It
|
||||
* continues to be notified of new events until it is uninstalled.
|
||||
*
|
||||
* TAsyncSSLSocket does not provide read timeout functionality,
|
||||
* because it typically cannot determine when the timeout should be
|
||||
* active. Generally, a timeout should only be enabled when
|
||||
* processing is blocked waiting on data from the remote endpoint.
|
||||
* For server connections, the timeout should not be active if the
|
||||
* server is currently processing one or more outstanding requests for
|
||||
* this connection. For client connections, the timeout should not be
|
||||
* active if there are no requests pending on the connection.
|
||||
* Additionally, if a client has multiple pending requests, it will
|
||||
* ususally want a separate timeout for each request, rather than a
|
||||
* single read timeout.
|
||||
*
|
||||
* The write API is fairly intuitive: a user can request to send a
|
||||
* block of data, and a callback will be informed once the entire
|
||||
* block has been transferred to the kernel, or on error.
|
||||
* TAsyncSSLSocket does provide a send timeout, since most callers
|
||||
* want to give up if the remote end stops responding and no further
|
||||
* progress can be made sending the data.
|
||||
*/
|
||||
class TAsyncSSLSocket : public TAsyncSocket {
|
||||
public:
|
||||
|
||||
#if THRIFT_HAVE_UNIQUE_PTR
|
||||
typedef std::unique_ptr<TAsyncSSLSocket, Destructor> UniquePtr;
|
||||
#endif
|
||||
|
||||
class HandshakeCallback {
|
||||
public:
|
||||
virtual ~HandshakeCallback() {}
|
||||
|
||||
/**
|
||||
* handshakeSuccess() is called when a new SSL connection is
|
||||
* established, i.e., after SSL_accept/connect() returns successfully.
|
||||
*
|
||||
* The HandshakeCallback will be uninstalled before handshakeSuccess()
|
||||
* is called.
|
||||
*
|
||||
* @param sock SSL socket on which the handshake was initiated
|
||||
*/
|
||||
virtual void handshakeSuccess(TAsyncSSLSocket *sock) THRIFT_NOEXCEPT = 0;
|
||||
|
||||
/**
|
||||
* handshakeError() is called if an error occurs while
|
||||
* establishing the SSL connection.
|
||||
*
|
||||
* The HandshakeCallback will be uninstalled before handshakeError()
|
||||
* is called.
|
||||
*
|
||||
* @param sock SSL socket on which the handshake was initiated
|
||||
* @param ex An exception representing the error.
|
||||
*/
|
||||
virtual void handshakeError(
|
||||
TAsyncSSLSocket *sock,
|
||||
const apache::thrift::transport::TTransportException& ex)
|
||||
THRIFT_NOEXCEPT = 0;
|
||||
};
|
||||
|
||||
class HandshakeTimeout : public TAsyncTimeout {
|
||||
public:
|
||||
HandshakeTimeout(TAsyncSSLSocket* sslSocket, TEventBase* eventBase)
|
||||
: TAsyncTimeout(eventBase)
|
||||
, sslSocket_(sslSocket) {}
|
||||
|
||||
virtual void timeoutExpired() THRIFT_NOEXCEPT {
|
||||
sslSocket_->timeoutExpired();
|
||||
}
|
||||
|
||||
private:
|
||||
TAsyncSSLSocket* sslSocket_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a client TAsyncSSLSocket
|
||||
*/
|
||||
TAsyncSSLSocket(const boost::shared_ptr<transport::SSLContext> &ctx,
|
||||
TEventBase* evb) :
|
||||
TAsyncSocket(evb),
|
||||
corked_(false),
|
||||
server_(false),
|
||||
sslState_(STATE_UNINIT),
|
||||
ctx_(ctx),
|
||||
handshakeCallback_(NULL),
|
||||
ssl_(NULL),
|
||||
sslSession_(NULL),
|
||||
handshakeTimeout_(this, evb) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TAsyncSSLSocket from an already connected socket file descriptor.
|
||||
*
|
||||
* Note that while TAsyncSSLSocket enables TCP_NODELAY for sockets it creates
|
||||
* when connecting, it does not change the socket options when given an
|
||||
* existing file descriptor. If callers want TCP_NODELAY enabled when using
|
||||
* this version of the constructor, they need to explicitly call
|
||||
* setNoDelay(true) after the constructor returns.
|
||||
*
|
||||
* @param ctx SSL context for this connection.
|
||||
* @param evb EventBase that will manage this socket.
|
||||
* @param fd File descriptor to take over (should be a connected socket).
|
||||
*/
|
||||
TAsyncSSLSocket(const boost::shared_ptr<transport::
|
||||
SSLContext>& ctx,
|
||||
TEventBase* evb, int fd, bool server = true);
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TAsyncSSLSocket>.
|
||||
*/
|
||||
static boost::shared_ptr<TAsyncSSLSocket> newSocket(
|
||||
const boost::shared_ptr<transport::SSLContext>& ctx,
|
||||
TEventBase* evb, int fd, bool server=true) {
|
||||
return boost::shared_ptr<TAsyncSSLSocket>(
|
||||
new TAsyncSSLSocket(ctx, evb, fd, server),
|
||||
Destructor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TAsyncSSLSocket>.
|
||||
*/
|
||||
static boost::shared_ptr<TAsyncSSLSocket> newSocket(
|
||||
const boost::shared_ptr<transport::SSLContext>& ctx,
|
||||
TEventBase* evb) {
|
||||
return boost::shared_ptr<TAsyncSSLSocket>(
|
||||
new TAsyncSSLSocket(ctx, evb),
|
||||
Destructor());
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: implement support for SSL renegosiation.
|
||||
*
|
||||
* This involves proper handling of the SSL_ERROR_WANT_READ/WRITE
|
||||
* code as a result of SSL_write/read(), instead of returning an
|
||||
* error. In that case, the READ/WRITE event should be registered,
|
||||
* and a flag (e.g., writeBlockedOnRead) should be set to indiciate
|
||||
* the condition. In the next invocation of read/write callback, if
|
||||
* the flag is on, performWrite()/performRead() should be called in
|
||||
* addition to the normal call to performRead()/performWrite(), and
|
||||
* the flag should be reset.
|
||||
*/
|
||||
|
||||
// Inherit TAsyncTransport methods from TAsyncSocket except the
|
||||
// following.
|
||||
// See the documentation in TAsyncTransport.h
|
||||
// TODO: implement graceful shutdown in close()
|
||||
// TODO: implement detachSSL() that returns the SSL connection
|
||||
virtual void closeNow();
|
||||
virtual void shutdownWrite();
|
||||
virtual void shutdownWriteNow();
|
||||
virtual bool good() const;
|
||||
virtual bool connecting() const;
|
||||
|
||||
/**
|
||||
* Accept an SSL connection on the socket.
|
||||
*
|
||||
* The callback will be invoked and uninstalled when an SSL
|
||||
* connection has been established on the underlying socket.
|
||||
*
|
||||
* @param callback callback object to invoke on success/failure
|
||||
* @param timeout timeout for this function in milliseconds, or 0 for no
|
||||
* timeout
|
||||
*/
|
||||
void sslAccept(HandshakeCallback* callback, uint32_t timeout = 0);
|
||||
|
||||
/**
|
||||
* Invoke SSL accept following an asynchronous session cache lookup
|
||||
*/
|
||||
void restartSSLAccept();
|
||||
|
||||
/**
|
||||
* Connect to the given address, invoking callback when complete or on error
|
||||
*
|
||||
* Note timeout applies to TCP + SSL connection time
|
||||
*/
|
||||
void connect(ConnectCallback* callback,
|
||||
const transport::TSocketAddress& address,
|
||||
int timeout = 0,
|
||||
const OptionList &options = emptyOptionList) THRIFT_NOEXCEPT;
|
||||
|
||||
using TAsyncSocket::connect;
|
||||
|
||||
/**
|
||||
* Initiate an SSL connection on the socket
|
||||
* THe callback will be invoked and uninstalled when an SSL connection
|
||||
* has been establshed on the underlying socket.
|
||||
*
|
||||
* @param callback callback object to invoke on success/failure
|
||||
* @param timeout timeout for this function in milliseconds, or 0 for no
|
||||
* timeout
|
||||
*/
|
||||
void sslConnect(HandshakeCallback *callback, uint64_t timeout = 0);
|
||||
|
||||
enum SSLStateEnum {
|
||||
STATE_UNINIT,
|
||||
STATE_ACCEPTING,
|
||||
STATE_CACHE_LOOKUP,
|
||||
STATE_CONNECTING,
|
||||
STATE_ESTABLISHED,
|
||||
STATE_REMOTE_CLOSED, /// remote end closed; we can still write
|
||||
STATE_CLOSING, ///< close() called, but waiting on writes to complete
|
||||
/// close() called with pending writes, before connect() has completed
|
||||
STATE_CONNECTING_CLOSING,
|
||||
STATE_CLOSED,
|
||||
STATE_ERROR
|
||||
};
|
||||
|
||||
SSLStateEnum getSSLState() const { return sslState_;}
|
||||
|
||||
/**
|
||||
* Get a handle to the negotiated SSL session. This increments the session
|
||||
* refcount and must be deallocated by the caller.
|
||||
*/
|
||||
SSL_SESSION *getSSLSession();
|
||||
|
||||
/**
|
||||
* Set the SSL session to be used during sslConnect. TAsyncSSLSocket will
|
||||
* hold a reference to the session until it is destroyed or released by the
|
||||
* underlying SSL structure.
|
||||
*
|
||||
* @param takeOwnership if true, TAsyncSSLSocket will assume the caller's
|
||||
* reference count to session.
|
||||
*/
|
||||
void setSSLSession(SSL_SESSION *session, bool takeOwnership = false);
|
||||
|
||||
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||
/**
|
||||
* Get the name of the protocol selected by the client during
|
||||
* Next Protocol Negotiation (NPN)
|
||||
*
|
||||
* @param protoName Name of the protocol (not guaranteed to be
|
||||
* null terminated); will be set to NULL if
|
||||
* the client did not negotiate a protocol.
|
||||
* Note: the TAsyncSSLSocket retains ownership
|
||||
* of this string.
|
||||
* @param protoNameLen Length of the name.
|
||||
*/
|
||||
void getSelectedNextProtocol(const unsigned char** protoName,
|
||||
unsigned* protoLen);
|
||||
#endif // OPENSSL_NPN_NEGOTIATED
|
||||
|
||||
/**
|
||||
* Determine if the session specified during setSSLSession was reused
|
||||
* or if the server rejected it and issued a new session.
|
||||
*/
|
||||
bool getSSLSessionReused() const;
|
||||
|
||||
virtual void attachEventBase(TEventBase* eventBase) {
|
||||
TAsyncSocket::attachEventBase(eventBase);
|
||||
handshakeTimeout_.attachEventBase(eventBase);
|
||||
}
|
||||
|
||||
virtual void detachEventBase() {
|
||||
TAsyncSocket::detachEventBase();
|
||||
handshakeTimeout_.detachEventBase();
|
||||
}
|
||||
|
||||
void timeoutExpired() THRIFT_NOEXCEPT;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Users of TAsyncSSLSocket must never delete it directly. Instead, invoke
|
||||
* destroy() instead. (See the documentation in TDelayedDestruction.h for
|
||||
* more details.)
|
||||
*/
|
||||
~TAsyncSSLSocket();
|
||||
|
||||
// Inherit event notification methods from TAsyncSocket except
|
||||
// the following.
|
||||
|
||||
void handleRead() THRIFT_NOEXCEPT;
|
||||
void handleWrite() THRIFT_NOEXCEPT;
|
||||
void handleAccept() THRIFT_NOEXCEPT;
|
||||
void handleConnect() THRIFT_NOEXCEPT;
|
||||
|
||||
void invalidState(HandshakeCallback* callback);
|
||||
bool willBlock(int ret, int *errorOut) THRIFT_NOEXCEPT;
|
||||
|
||||
// TAsyncSocket calls this at the wrong time for SSL
|
||||
void handleInitialReadWrite() THRIFT_NOEXCEPT {}
|
||||
|
||||
ssize_t performRead(void* buf, size_t buflen);
|
||||
ssize_t performWrite(const iovec* vec, uint32_t count, bool haveMore,
|
||||
uint32_t* countWritten, uint32_t* partialWritten);
|
||||
|
||||
// Inherit error handling methods from TAsyncSocket, plus the following.
|
||||
void failHandshake(const char* fn, const transport::TTransportException& ex);
|
||||
|
||||
void invokeHandshakeCallback();
|
||||
|
||||
// Whether we've applied the TCP_CORK option to the socket
|
||||
bool corked_;
|
||||
// SSL related members.
|
||||
bool server_;
|
||||
SSLStateEnum sslState_;
|
||||
boost::shared_ptr<transport::SSLContext> ctx_;
|
||||
// Callback for SSL_accept() or SSL_connect()
|
||||
HandshakeCallback* handshakeCallback_;
|
||||
SSL* ssl_;
|
||||
SSL_SESSION *sslSession_;
|
||||
HandshakeTimeout handshakeTimeout_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // #ifndef THRIFT_ASYNC_TASYNCSSLSOCKET_H_
|
|
@ -0,0 +1,513 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TASYNCSERVERSOCKET_H_
|
||||
#define THRIFT_ASYNC_TASYNCSERVERSOCKET_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/thrift_config.h"
|
||||
#include "thrift/lib/cpp/async/TDelayedDestruction.h"
|
||||
#include "thrift/lib/cpp/async/TEventHandler.h"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <exception>
|
||||
#include <vector>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
#include <sys/socket.h>
|
||||
|
||||
namespace apache { namespace thrift {
|
||||
|
||||
namespace transport {
|
||||
class TSocketAddress;
|
||||
}
|
||||
|
||||
namespace async {
|
||||
|
||||
class TNotificationPipe;
|
||||
|
||||
/**
|
||||
* A listening socket that asynchronously informs a callback whenever a new
|
||||
* connection has been accepted.
|
||||
*
|
||||
* Unlike most async interfaces that always invoke their callback in the same
|
||||
* TEventBase thread, TAsyncServerSocket is unusual in that it can distribute
|
||||
* the callbacks across multiple TEventBase threads.
|
||||
*
|
||||
* This supports a common use case for network servers to distribute incoming
|
||||
* connections across a number of TEventBase threads. (Servers typically run
|
||||
* with one TEventBase thread per CPU.)
|
||||
*
|
||||
* Despite being able to invoke callbacks in multiple TEventBase threads,
|
||||
* TAsyncServerSocket still has one "primary" TEventBase. Operations that
|
||||
* modify the TAsyncServerSocket state may only be performed from the primary
|
||||
* TEventBase thread.
|
||||
*/
|
||||
class TAsyncServerSocket : public TDelayedDestruction,
|
||||
private TEventHandler {
|
||||
public:
|
||||
#if THRIFT_HAVE_UNIQUE_PTR
|
||||
typedef std::unique_ptr<TAsyncServerSocket, Destructor> UniquePtr;
|
||||
#endif
|
||||
|
||||
class AcceptCallback {
|
||||
public:
|
||||
virtual ~AcceptCallback() {}
|
||||
|
||||
/**
|
||||
* connectionAccepted() is called whenever a new client connection is
|
||||
* received.
|
||||
*
|
||||
* The AcceptCallback will remain installed after connectionAccepted()
|
||||
* returns.
|
||||
*
|
||||
* @param fd The newly accepted client socket. The AcceptCallback
|
||||
* assumes ownership of this socket, and is responsible
|
||||
* for closing it when done. The newly accepted file
|
||||
* descriptor will have already been put into
|
||||
* non-blocking mode.
|
||||
* @param clientAddr A reference to a TSocketAddress struct containing the
|
||||
* client's address. This struct is only guaranteed to
|
||||
* remain valid until connectionAccepted() returns.
|
||||
*/
|
||||
virtual void connectionAccepted(int fd,
|
||||
const transport::TSocketAddress& clientAddr)
|
||||
THRIFT_NOEXCEPT = 0;
|
||||
|
||||
/**
|
||||
* acceptError() is called if an error occurs while accepting.
|
||||
*
|
||||
* The AcceptCallback will remain installed even after an accept error,
|
||||
* as the errors are typically somewhat transient, such as being out of
|
||||
* file descriptors. The server socket must be explicitly stopped if you
|
||||
* wish to stop accepting after an error.
|
||||
*
|
||||
* @param ex An exception representing the error.
|
||||
*/
|
||||
virtual void acceptError(const std::exception& ex) THRIFT_NOEXCEPT = 0;
|
||||
|
||||
/**
|
||||
* acceptStarted() will be called in the callback's TEventBase thread
|
||||
* after this callback has been added to the TAsyncServerSocket.
|
||||
*
|
||||
* acceptStarted() will be called before any calls to connectionAccepted()
|
||||
* or acceptError() are made on this callback.
|
||||
*
|
||||
* acceptStarted() makes it easier for callbacks to perform initialization
|
||||
* inside the callback thread. (The call to addAcceptCallback() must
|
||||
* always be made from the TAsyncServerSocket's primary TEventBase thread.
|
||||
* acceptStarted() provides a hook that will always be invoked in the
|
||||
* callback's thread.)
|
||||
*
|
||||
* Note that the call to acceptStarted() is made once the callback is
|
||||
* added, regardless of whether or not the TAsyncServerSocket is actually
|
||||
* accepting at the moment. acceptStarted() will be called even if the
|
||||
* TAsyncServerSocket is paused when the callback is added (including if
|
||||
* the initial call to startAccepting() on the TAsyncServerSocket has not
|
||||
* been made yet).
|
||||
*/
|
||||
virtual void acceptStarted() THRIFT_NOEXCEPT {}
|
||||
|
||||
/**
|
||||
* acceptStopped() will be called when this AcceptCallback is removed from
|
||||
* the TAsyncServerSocket, or when the TAsyncServerSocket is destroyed,
|
||||
* whichever occurs first.
|
||||
*
|
||||
* No more calls to connectionAccepted() or acceptError() will be made
|
||||
* after acceptStopped() is invoked.
|
||||
*/
|
||||
virtual void acceptStopped() THRIFT_NOEXCEPT {}
|
||||
};
|
||||
|
||||
static const uint32_t kDefaultMaxAcceptAtOnce = 30;
|
||||
static const uint32_t kDefaultCallbackAcceptAtOnce = 5;
|
||||
static const uint32_t kDefaultMaxMessagesInPipe = 0xffffffff;
|
||||
/**
|
||||
* Create a new TAsyncServerSocket with the specified TEventBase.
|
||||
*
|
||||
* @param eventBase The TEventBase to use for driving the asynchronous I/O.
|
||||
* If this parameter is NULL, attachEventBase() must be
|
||||
* called before this socket can begin accepting
|
||||
* connections.
|
||||
*/
|
||||
explicit TAsyncServerSocket(TEventBase* eventBase = NULL);
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TAsyncServerSocket>.
|
||||
*
|
||||
* This passes in the correct destructor object, since TAsyncServerSocket's
|
||||
* destructor is protected and cannot be invoked directly.
|
||||
*/
|
||||
static boost::shared_ptr<TAsyncServerSocket> newSocket(TEventBase* evb) {
|
||||
return boost::shared_ptr<TAsyncServerSocket>(new TAsyncServerSocket(evb),
|
||||
Destructor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the socket.
|
||||
*
|
||||
* TAsyncServerSocket::destroy() must be called to destroy the socket.
|
||||
* The normal destructor is private, and should not be invoked directly.
|
||||
* This prevents callers from deleting a TAsyncServerSocket while it is
|
||||
* invoking a callback.
|
||||
*
|
||||
* destroy() must be invoked from the socket's primary TEventBase thread.
|
||||
*
|
||||
* If there are AcceptCallbacks still installed when destroy() is called,
|
||||
* acceptStopped() will be called on these callbacks to notify them that
|
||||
* accepting has stopped. Accept callbacks being driven by other TEventBase
|
||||
* threads may continue to receive new accept callbacks for a brief period of
|
||||
* time after destroy() returns. They will not receive any more callback
|
||||
* invocations once acceptStopped() is invoked.
|
||||
*/
|
||||
virtual void destroy();
|
||||
|
||||
/**
|
||||
* Attach this TAsyncServerSocket to its primary TEventBase.
|
||||
*
|
||||
* This may only be called if the TAsyncServerSocket is not already attached
|
||||
* to a TEventBase. The TAsyncServerSocket must be attached to a TEventBase
|
||||
* before it can begin accepting connections.
|
||||
*/
|
||||
void attachEventBase(TEventBase *eventBase);
|
||||
|
||||
/**
|
||||
* Detach the TAsyncServerSocket from its primary TEventBase.
|
||||
*
|
||||
* detachEventBase() may only be called if the TAsyncServerSocket is not
|
||||
* currently accepting connections.
|
||||
*/
|
||||
void detachEventBase();
|
||||
|
||||
/**
|
||||
* Get the TEventBase used by this socket.
|
||||
*/
|
||||
TEventBase* getEventBase() const {
|
||||
return eventBase_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TAsyncServerSocket from an existing socket file descriptor.
|
||||
*
|
||||
* useExistingSocket() will cause the TAsyncServerSocket to take ownership of
|
||||
* the specified file descriptor, and use it to listen for new connections.
|
||||
* The TAsyncServerSocket will close the file descriptor when it is
|
||||
* destroyed.
|
||||
*
|
||||
* useExistingSocket() must be called before bind() or listen().
|
||||
*
|
||||
* The supplied file descriptor will automatically be put into non-blocking
|
||||
* mode. The caller may have already directly called bind() and possibly
|
||||
* listen on the file descriptor. If so the caller should skip calling the
|
||||
* corresponding TAsyncServerSocket::bind() and listen() methods.
|
||||
*
|
||||
* On error a TTransportException will be thrown and the caller will retain
|
||||
* ownership of the file descriptor.
|
||||
*/
|
||||
void useExistingSocket(int fd);
|
||||
|
||||
/**
|
||||
* Return the underlying file descriptor
|
||||
*/
|
||||
int getSocket() const {
|
||||
return socket_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind to the specified address.
|
||||
*
|
||||
* This must be called from the primary TEventBase thread.
|
||||
*
|
||||
* Throws TTransportException on error.
|
||||
*/
|
||||
void bind(const transport::TSocketAddress& address);
|
||||
|
||||
/**
|
||||
* Bind to the specified port.
|
||||
*
|
||||
* This must be called from the primary TEventBase thread.
|
||||
*
|
||||
* Throws TTransportException on error.
|
||||
*/
|
||||
void bind(uint16_t port);
|
||||
|
||||
/**
|
||||
* Get the local address to which the socket is bound.
|
||||
*
|
||||
* Throws TTransportException on error.
|
||||
*/
|
||||
void getAddress(transport::TSocketAddress* addressReturn);
|
||||
|
||||
/**
|
||||
* Begin listening for connections.
|
||||
*
|
||||
* This calls ::listen() with the specified backlog.
|
||||
*
|
||||
* Once listen() is invoked the socket will actually be open so that remote
|
||||
* clients may establish connections. (Clients that attempt to connect
|
||||
* before listen() is called will receive a connection refused error.)
|
||||
*
|
||||
* At least one callback must be set and startAccepting() must be called to
|
||||
* actually begin notifying the accept callbacks of newly accepted
|
||||
* connections. The backlog parameter controls how many connections the
|
||||
* kernel will accept and buffer internally while the accept callbacks are
|
||||
* paused (or if accepting is enabled but the callbacks cannot keep up).
|
||||
*
|
||||
* bind() must be called before calling listen().
|
||||
* listen() must be called from the primary TEventBase thread.
|
||||
*
|
||||
* Throws TTransportException on error.
|
||||
*/
|
||||
void listen(int backlog);
|
||||
|
||||
/**
|
||||
* Add an AcceptCallback.
|
||||
*
|
||||
* When a new socket is accepted, one of the AcceptCallbacks will be invoked
|
||||
* with the new socket. The AcceptCallbacks are invoked in a round-robin
|
||||
* fashion. This allows the accepted sockets to distributed among a pool of
|
||||
* threads, each running its own TEventBase object. This is a common model,
|
||||
* since most asynchronous-style servers typically run one TEventBase thread
|
||||
* per CPU.
|
||||
*
|
||||
* The TEventBase object associated with each AcceptCallback must be running
|
||||
* its loop. If the TEventBase loop is not running, sockets will still be
|
||||
* scheduled for the callback, but the callback cannot actually get invoked
|
||||
* until the loop runs.
|
||||
*
|
||||
* This method must be invoked from the TAsyncServerSocket's primary
|
||||
* TEventBase thread.
|
||||
*
|
||||
* Note that startAccepting() must be called on the TAsyncServerSocket to
|
||||
* cause it to actually start accepting sockets once callbacks have been
|
||||
* installed.
|
||||
*
|
||||
* @param callback The callback to invoke.
|
||||
* @param eventBase The TEventBase to use to invoke the callback. This
|
||||
* parameter may be NULL, in which case the callback will be invoked in
|
||||
* the TAsyncServerSocket's primary TEventBase.
|
||||
* @param maxAtOnce The maximum number of connections to accept in this
|
||||
* callback on a single iteration of the event base loop.
|
||||
* This only takes effect when eventBase is non-NULL. When
|
||||
* using a NULL eventBase for the callback, the
|
||||
* setMaxAcceptAtOnce() method controls how many
|
||||
* connections the main event base will accept at once.
|
||||
*/
|
||||
void addAcceptCallback(AcceptCallback *callback, TEventBase *eventBase,
|
||||
uint32_t maxAtOnce = kDefaultCallbackAcceptAtOnce);
|
||||
|
||||
/**
|
||||
* Remove an AcceptCallback.
|
||||
*
|
||||
* This allows a single AcceptCallback to be removed from the round-robin
|
||||
* pool.
|
||||
*
|
||||
* This method must be invoked from the TAsyncServerSocket's primary
|
||||
* TEventBase thread. Use TEventBase::runInEventBaseThread() to schedule the
|
||||
* operation in the correct TEventBase if your code is not in the server
|
||||
* socket's primary TEventBase.
|
||||
*
|
||||
* Given that the accept callback is being driven by a different TEventBase,
|
||||
* the AcceptCallback may continue to be invoked for a short period of time
|
||||
* after removeAcceptCallback() returns in this thread. Once the other
|
||||
* TEventBase thread receives the notification to stop, it will call
|
||||
* acceptStopped() on the callback to inform it that it is fully stopped and
|
||||
* will not receive any new sockets.
|
||||
*
|
||||
* If the last accept callback is removed while the socket is accepting,
|
||||
* the socket will implicitly pause accepting. If a callback is later added,
|
||||
* it will resume accepting immediately, without requiring startAccepting()
|
||||
* to be invoked.
|
||||
*
|
||||
* @param callback The callback to uninstall.
|
||||
* @param eventBase The TEventBase associated with this callback. This must
|
||||
* be the same TEventBase that was used when the callback was installed
|
||||
* with addAcceptCallback().
|
||||
*/
|
||||
void removeAcceptCallback(AcceptCallback *callback, TEventBase *eventBase);
|
||||
|
||||
/**
|
||||
* Begin accepting connctions on this socket.
|
||||
*
|
||||
* bind() and listen() must be called before calling startAccepting().
|
||||
*
|
||||
* When a TAsyncServerSocket is initially created, it will not begin
|
||||
* accepting connections until at least one callback has been added and
|
||||
* startAccepting() has been called. startAccepting() can also be used to
|
||||
* resume accepting connections after a call to pauseAccepting().
|
||||
*
|
||||
* If startAccepting() is called when there are no accept callbacks
|
||||
* installed, the socket will not actually begin accepting until an accept
|
||||
* callback is added.
|
||||
*
|
||||
* This method may only be called from the primary TEventBase thread.
|
||||
*/
|
||||
void startAccepting();
|
||||
|
||||
/**
|
||||
* Pause accepting connections.
|
||||
*
|
||||
* startAccepting() may be called to resume accepting.
|
||||
*
|
||||
* This method may only be called from the primary TEventBase thread.
|
||||
* If there are AcceptCallbacks being driven by other TEventBase threads they
|
||||
* may continue to receive callbacks for a short period of time after
|
||||
* pauseAccepting() returns.
|
||||
*
|
||||
* Unlike removeAcceptCallback() or destroy(), acceptStopped() will not be
|
||||
* called on the AcceptCallback objects simply due to a temporary pause. If
|
||||
* the server socket is later destroyed while paused, acceptStopped() will be
|
||||
* called all of the installed AcceptCallbacks.
|
||||
*/
|
||||
void pauseAccepting();
|
||||
|
||||
/**
|
||||
* Get the maximum number of connections that will be accepted each time
|
||||
* around the event loop.
|
||||
*/
|
||||
uint32_t getMaxAcceptAtOnce() const {
|
||||
return maxAcceptAtOnce_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of connections that will be accepted each time
|
||||
* around the event loop.
|
||||
*
|
||||
* This provides a very coarse-grained way of controlling how fast the
|
||||
* TAsyncServerSocket will accept connections. If you find that when your
|
||||
* server is overloaded TAsyncServerSocket accepts connections more quickly
|
||||
* than your code can process them, you can try lowering this number so that
|
||||
* fewer connections will be accepted each event loop iteration.
|
||||
*
|
||||
* For more explicit control over the accept rate, you can also use
|
||||
* pauseAccepting() to temporarily pause accepting when your server is
|
||||
* overloaded, and then use startAccepting() later to resume accepting.
|
||||
*/
|
||||
void setMaxAcceptAtOnce(uint32_t numConns) {
|
||||
maxAcceptAtOnce_ = numConns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of unprocessed messages which a NotificationPipe
|
||||
* can hold.
|
||||
*/
|
||||
uint32_t getMaxNumMessagesInPipe() const {
|
||||
return maxNumMsgsInPipe_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of unprocessed messages in NotificationPipe.
|
||||
* No new message will be sent to that NotificationPipe if there are more
|
||||
* than such number of unprocessed messages in that pipe.
|
||||
*/
|
||||
void setMaxNumMessagesInPipe(uint32_t num) {
|
||||
maxNumMsgsInPipe_ = num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the speed of adjusting connection accept rate.
|
||||
*/
|
||||
double getAcceptRateAdjustSpeed() const {
|
||||
return acceptRateAdjustSpeed_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the speed of adjusting connection accept rate.
|
||||
*/
|
||||
void setAcceptRateAdjustSpeed(double speed) {
|
||||
acceptRateAdjustSpeed_ = speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of connections dropped by the TAsyncServerSocket
|
||||
*/
|
||||
double getNumDroppedConnections() const {
|
||||
return numDroppedConnections_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Invoke destroy() instead to destroy the TAsyncServerSocket.
|
||||
*/
|
||||
virtual ~TAsyncServerSocket();
|
||||
|
||||
private:
|
||||
/**
|
||||
* A struct to keep track of the callbacks associated with this server
|
||||
* socket.
|
||||
*/
|
||||
struct CallbackInfo {
|
||||
CallbackInfo(AcceptCallback *callback, TEventBase *eventBase)
|
||||
: callback(callback),
|
||||
eventBase(eventBase),
|
||||
pipe(NULL) {}
|
||||
|
||||
AcceptCallback *callback;
|
||||
TEventBase *eventBase;
|
||||
// Note that the TNotificationPipe is actually owned by the RemoteAcceptor.
|
||||
// The RemoteAcceptor will destroy the TNotificationPipe (and itself)
|
||||
// once the pipe is closed by the TAsyncServerSocket.
|
||||
TNotificationPipe *pipe;
|
||||
};
|
||||
class RemoteAcceptor;
|
||||
enum MessageType {
|
||||
MSG_NEW_CONN = 0,
|
||||
MSG_ERROR = 1
|
||||
};
|
||||
class BackoffTimeout;
|
||||
|
||||
// Inherited from TEventHandler
|
||||
virtual void handlerReady(uint16_t events) THRIFT_NOEXCEPT;
|
||||
|
||||
int createSocket(int family);
|
||||
void setupSocket(int fd);
|
||||
void dispatchSocket(int socket, const transport::TSocketAddress& address);
|
||||
void dispatchError(const char *msg, int errnoValue);
|
||||
void enterBackoff();
|
||||
void backoffTimeoutExpired();
|
||||
|
||||
CallbackInfo* nextCallback() {
|
||||
CallbackInfo* info = &callbacks_[callbackIndex_];
|
||||
|
||||
++callbackIndex_;
|
||||
if (callbackIndex_ >= callbacks_.size()) {
|
||||
callbackIndex_ = 0;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
TEventBase *eventBase_;
|
||||
sa_family_t addressFamily_;
|
||||
int socket_;
|
||||
bool accepting_;
|
||||
uint32_t maxAcceptAtOnce_;
|
||||
uint32_t maxNumMsgsInPipe_;
|
||||
double acceptRateAdjustSpeed_; //0 to disable auto adjust
|
||||
double acceptRate_;
|
||||
int64_t lastAccepTimestamp_; // milliseconds
|
||||
int64_t numDroppedConnections_;
|
||||
uint32_t callbackIndex_;
|
||||
BackoffTimeout *backoffTimeout_;
|
||||
std::vector<CallbackInfo> callbacks_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TASYNCSERVERSOCKET_H_
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TASYNCSIGNALHANDLER_H_
|
||||
#define THRIFT_ASYNC_TASYNCSIGNALHANDLER_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/thrift_config.h"
|
||||
#include <event.h>
|
||||
#include <map>
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
class TEventBase;
|
||||
|
||||
/**
|
||||
* A handler to receive notification about POSIX signals.
|
||||
*
|
||||
* TAsyncSignalHandler allows code to process signals from within a TEventBase
|
||||
* loop. Standard signal handlers interrupt execution of the main thread, and
|
||||
* are run while the main thread is paused. As a result, great care must be
|
||||
* taken to avoid race conditions if the signal handler has to access or modify
|
||||
* any data used by the main thread.
|
||||
*
|
||||
* TAsyncSignalHandler solves this problem by running the TAsyncSignalHandler
|
||||
* callback in normal thread of execution, as a TEventBase callback.
|
||||
*
|
||||
* TAsyncSignalHandler may only be used in a single thread. It will only
|
||||
* process signals received by the thread where the TAsyncSignalHandler is
|
||||
* registered. It is the user's responsibility to ensure that signals are
|
||||
* delivered to the desired thread in multi-threaded programs.
|
||||
*/
|
||||
class TAsyncSignalHandler {
|
||||
public:
|
||||
/**
|
||||
* Create a new TAsyncSignalHandler.
|
||||
*/
|
||||
TAsyncSignalHandler(TEventBase* eventBase);
|
||||
virtual ~TAsyncSignalHandler();
|
||||
|
||||
/**
|
||||
* Register to receive callbacks about the specified signal.
|
||||
*
|
||||
* Once the handler has been registered for a particular signal,
|
||||
* signalReceived() will be called each time this thread receives this
|
||||
* signal.
|
||||
*
|
||||
* Throws a TException if an error occurs, or if this handler is already
|
||||
* registered for this signal.
|
||||
*/
|
||||
void registerSignalHandler(int signum);
|
||||
|
||||
/**
|
||||
* Unregister for callbacks about the specified signal.
|
||||
*
|
||||
* Throws a TException if an error occurs, or if this signal was not
|
||||
* registered.
|
||||
*/
|
||||
void unregisterSignalHandler(int signum);
|
||||
|
||||
/**
|
||||
* signalReceived() will called to indicate that the specified signal has
|
||||
* been received.
|
||||
*
|
||||
* signalReceived() will always be invoked from the TEventBase loop (i.e.,
|
||||
* after the main POSIX signal handler has returned control to the TEventBase
|
||||
* thread).
|
||||
*/
|
||||
virtual void signalReceived(int signum) THRIFT_NOEXCEPT = 0;
|
||||
|
||||
private:
|
||||
typedef std::map<int, struct event> SignalEventMap;
|
||||
|
||||
// Forbidden copy constructor and assignment operator
|
||||
TAsyncSignalHandler(TAsyncSignalHandler const &);
|
||||
TAsyncSignalHandler& operator=(TAsyncSignalHandler const &);
|
||||
|
||||
static void libeventCallback(int signum, short events, void* arg);
|
||||
|
||||
TEventBase* eventBase_;
|
||||
SignalEventMap signalEvents_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TASYNCSIGNALHANDLER_H_
|
|
@ -0,0 +1,551 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TASYNCSOCKET_H_
|
||||
#define THRIFT_ASYNC_TASYNCSOCKET_H_ 1
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include "thrift/lib/cpp/async/TAsyncTimeout.h"
|
||||
#include "thrift/lib/cpp/async/TAsyncTransport.h"
|
||||
#include "thrift/lib/cpp/async/TDelayedDestruction.h"
|
||||
#include "thrift/lib/cpp/async/TEventHandler.h"
|
||||
|
||||
#include <list>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
/**
|
||||
* A class for performing asynchronous I/O on a socket.
|
||||
*
|
||||
* TAsyncSocket allows users to asynchronously wait for data on a socket, and
|
||||
* to asynchronously send data.
|
||||
*
|
||||
* The APIs for reading and writing are intentionally asymmetric. Waiting for
|
||||
* data to read is a persistent API: a callback is installed, and is notified
|
||||
* whenever new data is available. It continues to be notified of new events
|
||||
* until it is uninstalled.
|
||||
*
|
||||
* TAsyncSocket does not provide read timeout functionality, because it
|
||||
* typically cannot determine when the timeout should be active. Generally, a
|
||||
* timeout should only be enabled when processing is blocked waiting on data
|
||||
* from the remote endpoint. For server sockets, the timeout should not be
|
||||
* active if the server is currently processing one or more outstanding
|
||||
* requests for this socket. For client sockets, the timeout should not be
|
||||
* active if there are no requests pending on the socket. Additionally, if a
|
||||
* client has multiple pending requests, it will ususally want a separate
|
||||
* timeout for each request, rather than a single read timeout.
|
||||
*
|
||||
* The write API is fairly intuitive: a user can request to send a block of
|
||||
* data, and a callback will be informed once the entire block has been
|
||||
* transferred to the kernel, or on error. TAsyncSocket does provide a send
|
||||
* timeout, since most callers want to give up if the remote end stops
|
||||
* responding and no further progress can be made sending the data.
|
||||
*/
|
||||
class TAsyncSocket : public TAsyncTransport,
|
||||
public TDelayedDestruction {
|
||||
public:
|
||||
#if THRIFT_HAVE_UNIQUE_PTR
|
||||
typedef std::unique_ptr<TAsyncSocket, Destructor> UniquePtr;
|
||||
#endif
|
||||
|
||||
class ConnectCallback {
|
||||
public:
|
||||
virtual ~ConnectCallback() {}
|
||||
|
||||
/**
|
||||
* connectSuccess() will be invoked when the connection has been
|
||||
* successfully established.
|
||||
*/
|
||||
virtual void connectSuccess() THRIFT_NOEXCEPT = 0;
|
||||
|
||||
/**
|
||||
* connectError() will be invoked if the connection attempt fails.
|
||||
*
|
||||
* @param ex An exception describing the error that occurred.
|
||||
*/
|
||||
virtual void connectError(const transport::TTransportException& ex)
|
||||
THRIFT_NOEXCEPT = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new unconnected TAsyncSocket.
|
||||
*
|
||||
* connect() must later be called on this socket to establish a connection.
|
||||
*/
|
||||
explicit TAsyncSocket(TEventBase* evb);
|
||||
|
||||
/**
|
||||
* Create a new TAsyncSocket and begin the connection process.
|
||||
*
|
||||
* @param evb EventBase that will manage this socket.
|
||||
* @param address The address to connect to.
|
||||
* @param connectTimeout Optional timeout in milliseconds for the connection
|
||||
* attempt.
|
||||
*/
|
||||
TAsyncSocket(TEventBase* evb,
|
||||
const transport::TSocketAddress& address,
|
||||
uint32_t connectTimeout = 0);
|
||||
|
||||
/**
|
||||
* Create a new TAsyncSocket and begin the connection process.
|
||||
*
|
||||
* @param evb EventBase that will manage this socket.
|
||||
* @param ip IP address to connect to (dotted-quad).
|
||||
* @param port Destination port in host byte order.
|
||||
* @param connectTimeout Optional timeout in milliseconds for the connection
|
||||
* attempt.
|
||||
*/
|
||||
TAsyncSocket(TEventBase* evb,
|
||||
const std::string& ip,
|
||||
uint16_t port,
|
||||
uint32_t connectTimeout = 0);
|
||||
|
||||
/**
|
||||
* Create a TAsyncSocket from an already connected socket file descriptor.
|
||||
*
|
||||
* Note that while TAsyncSocket enables TCP_NODELAY for sockets it creates
|
||||
* when connecting, it does not change the socket options when given an
|
||||
* existing file descriptor. If callers want TCP_NODELAY enabled when using
|
||||
* this version of the constructor, they need to explicitly call
|
||||
* setNoDelay(true) after the constructor returns.
|
||||
*
|
||||
* @param evb EventBase that will manage this socket.
|
||||
* @param fd File descriptor to take over (should be a connected socket).
|
||||
*/
|
||||
TAsyncSocket(TEventBase* evb, int fd);
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TAsyncSocket>.
|
||||
*
|
||||
* This passes in the correct destructor object, since TAsyncSocket's
|
||||
* destructor is protected and cannot be invoked directly.
|
||||
*/
|
||||
static boost::shared_ptr<TAsyncSocket> newSocket(TEventBase* evb) {
|
||||
return boost::shared_ptr<TAsyncSocket>(new TAsyncSocket(evb),
|
||||
Destructor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TAsyncSocket>.
|
||||
*/
|
||||
static boost::shared_ptr<TAsyncSocket> newSocket(
|
||||
TEventBase* evb,
|
||||
const transport::TSocketAddress& address,
|
||||
uint32_t connectTimeout = 0) {
|
||||
return boost::shared_ptr<TAsyncSocket>(
|
||||
new TAsyncSocket(evb, address, connectTimeout),
|
||||
Destructor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TAsyncSocket>.
|
||||
*/
|
||||
static boost::shared_ptr<TAsyncSocket> newSocket(
|
||||
TEventBase* evb,
|
||||
const std::string& ip,
|
||||
uint16_t port,
|
||||
uint32_t connectTimeout = 0) {
|
||||
return boost::shared_ptr<TAsyncSocket>(
|
||||
new TAsyncSocket(evb, ip, port, connectTimeout),
|
||||
Destructor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TAsyncSocket>.
|
||||
*/
|
||||
static boost::shared_ptr<TAsyncSocket> newSocket(TEventBase* evb, int fd) {
|
||||
return boost::shared_ptr<TAsyncSocket>(new TAsyncSocket(evb, fd),
|
||||
Destructor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the socket.
|
||||
*
|
||||
* TAsyncSocket::destroy() must be called to destroy the socket.
|
||||
* The normal destructor is private, and should not be invoked directly.
|
||||
* This prevents callers from deleting a TAsyncSocket while it is invoking a
|
||||
* callback.
|
||||
*/
|
||||
virtual void destroy();
|
||||
|
||||
/**
|
||||
* Get the TEventBase used by this socket.
|
||||
*/
|
||||
virtual TEventBase* getEventBase() const {
|
||||
return eventBase_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file descriptor used by the TAsyncSocket.
|
||||
*/
|
||||
int getFd() const {
|
||||
return fd_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the file descriptor from the TAsyncSocket.
|
||||
*
|
||||
* This will immediately cause any installed callbacks to be invoked with an
|
||||
* error. The TAsyncSocket may no longer be used after the file descriptor
|
||||
* has been extracted.
|
||||
*
|
||||
* Returns the file descriptor. The caller assumes ownership of the
|
||||
* descriptor, and it will not be closed when the TAsyncSocket is destroyed.
|
||||
*/
|
||||
int detachFd();
|
||||
|
||||
/**
|
||||
* Class that consists of the input parameters for setsockopt().
|
||||
*
|
||||
* The memory referenced by optval should be valid throughout the
|
||||
* life cycle of the SocketOption object.
|
||||
*/
|
||||
class SocketOption {
|
||||
public:
|
||||
SocketOption(): level_(0), optname_(0), optval_(NULL), size_(0) {}
|
||||
|
||||
template <class T>
|
||||
SocketOption(int level, int optname, const T* optval):
|
||||
level_(level), optname_(optname), optval_(optval), size_(sizeof(T)) {}
|
||||
|
||||
int apply(int fd) const {
|
||||
return setsockopt(fd, level_, optname_, optval_, size_);
|
||||
}
|
||||
|
||||
protected:
|
||||
int level_;
|
||||
int optname_;
|
||||
const void *optval_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
typedef std::list<SocketOption> OptionList;
|
||||
|
||||
static OptionList emptyOptionList;
|
||||
|
||||
/**
|
||||
* Initiate a connection.
|
||||
*
|
||||
* @param callback The callback to inform when the connection attempt
|
||||
* completes.
|
||||
* @param address The address to connect to.
|
||||
* @param timeout A timeout value, in milliseconds. If the connection
|
||||
* does not succeed within this period,
|
||||
* callback->connectError() will be invoked.
|
||||
*/
|
||||
virtual void connect(ConnectCallback* callback,
|
||||
const transport::TSocketAddress& address,
|
||||
int timeout = 0,
|
||||
const OptionList &options = emptyOptionList) THRIFT_NOEXCEPT;
|
||||
void connect(ConnectCallback* callback, const std::string& ip, uint16_t port,
|
||||
int timeout = 00,
|
||||
const OptionList &options = emptyOptionList) THRIFT_NOEXCEPT;
|
||||
|
||||
/**
|
||||
* Set the send timeout.
|
||||
*
|
||||
* If write requests do not make any progress for more than the specified
|
||||
* number of milliseconds, fail all pending writes and close the socket.
|
||||
*
|
||||
* If write requests are currently pending when setSendTimeout() is called,
|
||||
* the timeout interval is immediately restarted using the new value.
|
||||
*
|
||||
* (See the comments for TAsyncSocket for an explanation of why TAsyncSocket
|
||||
* provides setSendTimeout() but not setRecvTimeout().)
|
||||
*
|
||||
* @param milliseconds The timeout duration, in milliseconds. If 0, no
|
||||
* timeout will be used.
|
||||
*/
|
||||
void setSendTimeout(uint32_t milliseconds);
|
||||
|
||||
/**
|
||||
* Get the send timeout.
|
||||
*
|
||||
* @return Returns the current send timeout, in milliseconds. A return value
|
||||
* of 0 indicates that no timeout is set.
|
||||
*/
|
||||
uint32_t getSendTimeout() const {
|
||||
return sendTimeout_;
|
||||
}
|
||||
|
||||
// Methods inherited from TAsyncTransport
|
||||
// See the documentation in TAsyncTransport.h
|
||||
virtual void setReadCallback(ReadCallback* callback);
|
||||
virtual ReadCallback* getReadCallback() const;
|
||||
|
||||
virtual void write(WriteCallback* callback, const void* buf, size_t bytes);
|
||||
virtual void writev(WriteCallback* callback, const iovec* vec, size_t count);
|
||||
virtual void writeChain(WriteCallback* callback,
|
||||
std::unique_ptr<folly::IOBuf>&& buf,
|
||||
bool cork = false);
|
||||
|
||||
virtual void close();
|
||||
virtual void closeNow();
|
||||
virtual void shutdownWrite();
|
||||
virtual void shutdownWriteNow();
|
||||
|
||||
virtual bool readable() const;
|
||||
virtual bool good() const;
|
||||
virtual bool error() const;
|
||||
virtual void attachEventBase(TEventBase* eventBase);
|
||||
virtual void detachEventBase();
|
||||
|
||||
virtual void getLocalAddress(transport::TSocketAddress* address) const;
|
||||
virtual void getPeerAddress(transport::TSocketAddress* address) const;
|
||||
|
||||
virtual bool connecting() const {
|
||||
return (state_ == STATE_CONNECTING);
|
||||
}
|
||||
|
||||
// Methods controlling socket options
|
||||
|
||||
/**
|
||||
* Force writes to be transmitted immediately.
|
||||
*
|
||||
* This controls the TCP_NODELAY socket option. When enabled, TCP segments
|
||||
* are sent as soon as possible, even if it is not a full frame of data.
|
||||
* When disabled, the data may be buffered briefly to try and wait for a full
|
||||
* frame of data.
|
||||
*
|
||||
* By default, TCP_NODELAY is enabled for TAsyncSocket objects.
|
||||
*
|
||||
* This method will fail if the socket is not currently open.
|
||||
*
|
||||
* @return Returns 0 if the TCP_NODELAY flag was successfully updated,
|
||||
* or a non-zero errno value on error.
|
||||
*/
|
||||
int setNoDelay(bool noDelay);
|
||||
|
||||
/**
|
||||
* Generic API for reading a socket option.
|
||||
*
|
||||
* @param level same as the "level" parameter in getsockopt().
|
||||
* @param optname same as the "optname" parameter in getsockopt().
|
||||
* @param optval pointer to the variable in which the option value should
|
||||
* be returned.
|
||||
* @return same as the return value of getsockopt().
|
||||
*/
|
||||
template <typename T>
|
||||
int getSockOpt(int level, int optname, T *optval) {
|
||||
return getsockopt(fd_, level, optname, optval, sizeof(T));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic API for setting a socket option.
|
||||
*
|
||||
* @param level same as the "level" parameter in getsockopt().
|
||||
* @param optname same as the "optname" parameter in getsockopt().
|
||||
* @param optval the option value to set.
|
||||
* @return same as the return value of setsockopt().
|
||||
*/
|
||||
template <typename T>
|
||||
int setSockOpt(int level, int optname, const T *optval) {
|
||||
return setsockopt(fd_, level, optname, optval, sizeof(T));
|
||||
}
|
||||
|
||||
protected:
|
||||
enum ReadResultEnum {
|
||||
READ_EOF = 0,
|
||||
READ_ERROR = -1,
|
||||
READ_BLOCKING = -2,
|
||||
};
|
||||
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Users of TAsyncSocket must never delete it directly. Instead, invoke
|
||||
* destroy() instead. (See the documentation in TDelayedDestruction.h for
|
||||
* more details.)
|
||||
*/
|
||||
~TAsyncSocket();
|
||||
|
||||
enum StateEnum {
|
||||
STATE_UNINIT,
|
||||
STATE_CONNECTING,
|
||||
STATE_ESTABLISHED,
|
||||
STATE_CLOSED,
|
||||
STATE_ERROR
|
||||
};
|
||||
enum ShutdownFlags {
|
||||
/// shutdownWrite() called, but we are still waiting on writes to drain
|
||||
SHUT_WRITE_PENDING = 0x01,
|
||||
/// writes have been completely shut down
|
||||
SHUT_WRITE = 0x02,
|
||||
/**
|
||||
* Reads have been shutdown.
|
||||
*
|
||||
* At the moment we don't distinguish between remote read shutdown
|
||||
* (received EOF from the remote end) and local read shutdown. We can
|
||||
* only receive EOF when a read callback is set, and we immediately inform
|
||||
* it of the EOF. Therefore there doesn't seem to be any reason to have a
|
||||
* separate state of "received EOF but the local side may still want to
|
||||
* read".
|
||||
*
|
||||
* We also don't currently provide any API for only shutting down the read
|
||||
* side of a socket. (This is a no-op as far as TCP is concerned, anyway.)
|
||||
*/
|
||||
SHUT_READ = 0x04,
|
||||
};
|
||||
|
||||
class WriteRequest;
|
||||
|
||||
class WriteTimeout : public TAsyncTimeout {
|
||||
public:
|
||||
WriteTimeout(TAsyncSocket* socket, TEventBase* eventBase)
|
||||
: TAsyncTimeout(eventBase)
|
||||
, socket_(socket) {}
|
||||
|
||||
virtual void timeoutExpired() THRIFT_NOEXCEPT {
|
||||
socket_->timeoutExpired();
|
||||
}
|
||||
|
||||
private:
|
||||
TAsyncSocket* socket_;
|
||||
};
|
||||
|
||||
class IoHandler : public TEventHandler {
|
||||
public:
|
||||
IoHandler(TAsyncSocket* socket, TEventBase* eventBase)
|
||||
: TEventHandler(eventBase, -1)
|
||||
, socket_(socket) {}
|
||||
IoHandler(TAsyncSocket* socket, TEventBase* eventBase, int fd)
|
||||
: TEventHandler(eventBase, fd)
|
||||
, socket_(socket) {}
|
||||
|
||||
virtual void handlerReady(uint16_t events) THRIFT_NOEXCEPT {
|
||||
socket_->ioReady(events);
|
||||
}
|
||||
|
||||
private:
|
||||
TAsyncSocket* socket_;
|
||||
};
|
||||
|
||||
void init();
|
||||
|
||||
// event notification methods
|
||||
void ioReady(uint16_t events) THRIFT_NOEXCEPT;
|
||||
virtual void handleInitialReadWrite() THRIFT_NOEXCEPT;
|
||||
virtual void handleRead() THRIFT_NOEXCEPT;
|
||||
virtual void handleWrite() THRIFT_NOEXCEPT;
|
||||
virtual void handleConnect() THRIFT_NOEXCEPT;
|
||||
void timeoutExpired() THRIFT_NOEXCEPT;
|
||||
|
||||
/**
|
||||
* Attempt to read from the socket.
|
||||
*
|
||||
* @param buf The buffer to read data into.
|
||||
* @param buflen The length of the buffer.
|
||||
*
|
||||
* @return Returns the number of bytes read, or READ_EOF on EOF, or
|
||||
* READ_ERROR on error, or READ_BLOCKING if the operation will
|
||||
* block.
|
||||
*/
|
||||
virtual ssize_t performRead(void* buf, size_t buflen);
|
||||
|
||||
/**
|
||||
* Write as much data as possible to the socket without blocking,
|
||||
* and queue up any leftover data to send when the socket can
|
||||
* handle writes again.
|
||||
*
|
||||
* @param callback The callback to invoke when the write is completed.
|
||||
* @param vec Array of buffers to write; this method will make a
|
||||
* copy of the vector (but not the buffers themselves)
|
||||
* if the write has to be completed asynchronously.
|
||||
* @param count Number of elements in vec.
|
||||
* @param buf The IOBuf that manages the buffers referenced by
|
||||
* vec, or a pointer to NULL if the buffers are not
|
||||
* associated with an IOBuf. Note that ownership of
|
||||
* the IOBuf is transferred here; upon completion of
|
||||
* the write, the TAsyncSocket deletes the IOBuf.
|
||||
* @param cork Whether to delay the write until the next non-corked
|
||||
* write operation. (Note: may not be supported in all
|
||||
* subclasses or on all platforms.)
|
||||
*/
|
||||
void writeImpl(WriteCallback* callback, const iovec* vec, size_t count,
|
||||
std::unique_ptr<folly::IOBuf>&& buf, bool cork = false);
|
||||
|
||||
/**
|
||||
* Attempt to write to the socket.
|
||||
*
|
||||
* @param vec The iovec array pointing to the buffers to write.
|
||||
* @param count The length of the iovec array.
|
||||
* @param haveMore This flag is inherited from TAsyncSocket but is
|
||||
* not handled here.
|
||||
* @param countWritten On return, the value pointed to by this parameter
|
||||
* will contain the number of iovec entries that were
|
||||
* fully written.
|
||||
* @param partialWritten On return, the value pointed to by this parameter
|
||||
* will contain the number of bytes written in the
|
||||
* partially written iovec entry.
|
||||
*
|
||||
* @return Returns the total number of bytes written, or -1 on error. If no
|
||||
* data can be written immediately, 0 is returned.
|
||||
*/
|
||||
virtual ssize_t performWrite(const iovec* vec, uint32_t count,
|
||||
bool haveMore, uint32_t* countWritten,
|
||||
uint32_t* partialWritten);
|
||||
|
||||
bool updateEventRegistration();
|
||||
|
||||
/**
|
||||
* Update event registration.
|
||||
*
|
||||
* @param enable Flags of events to enable. Set it to 0 if no events
|
||||
* need to be enabled in this call.
|
||||
* @param disable Flags of events
|
||||
* to disable. Set it to 0 if no events need to be disabled in this
|
||||
* call.
|
||||
*
|
||||
* @return true iff the update is successful.
|
||||
*/
|
||||
bool updateEventRegistration(uint16_t enable, uint16_t disable);
|
||||
|
||||
// error handling methods
|
||||
void startFail();
|
||||
void finishFail();
|
||||
void fail(const char* fn, const transport::TTransportException& ex);
|
||||
void failConnect(const char* fn, const transport::TTransportException& ex);
|
||||
void failRead(const char* fn, const transport::TTransportException& ex);
|
||||
void failWrite(const char* fn, WriteCallback* callback, size_t bytesWritten,
|
||||
const transport::TTransportException& ex);
|
||||
void failWrite(const char* fn, const transport::TTransportException& ex);
|
||||
void failAllWrites(const transport::TTransportException& ex);
|
||||
void invalidState(ConnectCallback* callback);
|
||||
void invalidState(ReadCallback* callback);
|
||||
void invalidState(WriteCallback* callback);
|
||||
|
||||
uint8_t state_; ///< StateEnum describing current state
|
||||
uint8_t shutdownFlags_; ///< Shutdown state (ShutdownFlags)
|
||||
uint16_t eventFlags_; ///< TEventBase::HandlerFlags settings
|
||||
int fd_; ///< The socket file descriptor
|
||||
uint32_t sendTimeout_; ///< The send timeout, in milliseconds
|
||||
TEventBase* eventBase_; ///< The TEventBase
|
||||
WriteTimeout writeTimeout_; ///< A timeout for connect and write
|
||||
IoHandler ioHandler_; ///< A TEventHandler to monitor the fd
|
||||
|
||||
ConnectCallback* connectCallback_; ///< ConnectCallback
|
||||
ReadCallback* readCallback_; ///< ReadCallback
|
||||
WriteRequest* writeReqHead_; ///< Chain of WriteRequests
|
||||
WriteRequest* writeReqTail_; ///< End of WriteRequest chain
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // #ifndef THRIFT_ASYNC_TASYNCSOCKET_H_
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TASYNCTIMEOUT_H_
|
||||
#define THRIFT_ASYNC_TASYNCTIMEOUT_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/thrift_config.h"
|
||||
#include "thrift/lib/cpp/async/TEventUtil.h"
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
class TEventBase;
|
||||
|
||||
/**
|
||||
* TAsyncTimeout is used to asynchronously wait for a timeout to occur.
|
||||
*/
|
||||
class TAsyncTimeout : private boost::noncopyable {
|
||||
public:
|
||||
enum InternalEnum {
|
||||
INTERNAL,
|
||||
NORMAL
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new TAsyncTimeout object, driven by the specified TEventBase.
|
||||
*/
|
||||
explicit TAsyncTimeout(TEventBase* eventBase);
|
||||
|
||||
/**
|
||||
* Create a new internal TAsyncTimeout object.
|
||||
*
|
||||
* Internal timeouts are like regular timeouts, but will not stop the
|
||||
* TEventBase loop from exiting if the only remaining events are internal
|
||||
* timeouts.
|
||||
*
|
||||
* This is useful for implementing fallback timeouts to abort the TEventBase
|
||||
* loop if the other events have not been processed within a specified time
|
||||
* period: if the event loop takes too long the timeout will fire and can
|
||||
* stop the event loop. However, if all other events complete, the event
|
||||
* loop will exit even though the internal timeout is still installed.
|
||||
*/
|
||||
TAsyncTimeout(TEventBase* eventBase, InternalEnum internal);
|
||||
|
||||
/**
|
||||
* Create a new TAsyncTimeout object, not yet assigned to a TEventBase.
|
||||
*
|
||||
* attachEventBase() must be called prior to scheduling the timeout.
|
||||
*/
|
||||
TAsyncTimeout();
|
||||
|
||||
/**
|
||||
* TAsyncTimeout destructor.
|
||||
*
|
||||
* The timeout will be automatically cancelled if it is running.
|
||||
*/
|
||||
virtual ~TAsyncTimeout();
|
||||
|
||||
/**
|
||||
* timeoutExpired() is invoked when the timeout period has expired.
|
||||
*/
|
||||
virtual void timeoutExpired() THRIFT_NOEXCEPT = 0;
|
||||
|
||||
/**
|
||||
* Schedule the timeout to fire in the specified number of milliseconds.
|
||||
*
|
||||
* After the specified number of milliseconds has elapsed, timeoutExpired()
|
||||
* will be invoked by the TEventBase's main loop.
|
||||
*
|
||||
* If the timeout is already running, it will be rescheduled with the
|
||||
* new timeout value.
|
||||
*
|
||||
* @param milliseconds The timeout duration, in milliseconds.
|
||||
*
|
||||
* @return Returns true if the timeout was successfully scheduled,
|
||||
* and false if an error occurred. After an error, the timeout is
|
||||
* always unscheduled, even if scheduleTimeout() was just
|
||||
* rescheduling an existing timeout.
|
||||
*/
|
||||
bool scheduleTimeout(uint32_t milliseconds);
|
||||
|
||||
/**
|
||||
* Cancel the timeout, if it is running.
|
||||
*/
|
||||
void cancelTimeout();
|
||||
|
||||
/**
|
||||
* Returns true if the timeout is currently scheduled.
|
||||
*/
|
||||
bool isScheduled() {
|
||||
return TEventUtil::isEventRegistered(&event_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach the timeout to a TEventBase.
|
||||
*
|
||||
* This may only be called if the timeout is not currently attached to a
|
||||
* TEventBase (either by using the default constructor, or by calling
|
||||
* detachEventBase()).
|
||||
*
|
||||
* This method must be invoked in the TEventBase's thread.
|
||||
*
|
||||
* The internal parameter specifies if this timeout should be treated as an
|
||||
* internal event. TEventBase::loop() will return when there are no more
|
||||
* non-internal events remaining.
|
||||
*/
|
||||
void attachEventBase(TEventBase* eventBase,
|
||||
InternalEnum internal = NORMAL);
|
||||
|
||||
/**
|
||||
* Detach the timeout from its TEventBase.
|
||||
*
|
||||
* This may only be called when the timeout is not running.
|
||||
* Once detached, the timeout may not be scheduled again until it is
|
||||
* re-attached to a TEventBase by calling attachEventBase().
|
||||
*
|
||||
* This method must be called from the current TEventBase's thread.
|
||||
*/
|
||||
void detachEventBase();
|
||||
|
||||
private:
|
||||
static void libeventCallback(int fd, short events, void* arg);
|
||||
|
||||
struct event event_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TASYNCTIMEOUT_H_
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TASYNCTIMEOUTSET_H_
|
||||
#define THRIFT_ASYNC_TASYNCTIMEOUTSET_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TAsyncTimeout.h"
|
||||
#include "thrift/lib/cpp/async/TDelayedDestruction.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
/**
|
||||
* TAsyncTimeoutSet exists for efficiently managing a group of timeouts events
|
||||
* that always have the same timeout interval.
|
||||
*
|
||||
* TAsyncTimeoutSet takes advantage of the fact that the timeouts are always
|
||||
* scheduled in sorted order. (Since each timeout has the same interval, when
|
||||
* a new timeout is scheduled it will always be the last timeout in the set.)
|
||||
* This avoids the need to perform any additional sorting of the timeouts
|
||||
* within a single TAsyncTimeoutSet.
|
||||
*
|
||||
* TAsyncTimeoutSet is useful whenever you have a large group of objects that
|
||||
* each need their own timeout, but with the same interval for each object.
|
||||
* For example, managing idle timeouts for thousands of connection, or
|
||||
* scheduling health checks for a large group of servers.
|
||||
*/
|
||||
class TAsyncTimeoutSet : private TAsyncTimeout, public TDelayedDestruction {
|
||||
public:
|
||||
typedef std::unique_ptr<TAsyncTimeoutSet, Destructor> UniquePtr;
|
||||
|
||||
/**
|
||||
* A callback to be notified when a timeout has expired.
|
||||
*
|
||||
* TAsyncTimeoutSet::Callback is very similar to TAsyncTimeout. The primary
|
||||
* distinction is that TAsyncTimeout can choose its timeout interval each
|
||||
* time it is scheduled. On the other hand, TAsyncTimeoutSet::Callback
|
||||
* always uses the timeout interval defined by the TAsyncTimeoutSet where it
|
||||
* is scheduled.
|
||||
*/
|
||||
class Callback {
|
||||
public:
|
||||
Callback()
|
||||
: timeoutSet_(NULL),
|
||||
expiration_(0),
|
||||
prev_(NULL),
|
||||
next_(NULL) {}
|
||||
|
||||
virtual ~Callback();
|
||||
|
||||
/**
|
||||
* timeoutExpired() is invoked when the timeout has expired.
|
||||
*/
|
||||
virtual void timeoutExpired() THRIFT_NOEXCEPT = 0;
|
||||
|
||||
/**
|
||||
* Cancel the timeout, if it is running.
|
||||
*
|
||||
* If the timeout is not scheduled, cancelTimeout() does nothing.
|
||||
*/
|
||||
void cancelTimeout() {
|
||||
if (timeoutSet_ == NULL) {
|
||||
// We're not scheduled, so there's nothing to do.
|
||||
return;
|
||||
}
|
||||
cancelTimeoutImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this timeout is currently scheduled, and false otherwise.
|
||||
*/
|
||||
bool isScheduled() const {
|
||||
return timeoutSet_ != NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
// Get the time remaining until this timeout expires
|
||||
std::chrono::milliseconds getTimeRemaining(
|
||||
std::chrono::milliseconds now) const {
|
||||
if (now >= expiration_) {
|
||||
return std::chrono::milliseconds(0);
|
||||
}
|
||||
return expiration_ - now;
|
||||
}
|
||||
|
||||
void setScheduled(TAsyncTimeoutSet* timeoutSet, Callback* prev);
|
||||
void cancelTimeoutImpl();
|
||||
|
||||
TAsyncTimeoutSet* timeoutSet_;
|
||||
std::chrono::milliseconds expiration_;
|
||||
Callback* prev_;
|
||||
Callback* next_;
|
||||
|
||||
// Give TAsyncTimeoutSet direct access to our members so it can take care
|
||||
// of scheduling/cancelling.
|
||||
friend class TAsyncTimeoutSet;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new TAsyncTimeoutSet with the specified interval.
|
||||
*/
|
||||
TAsyncTimeoutSet(TEventBase* eventBase,
|
||||
std::chrono::milliseconds intervalMS,
|
||||
std::chrono::milliseconds atMostEveryN =
|
||||
std::chrono::milliseconds(0));
|
||||
|
||||
/**
|
||||
* Destroy the TAsyncTimeoutSet.
|
||||
*
|
||||
* Normally a TAsyncTimeoutSet should only be destroyed when there are no
|
||||
* more callbacks pending in the set. If there are timeout callbacks pending
|
||||
* for this set, destroying the TAsyncTimeoutSet will automatically cancel
|
||||
* them. If you destroy a TAsyncTimeoutSet with callbacks pending, your
|
||||
* callback code needs to be aware that the callbacks will never be invoked.
|
||||
*/
|
||||
virtual void destroy();
|
||||
|
||||
/**
|
||||
* Get the interval for this TAsyncTimeoutSet.
|
||||
*
|
||||
* Returns the timeout interval in milliseconds. All callbacks scheduled
|
||||
* with scheduleTimeout() will be invoked after this amount of time has
|
||||
* passed since the call to scheduleTimeout().
|
||||
*/
|
||||
std::chrono::milliseconds getInterval() const {
|
||||
return interval_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the specified Callback to be invoked after the TAsyncTimeoutSet's
|
||||
* specified timeout interval.
|
||||
*
|
||||
* If the callback is already scheduled, this cancels the existing timeout
|
||||
* before scheduling the new timeout.
|
||||
*/
|
||||
void scheduleTimeout(Callback* callback);
|
||||
|
||||
/**
|
||||
* Limit how frequently this TAsyncTimeoutSet will fire.
|
||||
*/
|
||||
void fireAtMostEvery(const std::chrono::milliseconds& ms) {
|
||||
atMostEveryN_ = ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer to the next Callback scheduled to be invoked (may be null).
|
||||
*/
|
||||
Callback* front() { return head_; }
|
||||
const Callback* front() const { return head_; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Use destroy() instead. See the comments in TDelayedDestruction for more
|
||||
* details.
|
||||
*/
|
||||
virtual ~TAsyncTimeoutSet();
|
||||
|
||||
private:
|
||||
// Forbidden copy constructor and assignment operator
|
||||
TAsyncTimeoutSet(TAsyncTimeoutSet const &) = delete;
|
||||
TAsyncTimeoutSet& operator=(TAsyncTimeoutSet const &) = delete;
|
||||
|
||||
// Private methods to be invoked by TAsyncTimeoutSet::Callback
|
||||
void headChanged();
|
||||
|
||||
// Methods inherited from TAsyncTimeout
|
||||
virtual void timeoutExpired() THRIFT_NOEXCEPT;
|
||||
|
||||
std::chrono::milliseconds interval_;
|
||||
std::chrono::milliseconds atMostEveryN_;
|
||||
bool inTimeoutExpired_;
|
||||
Callback* head_;
|
||||
Callback* tail_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TASYNCTIMEOUTSET_H_
|
|
@ -0,0 +1,428 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TASYNCTRANSPORT_H_
|
||||
#define THRIFT_ASYNC_TASYNCTRANSPORT_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/thrift_config.h"
|
||||
#include <sys/uio.h>
|
||||
#include <inttypes.h>
|
||||
#include <memory>
|
||||
|
||||
namespace folly {
|
||||
class IOBuf;
|
||||
}
|
||||
|
||||
namespace apache { namespace thrift {
|
||||
|
||||
namespace transport {
|
||||
class TSocketAddress;
|
||||
class TTransportException;
|
||||
}
|
||||
|
||||
namespace async {
|
||||
|
||||
class TEventBase;
|
||||
|
||||
/**
|
||||
* TAsyncTransport defines an asynchronous API for streaming I/O.
|
||||
*
|
||||
* This class provides an API to for asynchronously waiting for data
|
||||
* on a streaming transport, and for asynchronously sending data.
|
||||
*
|
||||
* The APIs for reading and writing are intentionally asymmetric. Waiting for
|
||||
* data to read is a persistent API: a callback is installed, and is notified
|
||||
* whenever new data is available. It continues to be notified of new events
|
||||
* until it is uninstalled.
|
||||
*
|
||||
* TAsyncTransport does not provide read timeout functionality, because it
|
||||
* typically cannot determine when the timeout should be active. Generally, a
|
||||
* timeout should only be enabled when processing is blocked waiting on data
|
||||
* from the remote endpoint. For server-side applications, the timeout should
|
||||
* not be active if the server is currently processing one or more outstanding
|
||||
* requests on this transport. For client-side applications, the timeout
|
||||
* should not be active if there are no requests pending on the transport.
|
||||
* Additionally, if a client has multiple pending requests, it will ususally
|
||||
* want a separate timeout for each request, rather than a single read timeout.
|
||||
*
|
||||
* The write API is fairly intuitive: a user can request to send a block of
|
||||
* data, and a callback will be informed once the entire block has been
|
||||
* transferred to the kernel, or on error. TAsyncTransport does provide a send
|
||||
* timeout, since most callers want to give up if the remote end stops
|
||||
* responding and no further progress can be made sending the data.
|
||||
*/
|
||||
class TAsyncTransport {
|
||||
public:
|
||||
class ReadCallback {
|
||||
public:
|
||||
virtual ~ReadCallback() {}
|
||||
|
||||
/**
|
||||
* When data becomes available, getReadBuffer() will be invoked to get the
|
||||
* buffer into which data should be read.
|
||||
*
|
||||
* This method allows the ReadCallback to delay buffer allocation until
|
||||
* data becomes available. This allows applications to manage large
|
||||
* numbers of idle connections, without having to maintain a separate read
|
||||
* buffer for each idle connection.
|
||||
*
|
||||
* It is possible that in some cases, getReadBuffer() may be called
|
||||
* multiple times before readDataAvailable() is invoked. In this case, the
|
||||
* data will be written to the buffer returned from the most recent call to
|
||||
* readDataAvailable(). If the previous calls to readDataAvailable()
|
||||
* returned different buffers, the ReadCallback is responsible for ensuring
|
||||
* that they are not leaked.
|
||||
*
|
||||
* If getReadBuffer() throws an exception, returns a NULL buffer, or
|
||||
* returns a 0 length, the ReadCallback will be uninstalled and its
|
||||
* readError() method will be invoked.
|
||||
*
|
||||
* getReadBuffer() is not allowed to change the transport state before it
|
||||
* returns. (For example, it should never uninstall the read callback, or
|
||||
* set a different read callback.)
|
||||
*
|
||||
* @param bufReturn getReadBuffer() should update *bufReturn to contain the
|
||||
* address of the read buffer. This parameter will never
|
||||
* be NULL.
|
||||
* @param lenReturn getReadBuffer() should update *lenReturn to contain the
|
||||
* maximum number of bytes that may be written to the read
|
||||
* buffer. This parameter will never be NULL.
|
||||
*/
|
||||
virtual void getReadBuffer(void** bufReturn, size_t* lenReturn) = 0;
|
||||
|
||||
/**
|
||||
* readDataAvailable() will be invoked when data has been successfully read
|
||||
* into the buffer returned by the last call to getReadBuffer().
|
||||
*
|
||||
* The read callback remains installed after readDataAvailable() returns.
|
||||
* It must be explicitly uninstalled to stop receiving read events.
|
||||
* getReadBuffer() will be called at least once before each call to
|
||||
* readDataAvailable(). getReadBuffer() will also be called before any
|
||||
* call to readEOF().
|
||||
*
|
||||
* @param len The number of bytes placed in the buffer.
|
||||
*/
|
||||
virtual void readDataAvailable(size_t len) THRIFT_NOEXCEPT = 0;
|
||||
|
||||
/**
|
||||
* readEOF() will be invoked when the transport is closed.
|
||||
*
|
||||
* The read callback will be automatically uninstalled immediately before
|
||||
* readEOF() is invoked.
|
||||
*/
|
||||
virtual void readEOF() THRIFT_NOEXCEPT = 0;
|
||||
|
||||
/**
|
||||
* readError() will be invoked if an error occurs reading from the
|
||||
* transport.
|
||||
*
|
||||
* The read callback will be automatically uninstalled immediately before
|
||||
* readError() is invoked.
|
||||
*
|
||||
* @param ex An exception describing the error that occurred.
|
||||
*/
|
||||
virtual void readError(const transport::TTransportException& ex)
|
||||
THRIFT_NOEXCEPT = 0;
|
||||
};
|
||||
|
||||
class WriteCallback {
|
||||
public:
|
||||
virtual ~WriteCallback() {}
|
||||
|
||||
/**
|
||||
* writeSuccess() will be invoked when all of the data has been
|
||||
* successfully written.
|
||||
*
|
||||
* Note that this mainly signals that the buffer containing the data to
|
||||
* write is no longer needed and may be freed or re-used. It does not
|
||||
* guarantee that the data has been fully transmitted to the remote
|
||||
* endpoint. For example, on socket-based transports, writeSuccess() only
|
||||
* indicates that the data has been given to the kernel for eventual
|
||||
* transmission.
|
||||
*/
|
||||
virtual void writeSuccess() THRIFT_NOEXCEPT = 0;
|
||||
|
||||
/**
|
||||
* writeError() will be invoked if an error occurs writing the data.
|
||||
*
|
||||
* @param bytesWritten The number of bytes that were successfull
|
||||
* @param ex An exception describing the error that occurred.
|
||||
*/
|
||||
virtual void writeError(size_t bytesWritten,
|
||||
const transport::TTransportException& ex)
|
||||
THRIFT_NOEXCEPT = 0;
|
||||
};
|
||||
|
||||
virtual ~TAsyncTransport() {}
|
||||
|
||||
/**
|
||||
* Set the read callback.
|
||||
*
|
||||
* See the documentation for ReadCallback above for a description of how the
|
||||
* callback will be invoked. Note that the callback remains installed until
|
||||
* it is explicitly uninstalled, or until an error occurs.
|
||||
*
|
||||
* If a ReadCallback is already installed, it is replaced with the new
|
||||
* callback.
|
||||
*
|
||||
* @param callback The callback to invoke when data is available.
|
||||
* This parameter may be NULL to uninstall the current
|
||||
* read callback.
|
||||
*/
|
||||
virtual void setReadCallback(ReadCallback* callback) = 0;
|
||||
|
||||
/**
|
||||
* Get the currently installed read callback.
|
||||
*
|
||||
* @return Returns a pointer to the installed ReadCallback, or NULL if no
|
||||
* ReadCallback is installed.
|
||||
*/
|
||||
virtual ReadCallback* getReadCallback() const = 0;
|
||||
|
||||
/**
|
||||
* Write data to the transport.
|
||||
*
|
||||
* write() will always return immediately. The WriteCallback will later be
|
||||
* invoked from the main TEventBase loop when the write has completed.
|
||||
*
|
||||
* Additional write attempts may be started before the first write completes.
|
||||
* The subsequent write requests will be queued, and processed in the order
|
||||
* in which they were called.
|
||||
*
|
||||
* @param callback The callback to invoke when the data has been written.
|
||||
* The callback may not be NULL.
|
||||
* @param buf The buffer containing the data to write. The caller is
|
||||
* responsible for ensuring that this buffer remains valid
|
||||
* until the callback is invoked. This parameter may not
|
||||
* be NULL.
|
||||
* @param bytes The number of bytes to write.
|
||||
*/
|
||||
virtual void write(WriteCallback* callback,
|
||||
const void* buf, size_t bytes) = 0;
|
||||
|
||||
/**
|
||||
* Write non-contiguous data to the transport.
|
||||
*
|
||||
* writev() will always return immediately. The WriteCallback will later be
|
||||
* invoked from the main TEventBase loop when the write has completed.
|
||||
*
|
||||
* Additional write attempts may be started before the first write completes.
|
||||
* The subsequent write requests will be queued, and processed in the order
|
||||
* in which they were called.
|
||||
*
|
||||
* @param callback The callback to invoke when the data has been written.
|
||||
* The callback may not be NULL.
|
||||
* @param vec A pointer to an array of iovec objects. The caller is
|
||||
* responsible for ensuring that the buffers remain valid
|
||||
* until the callback is invoked. This parameter may not
|
||||
* be NULL.
|
||||
* @param count The number of iovec objects in the vec array.
|
||||
*/
|
||||
virtual void writev(WriteCallback* callback,
|
||||
const iovec* vec, size_t count) = 0;
|
||||
|
||||
/**
|
||||
* Write a chain of IOBufs to the transport.
|
||||
*
|
||||
* writeChain() will always return immediately. The WriteCallback will
|
||||
* later be invoked from the main TEventBase loop when the write has
|
||||
* completed.
|
||||
*
|
||||
* Additional write attempts may be started before the first write completes.
|
||||
* The subsequent write requests will be queued, and processed in the order
|
||||
* in which they were called.
|
||||
*
|
||||
* @param callback The callback to invoke when the data has been written.
|
||||
* The callback may not be NULL.
|
||||
* @param iob The head of an IOBuf chain. The TAsyncTransport
|
||||
* will take ownership of this chain and delete it
|
||||
* after writing.
|
||||
* @param cork Whether to delay the write until the next non-corked
|
||||
* write operation. (Note: may not be supported in all
|
||||
* subclasses or on all platforms.)
|
||||
*/
|
||||
virtual void writeChain(WriteCallback* callback,
|
||||
std::unique_ptr<folly::IOBuf>&& iob,
|
||||
bool cork = false) = 0;
|
||||
|
||||
/**
|
||||
* Close the transport.
|
||||
*
|
||||
* This gracefully closes the transport, waiting for all pending write
|
||||
* requests to complete before actually closing the underlying transport.
|
||||
*
|
||||
* If a read callback is set, readEOF() will be called immediately. If there
|
||||
* are outstanding write requests, the close will be delayed until all
|
||||
* remaining writes have completed. No new writes may be started after
|
||||
* close() has been called.
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
/**
|
||||
* Close the transport immediately.
|
||||
*
|
||||
* This closes the transport immediately, dropping any outstanding data
|
||||
* waiting to be written.
|
||||
*
|
||||
* If a read callback is set, readEOF() will be called immediately.
|
||||
* If there are outstanding write requests, these requests will be aborted
|
||||
* and writeError() will be invoked immediately on all outstanding write
|
||||
* callbacks.
|
||||
*/
|
||||
virtual void closeNow() = 0;
|
||||
|
||||
/**
|
||||
* Perform a half-shutdown of the write side of the transport.
|
||||
*
|
||||
* The caller should not make any more calls to write() or writev() after
|
||||
* shutdownWrite() is called. Any future write attempts will fail
|
||||
* immediately.
|
||||
*
|
||||
* Not all transport types support half-shutdown. If the underlying
|
||||
* transport does not support half-shutdown, it will fully shutdown both the
|
||||
* read and write sides of the transport. (Fully shutting down the socket is
|
||||
* better than doing nothing at all, since the caller may rely on the
|
||||
* shutdownWrite() call to notify the other end of the connection that no
|
||||
* more data can be read.)
|
||||
*
|
||||
* If there is pending data still waiting to be written on the transport,
|
||||
* the actual shutdown will be delayed until the pending data has been
|
||||
* written.
|
||||
*
|
||||
* Note: There is no corresponding shutdownRead() equivalent. Simply
|
||||
* uninstall the read callback if you wish to stop reading. (On TCP sockets
|
||||
* at least, shutting down the read side of the socket is a no-op anyway.)
|
||||
*/
|
||||
virtual void shutdownWrite() = 0;
|
||||
|
||||
/**
|
||||
* Perform a half-shutdown of the write side of the transport.
|
||||
*
|
||||
* shutdownWriteNow() is identical to shutdownWrite(), except that it
|
||||
* immediately performs the shutdown, rather than waiting for pending writes
|
||||
* to complete. Any pending write requests will be immediately failed when
|
||||
* shutdownWriteNow() is called.
|
||||
*/
|
||||
virtual void shutdownWriteNow() = 0;
|
||||
|
||||
/**
|
||||
* Determine if transport is open and ready to read or write.
|
||||
*
|
||||
* Note that this function returns false on EOF; you must also call error()
|
||||
* to distinguish between an EOF and an error.
|
||||
*
|
||||
* @return true iff the transport is open and ready, false otherwise.
|
||||
*/
|
||||
virtual bool good() const = 0;
|
||||
|
||||
/**
|
||||
* Determine if the transport is readable or not.
|
||||
*
|
||||
* @return true iff the transport is readable, false otherwise.
|
||||
*/
|
||||
virtual bool readable() const = 0;
|
||||
|
||||
/**
|
||||
* Determine if transport is connected to the endpoint
|
||||
*
|
||||
* @return false iff the transport is connected, otherwise true
|
||||
*/
|
||||
virtual bool connecting() const = 0;
|
||||
|
||||
/**
|
||||
* Determine if an error has occurred with this transport.
|
||||
*
|
||||
* @return true iff an error has occurred (not EOF).
|
||||
*/
|
||||
virtual bool error() const = 0;
|
||||
|
||||
/**
|
||||
* Attach the transport to a TEventBase.
|
||||
*
|
||||
* This may only be called if the transport is not currently attached to a
|
||||
* TEventBase (by an earlier call to detachEventBase()).
|
||||
*
|
||||
* This method must be invoked in the TEventBase's thread.
|
||||
*/
|
||||
virtual void attachEventBase(TEventBase* eventBase) = 0;
|
||||
|
||||
/**
|
||||
* Detach the transport from its TEventBase.
|
||||
*
|
||||
* This may only be called when the transport is idle and has no reads or
|
||||
* writes pending. Once detached, the transport may not be used again until
|
||||
* it is re-attached to a TEventBase by calling attachEventBase().
|
||||
*
|
||||
* This method must be called from the current TEventBase's thread.
|
||||
*/
|
||||
virtual void detachEventBase() = 0;
|
||||
|
||||
/**
|
||||
* Get the TEventBase used by this transport.
|
||||
*
|
||||
* Returns NULL if this transport is not currently attached to a TEventBase.
|
||||
*/
|
||||
virtual TEventBase* getEventBase() const = 0;
|
||||
|
||||
/**
|
||||
* Set the send timeout.
|
||||
*
|
||||
* If write requests do not make any progress for more than the specified
|
||||
* number of milliseconds, fail all pending writes and close the transport.
|
||||
*
|
||||
* If write requests are currently pending when setSendTimeout() is called,
|
||||
* the timeout interval is immediately restarted using the new value.
|
||||
*
|
||||
* @param milliseconds The timeout duration, in milliseconds. If 0, no
|
||||
* timeout will be used.
|
||||
*/
|
||||
virtual void setSendTimeout(uint32_t milliseconds) = 0;
|
||||
|
||||
/**
|
||||
* Get the send timeout.
|
||||
*
|
||||
* @return Returns the current send timeout, in milliseconds. A return value
|
||||
* of 0 indicates that no timeout is set.
|
||||
*/
|
||||
virtual uint32_t getSendTimeout() const = 0;
|
||||
|
||||
/**
|
||||
* Get the address of the local endpoint of this transport.
|
||||
*
|
||||
* This function may throw TTransportException on error.
|
||||
*
|
||||
* @param address The local address will be stored in the specified
|
||||
* TSocketAddress.
|
||||
*/
|
||||
virtual void getLocalAddress(transport::TSocketAddress* address) const = 0;
|
||||
|
||||
/**
|
||||
* Get the address of the remote endpoint to which this transport is
|
||||
* connected.
|
||||
*
|
||||
* This function may throw TTransportException on error.
|
||||
*
|
||||
* @param address The remote endpoint's address will be stored in the
|
||||
* specified TSocketAddress.
|
||||
*/
|
||||
virtual void getPeerAddress(transport::TSocketAddress* address) const = 0;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // #ifndef THRIFT_ASYNC_TASYNCTRANSPORT_H_
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TBINARYASYNCCHANNEL_H_
|
||||
#define THRIFT_ASYNC_TBINARYASYNCCHANNEL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TUnframedAsyncChannel.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* A class to determine the end of a raw TBinaryProtocol message.
|
||||
*/
|
||||
class TBinaryACProtocolTraits {
|
||||
public:
|
||||
TBinaryACProtocolTraits() : strictRead_(true) {}
|
||||
|
||||
// Methods required by TUnframedACReadState
|
||||
bool getMessageLength(uint8_t* buffer,
|
||||
uint32_t bufferLength,
|
||||
uint32_t* messageLength);
|
||||
|
||||
// Methods specific to TBinaryAsyncChannel
|
||||
|
||||
void setStrictRead(bool strictRead) {
|
||||
strictRead_ = strictRead;
|
||||
}
|
||||
bool getStrictRead() const {
|
||||
return strictRead_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool strictRead_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* TBinaryAsyncChannel
|
||||
*
|
||||
* This is a TAsyncChannel implementation that reads and writes raw (unframed)
|
||||
* messages encoded using TBinaryProtocol.
|
||||
*/
|
||||
class TBinaryAsyncChannel :
|
||||
public TUnframedAsyncChannel<detail::TBinaryACProtocolTraits> {
|
||||
private:
|
||||
typedef TUnframedAsyncChannel<detail::TBinaryACProtocolTraits> Parent;
|
||||
|
||||
public:
|
||||
TBinaryAsyncChannel(const boost::shared_ptr<TAsyncTransport>& transport)
|
||||
: Parent(transport) {}
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TBinaryAsyncChannel>.
|
||||
*
|
||||
* This passes in the correct destructor object, since TBinaryAsyncChannel's
|
||||
* destructor is protected and cannot be invoked directly.
|
||||
*/
|
||||
static boost::shared_ptr<TBinaryAsyncChannel> newChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport) {
|
||||
return boost::shared_ptr<TBinaryAsyncChannel>(
|
||||
new TBinaryAsyncChannel(transport), Destructor());
|
||||
}
|
||||
|
||||
// Note that we inherit setMaxMessageSize() and getMaxMessageSize()
|
||||
// from TUnframedAsyncChannel.
|
||||
|
||||
void setStrictRead(bool strictRead) {
|
||||
readState_.getProtocolTraits()->setStrictRead(strictRead);
|
||||
}
|
||||
bool getStrictRead() const {
|
||||
return readState_.getProtocolTraits()->getStrictRead();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Users of TBinaryAsyncChannel must never delete it directly. Instead,
|
||||
* invoke destroy().
|
||||
*/
|
||||
virtual ~TBinaryAsyncChannel() { }
|
||||
};
|
||||
|
||||
class TBinaryAsyncChannelFactory : public TStreamAsyncChannelFactory {
|
||||
public:
|
||||
TBinaryAsyncChannelFactory()
|
||||
: maxMessageSize_(0x7fffffff)
|
||||
, recvTimeout_(0)
|
||||
, sendTimeout_(0)
|
||||
, strictRead_(true) {}
|
||||
|
||||
void setMaxMessageSize(uint32_t bytes) {
|
||||
maxMessageSize_ = bytes;
|
||||
}
|
||||
|
||||
void setRecvTimeout(uint32_t milliseconds) {
|
||||
recvTimeout_ = milliseconds;
|
||||
}
|
||||
|
||||
void setSendTimeout(uint32_t milliseconds) {
|
||||
sendTimeout_ = milliseconds;
|
||||
}
|
||||
|
||||
void setStrictRead(bool strict) {
|
||||
strictRead_ = strict;
|
||||
}
|
||||
|
||||
virtual boost::shared_ptr<TAsyncEventChannel> newChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport) {
|
||||
boost::shared_ptr<TBinaryAsyncChannel> channel(
|
||||
TBinaryAsyncChannel::newChannel(transport));
|
||||
transport->setSendTimeout(sendTimeout_);
|
||||
channel->setMaxMessageSize(maxMessageSize_);
|
||||
channel->setRecvTimeout(recvTimeout_);
|
||||
channel->setStrictRead(strictRead_);
|
||||
return channel;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t maxMessageSize_;
|
||||
uint32_t recvTimeout_;
|
||||
uint32_t sendTimeout_;
|
||||
bool strictRead_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TBINARYASYNCCHANNEL_H_
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TDELAYEDDESTRUCTION_H_
|
||||
#define THRIFT_ASYNC_TDELAYEDDESTRUCTION_H_ 1
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
/**
|
||||
* TDelayedDestruction is a helper class to ensure objects are not deleted
|
||||
* while they still have functions executing in a higher stack frame.
|
||||
*
|
||||
* This is useful for objects that invoke callback functions, to ensure that a
|
||||
* callback does not destroy the calling object.
|
||||
*
|
||||
* Classes needing this functionality should:
|
||||
* - derive from TDelayedDestruction
|
||||
* - make their destructor private or protected, so it cannot be called
|
||||
* directly
|
||||
* - create a DestructorGuard object on the stack in each public method that
|
||||
* may invoke a callback
|
||||
*
|
||||
* TDelayedDestruction does not perform any locking. It is intended to be used
|
||||
* only from a single thread.
|
||||
*/
|
||||
class TDelayedDestruction : private boost::noncopyable {
|
||||
public:
|
||||
/**
|
||||
* Helper class to allow TDelayedDestruction classes to be used with
|
||||
* boost::shared_ptr.
|
||||
*
|
||||
* This class can be specified as the destructor argument when creating the
|
||||
* shared_ptr, and it will destroy the guarded class properly when all
|
||||
* shared_ptr references are released.
|
||||
*/
|
||||
class Destructor {
|
||||
public:
|
||||
void operator()(TDelayedDestruction* dd) const {
|
||||
dd->destroy();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* destroy() requests destruction of the object.
|
||||
*
|
||||
* This method will destroy the object after it has no more functions running
|
||||
* higher up on the stack. (i.e., No more DestructorGuard objects exist for
|
||||
* this object.) This method must be used instead of the destructor.
|
||||
*/
|
||||
virtual void destroy() {
|
||||
// If guardCount_ is not 0, just set destroyPending_ to delay
|
||||
// actual destruction.
|
||||
if (guardCount_ != 0) {
|
||||
destroyPending_ = true;
|
||||
} else {
|
||||
destroyNow(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes should create a DestructorGuard object on the stack in any
|
||||
* function that may invoke callback functions.
|
||||
*
|
||||
* The DestructorGuard prevents the guarded class from being destroyed while
|
||||
* it exists. Without this, the callback function could delete the guarded
|
||||
* object, causing problems when the callback function returns and the
|
||||
* guarded object's method resumes execution.
|
||||
*/
|
||||
class DestructorGuard {
|
||||
public:
|
||||
explicit DestructorGuard(TDelayedDestruction* dd) : dd_(dd) {
|
||||
++dd_->guardCount_;
|
||||
assert(dd_->guardCount_ > 0); // check for wrapping
|
||||
}
|
||||
|
||||
~DestructorGuard() {
|
||||
assert(dd_->guardCount_ > 0);
|
||||
--dd_->guardCount_;
|
||||
if (dd_->guardCount_ == 0 && dd_->destroyPending_) {
|
||||
dd_->destroyNow(true);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TDelayedDestruction* dd_;
|
||||
};
|
||||
|
||||
protected:
|
||||
/**
|
||||
* destroyNow() is invoked to actually destroy the object, after destroy()
|
||||
* has been called and no more DestructorGuard objects exist. By default it
|
||||
* calls "delete this", but subclasses may override this behavior.
|
||||
*
|
||||
* @param delayed This parameter is true if destruction was delayed because
|
||||
* of a DestructorGuard object, or false if destroyNow() is
|
||||
* being called directly from destroy().
|
||||
*/
|
||||
virtual void destroyNow(bool delayed) {
|
||||
delete this;
|
||||
(void)delayed; // prevent unused variable warnings
|
||||
}
|
||||
|
||||
TDelayedDestruction()
|
||||
: guardCount_(0)
|
||||
, destroyPending_(false) {}
|
||||
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Making this protected ensures that users cannot delete TDelayedDestruction
|
||||
* objects directly, and that everyone must use destroy() instead.
|
||||
* Subclasses of TDelayedDestruction must also define their destructors as
|
||||
* protected or private in order for this to work.
|
||||
*
|
||||
* This also means that TDelayedDestruction objects cannot be created
|
||||
* directly on the stack; they must always be dynamically allocated on the
|
||||
* heap.
|
||||
*
|
||||
* In order to use a TDelayedDestruction object with a shared_ptr, create the
|
||||
* shared_ptr using a TDelayedDestruction::Destructor as the second argument
|
||||
* to the shared_ptr constructor.
|
||||
*/
|
||||
virtual ~TDelayedDestruction() {}
|
||||
|
||||
/**
|
||||
* Get the number of DestructorGuards currently protecting this object.
|
||||
*
|
||||
* This is primarily intended for debugging purposes, such as asserting
|
||||
* that an object has at least 1 guard.
|
||||
*/
|
||||
uint32_t getDestructorGuardCount() const {
|
||||
return guardCount_;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* guardCount_ is incremented by DestructorGuard, to indicate that one of
|
||||
* the TDelayedDestruction object's methods is currently running.
|
||||
*
|
||||
* If destroy() is called while guardCount_ is non-zero, destruction will
|
||||
* be delayed until guardCount_ drops to 0. This allows TDelayedDestruction
|
||||
* objects to invoke callbacks without having to worry about being deleted
|
||||
* before the callback returns.
|
||||
*/
|
||||
uint32_t guardCount_;
|
||||
|
||||
/**
|
||||
* destroyPending_ is set to true if destoy() is called while guardCount_ is
|
||||
* non-zero.
|
||||
*
|
||||
* If destroyPending_ is true, the object will be destroyed the next time
|
||||
* guardCount_ drops to 0.
|
||||
*/
|
||||
bool destroyPending_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TDELAYEDDESTRUCTION_H_
|
|
@ -0,0 +1,397 @@
|
|||
// Copyright (c) 2006- Facebook
|
||||
// Distributed under the Thrift Software License
|
||||
//
|
||||
// See accompanying file LICENSE or visit the Thrift site at:
|
||||
// http://developers.facebook.com/thrift/
|
||||
|
||||
#ifndef THRIFT_ASYNC_TEVENTBASE_H_
|
||||
#define THRIFT_ASYNC_TEVENTBASE_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/Thrift.h"
|
||||
#include "thrift/lib/cpp/async/TAsyncTimeout.h"
|
||||
#include "thrift/lib/cpp/server/TServer.h"
|
||||
#include "thrift/lib/cpp/transport/TTransportUtils.h"
|
||||
#include "thrift/lib/cpp/concurrency/ThreadManager.h"
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <cstdlib>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/utility.hpp>
|
||||
#include <tr1/functional>
|
||||
#include <event.h> // libevent
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
typedef std::tr1::function<void()> Cob;
|
||||
template <typename MessageT>
|
||||
class TNotificationQueue;
|
||||
|
||||
/**
|
||||
* This class is a wrapper for all asynchronous I/O processing functionality
|
||||
* used in thrift.
|
||||
*
|
||||
* TEventBase provides a main loop that notifies TEventHandler callback objects
|
||||
* when I/O is ready on a file descriptor, and notifies TAsyncTimeout objects
|
||||
* when a specified timeout has expired. More complex, higher-level callback
|
||||
* mechanisms can then be built on top of TEventHandler and TAsyncTimeout.
|
||||
*
|
||||
* A TEventBase object can only drive an event loop for a single thread. To
|
||||
* take advantage of multiple CPU cores, most asynchronous I/O servers have one
|
||||
* thread per CPU, and use a separate TEventBase for each thread.
|
||||
*
|
||||
* In general, most TEventBase methods may only be called from the thread
|
||||
* running the TEventBase's loop. There are a few exceptions to this rule, for
|
||||
* methods that are explicitly intended to allow communication with a
|
||||
* TEventBase from other threads. When it is safe to call a method from
|
||||
* another thread it is explicitly listed in the method comments.
|
||||
*/
|
||||
class TEventBase : private boost::noncopyable {
|
||||
public:
|
||||
/**
|
||||
* A callback interface to use with runInLoop()
|
||||
*
|
||||
* Derive from this class if you need to delay some code execution until the
|
||||
* next iteration of the event loop. This allows you to schedule code to be
|
||||
* invoked from the top-level of the loop, after your immediate callers have
|
||||
* returned.
|
||||
*
|
||||
* If a LoopCallback object is destroyed while it is scheduled to be run in
|
||||
* the next loop iteration, it will automatically be cancelled.
|
||||
*/
|
||||
class LoopCallback {
|
||||
public:
|
||||
virtual ~LoopCallback() {}
|
||||
|
||||
virtual void runLoopCallback() THRIFT_NOEXCEPT = 0;
|
||||
void cancelLoopCallback() {
|
||||
hook_.unlink();
|
||||
}
|
||||
|
||||
bool isLoopCallbackScheduled() const {
|
||||
return hook_.is_linked();
|
||||
}
|
||||
|
||||
private:
|
||||
typedef boost::intrusive::list_member_hook<
|
||||
boost::intrusive::link_mode<boost::intrusive::auto_unlink> > ListHook;
|
||||
|
||||
ListHook hook_;
|
||||
|
||||
typedef boost::intrusive::list<
|
||||
LoopCallback,
|
||||
boost::intrusive::member_hook<LoopCallback, ListHook,
|
||||
&LoopCallback::hook_>,
|
||||
boost::intrusive::constant_time_size<false> > List;
|
||||
|
||||
// TEventBase needs access to LoopCallbackList (and therefore to hook_)
|
||||
friend class TEventBase;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new TEventBase object.
|
||||
*/
|
||||
TEventBase();
|
||||
|
||||
/**
|
||||
* Create a new TEventBase object that will use the specified libevent
|
||||
* event_base object to drive the event loop.
|
||||
*
|
||||
* The TEventBase will take ownership of this event_base, and will call
|
||||
* event_base_free(evb) when the TEventBase is destroyed.
|
||||
*/
|
||||
explicit TEventBase(event_base* evb);
|
||||
~TEventBase();
|
||||
|
||||
/**
|
||||
* Runs the event loop.
|
||||
*
|
||||
* loop() will loop waiting for I/O or timeouts and invoking TEventHandler
|
||||
* and TAsyncTimeout callbacks as their events become ready. loop() will
|
||||
* only return when there are no more events remaining to process, or after
|
||||
* terminateLoopSoon() has been called.
|
||||
*
|
||||
* loop() may be called again to restart event processing after a previous
|
||||
* call to loop() or loopForever() has returned.
|
||||
*
|
||||
* Returns true if the loop completed normally (if it processed all
|
||||
* outstanding requests, or if terminateLoopSoon() was called). If an error
|
||||
* occurs waiting for events, false will be returned.
|
||||
*/
|
||||
bool loop();
|
||||
|
||||
/**
|
||||
* Runs the event loop.
|
||||
*
|
||||
* loopForever() behaves like loop(), except that it keeps running even if
|
||||
* when there are no more user-supplied TEventHandlers or TAsyncTimeouts
|
||||
* registered. It will only return after terminateLoopSoon() has been
|
||||
* called.
|
||||
*
|
||||
* This is useful for callers that want to wait for other threads to call
|
||||
* runInEventBaseThread(), even when there are no other scheduled events.
|
||||
*
|
||||
* loopForever() may be called again to restart event processing after a
|
||||
* previous call to loop() or loopForever() has returned.
|
||||
*
|
||||
* Throws a TLibraryException if an error occurs.
|
||||
*/
|
||||
void loopForever();
|
||||
|
||||
/**
|
||||
* Causes the event loop to exit soon.
|
||||
*
|
||||
* This will cause an existing call to loop() or loopForever() to stop event
|
||||
* processing and return, even if there are still events remaining to be
|
||||
* processed.
|
||||
*
|
||||
* It is safe to call terminateLoopSoon() from another thread to cause loop()
|
||||
* to wake up and return in the TEventBase loop thread. terminateLoopSoon()
|
||||
* may also be called from the loop thread itself (for example, a
|
||||
* TEventHandler or TAsyncTimeout callback may call terminateLoopSoon() to
|
||||
* cause the loop to exit after the callback returns.)
|
||||
*
|
||||
* Note that the caller is responsible for ensuring that cleanup of all event
|
||||
* callbacks occurs properly. Since terminateLoopSoon() causes the loop to
|
||||
* exit even when there are pending events present, there may be remaining
|
||||
* callbacks present waiting to be invoked. If the loop is later restarted
|
||||
* pending events will continue to be processed normally, however if the
|
||||
* TEventBase is destroyed after calling terminateLoopSoon() it is the
|
||||
* caller's responsibility to ensure that cleanup happens properly even if
|
||||
* some outstanding events are never processed.
|
||||
*/
|
||||
void terminateLoopSoon();
|
||||
|
||||
/**
|
||||
* Adds the given callback to a queue of things run after the current pass
|
||||
* through the event loop completes. Note that if this callback calls
|
||||
* runInLoop() the new callback won't be called until the main event loop
|
||||
* has gone through a cycle.
|
||||
*
|
||||
* This method may only be called from the TEventBase's thread. This
|
||||
* essentially allows an event handler to schedule an additional callback to
|
||||
* be invoked after it returns.
|
||||
*
|
||||
* Use runInEventBaseThread() to schedule functions from another thread.
|
||||
*/
|
||||
void runInLoop(LoopCallback* callback);
|
||||
|
||||
/**
|
||||
* Convenience function to call runInLoop() with a tr1::function.
|
||||
*
|
||||
* This creates a LoopCallback object to wrap the tr1::function, and invoke
|
||||
* the tr1::function when the loop callback fires. This is slightly more
|
||||
* expensive than defining your own LoopCallback, but more convenient in
|
||||
* areas that aren't performance sensitive where you just want to use
|
||||
* tr1::bind. (tr1::bind is fairly slow on even by itself.)
|
||||
*
|
||||
* This method may only be called from the TEventBase's thread. This
|
||||
* essentially allows an event handler to schedule an additional callback to
|
||||
* be invoked after it returns.
|
||||
*
|
||||
* Use runInEventBaseThread() to schedule functions from another thread.
|
||||
*/
|
||||
void runInLoop(const Cob& c);
|
||||
|
||||
/**
|
||||
* Run the specified function in the TEventBase's thread.
|
||||
*
|
||||
* This method is thread-safe, and may be called from another thread.
|
||||
*
|
||||
* If runInEventBaseThread() is called when the TEventBase loop is not
|
||||
* running, the function call will be delayed until the next time the loop is
|
||||
* started.
|
||||
*
|
||||
* If runInEventBaseThread() returns true the function has successfully been
|
||||
* scheduled to run in the loop thread. However, if the loop is terminated
|
||||
* (and never later restarted) before it has a chance to run the requested
|
||||
* function, the function may never be run at all. The caller is responsible
|
||||
* for handling this situation correctly if they may terminate the loop with
|
||||
* outstanding runInEventBaseThread() calls pending.
|
||||
*
|
||||
* If two calls to runInEventBaseThread() are made from the same thread, the
|
||||
* functions will always be run in the order that they were scheduled.
|
||||
* Ordering between functions scheduled from separate threads is not
|
||||
* guaranteed.
|
||||
*
|
||||
* @param fn The function to run. The function must not throw any
|
||||
* exceptions.
|
||||
* @param arg An argument to pass to the function.
|
||||
*
|
||||
* @return Returns true if the function was successfully scheduled, or false
|
||||
* if there was an error scheduling the function.
|
||||
*/
|
||||
template<typename T>
|
||||
bool runInEventBaseThread(void (*fn)(T*), T* arg) {
|
||||
return runInEventBaseThread(reinterpret_cast<void (*)(void*)>(fn),
|
||||
reinterpret_cast<void*>(arg));
|
||||
}
|
||||
|
||||
bool runInEventBaseThread(void (*fn)(void*), void* arg);
|
||||
|
||||
/**
|
||||
* Run the specified function in the TEventBase's thread
|
||||
*
|
||||
* This version of runInEventBaseThread() takes a tr1::function object.
|
||||
* Note that this is less efficient than the version that takes a plain
|
||||
* function pointer and void* argument, as it has to allocate memory to copy
|
||||
* the tr1::function object.
|
||||
*
|
||||
* If the TEventBase loop is terminated before it has a chance to run this
|
||||
* function, the allocated memory will be leaked. The caller is responsible
|
||||
* for ensuring that the TEventBase loop is not terminated before this
|
||||
* function can run.
|
||||
*
|
||||
* The function must not throw any exceptions.
|
||||
*/
|
||||
bool runInEventBaseThread(const std::tr1::function<void()>& fn);
|
||||
|
||||
/**
|
||||
* Runs the given Cob at some time after the specified number of
|
||||
* milliseconds. (No guarantees exactly when.)
|
||||
*
|
||||
* @return true iff the cob was successfully registered.
|
||||
*/
|
||||
bool runAfterDelay(const Cob& c, int milliseconds);
|
||||
|
||||
/**
|
||||
* Set the maximum desired latency in us and provide a callback which will be
|
||||
* called when that latency is exceeded.
|
||||
*/
|
||||
void setMaxLatency(int64_t maxLatency, const Cob& maxLatencyCob) {
|
||||
maxLatency_ = maxLatency;
|
||||
maxLatencyCob_ = maxLatencyCob;
|
||||
}
|
||||
/**
|
||||
* Set smoothing coefficient for loop load average; # of milliseconds
|
||||
* for exp(-1) (1/2.71828...) decay.
|
||||
*/
|
||||
void setLoadAvgMsec(uint32_t ms);
|
||||
|
||||
/**
|
||||
* Get the average loop time in microseconds (an exponentially-smoothed ave)
|
||||
*/
|
||||
double getAvgLoopTime() const {
|
||||
return avgLoopTime_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that current thread is the TEventBase thread, if the TEventBase is
|
||||
* running.
|
||||
*
|
||||
* This is primarily intended for debugging, to assert that functions that
|
||||
* register or unregister events are only ever invoked in the TEventBase's
|
||||
* thread.
|
||||
*/
|
||||
bool isInEventBaseThread() const {
|
||||
return !running_ || pthread_equal(loopThread_, pthread_self());
|
||||
}
|
||||
|
||||
// --------- interface to underlying libevent base ------------
|
||||
// Avoid using these functions if possible. These functions are not
|
||||
// guaranteed to always be present if we ever provide alternative TEventBase
|
||||
// implementations that do not use libevent internally.
|
||||
event_base* getLibeventBase() const { return evb_; }
|
||||
static const char* getLibeventVersion() { return event_get_version(); }
|
||||
static const char* getLibeventMethod() { return event_get_method(); }
|
||||
|
||||
private:
|
||||
// --------- libevent callbacks (not for client use) ------------
|
||||
|
||||
/**
|
||||
* Called after a delay to break out of an idle event loop. We need to
|
||||
* use this instead of event_base_loopexit() since the latter installs
|
||||
* an event within libevent which is queued until it expires. Installing
|
||||
* our own timed event lets us delete it when another event causes the
|
||||
* loop to exit earlier..
|
||||
*/
|
||||
static void loopTimerCallback(int fd, short which, void* arg);
|
||||
|
||||
static void runTr1FunctionPtr(std::tr1::function<void()>* fn);
|
||||
|
||||
// small object used as a callback arg with enough info to execute the
|
||||
// appropriate client-provided Cob
|
||||
class CobTimeout : public TAsyncTimeout {
|
||||
public:
|
||||
CobTimeout(TEventBase* b, const Cob& c) : TAsyncTimeout(b), cob_(c) {}
|
||||
|
||||
virtual void timeoutExpired() THRIFT_NOEXCEPT;
|
||||
|
||||
private:
|
||||
Cob cob_;
|
||||
|
||||
public:
|
||||
typedef boost::intrusive::list_member_hook<
|
||||
boost::intrusive::link_mode<boost::intrusive::auto_unlink> > ListHook;
|
||||
|
||||
ListHook hook;
|
||||
|
||||
typedef boost::intrusive::list<
|
||||
CobTimeout,
|
||||
boost::intrusive::member_hook<CobTimeout, ListHook, &CobTimeout::hook>,
|
||||
boost::intrusive::constant_time_size<false> > List;
|
||||
};
|
||||
|
||||
typedef LoopCallback::List LoopCallbackList;
|
||||
class FunctionRunner;
|
||||
|
||||
// executes any callbacks queued by runInLoop()
|
||||
void runLoopCallbacks();
|
||||
|
||||
void initNotificationQueue();
|
||||
|
||||
CobTimeout::List pendingCobTimeouts_;
|
||||
|
||||
LoopCallbackList loopCallbacks_;
|
||||
|
||||
// stop_ is set by terminateLoopSoon() and is used by the main loop
|
||||
// to determine if it should exit
|
||||
bool stop_;
|
||||
// running_ is set to true while loop() is running
|
||||
bool running_;
|
||||
// The ID of the thread running the main loop.
|
||||
// Only valid while running_ is true.
|
||||
pthread_t loopThread_;
|
||||
|
||||
// pointer to underlying event_base class doing the heavy lifting
|
||||
event_base* evb_;
|
||||
|
||||
// A notification queue for runInEventBaseThread() to use
|
||||
// to send function requests to the TEventBase thread.
|
||||
std::unique_ptr<TNotificationQueue<std::pair<void (*)(void*), void*>>> queue_;
|
||||
std::unique_ptr<FunctionRunner> fnRunner_;
|
||||
|
||||
// limit for latency in microseconds (0 disables)
|
||||
int64_t maxLatency_;
|
||||
|
||||
// smoothed loop time used to invoke latency callbacks; differs from
|
||||
// avgLoopTime_ in that it's scaled down after triggering a callback
|
||||
// to reduce spamminess
|
||||
double maxLatencyLoopTime_;
|
||||
|
||||
// exponentially-smoothed average loop time for latency-limiting
|
||||
double avgLoopTime_;
|
||||
|
||||
// set to true if the event_base_loop(EVLOOP_ONCE) returned because
|
||||
// the loop timeout fired, rather than because it found events to process
|
||||
bool loopTimedOut_;
|
||||
|
||||
// factor used for exponential smoothing of load average
|
||||
double expCoeff_;
|
||||
|
||||
// callback called when latency limit is exceeded
|
||||
Cob maxLatencyCob_;
|
||||
|
||||
// we'll wait this long before running deferred callbacks if the event
|
||||
// loop is idle.
|
||||
static const int kDEFAULT_IDLE_WAIT_USEC = 20000; // 20ms
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // #ifndef THRIFT_ASYNC_TEVENTBASE_H_
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TEVENTBASEMANAGER_H
|
||||
#define THRIFT_ASYNC_TEVENTBASEMANAGER_H 1
|
||||
|
||||
#include "thrift/lib/cpp/concurrency/ThreadLocal.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
class TEventBase;
|
||||
|
||||
/**
|
||||
* Manager for per-thread TEventBase objects.
|
||||
* This class will find or create a TEventBase for the current
|
||||
* thread, associated with thread-specific storage for that thread.
|
||||
* Although a typical application will generally only have one
|
||||
* TEventBaseManager, there is no restriction on multiple instances;
|
||||
* the TEventBases belong to one instance are isolated from those of
|
||||
* another.
|
||||
*/
|
||||
class TEventBaseManager {
|
||||
public:
|
||||
TEventBaseManager() {}
|
||||
~TEventBaseManager() {}
|
||||
|
||||
/**
|
||||
* Get the TEventBase for this thread, or create one if none exists yet.
|
||||
*
|
||||
* If no TEventBase exists for this thread yet, a new one will be created and
|
||||
* returned. May throw std::bad_alloc if allocation fails.
|
||||
*/
|
||||
TEventBase* getEventBase() const {
|
||||
// localStore_.get() will never return NULL.
|
||||
// InfoManager::allocate() will throw an exception instead if it cannot
|
||||
// allocate a new EventBaseInfo or TEventBase.
|
||||
return localStore_.get()->eventBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the TEventBase for this thread.
|
||||
*
|
||||
* Returns NULL if no TEventBase has been created for this thread yet.
|
||||
*/
|
||||
TEventBase* getExistingEventBase() const {
|
||||
EventBaseInfo* info = localStore_.getNoAlloc();
|
||||
if (info == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return info->eventBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the TEventBase to be used by this thread.
|
||||
*
|
||||
* This may only be called if no TEventBase has been defined for this thread
|
||||
* yet. If a TEventBase is already defined for this thread, a
|
||||
* TLibraryException is thrown. std::bad_alloc may also be thrown if
|
||||
* allocation fails while setting the TEventBase.
|
||||
*
|
||||
* This should typically be invoked by the code that will call loop() on the
|
||||
* TEventBase, to make sure the TEventBaseManager points to the correct
|
||||
* TEventBase that is actually running in this thread.
|
||||
*/
|
||||
void setEventBase(TEventBase *eventBase, bool takeOwnership);
|
||||
|
||||
/**
|
||||
* Clear the TEventBase for this thread.
|
||||
*
|
||||
* This can be used if the code driving the TEventBase loop() has finished
|
||||
* the loop and new events should no longer be added to the TEventBase.
|
||||
*/
|
||||
void clearEventBase();
|
||||
|
||||
private:
|
||||
struct EventBaseInfo {
|
||||
EventBaseInfo(TEventBase *evb, bool owned)
|
||||
: eventBase(evb),
|
||||
owned(owned) {}
|
||||
|
||||
TEventBase *eventBase;
|
||||
bool owned;
|
||||
};
|
||||
|
||||
class InfoManager {
|
||||
public:
|
||||
EventBaseInfo* allocate();
|
||||
void destroy(EventBaseInfo* info);
|
||||
|
||||
void replace(EventBaseInfo* oldInfo, EventBaseInfo* newInfo) {
|
||||
if (oldInfo != newInfo) {
|
||||
destroy(oldInfo);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Forbidden copy constructor and assignment opererator
|
||||
TEventBaseManager(TEventBaseManager const &);
|
||||
TEventBaseManager& operator=(TEventBaseManager const &);
|
||||
|
||||
concurrency::ThreadLocal<EventBaseInfo, InfoManager> localStore_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TEVENTBASEMANAGER_H
|
|
@ -0,0 +1,239 @@
|
|||
// Copyright (c) 2006- Facebook
|
||||
// Distributed under the Thrift Software License
|
||||
//
|
||||
// See accompanying file LICENSE or visit the Thrift site at:
|
||||
// http://developers.facebook.com/thrift/
|
||||
|
||||
#ifndef THRIFT_ASYNC_TEVENTCONNECTION_H_
|
||||
#define THRIFT_ASYNC_TEVENTCONNECTION_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/server/TConnectionContext.h"
|
||||
#include "thrift/lib/cpp/transport/TSocketAddress.h"
|
||||
#include "thrift/lib/cpp/async/TEventServer.h"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
namespace apache { namespace thrift {
|
||||
|
||||
class TProcessor;
|
||||
|
||||
namespace protocol {
|
||||
class TProtocol;
|
||||
}
|
||||
|
||||
namespace server {
|
||||
class TServerEventHandler;
|
||||
}
|
||||
|
||||
namespace transport {
|
||||
class TMemoryBuffer;
|
||||
}
|
||||
|
||||
namespace async {
|
||||
|
||||
class TAsyncEventChannel;
|
||||
class TAsyncProcessor;
|
||||
class TEventWorker;
|
||||
class TAsyncSocket;
|
||||
class TaskCompletionMessage;
|
||||
|
||||
/**
|
||||
* Represents a connection that is handled via libevent. This connection
|
||||
* essentially encapsulates a socket that has some associated libevent state.
|
||||
*/
|
||||
class TEventConnection : private boost::noncopyable,
|
||||
public TEventBase::LoopCallback {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for TEventConnection.
|
||||
*
|
||||
* @param asyncSocket shared pointer to the async socket
|
||||
* @param address the peer address of this connection
|
||||
* @param worker the worker instance that is handling this connection
|
||||
*/
|
||||
TEventConnection(boost::shared_ptr<TAsyncSocket> asyncSocket,
|
||||
const transport::TSocketAddress* address,
|
||||
TEventWorker* worker, TEventServer::TransportType transport);
|
||||
|
||||
/**
|
||||
* (Re-)Initialize a TEventConnection. We break this out from the
|
||||
* constructor to allow for pooling.
|
||||
*
|
||||
* @param asyncSocket shared pointer to the async socket
|
||||
* @param address the peer address of this connection
|
||||
* @param worker the worker instance that is handling this connection
|
||||
*/
|
||||
void init(boost::shared_ptr<TAsyncSocket> asyncSocket,
|
||||
const transport::TSocketAddress* address,
|
||||
TEventWorker* worker, TEventServer::TransportType transport);
|
||||
|
||||
/// First cause -- starts i/o on connection
|
||||
void start();
|
||||
|
||||
/// Shut down the connection even if it's OK; used for load reduction.
|
||||
void stop() {
|
||||
shutdown_ = true;
|
||||
}
|
||||
|
||||
/// Return a pointer to the worker that owns us
|
||||
TEventWorker* getWorker() const {
|
||||
return worker_;
|
||||
}
|
||||
|
||||
/// cause the notification callback to occur within the appropriate context
|
||||
bool notifyCompletion(TaskCompletionMessage &&msg);
|
||||
|
||||
/// Run scheduled read when there are too many reads on the stack
|
||||
void runLoopCallback() THRIFT_NOEXCEPT;
|
||||
|
||||
boost::shared_ptr<apache::thrift::TProcessor> getProcessor() const {
|
||||
return processor_;
|
||||
}
|
||||
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol>
|
||||
getInputProtocol() const {
|
||||
return inputProtocol_;
|
||||
}
|
||||
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol>
|
||||
getOutputProtocol() const {
|
||||
return outputProtocol_;
|
||||
}
|
||||
|
||||
/// Get the per-server event handler set for this server, if any
|
||||
boost::shared_ptr<apache::thrift::server::TServerEventHandler>
|
||||
getServerEventHandler() const {
|
||||
return serverEventHandler_;
|
||||
}
|
||||
|
||||
/// Get the TConnectionContext for this connection
|
||||
server::TConnectionContext* getConnectionContext() {
|
||||
return &context_;
|
||||
}
|
||||
|
||||
/// Destructor -- close down the connection.
|
||||
~TEventConnection();
|
||||
|
||||
/**
|
||||
* Check the size of our memory buffers and resize if needed. Do not call
|
||||
* when a call is in progress.
|
||||
*/
|
||||
void checkBufferMemoryUsage();
|
||||
|
||||
private:
|
||||
class ConnContext : public server::TConnectionContext {
|
||||
public:
|
||||
void init(const transport::TSocketAddress* address,
|
||||
boost::shared_ptr<protocol::TProtocol> inputProtocol,
|
||||
boost::shared_ptr<protocol::TProtocol> outputProtocol) {
|
||||
address_ = *address;
|
||||
inputProtocol_ = inputProtocol;
|
||||
outputProtocol_ = outputProtocol;
|
||||
}
|
||||
|
||||
virtual const transport::TSocketAddress* getPeerAddress() const {
|
||||
return &address_;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
address_.reset();
|
||||
cleanupUserData();
|
||||
}
|
||||
|
||||
// TODO(dsanduleac): implement the virtual getInputProtocol() & such
|
||||
|
||||
virtual boost::shared_ptr<protocol::TProtocol> getInputProtocol() const {
|
||||
// from TEventConnection
|
||||
return inputProtocol_;
|
||||
}
|
||||
|
||||
virtual boost::shared_ptr<protocol::TProtocol> getOutputProtocol() const {
|
||||
return outputProtocol_;
|
||||
}
|
||||
|
||||
private:
|
||||
transport::TSocketAddress address_;
|
||||
boost::shared_ptr<protocol::TProtocol> inputProtocol_;
|
||||
boost::shared_ptr<protocol::TProtocol> outputProtocol_;
|
||||
};
|
||||
|
||||
void readNextRequest();
|
||||
void handleReadSuccess();
|
||||
void handleReadFailure();
|
||||
void handleAsyncTaskComplete(bool success);
|
||||
void handleSendSuccess();
|
||||
void handleSendFailure();
|
||||
|
||||
void handleEOF();
|
||||
void handleFailure(const char* msg);
|
||||
void cleanup();
|
||||
|
||||
//! The worker instance handling this connection.
|
||||
TEventWorker* worker_;
|
||||
|
||||
//! This connection's socket.
|
||||
boost::shared_ptr<TAsyncSocket> asyncSocket_;
|
||||
|
||||
//! Transport that the processor reads from.
|
||||
boost::shared_ptr<apache::thrift::transport::TMemoryBuffer> inputTransport_;
|
||||
|
||||
//! Transport that the processor writes to.
|
||||
boost::shared_ptr<apache::thrift::transport::TMemoryBuffer> outputTransport_;
|
||||
|
||||
/// Largest size of read buffer seen since buffer was constructed
|
||||
size_t largestReadBufferSize_;
|
||||
|
||||
/// Largest size of write buffer seen since buffer was constructed
|
||||
size_t largestWriteBufferSize_;
|
||||
|
||||
/// Count of the number of calls for use with getResizeBufferEveryN().
|
||||
int32_t callsForResize_;
|
||||
|
||||
//! Protocol decoder.
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol> inputProtocol_;
|
||||
|
||||
//! Protocol encoder.
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol> outputProtocol_;
|
||||
|
||||
//! Channel that actually performs the socket I/O and callbacks.
|
||||
TAsyncEventChannel* asyncChannel_;
|
||||
|
||||
/// Count of outstanding processor callbacks (generally 0 or 1).
|
||||
int32_t processorActive_;
|
||||
|
||||
//! Count of the number of handleReadSuccess frames on the stack
|
||||
int32_t readersActive_;
|
||||
|
||||
/// Sync processor if we're in queuing mode
|
||||
boost::shared_ptr<apache::thrift::TProcessor> processor_;
|
||||
|
||||
/// Flag used to shut down connection (used for load-shedding mechanism).
|
||||
bool shutdown_;
|
||||
|
||||
/// Flag indicating that we have deferred closing down (processor was active)
|
||||
bool closing_;
|
||||
|
||||
/// The per-server event handler set for this erver, if any
|
||||
boost::shared_ptr<apache::thrift::server::TServerEventHandler>
|
||||
serverEventHandler_;
|
||||
|
||||
/// per-connection context
|
||||
ConnContext context_;
|
||||
|
||||
/// Our processor
|
||||
boost::shared_ptr<TAsyncProcessor> asyncProcessor_;
|
||||
|
||||
/// So that TEventWorker can call handleAsyncTaskComplete();
|
||||
friend class TEventWorker;
|
||||
|
||||
/// Make the server a friend so it can manage tasks when overloaded
|
||||
friend class TEventServer;
|
||||
|
||||
/// Make an async task a friend so it can communicate a cleanup() to us.
|
||||
friend class TEventTask;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // #ifndef THRIFT_ASYNC_TEVENTCONNECTION_H_
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TEVENTHANDLER_H_
|
||||
#define THRIFT_ASYNC_TEVENTHANDLER_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/thrift_config.h"
|
||||
#include "thrift/lib/cpp/async/TEventUtil.h"
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
class TEventBase;
|
||||
|
||||
/**
|
||||
* The TEventHandler class is used to asynchronously wait for events on a file
|
||||
* descriptor.
|
||||
*
|
||||
* Users that wish to wait on I/O events should derive from TEventHandler and
|
||||
* implement the handlerReady() method.
|
||||
*/
|
||||
class TEventHandler : private boost::noncopyable {
|
||||
public:
|
||||
enum EventFlags {
|
||||
NONE = 0,
|
||||
READ = EV_READ,
|
||||
WRITE = EV_WRITE,
|
||||
READ_WRITE = (READ | WRITE),
|
||||
PERSIST = EV_PERSIST
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new TEventHandler object.
|
||||
*
|
||||
* @param eventBase The TEventBase to use to drive this event handler.
|
||||
* This may be NULL, in which case the TEventBase must be
|
||||
* set separately using initHandler() or attachEventBase()
|
||||
* before the handler can be registered.
|
||||
* @param fd The file descriptor that this TEventHandler will
|
||||
* monitor. This may be -1, in which case the file
|
||||
* descriptor must be set separately using initHandler() or
|
||||
* changeHandlerFD() before the handler can be registered.
|
||||
*/
|
||||
explicit TEventHandler(TEventBase* eventBase = NULL, int fd = -1);
|
||||
|
||||
/**
|
||||
* TEventHandler destructor.
|
||||
*
|
||||
* The event will be automatically unregistered if it is still registered.
|
||||
*/
|
||||
virtual ~TEventHandler();
|
||||
|
||||
/**
|
||||
* handlerReady() is invoked when the handler is ready.
|
||||
*
|
||||
* @param events A bitset indicating the events that are ready.
|
||||
*/
|
||||
virtual void handlerReady(uint16_t events) THRIFT_NOEXCEPT = 0;
|
||||
|
||||
/**
|
||||
* Register the handler.
|
||||
*
|
||||
* If the handler is already registered, the registration will be updated
|
||||
* to wait on the new set of events.
|
||||
*
|
||||
* @param events A bitset specifying the events to monitor.
|
||||
* If the PERSIST bit is set, the handler will remain
|
||||
* registered even after handlerReady() is called.
|
||||
*
|
||||
* @return Returns true if the handler was successfully registered,
|
||||
* or false if an error occurred. After an error, the handler is
|
||||
* always unregistered, even if it was already registered prior to
|
||||
* this call to registerHandler().
|
||||
*/
|
||||
bool registerHandler(uint16_t events) {
|
||||
return registerImpl(events, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the handler, if it is registered.
|
||||
*/
|
||||
void unregisterHandler();
|
||||
|
||||
/**
|
||||
* Returns true if the handler is currently registered.
|
||||
*/
|
||||
bool isHandlerRegistered() const {
|
||||
return TEventUtil::isEventRegistered(&event_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach the handler to a TEventBase.
|
||||
*
|
||||
* This may only be called if the handler is not currently attached to a
|
||||
* TEventBase (either by using the default constructor, or by calling
|
||||
* detachEventBase()).
|
||||
*
|
||||
* This method must be invoked in the TEventBase's thread.
|
||||
*/
|
||||
void attachEventBase(TEventBase* eventBase);
|
||||
|
||||
/**
|
||||
* Detach the handler from its TEventBase.
|
||||
*
|
||||
* This may only be called when the handler is not currently registered.
|
||||
* Once detached, the handler may not be registered again until it is
|
||||
* re-attached to a TEventBase by calling attachEventBase().
|
||||
*
|
||||
* This method must be called from the current TEventBase's thread.
|
||||
*/
|
||||
void detachEventBase();
|
||||
|
||||
/**
|
||||
* Change the file descriptor that this handler is associated with.
|
||||
*
|
||||
* This may only be called when the handler is not currently registered.
|
||||
*/
|
||||
void changeHandlerFD(int fd);
|
||||
|
||||
/**
|
||||
* Attach the handler to a TEventBase, and change the file descriptor.
|
||||
*
|
||||
* This method may only be called if the handler is not currently attached to
|
||||
* a TEventBase. This is primarily intended to be used to initialize
|
||||
* TEventHandler objects created using the default constructor.
|
||||
*/
|
||||
void initHandler(TEventBase* eventBase, int fd);
|
||||
|
||||
/**
|
||||
* Return the set of events that we're currently registered for.
|
||||
*/
|
||||
uint16_t getRegisteredEvents() const {
|
||||
return (isHandlerRegistered()) ?
|
||||
event_.ev_events : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the handler as an internal event.
|
||||
*
|
||||
* This event will not count as an active event for determining if the
|
||||
* TEventBase loop has more events to process. The TEventBase loop runs
|
||||
* only as long as there are active TEventHandlers, however "internal" event
|
||||
* handlers are not counted. Therefore this event handler will not prevent
|
||||
* TEventBase loop from exiting with no more work to do if there are no other
|
||||
* non-internal event handlers registered.
|
||||
*
|
||||
* This is intended to be used only in very rare cases by the internal
|
||||
* TEventBase code. This API is not guaranteed to remain stable or portable
|
||||
* in the future.
|
||||
*/
|
||||
bool registerInternalHandler(uint16_t events) {
|
||||
return registerImpl(events, true);
|
||||
}
|
||||
|
||||
private:
|
||||
bool registerImpl(uint16_t events, bool internal);
|
||||
void ensureNotRegistered(const char* fn);
|
||||
|
||||
static void libeventCallback(int fd, short events, void* arg);
|
||||
|
||||
struct event event_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TEVENTHANDLER_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,49 @@
|
|||
#ifndef _THRIFT_TEVENTTASK_H_
|
||||
#define _THRIFT_TEVENTTASK_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/Thrift.h"
|
||||
#include "thrift/lib/cpp/server/TServer.h"
|
||||
#include "thrift/lib/cpp/transport/TBufferTransports.h"
|
||||
#include "thrift/lib/cpp/concurrency/ThreadManager.h"
|
||||
#include "thrift/lib/cpp/async/TEventConnection.h"
|
||||
#include <tr1/functional>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "thrift/lib/cpp/TProcessor.h"
|
||||
#include "thrift/lib/cpp/protocol/TProtocol.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
class TEventTask : public apache::thrift::concurrency::Runnable {
|
||||
public:
|
||||
explicit TEventTask(TEventConnection* connection);
|
||||
|
||||
void run();
|
||||
|
||||
TEventConnection* getConnection() const {
|
||||
return connection_;
|
||||
}
|
||||
|
||||
private:
|
||||
boost::shared_ptr<apache::thrift::server::TProcessor> processor_;
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol> input_;
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol> output_;
|
||||
TEventConnection* connection_;
|
||||
TConnectionContext* connectionContext_;
|
||||
};
|
||||
|
||||
class TaskCompletionMessage {
|
||||
public:
|
||||
explicit TaskCompletionMessage(TEventConnection *inConnection)
|
||||
: connection(inConnection) {}
|
||||
|
||||
TaskCompletionMessage(TaskCompletionMessage &&msg)
|
||||
: connection(msg.connection) {
|
||||
msg.connection = NULL;
|
||||
}
|
||||
|
||||
TEventConnection *connection;
|
||||
};
|
||||
|
||||
} } } // namespace apache::thrift::async
|
||||
|
||||
#endif // !_THRIFT_TEVENTTASK_H_
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TEVENTUTIL_H_
|
||||
#define THRIFT_ASYNC_TEVENTUTIL_H_ 1
|
||||
|
||||
#include <event.h> // libevent
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
/**
|
||||
* low-level libevent utility functions
|
||||
*/
|
||||
class TEventUtil {
|
||||
public:
|
||||
static bool isEventRegistered(const struct event* ev) {
|
||||
// If any of these flags are set, the event is registered.
|
||||
enum {
|
||||
EVLIST_REGISTERED = (EVLIST_INSERTED | EVLIST_ACTIVE |
|
||||
EVLIST_TIMEOUT | EVLIST_SIGNAL)
|
||||
};
|
||||
return (ev->ev_flags & EVLIST_REGISTERED);
|
||||
}
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TEVENTUTIL_H_
|
|
@ -0,0 +1,264 @@
|
|||
// Copyright (c) 2006- Facebook
|
||||
// Distributed under the Thrift Software License
|
||||
//
|
||||
// See accompanying file LICENSE or visit the Thrift site at:
|
||||
// http://developers.facebook.com/thrift/
|
||||
|
||||
#ifndef THRIFT_SERVER_TEVENTWORKER_H_
|
||||
#define THRIFT_SERVER_TEVENTWORKER_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TAsyncServerSocket.h"
|
||||
#include "thrift/lib/cpp/async/TAsyncSSLSocket.h"
|
||||
#include "thrift/lib/cpp/async/TEventServer.h"
|
||||
#include "thrift/lib/cpp/async/TEventBase.h"
|
||||
#include "thrift/lib/cpp/async/TEventHandler.h"
|
||||
#include "thrift/lib/cpp/async/TNotificationQueue.h"
|
||||
#include "thrift/lib/cpp/server/TServer.h"
|
||||
#include <ext/hash_map>
|
||||
#include <list>
|
||||
#include <stack>
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
// Forward declaration of classes
|
||||
class TAsyncProcessorFactory;
|
||||
class TEventConnection;
|
||||
class TEventServer;
|
||||
class TaskCompletionMessage;
|
||||
/**
|
||||
* TEventWorker drives the actual I/O for TEventServer connections.
|
||||
*
|
||||
* The TEventServer itself accepts incoming connections, then hands off each
|
||||
* connection to a TEventWorker running in another thread. There should
|
||||
* typically be around one TEventWorker thread per core.
|
||||
*/
|
||||
class TEventWorker :
|
||||
public apache::thrift::server::TServer,
|
||||
public TAsyncServerSocket::AcceptCallback,
|
||||
public TAsyncSSLSocket::HandshakeCallback,
|
||||
public TNotificationQueue<TaskCompletionMessage>::Consumer {
|
||||
private:
|
||||
/// Object that processes requests.
|
||||
boost::shared_ptr<TAsyncProcessorFactory> asyncProcessorFactory_;
|
||||
|
||||
/// The mother ship.
|
||||
TEventServer* server_;
|
||||
|
||||
/// An instance's TEventBase for I/O.
|
||||
TEventBase eventBase_;
|
||||
|
||||
/// Our ID in [0:nWorkers).
|
||||
uint32_t workerID_;
|
||||
|
||||
/// Pipe that task completion notifications are sent over
|
||||
TNotificationQueue<TaskCompletionMessage> notificationQueue_;
|
||||
|
||||
/**
|
||||
* A stack of idle TEventConnection objects for reuse.
|
||||
* When we close a connection, we place it on this stack so that the
|
||||
* object can be reused later, rather than freeing the memory and
|
||||
* reallocating a new object later.
|
||||
*/
|
||||
std::stack<TEventConnection*> connectionStack_;
|
||||
|
||||
/// Transport type to use
|
||||
TEventServer::TransportType transportType_;
|
||||
|
||||
/**
|
||||
* Called when the connection is fully accepted (after SSL accept if needed)
|
||||
*/
|
||||
void finishConnectionAccepted(TAsyncSocket *asyncSock);
|
||||
|
||||
/**
|
||||
* Create or reuse a TEventConnection initialized for the given socket FD.
|
||||
*
|
||||
* @param socket the FD of a freshly-connected socket.
|
||||
* @param address the peer address of the socket.
|
||||
* @return pointer to a TConenction object for that socket.
|
||||
*/
|
||||
TEventConnection* createConnection(
|
||||
boost::shared_ptr<TAsyncSocket> asyncSocket,
|
||||
const transport::TSocketAddress* address);
|
||||
|
||||
/**
|
||||
* Handler called when a new connection may be available.
|
||||
*/
|
||||
void acceptConnections();
|
||||
|
||||
void makeCompletionCallback();
|
||||
|
||||
/**
|
||||
* Initialize our TEventBase to generate incoming connection events.
|
||||
* Note that this is called once before the main loop is executed and
|
||||
* sets up a READ event on the output of the listener's socktpair.
|
||||
*/
|
||||
void registerEvents();
|
||||
|
||||
/**
|
||||
* Callback used when loop latency exceeds the requested threshold.
|
||||
*/
|
||||
void maxLatencyCob();
|
||||
|
||||
typedef std::list<TEventConnection*> ConnectionList;
|
||||
|
||||
// pointer hash functor
|
||||
static const uint64_t kPtrHashMult = 5700357409661599291LL;
|
||||
static const uint64_t kPtrHashShift = 3;
|
||||
template<typename T>
|
||||
struct hash { };
|
||||
template<typename T>
|
||||
struct hash<T*> {
|
||||
size_t operator()(T* p) const {
|
||||
return ((size_t)p ^ ((size_t)p >> kPtrHashShift)) * kPtrHashMult;
|
||||
}
|
||||
size_t operator()(const T* p) const {
|
||||
return ((size_t)p ^ ((size_t)p >> kPtrHashShift)) * kPtrHashMult;
|
||||
}
|
||||
};
|
||||
typedef __gnu_cxx::hash_map<const TEventConnection*,
|
||||
ConnectionList::iterator,
|
||||
hash<TEventConnection*> > ConnectionMap;
|
||||
|
||||
/**
|
||||
* The list of active connections (used to allow the oldest connections
|
||||
* to be shed during overload).
|
||||
*/
|
||||
ConnectionList activeConnectionList_;
|
||||
/**
|
||||
* A hash map used to map connections to their place in the connection list
|
||||
*/
|
||||
ConnectionMap activeConnectionMap_;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* TEventWorker is the actual server object for existing connections.
|
||||
* One or more of these should be created by TEventServer (one per
|
||||
* CPU core is recommended).
|
||||
*
|
||||
* @param processorFactory a TAsyncProcessorFactory object as
|
||||
* obtained from the generated Thrift code (the user service
|
||||
* is integrated through this).
|
||||
* @param inputProtocolFactory the TProtocolFactory class supporting
|
||||
* inbound Thrift requests.
|
||||
* @param outputProtocolFactory the TProtocolFactory class supporting
|
||||
* responses (if any) after processing completes.
|
||||
* @param server the TEventServer which created us.
|
||||
* @param workerID the ID assigned to this worker
|
||||
*/
|
||||
TEventWorker(boost::shared_ptr<TAsyncProcessorFactory> processorFactory,
|
||||
boost::shared_ptr<apache::thrift::server::TDuplexProtocolFactory>
|
||||
protocolFactory,
|
||||
TEventServer* server,
|
||||
uint32_t workerID) :
|
||||
TServer(boost::shared_ptr<apache::thrift::server::TProcessor>()),
|
||||
asyncProcessorFactory_(processorFactory),
|
||||
server_(server),
|
||||
eventBase_(),
|
||||
workerID_(workerID),
|
||||
transportType_(TEventServer::FRAMED) {
|
||||
|
||||
setDuplexProtocolFactory(protocolFactory);
|
||||
transportType_ = server->getTransportType();
|
||||
if (transportType_ == TEventServer::FRAMED) {
|
||||
// do the dynamic cast once rather than per connection
|
||||
if (dynamic_cast<apache::thrift::protocol::THeaderProtocolFactory*>(
|
||||
protocolFactory.get())) {
|
||||
transportType_ = TEventServer::HEADER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a TEventWorker. We've use boost::scoped_ptr<> to take care
|
||||
* of freeing up memory, so nothing to be done here but release the
|
||||
* connection stack.
|
||||
*/
|
||||
virtual ~TEventWorker();
|
||||
|
||||
/**
|
||||
* Get my TAsyncProcessorFactory object.
|
||||
*
|
||||
* @returns pointer to my TAsyncProcessorFactory object.
|
||||
*/
|
||||
boost::shared_ptr<TAsyncProcessorFactory> getAsyncProcessorFactory() {
|
||||
return asyncProcessorFactory_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get my TEventBase object.
|
||||
*
|
||||
* @returns pointer to my TEventBase object.
|
||||
*/
|
||||
TEventBase* getEventBase() {
|
||||
return &eventBase_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get underlying server.
|
||||
*
|
||||
* @returns pointer to TEventServer
|
||||
*/
|
||||
TEventServer* getServer() const {
|
||||
return server_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get my numeric worker ID (for diagnostics).
|
||||
*
|
||||
* @return integer ID of this worker
|
||||
*/
|
||||
int32_t getID() {
|
||||
return workerID_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose of a TEventConnection object.
|
||||
* Will add to a pool of these objects or destroy as necessary.
|
||||
*
|
||||
* @param connection a now-idle connection.
|
||||
*/
|
||||
void returnConnection(TEventConnection* connection);
|
||||
|
||||
/**
|
||||
* Cause a completion callback for the requested connection to occur
|
||||
* within that connection's context.
|
||||
*
|
||||
* @param msg task completion message
|
||||
* @return true if notification was sent, false if it failed.
|
||||
*/
|
||||
bool notifyCompletion(TaskCompletionMessage &&msg);
|
||||
|
||||
/**
|
||||
* Task completed (called in this worker's thread)
|
||||
*/
|
||||
void messageAvailable(TaskCompletionMessage &&msg);
|
||||
|
||||
virtual void connectionAccepted(int fd,
|
||||
const transport::TSocketAddress& clientAddr)
|
||||
THRIFT_NOEXCEPT;
|
||||
virtual void acceptError(const std::exception& ex) THRIFT_NOEXCEPT;
|
||||
virtual void acceptStopped() THRIFT_NOEXCEPT;
|
||||
|
||||
/**
|
||||
* TAsyncSSLSocket::HandshakeCallback interface
|
||||
*/
|
||||
void handshakeSuccess(TAsyncSSLSocket *sock) THRIFT_NOEXCEPT;
|
||||
void handshakeError(TAsyncSSLSocket *sock,
|
||||
const transport::TTransportException& ex) THRIFT_NOEXCEPT;
|
||||
|
||||
|
||||
/**
|
||||
* Enter event loop and serve.
|
||||
*/
|
||||
void serve();
|
||||
|
||||
/**
|
||||
* Exit event loop.
|
||||
*/
|
||||
void shutdown();
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // #ifndef THRIFT_SERVER_TEVENTWORKER_H_
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TFRAMEDASYNCCHANNEL_H_
|
||||
#define THRIFT_ASYNC_TFRAMEDASYNCCHANNEL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TStreamAsyncChannel.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Encapsulation of one outstanding write request on a TFramedAsyncChannel.
|
||||
*/
|
||||
class TFramedACWriteRequest :
|
||||
public TAsyncChannelWriteRequestBase<TFramedACWriteRequest> {
|
||||
public:
|
||||
TFramedACWriteRequest(const VoidCallback& callback,
|
||||
const VoidCallback& errorCallback,
|
||||
transport::TMemoryBuffer* message,
|
||||
TAsyncEventChannel* channel);
|
||||
|
||||
void write(TAsyncTransport* transport,
|
||||
TAsyncTransport::WriteCallback* callback) THRIFT_NOEXCEPT;
|
||||
|
||||
void writeSuccess() THRIFT_NOEXCEPT;
|
||||
void writeError(size_t bytesWritten,
|
||||
const transport::TTransportException& ex) THRIFT_NOEXCEPT;
|
||||
|
||||
private:
|
||||
union {
|
||||
uint32_t frameSize_;
|
||||
char frameSizeBuf_[sizeof(uint32_t)];
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Read state for TFramedAsyncChannel
|
||||
*/
|
||||
class TFramedACReadState {
|
||||
public:
|
||||
typedef std::tr1::function<void()> VoidCallback;
|
||||
|
||||
TFramedACReadState();
|
||||
|
||||
// Methods required by TStreamAsyncChannel
|
||||
|
||||
void setCallbackBuffer(transport::TMemoryBuffer* buffer) {
|
||||
buffer_ = buffer;
|
||||
bytesRead_ = 0;
|
||||
}
|
||||
void unsetCallbackBuffer() {
|
||||
buffer_ = NULL;
|
||||
}
|
||||
|
||||
bool hasReadAheadData() {
|
||||
assert(bytesRead_ == 0);
|
||||
return false;
|
||||
}
|
||||
bool hasPartialMessage() {
|
||||
return (bytesRead_ > 0);
|
||||
}
|
||||
|
||||
void getReadBuffer(void** bufReturn, size_t* lenReturn);
|
||||
bool readDataAvailable(size_t len);
|
||||
|
||||
// Other methods specifict to TFramedAsyncChannel
|
||||
|
||||
void setMaxFrameSize(uint32_t size) {
|
||||
maxFrameSize_ = size;
|
||||
}
|
||||
|
||||
uint32_t getMaxFrameSize() const {
|
||||
return maxFrameSize_;
|
||||
}
|
||||
|
||||
private:
|
||||
/// maximum frame size accepted
|
||||
uint32_t maxFrameSize_;
|
||||
|
||||
union {
|
||||
uint32_t frameSize_;
|
||||
char frameSizeBuf_[sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
/**
|
||||
* The number of bytes read.
|
||||
*
|
||||
* This includes the bytes in the frame size. When bytesRead_ is less than
|
||||
* sizeof(uint32_t), we are still reading the frame size. Otherwise, we have
|
||||
* read bytesRead_ - sizeof(uint32_t) bytes of the body.
|
||||
*/
|
||||
uint32_t bytesRead_;
|
||||
apache::thrift::transport::TMemoryBuffer* buffer_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* TFramedAsyncChannel
|
||||
*
|
||||
* This is a TAsyncChannel implementation that reads and writes messages
|
||||
* prefixed with a 4-byte frame length.
|
||||
*
|
||||
* Its messages are compatible with TFramedTransport.
|
||||
*/
|
||||
class TFramedAsyncChannel :
|
||||
public TStreamAsyncChannel<detail::TFramedACWriteRequest,
|
||||
detail::TFramedACReadState> {
|
||||
private:
|
||||
typedef TStreamAsyncChannel<detail::TFramedACWriteRequest,
|
||||
detail::TFramedACReadState> Parent;
|
||||
|
||||
public:
|
||||
explicit TFramedAsyncChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport
|
||||
)
|
||||
: Parent(transport) {}
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TFramedAsyncChannel>.
|
||||
*
|
||||
* This passes in the correct destructor object, since TFramedAsyncChannel's
|
||||
* destructor is protected and cannot be invoked directly.
|
||||
*/
|
||||
static boost::shared_ptr<TFramedAsyncChannel> newChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport) {
|
||||
return boost::shared_ptr<TFramedAsyncChannel>(
|
||||
new TFramedAsyncChannel(transport), Destructor());
|
||||
}
|
||||
|
||||
/// size in bytes beyond which we'll reject a given frame size.
|
||||
void setMaxFrameSize(uint32_t size) {
|
||||
readState_.setMaxFrameSize(size);
|
||||
}
|
||||
|
||||
uint32_t getMaxFrameSize() const {
|
||||
return readState_.getMaxFrameSize();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Users of TFramedAsyncChannel must never delete it directly. Instead,
|
||||
* invoke destroy().
|
||||
*/
|
||||
virtual ~TFramedAsyncChannel() { }
|
||||
};
|
||||
|
||||
class TFramedAsyncChannelFactory : public TStreamAsyncChannelFactory {
|
||||
public:
|
||||
TFramedAsyncChannelFactory()
|
||||
: maxFrameSize_(0x7fffffff)
|
||||
, recvTimeout_(0)
|
||||
, sendTimeout_(0) {}
|
||||
|
||||
void setMaxFrameSize(uint32_t bytes) {
|
||||
maxFrameSize_ = bytes;
|
||||
}
|
||||
|
||||
void setRecvTimeout(uint32_t milliseconds) {
|
||||
recvTimeout_ = milliseconds;
|
||||
}
|
||||
|
||||
void setSendTimeout(uint32_t milliseconds) {
|
||||
sendTimeout_ = milliseconds;
|
||||
}
|
||||
|
||||
virtual boost::shared_ptr<TAsyncEventChannel> newChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport) {
|
||||
boost::shared_ptr<TFramedAsyncChannel> channel(
|
||||
TFramedAsyncChannel::newChannel(transport));
|
||||
transport->setSendTimeout(sendTimeout_);
|
||||
channel->setMaxFrameSize(maxFrameSize_);
|
||||
channel->setRecvTimeout(recvTimeout_);
|
||||
return channel;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t maxFrameSize_;
|
||||
uint32_t recvTimeout_;
|
||||
uint32_t sendTimeout_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TFRAMEDASYNCCHANNEL_H_
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_THEADERASYNCCHANNEL_H_
|
||||
#define THRIFT_ASYNC_THEADERASYNCCHANNEL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TUnframedAsyncChannel.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* A class to determine the end of a THeaderProtocol message. This is not as
|
||||
* sophisticated as the logic in THeaderTransport, so it cannot yet handle any
|
||||
* unframed transports, just THeader and TFramed. However, the previous
|
||||
* implementation used TFramedAsyncChannel, so the limitation is not new.
|
||||
*/
|
||||
class THeaderACProtocolTraits {
|
||||
public:
|
||||
|
||||
THeaderACProtocolTraits()
|
||||
: maxMessageSize_(0x7ffffff) {}
|
||||
|
||||
// Methods required by TUnframedACReadState
|
||||
bool getMessageLength(uint8_t* buffer,
|
||||
uint32_t bufferLength,
|
||||
uint32_t* messageLength);
|
||||
|
||||
void setMaxMessageSize(uint32_t maxMessageSize) {
|
||||
maxMessageSize_ = maxMessageSize;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t maxMessageSize_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* THeaderAsyncChannel
|
||||
*
|
||||
* This is a TAsyncChannel implementation that reads and writes
|
||||
* messages encoded using THeaderProtocol.
|
||||
*/
|
||||
class THeaderAsyncChannel :
|
||||
public TUnframedAsyncChannel<detail::THeaderACProtocolTraits> {
|
||||
private:
|
||||
typedef TUnframedAsyncChannel<detail::THeaderACProtocolTraits> Parent;
|
||||
|
||||
public:
|
||||
explicit THeaderAsyncChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport)
|
||||
: Parent(transport) {}
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<THeaderAsyncChannel>.
|
||||
*
|
||||
* This passes in the correct destructor object, since THeaderAsyncChannel's
|
||||
* destructor is protected and cannot be invoked directly.
|
||||
*/
|
||||
static boost::shared_ptr<THeaderAsyncChannel> newChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport) {
|
||||
return boost::shared_ptr<THeaderAsyncChannel>(
|
||||
new THeaderAsyncChannel(transport), Destructor());
|
||||
}
|
||||
|
||||
void setMaxMessageSize(uint32_t size) {
|
||||
Parent::setMaxMessageSize(size);
|
||||
readState_.getProtocolTraits()->setMaxMessageSize(size);
|
||||
}
|
||||
|
||||
// Note that we inherit getMaxMessageSize() from TUnframedAsyncChannel.
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Users of THeaderAsyncChannel must never delete it directly. Instead,
|
||||
* invoke destroy().
|
||||
*/
|
||||
virtual ~THeaderAsyncChannel() { }
|
||||
};
|
||||
|
||||
class THeaderAsyncChannelFactory : public TStreamAsyncChannelFactory {
|
||||
public:
|
||||
THeaderAsyncChannelFactory()
|
||||
: maxMessageSize_(0x7fffffff)
|
||||
, recvTimeout_(0)
|
||||
, sendTimeout_(0) {}
|
||||
|
||||
void setMaxMessageSize(uint32_t bytes) {
|
||||
maxMessageSize_ = bytes;
|
||||
}
|
||||
|
||||
void setRecvTimeout(uint32_t milliseconds) {
|
||||
recvTimeout_ = milliseconds;
|
||||
}
|
||||
|
||||
void setSendTimeout(uint32_t milliseconds) {
|
||||
sendTimeout_ = milliseconds;
|
||||
}
|
||||
|
||||
virtual boost::shared_ptr<TAsyncEventChannel> newChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport) {
|
||||
boost::shared_ptr<THeaderAsyncChannel> channel(
|
||||
THeaderAsyncChannel::newChannel(transport));
|
||||
transport->setSendTimeout(sendTimeout_);
|
||||
channel->setMaxMessageSize(maxMessageSize_);
|
||||
channel->setRecvTimeout(recvTimeout_);
|
||||
return channel;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t maxMessageSize_;
|
||||
uint32_t recvTimeout_;
|
||||
uint32_t sendTimeout_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_THEADERASYNCCHANNEL_H_
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_THTTPASYNCCHANNEL_H_
|
||||
#define THRIFT_ASYNC_THTTPASYNCCHANNEL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TStreamAsyncChannel.h"
|
||||
#include "thrift/lib/cpp/util/THttpParser.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
class THttpAsyncChannel;
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Encapsulation of one outstanding write request on a THttpAsyncChannel.
|
||||
*/
|
||||
class THttpACWriteRequest :
|
||||
public TAsyncChannelWriteRequestBase<THttpACWriteRequest> {
|
||||
public:
|
||||
typedef std::tr1::function<void()> VoidCallback;
|
||||
|
||||
THttpACWriteRequest(const VoidCallback& callback,
|
||||
const VoidCallback& errorCallback,
|
||||
transport::TMemoryBuffer* message,
|
||||
TAsyncEventChannel* channel);
|
||||
|
||||
void write(TAsyncTransport* transport,
|
||||
TAsyncTransport::WriteCallback* callback) THRIFT_NOEXCEPT;
|
||||
|
||||
void writeSuccess() THRIFT_NOEXCEPT;
|
||||
void writeError(size_t bytesWritten,
|
||||
const transport::TTransportException& ex) THRIFT_NOEXCEPT;
|
||||
|
||||
private:
|
||||
char lengthBuf_[64];
|
||||
THttpAsyncChannel* channel_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read state for THttpAsyncChannel
|
||||
*/
|
||||
class THttpACReadState {
|
||||
public:
|
||||
typedef std::tr1::function<void()> VoidCallback;
|
||||
|
||||
THttpACReadState() {
|
||||
}
|
||||
|
||||
// Methods required by TStreamAsyncChannel
|
||||
|
||||
void setCallbackBuffer(transport::TMemoryBuffer* buffer) {
|
||||
parser_->setDataBuffer(buffer);
|
||||
}
|
||||
void unsetCallbackBuffer() {
|
||||
parser_->unsetDataBuffer();
|
||||
}
|
||||
|
||||
bool hasReadAheadData() {
|
||||
return parser_->hasReadAheadData();
|
||||
}
|
||||
bool hasPartialMessage() {
|
||||
return parser_->hasPartialMessage();
|
||||
}
|
||||
|
||||
void getReadBuffer(void** bufReturn, size_t* lenReturn);
|
||||
bool readDataAvailable(size_t len);
|
||||
|
||||
// Other methods specific to THttpAsyncChannel
|
||||
void setParser(boost::shared_ptr<apache::thrift::util::THttpParser> parser) {
|
||||
parser_ = parser;
|
||||
}
|
||||
private:
|
||||
boost::shared_ptr<apache::thrift::util::THttpParser> parser_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* THttpAsyncChannel
|
||||
*
|
||||
* This is a TAsyncChannel implementation that reads and writes messages
|
||||
* encapuated in HTTP.
|
||||
*
|
||||
* Its messages are compatible with THttpTransport.
|
||||
*/
|
||||
class THttpAsyncChannel :
|
||||
public TStreamAsyncChannel<detail::THttpACWriteRequest,
|
||||
detail::THttpACReadState> {
|
||||
private:
|
||||
typedef TStreamAsyncChannel<detail::THttpACWriteRequest,
|
||||
detail::THttpACReadState> Parent;
|
||||
boost::shared_ptr<apache::thrift::util::THttpParser> parser_;
|
||||
|
||||
public:
|
||||
explicit THttpAsyncChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport)
|
||||
: Parent(transport) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<THttpAsyncChannel>.
|
||||
*
|
||||
* This passes in the correct destructor object, since THttpAsyncChannel's
|
||||
* destructor is protected and cannot be invoked directly.
|
||||
*/
|
||||
static boost::shared_ptr<THttpAsyncChannel> newChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport) {
|
||||
return boost::shared_ptr<THttpAsyncChannel>(
|
||||
new THttpAsyncChannel(transport), Destructor());
|
||||
}
|
||||
|
||||
/// size in bytes beyond which we'll reject a given http size.
|
||||
void setMaxHttpSize(uint32_t size) {
|
||||
parser_->setMaxSize(size);
|
||||
}
|
||||
|
||||
uint32_t getMaxHttpSize() const {
|
||||
return parser_->getMaxSize();
|
||||
}
|
||||
|
||||
void setParser(boost::shared_ptr<apache::thrift::util::THttpParser> parser) {
|
||||
parser_ = parser;
|
||||
readState_.setParser(parser);
|
||||
}
|
||||
|
||||
boost::shared_ptr<apache::thrift::util::THttpParser> getParser() const {
|
||||
return parser_;
|
||||
}
|
||||
|
||||
int constructHeader(iovec* ops,
|
||||
int opsLen,
|
||||
int contentLength,
|
||||
char* contentLengthBuf) {
|
||||
return parser_->constructHeader(ops,
|
||||
opsLen,
|
||||
contentLength,
|
||||
contentLengthBuf);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Users of THttpAsyncChannel must never delete it directly. Instead,
|
||||
* invoke destroy().
|
||||
*/
|
||||
virtual ~THttpAsyncChannel() { }
|
||||
};
|
||||
|
||||
class THttpAsyncChannelFactory : public TStreamAsyncChannelFactory {
|
||||
public:
|
||||
THttpAsyncChannelFactory()
|
||||
: maxHttpSize_(0x7fffffff)
|
||||
, recvTimeout_(0)
|
||||
, sendTimeout_(0) {}
|
||||
|
||||
void setMaxHttpSize(uint32_t bytes) {
|
||||
maxHttpSize_ = bytes;
|
||||
}
|
||||
|
||||
void setRecvTimeout(uint32_t milliseconds) {
|
||||
recvTimeout_ = milliseconds;
|
||||
}
|
||||
|
||||
void setSendTimeout(uint32_t milliseconds) {
|
||||
sendTimeout_ = milliseconds;
|
||||
}
|
||||
|
||||
virtual boost::shared_ptr<TAsyncEventChannel> newChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport) {
|
||||
boost::shared_ptr<THttpAsyncChannel> channel(
|
||||
THttpAsyncChannel::newChannel(transport));
|
||||
transport->setSendTimeout(sendTimeout_);
|
||||
channel->setMaxHttpSize(maxHttpSize_);
|
||||
channel->setRecvTimeout(recvTimeout_);
|
||||
return channel;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t maxHttpSize_;
|
||||
uint32_t recvTimeout_;
|
||||
uint32_t sendTimeout_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_THTTPASYNCCHANNEL_H_
|
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TNOTIFICATIONPIPE_H
|
||||
#define THRIFT_ASYNC_TNOTIFICATIONPIPE_H 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TDelayedDestruction.h"
|
||||
#include "thrift/lib/cpp/async/TEventBase.h"
|
||||
#include "thrift/lib/cpp/async/TEventHandler.h"
|
||||
#include "thrift/lib/cpp/concurrency/Mutex.h"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <exception>
|
||||
#include <limits.h>
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
/**
|
||||
* A simple notification pipe for sending messages to a TEventBase thread.
|
||||
*
|
||||
* TNotificationPipe is a unidirectional pipe for sending small, atomic
|
||||
* messages.
|
||||
*
|
||||
* TNotificationPipe cannot be send messages larger than a fixed size.
|
||||
* TNotificationPipe::kMaxMessageSize defines the maximum message size
|
||||
* supported. If you need to pass larger amounts of data between threads,
|
||||
* consider just passing a pointer to the data over the pipe, and using some
|
||||
* external mechanism to synchronize management of the memory.
|
||||
*
|
||||
*
|
||||
* TNotificationPipe provides two parallel APIs for writing and closing the
|
||||
* pipe: a thread-safe version and a non-thread-safe version. Which version to
|
||||
* use depends on how the caller uses the pipe:
|
||||
*
|
||||
* - If there is only a single writer thread, you can use the non-thread-safe
|
||||
* versions of trySendMessage() and close(). This guarantees close() is
|
||||
* never called by one thread while another thread is attempting to send a
|
||||
* message.
|
||||
*
|
||||
* - If there are multiple writers, but the pipe is never closed by the
|
||||
* writers, you can use the non-thread-safe version of trySendMessage().
|
||||
* Multiple simultaneous trySendMessage() calls will not interfere with each
|
||||
* other. Since none of the writer threads call close, a call to close()
|
||||
* cannot be running simultaneously with a write attempt. (With this model,
|
||||
* the TNotificationPipe is never closed until it is destroyed. It is up to
|
||||
* the caller to ensure the TNotificationPipe is not destroyed while write
|
||||
* threads still have a pointer or reference to it.)
|
||||
*
|
||||
* In other circumstances (if one thread may call close while another thread is
|
||||
* simultaneously trying to write), the thread-safe versions
|
||||
* trySendMessageSync() and closeSync() must be used.
|
||||
*/
|
||||
class TNotificationPipe : public TDelayedDestruction,
|
||||
private TEventHandler,
|
||||
private TEventBase::LoopCallback {
|
||||
public:
|
||||
/**
|
||||
* A callback interface for receiving notification of messages from the pipe.
|
||||
*/
|
||||
class Callback {
|
||||
public:
|
||||
virtual ~Callback() {}
|
||||
|
||||
/**
|
||||
* notificationMessage() will be invoked whenever a new
|
||||
* message is available from the pipe.
|
||||
*/
|
||||
virtual void notificationMessage(const void *msg, uint32_t msgSize) = 0;
|
||||
|
||||
/**
|
||||
* notificationPipeError() will be invoked if an error occurs while reading
|
||||
* from the pipe. Before notificationPipeError() is invoked, the read
|
||||
* callback will automatically be uninstalled and the pipe will be closed.
|
||||
*/
|
||||
virtual void notificationPipeError(const std::exception& ex) = 0;
|
||||
|
||||
/**
|
||||
* notificationPipeClosed() is invoked in the read thread after the write
|
||||
* end of the pipe is closed.
|
||||
*/
|
||||
virtual void notificationPipeClosed() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to create a new shared_ptr<TNotificationPipe>.
|
||||
*
|
||||
* This simply sets the correct destructor to call destroy() instead of
|
||||
* directly deleting the TNotificationPipe.
|
||||
*/
|
||||
static boost::shared_ptr<TNotificationPipe> newPipe(TEventBase *base) {
|
||||
return boost::shared_ptr<TNotificationPipe>(new TNotificationPipe(base),
|
||||
Destructor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new TNotificationPipe.
|
||||
*
|
||||
* @param eventBase The TEventBase to use for receiving read notifications
|
||||
* from this pipe. All read events will be processed in this
|
||||
* TEventBase's thread. trySendMessage() may be called from any thread.
|
||||
*/
|
||||
TNotificationPipe(TEventBase *eventBase);
|
||||
|
||||
/**
|
||||
* Destroy this TNotificationPipe.
|
||||
*
|
||||
* This method may only be called from the read thread.
|
||||
*
|
||||
* This will automatically close the pipe if it is not already closed.
|
||||
*/
|
||||
virtual void destroy();
|
||||
|
||||
/**
|
||||
* Close the pipe.
|
||||
*
|
||||
* This version of close() is not thread-safe. It should only be used if the
|
||||
* caller is sure no other thread is attempting to write a message at the
|
||||
* same time.
|
||||
*
|
||||
* Use closeSync() if other threads may be attempting to send a message
|
||||
* simultaneously. The other threads must use also use the thread-safe
|
||||
* trySendMessageSync() or trySendFrameSync() calls.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* A thread-safe version of close().
|
||||
*/
|
||||
void closeSync();
|
||||
|
||||
/**
|
||||
* Send a message over the pipe.
|
||||
*
|
||||
* trySendMessage() is best-effort. It will either immediately succeed to
|
||||
* send the message, or it will fail immediately if the pipe reader is too
|
||||
* busy and it's backlog of unread messages is too large.
|
||||
*
|
||||
* trySendMessage() also does not support arbitrarily large messages.
|
||||
* It will also fail immediately if msgSize is larger than (PIPE_BUF - 4).
|
||||
*
|
||||
* If trySendMessage() succeeds, the message is guaranteed to be delivered to
|
||||
* the pipe reader, except in the case where the pipe reader explicitly stops
|
||||
* reading and destroys the pipe before processing all of its messages.
|
||||
*
|
||||
* On failure a TTransportException is thrown. The error code will be
|
||||
* TTransportException::BAD_ARGS if the message is too large,
|
||||
* TTransportException::TIMED_OUT if the message cannot be sent right now
|
||||
* because the pipe is full, or TTransportException::NOT_OPEN if the pipe has
|
||||
* already been closed.
|
||||
*
|
||||
* This method is thread safe with other simultaneous trySendMessage() calls,
|
||||
* but not with close() calls. Use trySendMessageSync() and closeSync() if a
|
||||
* close may occur simultaneously on another thread.
|
||||
*/
|
||||
void trySendMessage(const void *msg, uint32_t msgSize);
|
||||
|
||||
/**
|
||||
* A thread-safe version of trySendMessage().
|
||||
*
|
||||
* This may be called simultaneously with closeSync().
|
||||
*/
|
||||
void trySendMessageSync(const void *msg, uint32_t msgSize);
|
||||
|
||||
/**
|
||||
* Send a message over the pipe.
|
||||
*
|
||||
* This is identical to trySendMessage(), except that the caller must provide
|
||||
* 4 bytes at the beginning of the message where we can write a frame length.
|
||||
* This allows us to avoid copying the message into a new buffer.
|
||||
* (trySendMessage() always has to make a copy of the message.)
|
||||
*
|
||||
* @param frame A pointer to the frame buffer. trySendFrame() will
|
||||
* overwrite the first 4 bytes of this buffer. When the read callback
|
||||
* receives the message, it will not see these first 4 bytes.
|
||||
* @param frameSize The full size of the frame buffer. This must be at
|
||||
* least 4 bytes long. The actual message size that will be sent is
|
||||
* frameSize - 4.
|
||||
*/
|
||||
void trySendFrame(void *frame, uint32_t frameSize);
|
||||
|
||||
/**
|
||||
* A thread-safe version of trySendFrame().
|
||||
*
|
||||
* This may be called simultaneously with closeSync().
|
||||
*/
|
||||
void trySendFrameSync(void *frame, uint32_t frameSize);
|
||||
|
||||
/**
|
||||
* Get the number of messages which haven't been processed.
|
||||
*/
|
||||
int64_t getNumNotProcessed() const {
|
||||
return numInputs_ - numOutputs_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the callback to receive read notifications from this pipe.
|
||||
*
|
||||
* This method must be invoked from the pipe's read thread.
|
||||
*
|
||||
* May throw TLibraryException on error. The callback will always be unset
|
||||
* (NULL) after an error.
|
||||
*/
|
||||
void setReadCallback(Callback *callback);
|
||||
|
||||
/**
|
||||
* Mark the pipe read event handler as an "internal" event handler.
|
||||
*
|
||||
* This causes the notification pipe not to be counted when determining if
|
||||
* the TEventBase has any more active events to wait on. This is intended to
|
||||
* be used only be internal TEventBase code. This API is not guaranteed to
|
||||
* remain stable or portable in the future.
|
||||
*
|
||||
* May throw TLibraryException if it fails to re-register its event handler
|
||||
* with the correct flags.
|
||||
*/
|
||||
void setInternal(bool internal);
|
||||
|
||||
/**
|
||||
* Get the maximum number of messages that will be read on a single iteration
|
||||
* of the event loop.
|
||||
*/
|
||||
uint32_t getMaxReadAtOnce() const {
|
||||
return maxReadAtOnce_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of messages to read each iteration of the event
|
||||
* loop.
|
||||
*
|
||||
* If messages are being received faster than they can be processed, this
|
||||
* helps limit the rate at which they will be read. This can be used to
|
||||
* prevent the notification pipe reader from starving other users of the
|
||||
* event loop.
|
||||
*/
|
||||
void setMaxReadAtOnce(uint32_t numMessages) {
|
||||
maxReadAtOnce_ = numMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum message size that can be sent over a TNotificationPipe.
|
||||
*
|
||||
* This restriction ensures that trySendMessage() can send all messages
|
||||
* atomically. This is (PIPE_BUF - 4) bytes. (On Linux, this is 4092
|
||||
* bytes.)
|
||||
*/
|
||||
static const uint32_t kMaxMessageSize = PIPE_BUF - 4;
|
||||
|
||||
/**
|
||||
* The default maximum number of messages that will be read each time around
|
||||
* the event loop.
|
||||
*
|
||||
* This value used for each TNotificationPipe can be changed using the
|
||||
* setMaxReadAtOnce() method.
|
||||
*/
|
||||
static const uint32_t kDefaultMaxReadAtOnce = 10;
|
||||
|
||||
private:
|
||||
enum ReadAction {
|
||||
kDoNothing,
|
||||
kContinue,
|
||||
kWaitForRead,
|
||||
kRunInNextLoop,
|
||||
};
|
||||
|
||||
// Forbidden copy constructor and assignment opererator
|
||||
TNotificationPipe(TNotificationPipe const &);
|
||||
TNotificationPipe& operator=(TNotificationPipe const &);
|
||||
|
||||
// TEventHandler methods
|
||||
virtual void handlerReady(uint16_t events) THRIFT_NOEXCEPT;
|
||||
|
||||
// TEventBase::LoopCallback methods
|
||||
virtual void runLoopCallback() THRIFT_NOEXCEPT;
|
||||
|
||||
void initPipe();
|
||||
void registerPipeEvent();
|
||||
void readMessages(ReadAction action);
|
||||
ReadAction performRead();
|
||||
ReadAction processReadData(uint32_t* messagesProcessed);
|
||||
ReadAction handleError(const char* fmt, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
void checkMessage(uint32_t msgSize);
|
||||
void writeFrame(const void *frame, uint32_t frameSize);
|
||||
|
||||
TEventBase *eventBase_;
|
||||
Callback *readCallback_;
|
||||
int readPipe_;
|
||||
int writePipe_;
|
||||
bool internal_;
|
||||
uint32_t maxReadAtOnce_;
|
||||
int64_t numInputs_;
|
||||
int64_t numOutputs_;
|
||||
|
||||
/**
|
||||
* Mutex for guarding numInputs_
|
||||
*/
|
||||
concurrency::Mutex numInputsMutex_;
|
||||
|
||||
/**
|
||||
* A mutex that guards writePipe_.
|
||||
*
|
||||
* This is used by closeSync(), trySendMessageSync(), and trySendFrameSync(),
|
||||
* since trySendMessageSync() and trySendFrameSync() read writePipe_
|
||||
* and closeSync() resets it to -1.
|
||||
*/
|
||||
concurrency::NoStarveReadWriteMutex writePipeMutex_;
|
||||
|
||||
/**
|
||||
* A pointer to the end of valid read data in the read buffer.
|
||||
*/
|
||||
uint8_t *readPtr_;
|
||||
/**
|
||||
* An internal read buffer
|
||||
*
|
||||
* This is large enough to contain the maximum possible message plus the
|
||||
* mssage length.
|
||||
*/
|
||||
uint8_t readBuffer_[kMaxMessageSize + 4];
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TNOTIFICATIONPIPE_H
|
|
@ -0,0 +1,592 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TNOTIFICATIONQUEUE_H_
|
||||
#define THRIFT_ASYNC_TNOTIFICATIONQUEUE_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/Thrift.h"
|
||||
#include "thrift/lib/cpp/async/TEventBase.h"
|
||||
#include "thrift/lib/cpp/async/TEventHandler.h"
|
||||
|
||||
#include "external/google_base/spinlock.h"
|
||||
#include "external/glog/logging.h"
|
||||
#include <deque>
|
||||
#include "folly/eventfd.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
/**
|
||||
* An exception class to be thrown when a TNotificationQueue is full.
|
||||
*/
|
||||
class TQueueFullException : public TLibraryException {
|
||||
public:
|
||||
TQueueFullException() :
|
||||
TLibraryException("unable to add message to TNotificationQueue: "
|
||||
"queue is full") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* A producer-consumer queue for passing messages between TEventBase threads.
|
||||
*
|
||||
* Messages can be added to the queue from any thread. Multiple consumers may
|
||||
* listen to the queue from multiple TEventBase threads.
|
||||
*
|
||||
* A TNotificationQueue may not be destroyed while there are still consumers
|
||||
* registered to receive events from the queue. It is the user's
|
||||
* responsibility to ensure that all consumers are unregistered before the
|
||||
* queue is destroyed.
|
||||
*
|
||||
* MessageT should be MoveConstructible (i.e., must support either a move
|
||||
* constructor or a copy constructor, or both). Ideally it's move constructor
|
||||
* (or copy constructor if no move constructor is provided) should never throw
|
||||
* exceptions. If the constructor may throw, the consumers could end up
|
||||
* spinning trying to move a message off the queue and failing, and then
|
||||
* retrying.
|
||||
*/
|
||||
template<typename MessageT>
|
||||
class TNotificationQueue {
|
||||
public:
|
||||
/**
|
||||
* A callback interface for consuming messages from the queue as they arrive.
|
||||
*/
|
||||
class Consumer : private TEventHandler {
|
||||
public:
|
||||
enum : uint16_t { kDefaultMaxReadAtOnce = 10 };
|
||||
|
||||
Consumer()
|
||||
: queue_(NULL),
|
||||
destroyedFlagPtr_(NULL),
|
||||
maxReadAtOnce_(kDefaultMaxReadAtOnce) {}
|
||||
|
||||
virtual ~Consumer();
|
||||
|
||||
/**
|
||||
* messageAvailable() will be invoked whenever a new
|
||||
* message is available from the pipe.
|
||||
*/
|
||||
virtual void messageAvailable(MessageT&& message) = 0;
|
||||
|
||||
/**
|
||||
* Begin consuming messages from the specified queue.
|
||||
*
|
||||
* messageAvailable() will be called whenever a message is available. This
|
||||
* consumer will continue to consume messages until stopConsuming() is
|
||||
* called.
|
||||
*
|
||||
* A Consumer may only consume messages from a single TNotificationQueue at
|
||||
* a time. startConsuming() should not be called if this consumer is
|
||||
* already consuming.
|
||||
*/
|
||||
void startConsuming(TEventBase* eventBase, TNotificationQueue* queue) {
|
||||
init(eventBase, queue);
|
||||
registerHandler(READ | PERSIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as above but registers this event handler as internal so that it
|
||||
* doesn't count towards the pending reader count for the IOLoop.
|
||||
*/
|
||||
void startConsumingInternal(
|
||||
TEventBase* eventBase, TNotificationQueue* queue) {
|
||||
init(eventBase, queue);
|
||||
registerInternalHandler(READ | PERSIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop consuming messages.
|
||||
*
|
||||
* startConsuming() may be called again to resume consumption of messages
|
||||
* at a later point in time.
|
||||
*/
|
||||
void stopConsuming();
|
||||
|
||||
/**
|
||||
* Get the TNotificationQueue that this consumer is currently consuming
|
||||
* messages from. Returns NULL if the consumer is not currently consuming
|
||||
* events from any queue.
|
||||
*/
|
||||
TNotificationQueue* getCurrentQueue() const {
|
||||
return queue_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a limit on how many messages this consumer will read each iteration
|
||||
* around the event loop.
|
||||
*
|
||||
* This helps rate-limit how much work the Consumer will do each event loop
|
||||
* iteration, to prevent it from starving other event handlers.
|
||||
*
|
||||
* A limit of 0 means no limit will be enforced. If unset, the limit
|
||||
* defaults to kDefaultMaxReadAtOnce (defined to 10 above).
|
||||
*/
|
||||
void setMaxReadAtOnce(uint32_t maxAtOnce) {
|
||||
maxReadAtOnce_ = maxAtOnce;
|
||||
}
|
||||
uint32_t getMaxReadAtOnce() const {
|
||||
return maxReadAtOnce_;
|
||||
}
|
||||
|
||||
private:
|
||||
void init(TEventBase* eventBase, TNotificationQueue* queue);
|
||||
|
||||
virtual void handlerReady(uint16_t events) THRIFT_NOEXCEPT;
|
||||
|
||||
TNotificationQueue* queue_;
|
||||
bool* destroyedFlagPtr_;
|
||||
uint32_t maxReadAtOnce_;
|
||||
};
|
||||
|
||||
enum class FdType {
|
||||
EVENTFD,
|
||||
PIPE
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new TNotificationQueue.
|
||||
*
|
||||
* If the maxSize parameter is specified, this sets the maximum queue size
|
||||
* that will be enforced by tryPutMessage(). (This size is advisory, and may
|
||||
* be exceeded if producers explicitly use putMessage() instead of
|
||||
* tryPutMessage().)
|
||||
*
|
||||
* The fdType parameter determines the type of file descriptor used
|
||||
* internally to signal message availability. The default (eventfd) is
|
||||
* preferable for performance and because it won't fail when the queue gets
|
||||
* too long. It is not available on on older and non-linux kernels, however.
|
||||
* In this case the code will fall back to using a pipe, the parameter is
|
||||
* mostly for testing purposes.
|
||||
*/
|
||||
explicit TNotificationQueue(uint32_t maxSize = 0,
|
||||
FdType fdType = FdType::EVENTFD)
|
||||
: spinlock_(),
|
||||
eventfd_(-1),
|
||||
pipeFds_({-1, -1}),
|
||||
advisoryMaxQueueSize_(maxSize),
|
||||
queue_() {
|
||||
if (fdType == FdType::EVENTFD) {
|
||||
eventfd_ = folly::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE);
|
||||
if (eventfd_ == -1) {
|
||||
if (errno == ENOSYS || errno == EINVAL) {
|
||||
// eventfd not availalble
|
||||
T_ERROR("failed to create eventfd for TNotificationQueue: %d, "
|
||||
"falling back to pipe mode", errno);
|
||||
fdType = FdType::PIPE;
|
||||
} else {
|
||||
// some other error
|
||||
throw TLibraryException("Failed to create eventfd for "
|
||||
"TNotificationQueue", errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fdType == FdType::PIPE) {
|
||||
if (pipe(pipeFds_)) {
|
||||
throw TLibraryException("Failed to create pipe for TNotificationQueue",
|
||||
errno);
|
||||
}
|
||||
try {
|
||||
// put both ends of the pipe into non-blocking mode
|
||||
if (fcntl(pipeFds_[0], F_SETFL, O_RDONLY | O_NONBLOCK) != 0) {
|
||||
throw TLibraryException("failed to put TNotificationQueue pipe read "
|
||||
"endpoint into non-blocking mode", errno);
|
||||
}
|
||||
if (fcntl(pipeFds_[1], F_SETFL, O_WRONLY | O_NONBLOCK) != 0) {
|
||||
throw TLibraryException("failed to put TNotificationQueue pipe write "
|
||||
"endpoint into non-blocking mode", errno);
|
||||
}
|
||||
} catch (...) {
|
||||
::close(pipeFds_[0]);
|
||||
::close(pipeFds_[1]);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~TNotificationQueue() {
|
||||
if (eventfd_ >= 0) {
|
||||
::close(eventfd_);
|
||||
eventfd_ = -1;
|
||||
}
|
||||
if (pipeFds_[0] >= 0) {
|
||||
::close(pipeFds_[0]);
|
||||
pipeFds_[0] = -1;
|
||||
}
|
||||
if (pipeFds_[1] >= 0) {
|
||||
::close(pipeFds_[1]);
|
||||
pipeFds_[1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the advisory maximum queue size.
|
||||
*
|
||||
* This maximum queue size affects calls to tryPutMessage(). Message
|
||||
* producers can still use the putMessage() call to unconditionally put a
|
||||
* message on the queue, ignoring the configured maximum queue size. This
|
||||
* can cause the queue size to exceed the configured maximum.
|
||||
*/
|
||||
void setMaxQueueSize(uint32_t max) {
|
||||
advisoryMaxQueueSize_ = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to put a message on the queue if the queue is not already full.
|
||||
*
|
||||
* If the queue is full, a TQueueFullException will be thrown. The
|
||||
* setMaxQueueSize() function controls the maximum queue size.
|
||||
*
|
||||
* This method may contend briefly on a spinlock if many threads are
|
||||
* concurrently accessing the queue, but for all intents and purposes it will
|
||||
* immediately place the message on the queue and return.
|
||||
*
|
||||
* tryPutMessage() may throw std::bad_alloc if memory allocation fails, and
|
||||
* may throw any other exception thrown by the MessageT move/copy
|
||||
* constructor.
|
||||
*/
|
||||
void tryPutMessage(MessageT&& message) {
|
||||
putMessageImpl(std::move(message), advisoryMaxQueueSize_);
|
||||
}
|
||||
void tryPutMessage(const MessageT& message) {
|
||||
putMessageImpl(message, advisoryMaxQueueSize_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally put a message on the queue.
|
||||
*
|
||||
* This method is like tryPutMessage(), but ignores the maximum queue size
|
||||
* and always puts the message on the queue, even if the maximum queue size
|
||||
* would be exceeded.
|
||||
*
|
||||
* putMessage() may throw std::bad_alloc if memory allocation fails, and may
|
||||
* throw any other exception thrown by the MessageT move/copy constructor.
|
||||
*/
|
||||
void putMessage(MessageT&& message) {
|
||||
putMessageImpl(std::move(message), 0);
|
||||
}
|
||||
void putMessage(const MessageT& message) {
|
||||
putMessageImpl(message, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put several messages on the queue.
|
||||
*/
|
||||
template<typename InputIteratorT>
|
||||
void putMessages(InputIteratorT first, InputIteratorT last) {
|
||||
typedef typename std::iterator_traits<InputIteratorT>::iterator_category
|
||||
IterCategory;
|
||||
putMessagesImpl(first, last, IterCategory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to immediately pull a message off of the queue, without blocking.
|
||||
*
|
||||
* If a message is immediately available, the result parameter will be
|
||||
* updated to contain the message contents and true will be returned.
|
||||
*
|
||||
* If no message is available, false will be returned and result will be left
|
||||
* unmodified.
|
||||
*/
|
||||
bool tryConsume(MessageT& result) {
|
||||
if (!tryConsumeEvent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
facebook::SpinLockHolder guard(&spinlock_);
|
||||
result = std::move(queue_.front());
|
||||
queue_.pop_front();
|
||||
} catch (...) {
|
||||
// Handle an exception if the assignment operator happens to throw.
|
||||
// We consumed an event but weren't able to pop the message off the
|
||||
// queue. Signal the event again since the message is still in the
|
||||
// queue.
|
||||
signalEvent(1);
|
||||
throw;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Forbidden copy constructor and assignment operator
|
||||
TNotificationQueue(TNotificationQueue const &) = delete;
|
||||
TNotificationQueue& operator=(TNotificationQueue const &) = delete;
|
||||
|
||||
inline void checkQueueSize(size_t maxSize) const {
|
||||
assert(spinlock_.IsHeld());
|
||||
if (maxSize > 0 && queue_.size() >= maxSize) {
|
||||
throw TQueueFullException();
|
||||
}
|
||||
}
|
||||
|
||||
inline void signalEvent(uint64_t numAdded = 1) const {
|
||||
static const uint8_t kPipeMessage[] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
||||
};
|
||||
|
||||
ssize_t bytes_written = 0;
|
||||
ssize_t bytes_expected = 0;
|
||||
if (eventfd_ >= 0) {
|
||||
bytes_expected = static_cast<ssize_t>(sizeof(numAdded));
|
||||
bytes_written = ::write(eventfd_, &numAdded, sizeof(numAdded));
|
||||
} else {
|
||||
// pipe semantics, add one message for each numAdded
|
||||
bytes_expected = numAdded;
|
||||
do {
|
||||
size_t messageSize = std::min(numAdded, sizeof(kPipeMessage));
|
||||
ssize_t rc = ::write(pipeFds_[1], kPipeMessage, messageSize);
|
||||
if (rc < 0) {
|
||||
// TODO: if the pipe is full, write will fail with EAGAIN.
|
||||
// See task #1044651 for how this could be handled
|
||||
break;
|
||||
}
|
||||
numAdded -= rc;
|
||||
bytes_written += rc;
|
||||
} while (numAdded > 0);
|
||||
}
|
||||
if (bytes_written != bytes_expected) {
|
||||
throw TLibraryException("failed to signal TNotificationQueue after "
|
||||
"write", errno);
|
||||
}
|
||||
}
|
||||
|
||||
bool tryConsumeEvent() {
|
||||
uint64_t value = 0;
|
||||
ssize_t rc = -1;
|
||||
if (eventfd_ >= 0) {
|
||||
rc = ::read(eventfd_, &value, sizeof(value));
|
||||
} else {
|
||||
uint8_t value8;
|
||||
rc = ::read(pipeFds_[0], &value8, sizeof(value8));
|
||||
value = value8;
|
||||
}
|
||||
if (rc < 0) {
|
||||
// EAGAIN should pretty much be the only error we can ever get.
|
||||
// This means someone else already processed the only available message.
|
||||
assert(errno == EAGAIN);
|
||||
return false;
|
||||
}
|
||||
assert(value == 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void putMessageImpl(MessageT&& message, size_t maxSize) {
|
||||
{
|
||||
facebook::SpinLockHolder guard(&spinlock_);
|
||||
checkQueueSize(maxSize);
|
||||
queue_.push_back(std::move(message));
|
||||
}
|
||||
signalEvent();
|
||||
}
|
||||
|
||||
void putMessageImpl(const MessageT& message, size_t maxSize) {
|
||||
{
|
||||
facebook::SpinLockHolder guard(&spinlock_);
|
||||
checkQueueSize(maxSize);
|
||||
queue_.push_back(message);
|
||||
}
|
||||
signalEvent();
|
||||
}
|
||||
|
||||
template<typename InputIteratorT>
|
||||
void putMessagesImpl(InputIteratorT first, InputIteratorT last,
|
||||
std::input_iterator_tag) {
|
||||
uint64_t numAdded = 0;
|
||||
{
|
||||
facebook::SpinLockHolder guard(&spinlock_);
|
||||
while (first != last) {
|
||||
queue_.push_back(*first);
|
||||
++first;
|
||||
++numAdded;
|
||||
}
|
||||
}
|
||||
signalEvent(numAdded);
|
||||
}
|
||||
|
||||
template<typename InputIteratorT>
|
||||
void putMessagesImpl(InputIteratorT first, InputIteratorT last,
|
||||
std::forward_iterator_tag) {
|
||||
uint64_t numAdded = std::distance(first, last);
|
||||
{
|
||||
facebook::SpinLockHolder guard(&spinlock_);
|
||||
queue_.insert(queue_.end(), first, last);
|
||||
}
|
||||
signalEvent(numAdded);
|
||||
}
|
||||
|
||||
facebook::SpinLock spinlock_;
|
||||
int eventfd_;
|
||||
int pipeFds_[2]; // to fallback to on older/non-linux systems
|
||||
uint32_t advisoryMaxQueueSize_;
|
||||
std::deque<MessageT> queue_;
|
||||
};
|
||||
|
||||
template<typename MessageT>
|
||||
TNotificationQueue<MessageT>::Consumer::~Consumer() {
|
||||
// If we are in the middle of a call to handlerReady(), destroyedFlagPtr_
|
||||
// will be non-NULL. Mark the value that it points to, so that
|
||||
// handlerReady() will know the callback is destroyed, and that it cannot
|
||||
// access any member variables anymore.
|
||||
if (destroyedFlagPtr_) {
|
||||
*destroyedFlagPtr_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename MessageT>
|
||||
void TNotificationQueue<MessageT>::Consumer::handlerReady(uint16_t events)
|
||||
THRIFT_NOEXCEPT {
|
||||
uint32_t numProcessed = 0;
|
||||
while (true) {
|
||||
// Try to decrement the eventfd.
|
||||
//
|
||||
// We decrement the eventfd before checking the queue, and only pop a
|
||||
// message off the queue if we read from the eventfd.
|
||||
//
|
||||
// Reading the eventfd first allows us to not have to hold the spinlock
|
||||
// while accessing the eventfd. If we popped from the queue first, we
|
||||
// would have to hold the lock while reading from or writing to the
|
||||
// eventfd. (Multiple consumers may be woken up from a single eventfd
|
||||
// notification. If we popped from the queue first, we could end up
|
||||
// popping a message from the queue before the eventfd has been notified by
|
||||
// the producer, unless the consumer and producer both held the spinlock
|
||||
// around the entire operation.)
|
||||
if (!queue_->tryConsumeEvent()) {
|
||||
// no message available right now
|
||||
return;
|
||||
}
|
||||
|
||||
// Now pop the message off of the queue.
|
||||
// We successfully consumed the eventfd notification.
|
||||
// There should be a message available for us to consume.
|
||||
//
|
||||
// We have to manually acquire and release the spinlock here, rather than
|
||||
// using SpinLockHolder since the MessageT has to be constructed while
|
||||
// holding the spinlock and available after we release it. SpinLockHolder
|
||||
// unfortunately doesn't provide a release() method. (We can't construct
|
||||
// MessageT first since we have no guarantee that MessageT has a default
|
||||
// constructor.
|
||||
queue_->spinlock_.Lock();
|
||||
bool locked = true;
|
||||
|
||||
try {
|
||||
// The eventfd is incremented once for every message, and only
|
||||
// decremented when a message is popped off. There should always be a
|
||||
// message here to read.
|
||||
CHECK(!queue_->queue_.empty());
|
||||
|
||||
// Pull a message off the queue.
|
||||
MessageT msg(std::move(queue_->queue_.front()));
|
||||
queue_->queue_.pop_front();
|
||||
|
||||
// Check to see if the queue is empty now.
|
||||
// We use this as an optimization to see if we should bother trying to
|
||||
// loop again and read another message after invoking this callback.
|
||||
bool wasEmpty = queue_->queue_.empty();
|
||||
|
||||
// Now unlock the spinlock before we invoke the callback.
|
||||
queue_->spinlock_.Unlock();
|
||||
locked = false;
|
||||
|
||||
// Call the callback
|
||||
bool callbackDestroyed = false;
|
||||
CHECK(destroyedFlagPtr_ == NULL);
|
||||
destroyedFlagPtr_ = &callbackDestroyed;
|
||||
messageAvailable(std::move(msg));
|
||||
|
||||
// If the callback was destroyed before it returned, we are done
|
||||
if (callbackDestroyed) {
|
||||
return;
|
||||
}
|
||||
destroyedFlagPtr_ = NULL;
|
||||
|
||||
// If the callback is no longer installed, we are done.
|
||||
if (queue_ == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have hit maxReadAtOnce_, we are done.
|
||||
++numProcessed;
|
||||
if (maxReadAtOnce_ > 0 && numProcessed >= maxReadAtOnce_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the queue was empty before we invoked the callback, it's probable
|
||||
// that it is still empty now. Just go ahead and return, rather than
|
||||
// looping again and trying to re-read from the eventfd. (If a new
|
||||
// message had in fact arrived while we were invoking the callback, we
|
||||
// will simply be woken up the next time around the event loop and will
|
||||
// process the message then.)
|
||||
if (wasEmpty) {
|
||||
return;
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
// This catch block is really just to handle the case where the MessageT
|
||||
// constructor throws. The messageAvailable() callback itself is
|
||||
// declared as noexcept and should never throw.
|
||||
//
|
||||
// If the MessageT constructor does throw we try to handle it as best as
|
||||
// we can, but we can't work miracles. We will just ignore the error for
|
||||
// now and return. The next time around the event loop we will end up
|
||||
// trying to read the message again. If MessageT continues to throw we
|
||||
// will never make forward progress and will keep trying each time around
|
||||
// the event loop.
|
||||
if (locked) {
|
||||
// Unlock the spinlock.
|
||||
queue_->spinlock_.Unlock();
|
||||
|
||||
// Push a notification back on the eventfd since we didn't actually
|
||||
// read the message off of the queue.
|
||||
queue_->signalEvent(1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename MessageT>
|
||||
void TNotificationQueue<MessageT>::Consumer::init(
|
||||
TEventBase* eventBase,
|
||||
TNotificationQueue* queue) {
|
||||
assert(eventBase->isInEventBaseThread());
|
||||
assert(queue_ == NULL);
|
||||
assert(!isHandlerRegistered());
|
||||
|
||||
queue_ = queue;
|
||||
if (queue_->eventfd_ >= 0) {
|
||||
initHandler(eventBase, queue_->eventfd_);
|
||||
} else {
|
||||
initHandler(eventBase, queue_->pipeFds_[0]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename MessageT>
|
||||
void TNotificationQueue<MessageT>::Consumer::stopConsuming() {
|
||||
if (queue_ == NULL) {
|
||||
assert(!isHandlerRegistered());
|
||||
return;
|
||||
}
|
||||
|
||||
assert(isHandlerRegistered());
|
||||
unregisterHandler();
|
||||
detachEventBase();
|
||||
queue_ = NULL;
|
||||
}
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TNOTIFICATIONQUEUE_H_
|
|
@ -0,0 +1,70 @@
|
|||
#ifndef _THRIFT_TQUEUINGASYNCPROCESSOR_H_
|
||||
#define _THRIFT_TQUEUINGASYNCPROCESSOR_H_ 1
|
||||
|
||||
#include <tr1/functional>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "thrift/lib/cpp/TProcessor.h"
|
||||
#include "thrift/lib/cpp/async/TAsyncProcessor.h"
|
||||
#include "thrift/lib/cpp/async/TEventTask.h"
|
||||
#include "thrift/lib/cpp/concurrency/Exception.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
/**
|
||||
* Adapter to allow a TProcessor to be used as a TAsyncProcessor.
|
||||
*
|
||||
* Note: this is not intended for use outside of TEventConnection since the
|
||||
* callback mechanism used in TEventTask will invoke handleAsyncTaskComplete()
|
||||
* regardless of what is passed in as the cob.
|
||||
*
|
||||
* Uses a per-server task queue for all calls.
|
||||
*/
|
||||
class TQueuingAsyncProcessor : public TAsyncProcessor {
|
||||
public:
|
||||
TQueuingAsyncProcessor(
|
||||
boost::shared_ptr<apache::thrift::server::TProcessor> processor,
|
||||
boost::shared_ptr<apache::thrift::concurrency::ThreadManager> threadManager,
|
||||
int64_t taskExpireTime,
|
||||
TEventConnection* connection)
|
||||
: processor_(processor)
|
||||
, threadManager_(threadManager)
|
||||
, taskExpireTime_(taskExpireTime)
|
||||
, connection_(connection)
|
||||
{}
|
||||
|
||||
virtual void process(
|
||||
std::tr1::function<void(bool success)> cob,
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol> in,
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol> out,
|
||||
TConnectionContext* context) {
|
||||
|
||||
boost::shared_ptr<apache::thrift::concurrency::Runnable> task =
|
||||
boost::shared_ptr<apache::thrift::concurrency::Runnable>(
|
||||
new TEventTask(connection_));
|
||||
|
||||
try {
|
||||
threadManager_->add(task, 0LL, taskExpireTime_);
|
||||
} catch (apache::thrift::concurrency::IllegalStateException & ise) {
|
||||
T_ERROR("IllegalStateException: TQueuingAsyncProcessor::process() %s",
|
||||
ise.what());
|
||||
// no task will be making a callback
|
||||
return cob(false);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
boost::shared_ptr<apache::thrift::server::TProcessor> processor_;
|
||||
|
||||
/// For processing via thread pool
|
||||
boost::shared_ptr<apache::thrift::concurrency::ThreadManager> threadManager_;
|
||||
|
||||
/// Time in milliseconds before an unperformed task expires (0 == infinite).
|
||||
int64_t taskExpireTime_;
|
||||
|
||||
/// The worker that started us
|
||||
TEventConnection* connection_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // #ifndef _THRIFT_TQUEUINGASYNCPROCESSOR_H_
|
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TSTREAMASYNCCHANNEL_H_
|
||||
#define THRIFT_ASYNC_TSTREAMASYNCCHANNEL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TAsyncEventChannel.h"
|
||||
#include "thrift/lib/cpp/async/TAsyncTransport.h"
|
||||
#include "thrift/lib/cpp/async/TAsyncTimeout.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
class TAsyncTransport;
|
||||
|
||||
template <class Subclass_>
|
||||
class TAsyncChannelWriteRequestBase {
|
||||
public:
|
||||
typedef std::tr1::function<void()> VoidCallback;
|
||||
|
||||
TAsyncChannelWriteRequestBase(const VoidCallback& callback,
|
||||
const VoidCallback& errorCallback,
|
||||
transport::TMemoryBuffer* message)
|
||||
: buffer_(message),
|
||||
next_(NULL),
|
||||
callback_(callback),
|
||||
errorCallback_(errorCallback) {
|
||||
|
||||
// The WriteRequest's buffer consumes all of the data in message,
|
||||
// so we don't attempt to resend data; yet is also an observer
|
||||
// which prevents consumed data from being overwritten while it's pending
|
||||
// for the transport
|
||||
uint32_t len = message->available_read();
|
||||
message->borrow(NULL, &len);
|
||||
message->consume(len);
|
||||
}
|
||||
|
||||
virtual ~TAsyncChannelWriteRequestBase() {
|
||||
}
|
||||
|
||||
void setNext(Subclass_* next) {
|
||||
assert(next_ == NULL);
|
||||
next_ = next;
|
||||
}
|
||||
|
||||
Subclass_* getNext() const {
|
||||
return next_;
|
||||
}
|
||||
|
||||
protected:
|
||||
apache::thrift::transport::TMemoryBuffer buffer_;
|
||||
|
||||
void invokeCallback() {
|
||||
// unlink the buffer before invoking the callback, since we are
|
||||
// now done with it. Not strictly required but faster.
|
||||
buffer_.unlink();
|
||||
callback_();
|
||||
}
|
||||
|
||||
void invokeErrorCallback() {
|
||||
// unlink the buffer before invoking the callback, since we are
|
||||
// now done with it. Not strictly required but faster.
|
||||
buffer_.unlink();
|
||||
errorCallback_();
|
||||
}
|
||||
|
||||
private:
|
||||
TAsyncChannelWriteRequestBase();
|
||||
|
||||
Subclass_* next_;
|
||||
|
||||
VoidCallback callback_;
|
||||
VoidCallback errorCallback_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* TStreamAsyncChannel is a helper class for channel implementations that use
|
||||
* TAsyncTransport underneath.
|
||||
*
|
||||
* TStreamAsyncChannel provides the basic functionality for implementing a
|
||||
* message-based asynchronous channel on top of a streaming TAsyncTransport.
|
||||
*
|
||||
* It requires two template arguments that control how the stream is broken up
|
||||
* into messagess:
|
||||
*
|
||||
* WriteRequest_:
|
||||
*
|
||||
* This template parameter controls how messages are written to the
|
||||
* underlying stream. It must implement the following methods:
|
||||
*
|
||||
* - WriteRequest_(const VoidCallback& callback,
|
||||
* const VoidCallback& errorCallback,
|
||||
* transport::TMemoryBuffer* message);
|
||||
*
|
||||
* The WriteRequest_ constructor accepts the success and error callbacks,
|
||||
* and the TMemoryBuffer containing the data to send. The WriteRequest_
|
||||
* may consume data from the message, but does not own the TMemoryBuffer
|
||||
* (i.e., it should not delete the TMemoryBuffer.)
|
||||
*
|
||||
* - void setNext(WriteRequest_* next);
|
||||
* - WriteRequest_* getNext() const;
|
||||
*
|
||||
* These two methods support chaining together a list of WriteRequest_
|
||||
* objects. This is used when multiple write requests are pending on the
|
||||
* channel.
|
||||
*
|
||||
* - void write(TAsyncTransport* transport,
|
||||
* TAsyncTransport::WriteCallback* callback) THRIFT_NOEXCEPT;
|
||||
*
|
||||
* This method will be called to schedule the write. The WriteRequest_
|
||||
* should invoke the transport's write() or writev() method with the data
|
||||
* to send, and set the specified callback as the transport callback.
|
||||
*
|
||||
* Note that this API requires the WriteRequest_ to write the entire
|
||||
* message with a single write() or writev() call. This allows the code
|
||||
* to let the TAsyncTransport perform the write queuing when multiple
|
||||
* messages are pending. (If needed we could rewrite this API in the
|
||||
* future to relax this restriction.)
|
||||
*
|
||||
* - void writeSuccess() THRIFT_NOEXCEPT;
|
||||
* - void writeError(size_t bytesWritten,
|
||||
* const TTransportException& ex) THRIFT_NOEXCEPT;
|
||||
*
|
||||
* Either writeSuccess() or writeError() will be invoked once the message
|
||||
* write has completed.
|
||||
*
|
||||
* ReadState_:
|
||||
*
|
||||
* This template parameter controls how the incoming stream is broken up into
|
||||
* individual messages. It must implement the following methods:
|
||||
*
|
||||
* - ReadState_();
|
||||
*
|
||||
* The ReadState_ constructor takes no arguments.
|
||||
*
|
||||
* - void setCallbackBuffer(transport::TMemoryBuffer* buffer);
|
||||
*
|
||||
* When a new read is started, setCallbackBuffer() is called to set the
|
||||
* buffer into which the message data should be placed.
|
||||
*
|
||||
* - void unsetCallbackBuffer();
|
||||
*
|
||||
* unsetCallbackBuffer() is called to clear the callback buffer when after
|
||||
* a full message has been read.
|
||||
*
|
||||
* - bool hasReadAheadData();
|
||||
*
|
||||
* Some ReadState_ implementations may perform read-ahead, and read past
|
||||
* the end of the message when reading from the underlying transport.
|
||||
* hasReadAheadData() is called when a new read starts, to see if the
|
||||
* ReadState_ has pending data for a new message that has already been read
|
||||
* from the transport.
|
||||
*
|
||||
* If hasReadAheadData() returns true, readDataAvailable(0) will be called
|
||||
* immediately, rather than waiting for new data from the transport.
|
||||
*
|
||||
* - bool hasPartialMessage();
|
||||
*
|
||||
* When EOF is read from the underlying transport, hasPartialMessage() is
|
||||
* called to see if the EOF should be treated as an error or a normal
|
||||
* close. (It is an error if hasPartialMessage() returns true.)
|
||||
*
|
||||
* - void getReadBuffer(void** bufReturn, size_t* lenReturn);
|
||||
*
|
||||
* When data becomes available on the underlying transport, getReadBuffer()
|
||||
* is called to get the buffer where the data should be placed.
|
||||
*
|
||||
* - bool readDataAvailable(size_t len);
|
||||
*
|
||||
* readDataAvailable() is called when new data has been read from the
|
||||
* underlying transport. The data will have been placed in the buffer
|
||||
* returned by the previous getReadBuffer() call.
|
||||
*/
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
class TStreamAsyncChannel : public TAsyncEventChannel,
|
||||
protected TAsyncTransport::ReadCallback,
|
||||
protected TAsyncTransport::WriteCallback,
|
||||
protected TAsyncTimeout {
|
||||
public:
|
||||
TStreamAsyncChannel(const boost::shared_ptr<TAsyncTransport>& transport);
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TStreamAsyncChannel>.
|
||||
*
|
||||
* This passes in the correct destructor object, since TStreamAsyncChannel's
|
||||
* destructor is protected and cannot be invoked directly.
|
||||
*/
|
||||
static boost::shared_ptr<TStreamAsyncChannel> newChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport) {
|
||||
return boost::shared_ptr<TStreamAsyncChannel>(
|
||||
new TStreamAsyncChannel(transport), Destructor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the channel.
|
||||
*
|
||||
* destroy() must be called to destroy the channel. The normal destructor
|
||||
* is private, and should not be invoked directly. This prevents callers
|
||||
* from deleting a TStreamAsyncChannel while it is invoking a callback.
|
||||
*/
|
||||
virtual void destroy();
|
||||
|
||||
// Methods inherited from TAsyncEventChannel
|
||||
virtual bool readable() const;
|
||||
virtual bool good() const;
|
||||
virtual bool error() const;
|
||||
virtual bool timedOut() const;
|
||||
|
||||
/**
|
||||
* Send a message to the channel; note that "errorCob" will be called
|
||||
* after a partial write as well as other errors. We will call "errorCob"
|
||||
* immediately (before return) if the channel is unusable for some reason,
|
||||
* and "cob" immediately if we're able to perform the write without delay.
|
||||
*/
|
||||
virtual void sendMessage(const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
apache::thrift::transport::TMemoryBuffer* message);
|
||||
|
||||
/**
|
||||
* Receive a message from the channel; note that "errorCob" will be called
|
||||
* after a partial read as well as other errors. We will call "errorCob"
|
||||
* immediately (before return) if the channel is unusable for some reason,
|
||||
* and "cob" immediately if we're able to perform the read without delay.
|
||||
*
|
||||
* Note that an EOF is considered normal, so "cob" will be called although
|
||||
* "good()" will be false.
|
||||
*/
|
||||
virtual void recvMessage(const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
apache::thrift::transport::TMemoryBuffer* message);
|
||||
|
||||
/**
|
||||
* Send a message to the channel and receive the response; note that the
|
||||
* "errorCob: will be called after a write error and no receive is attempted.
|
||||
* Also, a partial write or read will result in errorCob being called.
|
||||
* We call "errorCob" before return if the channel is unusable for some
|
||||
* reason. It is conceivable that "cob" will be called before return if data
|
||||
* is somehow available in the channel when a read is first attempted.
|
||||
*/
|
||||
virtual void sendAndRecvMessage(
|
||||
const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
transport::TMemoryBuffer* sendBuf,
|
||||
transport::TMemoryBuffer* recvBuf);
|
||||
|
||||
/**
|
||||
* Close this channel.
|
||||
*
|
||||
* This gracefully closes the channel, waiting for all pending send
|
||||
* requests to complete before actually closing the underlying transport.
|
||||
*
|
||||
* If a recvMessage() call is pending, it will be immediately failed.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Close the channel immediately.
|
||||
*
|
||||
* This closes the channel immediately, dropping any outstanding messages
|
||||
* waiting to be sent.
|
||||
*
|
||||
* If a recvMessage() call is pending, it will be immediately failed.
|
||||
*/
|
||||
void closeNow();
|
||||
|
||||
/**
|
||||
* Attach the channel to a TEventBase.
|
||||
*
|
||||
* This may only be called if the channel is not currently attached to a
|
||||
* TEventBase (by an earlier call to detachEventBase()).
|
||||
*
|
||||
* This method must be invoked in the TEventBase's thread.
|
||||
*/
|
||||
void attachEventBase(TEventBase* eventBase);
|
||||
|
||||
/**
|
||||
* Detach the channel from its TEventBase.
|
||||
*
|
||||
* This may only be called when the channel is idle and has no reads or
|
||||
* writes pending. Once detached, the channel may not be used again until it
|
||||
* is re-attached to a TEventBase by calling attachEventBase().
|
||||
*
|
||||
* This method must be called from the current TEventBase's thread.
|
||||
*/
|
||||
void detachEventBase();
|
||||
|
||||
/**
|
||||
* Get the TEventBase used by this channel.
|
||||
*/
|
||||
TEventBase* getEventBase() const;
|
||||
|
||||
/**
|
||||
* Set the timeout for receiving messages.
|
||||
*
|
||||
* When set to a non-zero value, the entire message must be received within
|
||||
* the specified number of milliseconds, or the receive will fail and the
|
||||
* channel will be closed.
|
||||
*
|
||||
* If setRecvTimeout() is invoked while a recvMessage() call is currently in
|
||||
* progress, the timeout will be restarted using the new value.
|
||||
*/
|
||||
void setRecvTimeout(uint32_t milliseconds);
|
||||
|
||||
/**
|
||||
* Get the receive timeout.
|
||||
*
|
||||
* @return Returns the current receive timeout, in milliseconds. A return
|
||||
* value of 0 indicates that no timeout is set.
|
||||
*/
|
||||
uint32_t getRecvTimeout() const {
|
||||
return recvTimeout_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the TAsyncTransport used by this channel.
|
||||
*/
|
||||
virtual boost::shared_ptr<TAsyncTransport> getTransport() {
|
||||
return transport_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this channel is idle (i.e., has no outstanding reads or
|
||||
* writes).
|
||||
*/
|
||||
bool isIdle() const {
|
||||
return (writeReqHead_ == NULL) && (!readCallback_) &&
|
||||
!transport_->connecting();
|
||||
}
|
||||
|
||||
protected:
|
||||
struct ReadQueueEntry {
|
||||
ReadQueueEntry(const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
apache::thrift::transport::TMemoryBuffer* message) {
|
||||
readCallback = cob;
|
||||
readErrorCallback = errorCob;
|
||||
readBuffer = message;
|
||||
}
|
||||
VoidCallback readCallback;
|
||||
VoidCallback readErrorCallback;
|
||||
transport::TMemoryBuffer *readBuffer;
|
||||
int64_t startTime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Users of TStreamAsyncChannel must never delete it directly. Instead,
|
||||
* invoke destroy().
|
||||
*/
|
||||
virtual ~TStreamAsyncChannel() {}
|
||||
|
||||
// callbacks from TAsyncTransport
|
||||
void getReadBuffer(void** bufReturn, size_t* lenReturn);
|
||||
void readDataAvailable(size_t len) THRIFT_NOEXCEPT;
|
||||
void readEOF() THRIFT_NOEXCEPT;
|
||||
void readError(const transport::TTransportException& ex) THRIFT_NOEXCEPT;
|
||||
|
||||
void writeSuccess() THRIFT_NOEXCEPT;
|
||||
void writeError(size_t bytesWritten,
|
||||
const transport::TTransportException& ex) THRIFT_NOEXCEPT;
|
||||
|
||||
// callback from TAsyncTimeout
|
||||
void timeoutExpired() THRIFT_NOEXCEPT;
|
||||
|
||||
bool invokeReadDataAvailable(size_t len) THRIFT_NOEXCEPT;
|
||||
void processReadEOF() THRIFT_NOEXCEPT;
|
||||
void invokeReadCallback(VoidCallback cb,
|
||||
char const* callbackName) THRIFT_NOEXCEPT;
|
||||
|
||||
void pushWriteRequest(WriteRequest_* req) {
|
||||
if (writeReqTail_ == NULL) {
|
||||
assert(writeReqHead_ == NULL);
|
||||
writeReqHead_ = req;
|
||||
} else {
|
||||
writeReqTail_->setNext(req);
|
||||
}
|
||||
writeReqTail_ = req;
|
||||
}
|
||||
|
||||
WriteRequest_* popWriteRequest() {
|
||||
assert(writeReqHead_ != NULL);
|
||||
|
||||
WriteRequest_* req = writeReqHead_;
|
||||
writeReqHead_ = req->getNext();
|
||||
if (writeReqHead_ == NULL) {
|
||||
assert(writeReqTail_ == req);
|
||||
writeReqTail_ = NULL;
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
void failAllReads();
|
||||
|
||||
boost::shared_ptr<TAsyncTransport> transport_;
|
||||
WriteRequest_* writeReqHead_;
|
||||
WriteRequest_* writeReqTail_;
|
||||
|
||||
ReadState_ readState_;
|
||||
VoidCallback readCallback_;
|
||||
VoidCallback readErrorCallback_;
|
||||
std::list<ReadQueueEntry> readCallbackQ_;
|
||||
|
||||
uint32_t recvTimeout_;
|
||||
// true if a timeout has occurred
|
||||
bool timedOut_;
|
||||
|
||||
private:
|
||||
// Forbidden copy constructor and assignment opererator
|
||||
TStreamAsyncChannel(TStreamAsyncChannel const &);
|
||||
TStreamAsyncChannel& operator=(TStreamAsyncChannel const &);
|
||||
};
|
||||
|
||||
class TStreamAsyncChannelFactory {
|
||||
public:
|
||||
virtual ~TStreamAsyncChannelFactory() {}
|
||||
|
||||
virtual boost::shared_ptr<TAsyncEventChannel> newChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport) = 0;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TSTREAMASYNCCHANNEL_H_
|
|
@ -0,0 +1,488 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TSTREAMASYNCCHANNEL_TCC_
|
||||
#define THRIFT_ASYNC_TSTREAMASYNCCHANNEL_TCC_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TStreamAsyncChannel.h"
|
||||
#include "thrift/lib/cpp/transport/TSocketAddress.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
TStreamAsyncChannel<WriteRequest_, ReadState_>::TStreamAsyncChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport)
|
||||
: TAsyncTimeout(transport->getEventBase())
|
||||
, transport_(transport)
|
||||
, writeReqHead_(NULL)
|
||||
, writeReqTail_(NULL)
|
||||
, readState_()
|
||||
, readCallback_()
|
||||
, readErrorCallback_()
|
||||
, recvTimeout_(0)
|
||||
, timedOut_(false) {
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::destroy() {
|
||||
// When destroy is called, close the channel immediately
|
||||
closeNow();
|
||||
|
||||
// Then call TDelayedDestruction::destroy() to take care of
|
||||
// whether or not we need immediate or delayed destruction
|
||||
TDelayedDestruction::destroy();
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
bool TStreamAsyncChannel<WriteRequest_, ReadState_>::readable() const {
|
||||
return transport_->readable();
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
bool TStreamAsyncChannel<WriteRequest_, ReadState_>::good() const {
|
||||
return transport_->good();
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
bool TStreamAsyncChannel<WriteRequest_, ReadState_>::error() const {
|
||||
return (timedOut_ || transport_->error());
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
bool TStreamAsyncChannel<WriteRequest_, ReadState_>::timedOut() const {
|
||||
return timedOut_;
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::sendMessage(
|
||||
const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
transport::TMemoryBuffer* message) {
|
||||
assert(message);
|
||||
DestructorGuard dg(this);
|
||||
|
||||
if (!good()) {
|
||||
T_DEBUG_T("sendMessage: transport went bad, bailing out.");
|
||||
return errorCob();
|
||||
}
|
||||
|
||||
if (message->available_read() == 0) {
|
||||
T_ERROR("sendMessage: buffer is empty");
|
||||
return errorCob();
|
||||
}
|
||||
|
||||
WriteRequest_* writeReq;
|
||||
try {
|
||||
writeReq = new WriteRequest_(cob, errorCob, message, this);
|
||||
} catch (const std::exception& ex) {
|
||||
T_ERROR("sendMessage: failed to allocate new write request object");
|
||||
errorCob();
|
||||
return;
|
||||
}
|
||||
|
||||
pushWriteRequest(writeReq);
|
||||
writeReq->write(transport_.get(), this);
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::recvMessage(
|
||||
const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
transport::TMemoryBuffer* message) {
|
||||
assert(message);
|
||||
DestructorGuard dg(this);
|
||||
|
||||
if (!good()) {
|
||||
T_DEBUG_T("recvMessage: transport went bad, bailing out.");
|
||||
return errorCob();
|
||||
}
|
||||
|
||||
if (message->available_read() != 0) {
|
||||
T_ERROR("recvMessage: buffer is not empty.");
|
||||
return errorCob();
|
||||
}
|
||||
|
||||
if (readCallbackQ_.empty() && readCallback_ == NULL) {
|
||||
readState_.setCallbackBuffer(message);
|
||||
readCallback_ = cob;
|
||||
readErrorCallback_ = errorCob;
|
||||
} else {
|
||||
readCallbackQ_.push_back(ReadQueueEntry(cob, errorCob, message));
|
||||
return;
|
||||
}
|
||||
|
||||
// Some ReadState implementations perform read-ahead,
|
||||
// and they may already have data waiting to be processed.
|
||||
// If so, we need to invoke readDataAvailable() immediately, rather than
|
||||
// waiting for new data from the transport.
|
||||
if (readState_.hasReadAheadData()) {
|
||||
if (invokeReadDataAvailable(0)) {
|
||||
// We already invoked the callback
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// start the read timeout
|
||||
if (recvTimeout_ > 0) {
|
||||
scheduleTimeout(recvTimeout_);
|
||||
}
|
||||
|
||||
// start reading from the transport
|
||||
// Note that setReadCallback() may invoke our read callback methods
|
||||
// immediately, so the read may complete before setReadCallback() returns.
|
||||
transport_->setReadCallback(this);
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::sendAndRecvMessage(
|
||||
const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
transport::TMemoryBuffer* sendBuf,
|
||||
transport::TMemoryBuffer* recvBuf) {
|
||||
// TODO: it would be better to perform this bind once, rather than
|
||||
// each time sendAndRecvMessage() is called.
|
||||
const VoidCallback& send_done =
|
||||
std::tr1::bind(&TStreamAsyncChannel::recvMessage, this, cob, errorCob,
|
||||
recvBuf);
|
||||
|
||||
return sendMessage(send_done, errorCob, sendBuf);
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::close() {
|
||||
DestructorGuard dg(this); // transport::close can invoke callbacks
|
||||
|
||||
transport_->setReadCallback(NULL);
|
||||
transport_->close();
|
||||
|
||||
if (readCallback_) {
|
||||
processReadEOF();
|
||||
}
|
||||
|
||||
// no need to free the write-queue here. The underlying transport will
|
||||
// drain the writes first
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::closeNow() {
|
||||
DestructorGuard dg(this); // transport::closeNow can invoke callbacks
|
||||
|
||||
transport_->setReadCallback(NULL);
|
||||
transport_->closeNow();
|
||||
|
||||
if (readCallback_) {
|
||||
processReadEOF();
|
||||
}
|
||||
|
||||
// no need to free the write-queue here. The underlying transport will
|
||||
// fail pending writes first
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::attachEventBase(
|
||||
TEventBase* eventBase) {
|
||||
TAsyncTimeout::attachEventBase(eventBase);
|
||||
transport_->attachEventBase(eventBase);
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::detachEventBase() {
|
||||
// detachEventBase() may not be called while in the middle of reading or
|
||||
// writing a message. Make sure there are no read callbacks
|
||||
assert(!readCallback_ && readCallbackQ_.empty());
|
||||
// Even though readCallback_ is unset, the read timeout might still be
|
||||
// installed. This happens when detachEventBase() is invoked by the
|
||||
// recvMessage() callback, because invokeReadDataAvailable() optimizes and
|
||||
// leaves the timeout and transport read callback installed while invoking
|
||||
// the recvMessage() callback. Make sure we cancel the read timeout before
|
||||
// detaching from the event base.
|
||||
if (transport_->getReadCallback() == this) {
|
||||
cancelTimeout();
|
||||
transport_->setReadCallback(NULL);
|
||||
}
|
||||
|
||||
TAsyncTimeout::detachEventBase();
|
||||
transport_->detachEventBase();
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
TEventBase*
|
||||
TStreamAsyncChannel<WriteRequest_, ReadState_>::getEventBase() const {
|
||||
return transport_->getEventBase();
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::setRecvTimeout(
|
||||
uint32_t milliseconds) {
|
||||
recvTimeout_ = milliseconds;
|
||||
// If we are currently reading, update the timeout
|
||||
if (transport_->getReadCallback() == this) {
|
||||
if (milliseconds > 0) {
|
||||
scheduleTimeout(milliseconds);
|
||||
} else {
|
||||
cancelTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::getReadBuffer(
|
||||
void** bufReturn, size_t* lenReturn) {
|
||||
readState_.getReadBuffer(bufReturn, lenReturn);
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::readDataAvailable(
|
||||
size_t len) THRIFT_NOEXCEPT {
|
||||
invokeReadDataAvailable(len);
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::readEOF() THRIFT_NOEXCEPT {
|
||||
// readCallback_ may be NULL if readEOF() is invoked while the read callback
|
||||
// is already running inside invokeReadDataAvailable(), since
|
||||
// invokeReadDataAvailable() leaves the transport read callback installed
|
||||
// while calling the channel read callback.
|
||||
if (readCallback_) {
|
||||
processReadEOF();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::readError(
|
||||
const transport::TTransportException& ex) THRIFT_NOEXCEPT {
|
||||
// readCallback_ may be NULL if readEOF() is invoked while the read callback
|
||||
// is already running inside invokeReadDataAvailable(), since
|
||||
// invokeReadDataAvailable() leaves the transport read callback installed
|
||||
// while calling the channel read callback.
|
||||
if (!readCallback_) {
|
||||
return;
|
||||
}
|
||||
|
||||
DestructorGuard dg(this);
|
||||
|
||||
cancelTimeout();
|
||||
failAllReads();
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::writeSuccess()
|
||||
THRIFT_NOEXCEPT {
|
||||
DestructorGuard dg(this);
|
||||
|
||||
WriteRequest_* req = popWriteRequest();
|
||||
req->writeSuccess();
|
||||
delete req;
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::writeError(
|
||||
size_t bytesWritten,
|
||||
const transport::TTransportException& ex) THRIFT_NOEXCEPT {
|
||||
DestructorGuard dg(this);
|
||||
|
||||
if (ex.getType() == transport::TTransportException::TIMED_OUT) {
|
||||
timedOut_ = true;
|
||||
}
|
||||
|
||||
WriteRequest_* req = popWriteRequest();
|
||||
req->writeError(bytesWritten, ex);
|
||||
delete req;
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::timeoutExpired()
|
||||
THRIFT_NOEXCEPT {
|
||||
DestructorGuard dg(this);
|
||||
|
||||
timedOut_ = true;
|
||||
|
||||
// Close the transport. It isn't usable anymore, since we are leaving
|
||||
// it in a state with a partial message outstanding.
|
||||
transport_->setReadCallback(NULL);
|
||||
transport_->close();
|
||||
|
||||
// TODO: It would be nice not to have to always log an error message here;
|
||||
// ideally the callback should decide if this is worth logging or not.
|
||||
// Unfortunately the TAsyncChannel API doesn't allow us to pass any error
|
||||
// info back to the callback.
|
||||
T_ERROR("TStreamAsyncChannel: read timeout");
|
||||
|
||||
failAllReads();
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
bool TStreamAsyncChannel<WriteRequest_, ReadState_>::invokeReadDataAvailable(
|
||||
size_t len) THRIFT_NOEXCEPT {
|
||||
DestructorGuard dg(this);
|
||||
assert(readCallback_);
|
||||
|
||||
bool readDone;
|
||||
try {
|
||||
readDone = readState_.readDataAvailable(len);
|
||||
} catch (const std::exception& ex) {
|
||||
// The channel is in an unknown state after an error processing read data.
|
||||
// Close the channel to ensure that callers cannot try to read from this
|
||||
// channel again.
|
||||
//
|
||||
// Make sure we do this after clearing our callbacks, so that the
|
||||
// channel won't call our readEOF() method.
|
||||
cancelTimeout();
|
||||
transport_->setReadCallback(NULL);
|
||||
|
||||
std::string addressStr;
|
||||
try {
|
||||
transport::TSocketAddress addr;
|
||||
transport_->getPeerAddress(&addr);
|
||||
addressStr = addr.describe();
|
||||
} catch (const std::exception& e) {
|
||||
addressStr = "unknown";
|
||||
}
|
||||
|
||||
T_ERROR("error reading message from %s: %s", addressStr.c_str(), ex.what());
|
||||
failAllReads();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!readDone) {
|
||||
// We read some data, but didn't finish reading a full message.
|
||||
if (recvTimeout_ > 0) {
|
||||
// Reset the timeout whenever we receive any data.
|
||||
// TODO: This matches the old TAsyncChannel behavior, but it seems like
|
||||
// it would make more sense to have the timeout apply to the entire
|
||||
// message as a whole. Eventually we should remove this code that resets
|
||||
// the timeout.
|
||||
scheduleTimeout(recvTimeout_);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TEventBase* ourEventBase = transport_->getEventBase();
|
||||
|
||||
// We read a full message. Invoke the read callback.
|
||||
invokeReadCallback(readCallback_, "read callback");
|
||||
|
||||
// Note that we cleared readCallback_ and readErrorCallback_ before invoking
|
||||
// the callback, but left ourself installed as the TAsyncTransport read
|
||||
// callback.
|
||||
//
|
||||
// This allows us to avoid changing the TAsyncTransport read callback if the
|
||||
// channel read callback immediately called recvMessage() again. This is
|
||||
// fairly common, and we avoid 2 unnecessary epoll_ctl() calls by not
|
||||
// changing the transport read callback. This results in a noticeable
|
||||
// performance improvement.
|
||||
//
|
||||
// If readCallback_ is set again after the callback returns, we're still
|
||||
// reading. recvMessage() will have taken care of reseting the receive
|
||||
// timeout, so we have nothing else to do.
|
||||
//
|
||||
// If readCallback_ is unset, recvMessage() wasn't called again and we need
|
||||
// to stop reading. If our TEventBase has changed, detachEventBase() will
|
||||
// have already stopped reading. (Note that if the TEventBase has changed,
|
||||
// it's possible that readCallback_ has already been set again to start
|
||||
// reading in the other thread.)
|
||||
if (transport_->getEventBase() == ourEventBase && !readCallback_) {
|
||||
if (readCallbackQ_.empty()) {
|
||||
cancelTimeout();
|
||||
transport_->setReadCallback(NULL);
|
||||
} else {
|
||||
// There are queued readers, pop one. This block should have the same
|
||||
// effect as if recvMessage were called
|
||||
const ReadQueueEntry &qentry = readCallbackQ_.front();
|
||||
readCallback_ = qentry.readCallback;
|
||||
readErrorCallback_ = qentry.readErrorCallback;
|
||||
readState_.setCallbackBuffer(qentry.readBuffer);
|
||||
readCallbackQ_.pop_front();
|
||||
|
||||
if (readState_.hasReadAheadData()) {
|
||||
return invokeReadDataAvailable(0);
|
||||
} else if (recvTimeout_ > 0) {
|
||||
scheduleTimeout(recvTimeout_);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::failAllReads() {
|
||||
invokeReadCallback(readErrorCallback_, "read error callback");
|
||||
|
||||
while (!readCallbackQ_.empty()) {
|
||||
const ReadQueueEntry &qentry = readCallbackQ_.front();
|
||||
invokeReadCallback(qentry.readErrorCallback, "read error callback");
|
||||
readCallbackQ_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::processReadEOF()
|
||||
THRIFT_NOEXCEPT {
|
||||
DestructorGuard dg(this);
|
||||
assert(readCallback_);
|
||||
|
||||
VoidCallback cb;
|
||||
const char* cbName;
|
||||
if (readState_.hasPartialMessage()) {
|
||||
cb = readErrorCallback_;
|
||||
cbName = "read error callback";
|
||||
} else {
|
||||
// We call the normal (non-error) callback if no data has been received yet
|
||||
// when EOF occurs.
|
||||
//
|
||||
// TODO: It would be nicer to have a mechanism to indicate to the caller
|
||||
// that EOF was received, instead of treating this just like 0-sized
|
||||
// message.
|
||||
cb = readCallback_;
|
||||
cbName = "read callback";
|
||||
}
|
||||
|
||||
cancelTimeout();
|
||||
invokeReadCallback(cb, cbName);
|
||||
|
||||
// Any queued reads should be notified like the else case above as only
|
||||
// the first reader can have partial data.
|
||||
while (!readCallbackQ_.empty()) {
|
||||
const ReadQueueEntry &qentry = readCallbackQ_.front();
|
||||
invokeReadCallback(qentry.readCallback, cbName);
|
||||
readCallbackQ_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename WriteRequest_, typename ReadState_>
|
||||
void TStreamAsyncChannel<WriteRequest_, ReadState_>::invokeReadCallback(
|
||||
VoidCallback cb, char const* callbackName) THRIFT_NOEXCEPT {
|
||||
readState_.unsetCallbackBuffer();
|
||||
readCallback_ = VoidCallback();
|
||||
readErrorCallback_ = VoidCallback();
|
||||
|
||||
try {
|
||||
cb();
|
||||
} catch (const std::exception& ex) {
|
||||
T_ERROR("TAsyncChannel: %s threw %s exception: %s",
|
||||
callbackName, typeid(ex).name(), ex.what());
|
||||
abort();
|
||||
} catch (...) {
|
||||
T_ERROR("TAsyncChannel: %s threw exception", callbackName);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TSTREAMASYNCCHANNEL_TCC_
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2006- Facebook
|
||||
// Distributed under the Thrift Software License
|
||||
//
|
||||
// See accompanying file LICENSE or visit the Thrift site at:
|
||||
// http://developers.facebook.com/thrift/
|
||||
|
||||
#ifndef _THRIFT_TSYNCTOASYNCPROCESSOR_H_
|
||||
#define _THRIFT_TSYNCTOASYNCPROCESSOR_H_ 1
|
||||
|
||||
#include <tr1/functional>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "thrift/lib/cpp/TProcessor.h"
|
||||
#include "thrift/lib/cpp/async/TAsyncProcessor.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
/**
|
||||
* Adapter to allow a TProcessor to be used as a TAsyncProcessor.
|
||||
*
|
||||
* Note that this should only be used for handlers that return quickly without
|
||||
* blocking, since async servers can be stalled by a single blocking operation.
|
||||
*/
|
||||
class TSyncToAsyncProcessor : public TAsyncProcessor {
|
||||
public:
|
||||
TSyncToAsyncProcessor(boost::shared_ptr<TProcessor> processor)
|
||||
: processor_(processor)
|
||||
{}
|
||||
|
||||
virtual void process(std::tr1::function<void(bool success)> _return,
|
||||
boost::shared_ptr<protocol::TProtocol> in,
|
||||
boost::shared_ptr<protocol::TProtocol> out,
|
||||
TConnectionContext* context) {
|
||||
return _return(processor_->process(in, out, context));
|
||||
}
|
||||
|
||||
private:
|
||||
boost::shared_ptr<TProcessor> processor_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // #ifndef _THRIFT_TSYNCTOASYNCPROCESSOR_H_
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TUNDELAYEDDESTRUCTION_H_
|
||||
#define THRIFT_ASYNC_TUNDELAYEDDESTRUCTION_H_ 1
|
||||
|
||||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
/**
|
||||
* A helper class to allow a TDelayedDestruction object to be instantiated on
|
||||
* the stack.
|
||||
*
|
||||
* This class derives from an existing TDelayedDestruction type and makes the
|
||||
* destructor public again. This allows objects of this type to be declared on
|
||||
* the stack or directly inside another class. Normally TDelayedDestruction
|
||||
* objects must be dynamically allocated on the heap.
|
||||
*
|
||||
* However, the trade-off is that you lose some of the protections provided by
|
||||
* TDelayedDestruction::destroy(). TDelayedDestruction::destroy() will
|
||||
* automatically delay destruction of the object until it is safe to do so.
|
||||
* If you use TUndelayedDestruction, you become responsible for ensuring that
|
||||
* you only destroy the object where it is safe to do so. Attempting to
|
||||
* destroy a TUndelayedDestruction object while it has a non-zero destructor
|
||||
* guard count will abort the program.
|
||||
*/
|
||||
template<typename TDD>
|
||||
class TUndelayedDestruction : public TDD {
|
||||
public:
|
||||
// We want to expose all constructors provided by the parent class.
|
||||
// C++11 adds constructor inheritance to support this. Unfortunately gcc
|
||||
// does not implement constructor inheritance yet, so we have to fake it with
|
||||
// variadic templates.
|
||||
#if THRIFT_HAVE_CONSTRUCTOR_INHERITANCE
|
||||
using TDD::TDD;
|
||||
#else
|
||||
// We unfortunately can't simulate constructor inheritance as well as I'd
|
||||
// like.
|
||||
//
|
||||
// Ideally we would use std::enable_if<> and std::is_constructible<> to
|
||||
// provide only constructor methods that are valid for our parent class.
|
||||
// Unfortunately std::is_constructible<> doesn't work for types that aren't
|
||||
// destructible. In gcc-4.6 it results in a compiler error. In the latest
|
||||
// gcc code it looks like it has been fixed to return false. (The language
|
||||
// in the standard seems to indicate that returning false is the correct
|
||||
// behavior for non-destructible types, which is unfortunate.)
|
||||
template<typename ...Args>
|
||||
explicit TUndelayedDestruction(Args&& ...args)
|
||||
: TDD(std::forward<Args>(args)...) {}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Public destructor.
|
||||
*
|
||||
* The caller is responsible for ensuring that the object is only destroyed
|
||||
* where it is safe to do so. (i.e., when the destructor guard count is 0).
|
||||
*
|
||||
* The exact conditions for meeting this may be dependant upon your class
|
||||
* semantics. Typically you are only guaranteed that it is safe to destroy
|
||||
* the object directly from the event loop (e.g., directly from a
|
||||
* TEventBase::LoopCallback), or when the event loop is stopped.
|
||||
*/
|
||||
virtual ~TUndelayedDestruction() {
|
||||
// Crash if the caller is destroying us with outstanding destructor guards.
|
||||
if (this->getDestructorGuardCount() != 0) {
|
||||
abort();
|
||||
}
|
||||
// Invoke destroy. This is necessary since our base class may have
|
||||
// implemented custom behavior in destroy().
|
||||
this->destroy();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Override our parent's destroy() method to make it protected.
|
||||
* Callers should use the normal destructor instead of destroy
|
||||
*/
|
||||
virtual void destroy() {
|
||||
this->TDD::destroy();
|
||||
}
|
||||
|
||||
virtual void destroyNow(bool delayed) {
|
||||
// Do nothing. This will always be invoked from the call to destroy inside
|
||||
// our destructor.
|
||||
assert(!delayed);
|
||||
// prevent unused variable warnings when asserts are compiled out.
|
||||
(void)delayed;
|
||||
}
|
||||
|
||||
private:
|
||||
// Forbidden copy constructor and assignment operator
|
||||
TUndelayedDestruction(TUndelayedDestruction const &) = delete;
|
||||
TUndelayedDestruction& operator=(TUndelayedDestruction const &) = delete;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TUNDELAYEDDESTRUCTION_H_
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TUNFRAMEDASYNCCHANNEL_H_
|
||||
#define THRIFT_ASYNC_TUNFRAMEDASYNCCHANNEL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TStreamAsyncChannel.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Encapsulation of one outstanding write request on a TUnframedAsyncChannel.
|
||||
*/
|
||||
class TUnframedACWriteRequest :
|
||||
public TAsyncChannelWriteRequestBase<TUnframedACWriteRequest> {
|
||||
public:
|
||||
typedef std::tr1::function<void()> VoidCallback;
|
||||
|
||||
TUnframedACWriteRequest(const VoidCallback& callback,
|
||||
const VoidCallback& errorCallback,
|
||||
transport::TMemoryBuffer* message,
|
||||
TAsyncEventChannel* channel);
|
||||
|
||||
void write(TAsyncTransport* transport,
|
||||
TAsyncTransport::WriteCallback* callback) THRIFT_NOEXCEPT;
|
||||
|
||||
void writeSuccess() THRIFT_NOEXCEPT;
|
||||
void writeError(size_t bytesWritten,
|
||||
const transport::TTransportException& ex) THRIFT_NOEXCEPT;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read state for TUnframedAsyncChannel
|
||||
*/
|
||||
template<typename ProtocolTraits_>
|
||||
class TUnframedACReadState {
|
||||
public:
|
||||
typedef std::tr1::function<void()> VoidCallback;
|
||||
typedef ProtocolTraits_ ProtocolTraits;
|
||||
|
||||
TUnframedACReadState();
|
||||
~TUnframedACReadState();
|
||||
|
||||
// Methods required by TStreamAsyncChannel
|
||||
|
||||
void setCallbackBuffer(transport::TMemoryBuffer* buffer) {
|
||||
callbackBuffer_ = buffer;
|
||||
}
|
||||
void unsetCallbackBuffer() {
|
||||
callbackBuffer_ = NULL;
|
||||
}
|
||||
|
||||
bool hasReadAheadData() {
|
||||
return (memBuffer_.available_read() > 0);
|
||||
}
|
||||
bool hasPartialMessage() {
|
||||
return (memBuffer_.available_read() > 0);
|
||||
}
|
||||
|
||||
void getReadBuffer(void** bufReturn, size_t* lenReturn);
|
||||
bool readDataAvailable(size_t len);
|
||||
|
||||
// Methods specific to TUnframedACReadState
|
||||
|
||||
void setMaxMessageSize(uint32_t size) {
|
||||
maxMessageSize_ = size;
|
||||
}
|
||||
|
||||
uint32_t getMaxMessageSize() const {
|
||||
return maxMessageSize_;
|
||||
}
|
||||
|
||||
ProtocolTraits_* getProtocolTraits() {
|
||||
return &protocolTraits_;
|
||||
}
|
||||
const ProtocolTraits_* getProtocolTraits() const {
|
||||
return &protocolTraits_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool getMessageLength(uint8_t* buffer,
|
||||
uint32_t bufferLength,
|
||||
uint32_t* messageLength);
|
||||
|
||||
/// maximum frame size accepted
|
||||
uint32_t maxMessageSize_;
|
||||
|
||||
apache::thrift::transport::TMemoryBuffer memBuffer_;
|
||||
apache::thrift::transport::TMemoryBuffer* callbackBuffer_;
|
||||
ProtocolTraits_ protocolTraits_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* TUnframedAsyncChannel
|
||||
*
|
||||
* This is a TAsyncChannel implementation that reads and writes raw (unframed)
|
||||
* messages. When reading messages, ProtocolTraits_ is used to determine the
|
||||
* end of a message.
|
||||
*/
|
||||
template<typename ProtocolTraits_>
|
||||
class TUnframedAsyncChannel :
|
||||
public TStreamAsyncChannel<detail::TUnframedACWriteRequest,
|
||||
detail::TUnframedACReadState<ProtocolTraits_> > {
|
||||
private:
|
||||
typedef TStreamAsyncChannel<detail::TUnframedACWriteRequest,
|
||||
detail::TUnframedACReadState<ProtocolTraits_> >
|
||||
Parent;
|
||||
typedef TUnframedAsyncChannel<ProtocolTraits_> Self;
|
||||
|
||||
public:
|
||||
explicit TUnframedAsyncChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport
|
||||
)
|
||||
: Parent(transport) {}
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TUnframedAsyncChannel>.
|
||||
*
|
||||
* This passes in the correct destructor object, since TUnframedAsyncChannel's
|
||||
* destructor is protected and cannot be invoked directly.
|
||||
*/
|
||||
static boost::shared_ptr<Self> newChannel(
|
||||
const boost::shared_ptr<TAsyncTransport>& transport) {
|
||||
return boost::shared_ptr<Self>(new Self(transport),
|
||||
typename Self::Destructor());
|
||||
}
|
||||
|
||||
/// size in bytes beyond which we'll reject a given message.
|
||||
void setMaxMessageSize(uint32_t size) {
|
||||
this->readState_.setMaxMessageSize(size);
|
||||
}
|
||||
|
||||
uint32_t getMaxMessageSize() const {
|
||||
return this->readState_.getMaxMessageSize();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Users of TUnframedAsyncChannel must never delete it directly. Instead,
|
||||
* invoke destroy().
|
||||
*/
|
||||
virtual ~TUnframedAsyncChannel() { }
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TUNFRAMEDASYNCCHANNEL_H_
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TUNFRAMEDASYNCCHANNEL_TCC_
|
||||
#define THRIFT_ASYNC_TUNFRAMEDASYNCCHANNEL_TCC_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TUnframedAsyncChannel.h"
|
||||
|
||||
#include "thrift/lib/cpp/transport/TBufferTransports.h"
|
||||
|
||||
namespace {
|
||||
const uint32_t kInitialBufferSize = 4096;
|
||||
}
|
||||
|
||||
namespace apache { namespace thrift { namespace async { namespace detail {
|
||||
|
||||
template<typename ProtocolTraits_>
|
||||
TUnframedACReadState<ProtocolTraits_>::TUnframedACReadState()
|
||||
: maxMessageSize_(0x7fffffff)
|
||||
, memBuffer_(kInitialBufferSize)
|
||||
, callbackBuffer_(NULL)
|
||||
, protocolTraits_() {
|
||||
}
|
||||
|
||||
template<typename ProtocolTraits_>
|
||||
TUnframedACReadState<ProtocolTraits_>::~TUnframedACReadState() {
|
||||
}
|
||||
|
||||
template<typename ProtocolTraits_>
|
||||
void TUnframedACReadState<ProtocolTraits_>::getReadBuffer(void** bufReturn,
|
||||
size_t* lenReturn) {
|
||||
uint32_t bytesAvailable = memBuffer_.available_write();
|
||||
if (bytesAvailable > 0) {
|
||||
// If there is room available in the buffer, just return it.
|
||||
*lenReturn = bytesAvailable;
|
||||
*bufReturn = memBuffer_.getWritePtr(bytesAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t bufferSize = memBuffer_.getBufferSize();
|
||||
uint32_t available_read = memBuffer_.available_read();
|
||||
// we get this much without growing the buffer capacity
|
||||
uint32_t additionalSpace = bufferSize - available_read;
|
||||
if (additionalSpace == 0) {
|
||||
// We need more room. memBuffer_ will at least double it's capacity when
|
||||
// asked for even a single byte.
|
||||
additionalSpace = kInitialBufferSize;
|
||||
}
|
||||
|
||||
// Don't allow more than maxMessageSize_.
|
||||
// Be careful not to over- or underflow uint32_t when checking.
|
||||
//
|
||||
// readDataAvailable() fails the read when we've already read maxMessageSize_
|
||||
// bytes, so available_read should always be less than maxMessageSize_ here.
|
||||
// (Unless maxMessageSize_ is 0, but that's a programmer bug.)
|
||||
assert(available_read < maxMessageSize_);
|
||||
if (available_read > maxMessageSize_ - additionalSpace) {
|
||||
// Don't ask for more than maxMessageSize_ total (but we might get more)
|
||||
additionalSpace = maxMessageSize_ - available_read;
|
||||
}
|
||||
|
||||
try {
|
||||
uint8_t* newBuffer = memBuffer_.getWritePtr(additionalSpace);
|
||||
*lenReturn = memBuffer_.available_write();
|
||||
*bufReturn = newBuffer;
|
||||
} catch (std::exception &ex) {
|
||||
T_ERROR("TUnframedAsyncChannel: failed to allocate larger read buffer: %s",
|
||||
ex.what());
|
||||
*lenReturn = 0;
|
||||
*bufReturn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ProtocolTraits_>
|
||||
bool TUnframedACReadState<ProtocolTraits_>::readDataAvailable(size_t len) {
|
||||
assert(memBuffer_.available_read() + len <= memBuffer_.getBufferSize());
|
||||
memBuffer_.wroteBytes(len);
|
||||
|
||||
uint32_t messageLength = 0;
|
||||
uint32_t bytesRead = memBuffer_.available_read();
|
||||
uint8_t *buffer = (uint8_t *)memBuffer_.borrow(NULL, &bytesRead);
|
||||
if (!protocolTraits_.getMessageLength(buffer, bytesRead, &messageLength)) {
|
||||
// We're not at the end of the message yet.
|
||||
//
|
||||
// If we've hit maxMessageSize_ already, fail now instead of waiting until
|
||||
// getReadBuffer() is called again.
|
||||
if (bytesRead >= maxMessageSize_) {
|
||||
throw transport::TTransportException(
|
||||
transport::TTransportException::CORRUPTED_DATA,
|
||||
"TUnframedAsyncChannel: max message size exceeded");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// We've read a full message.
|
||||
// Swap the data into the callback's buffer.
|
||||
// Note that we may have actually read more than one message,
|
||||
// so we have to make sure to save any remaining data after the end of the
|
||||
// message.
|
||||
assert(messageLength <= bytesRead);
|
||||
|
||||
callbackBuffer_->link(&memBuffer_, messageLength);
|
||||
memBuffer_.consume(messageLength);
|
||||
|
||||
// We've put a new message in callbackBuffer_
|
||||
return true;
|
||||
}
|
||||
|
||||
}}}} // apache::thrift::async::detail
|
||||
|
||||
#endif // THRIFT_ASYNC_TUNFRAMEDASYNCCHANNEL_TCC_
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_ASYNC_TZLIBASYNCCHANNEL_H_
|
||||
#define THRIFT_ASYNC_TZLIBASYNCCHANNEL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/async/TAsyncEventChannel.h"
|
||||
#include "thrift/lib/cpp/transport/TZlibTransport.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace async {
|
||||
|
||||
class TZlibAsyncChannel : public TAsyncEventChannel {
|
||||
public:
|
||||
TZlibAsyncChannel(const boost::shared_ptr<TAsyncEventChannel>& channel);
|
||||
|
||||
/**
|
||||
* Helper function to create a shared_ptr<TZlibAsyncChannel>.
|
||||
*
|
||||
* This passes in the correct destructor object, since TZlibAsyncChannel's
|
||||
* destructor is protected and cannot be invoked directly.
|
||||
*/
|
||||
static boost::shared_ptr<TZlibAsyncChannel> newChannel(
|
||||
const boost::shared_ptr<TAsyncEventChannel>& channel) {
|
||||
return boost::shared_ptr<TZlibAsyncChannel>(
|
||||
new TZlibAsyncChannel(channel), Destructor());
|
||||
}
|
||||
virtual bool readable() const {
|
||||
return channel_->readable();
|
||||
}
|
||||
virtual bool good() const {
|
||||
return channel_->good();
|
||||
}
|
||||
virtual bool error() const {
|
||||
return channel_->error();
|
||||
}
|
||||
virtual bool timedOut() const {
|
||||
return channel_->timedOut();
|
||||
}
|
||||
virtual bool isIdle() const {
|
||||
return channel_->isIdle();
|
||||
}
|
||||
|
||||
virtual void sendMessage(const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
transport::TMemoryBuffer* message);
|
||||
virtual void recvMessage(const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
transport::TMemoryBuffer* message);
|
||||
virtual void sendAndRecvMessage(const VoidCallback& cob,
|
||||
const VoidCallback& errorCob,
|
||||
transport::TMemoryBuffer* sendBuf,
|
||||
transport::TMemoryBuffer* recvBuf);
|
||||
|
||||
virtual boost::shared_ptr<TAsyncTransport> getTransport() {
|
||||
return channel_->getTransport();
|
||||
}
|
||||
|
||||
virtual void attachEventBase(TEventBase* eventBase) {
|
||||
channel_->attachEventBase(eventBase);
|
||||
}
|
||||
virtual void detachEventBase() {
|
||||
channel_->detachEventBase();
|
||||
}
|
||||
|
||||
virtual uint32_t getRecvTimeout() const {
|
||||
return channel_->getRecvTimeout();
|
||||
}
|
||||
|
||||
virtual void setRecvTimeout(uint32_t milliseconds) {
|
||||
channel_->setRecvTimeout(milliseconds);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Protected destructor.
|
||||
*
|
||||
* Users of TZlibAsyncChannel must never delete it directly. Instead,
|
||||
* invoke destroy().
|
||||
*/
|
||||
virtual ~TZlibAsyncChannel() { }
|
||||
|
||||
private:
|
||||
class SendRequest {
|
||||
public:
|
||||
SendRequest();
|
||||
|
||||
bool isSet() const {
|
||||
return static_cast<bool>(callback_);
|
||||
}
|
||||
|
||||
void set(const VoidCallback& callback,
|
||||
const VoidCallback& errorCallback,
|
||||
transport::TMemoryBuffer* message);
|
||||
|
||||
void send(TAsyncEventChannel* channel);
|
||||
|
||||
private:
|
||||
void invokeCallback(VoidCallback callback);
|
||||
void sendSuccess();
|
||||
void sendError();
|
||||
|
||||
boost::shared_ptr<transport::TMemoryBuffer> compressedBuffer_;
|
||||
transport::TZlibTransport zlibTransport_;
|
||||
VoidCallback sendSuccess_;
|
||||
VoidCallback sendError_;
|
||||
|
||||
VoidCallback callback_;
|
||||
VoidCallback errorCallback_;
|
||||
};
|
||||
|
||||
class RecvRequest {
|
||||
public:
|
||||
RecvRequest();
|
||||
|
||||
bool isSet() const {
|
||||
return static_cast<bool>(callback_);
|
||||
}
|
||||
|
||||
void set(const VoidCallback& callback,
|
||||
const VoidCallback& errorCallback,
|
||||
transport::TMemoryBuffer* message);
|
||||
|
||||
void recv(TAsyncEventChannel* channel);
|
||||
|
||||
private:
|
||||
void invokeCallback(VoidCallback callback);
|
||||
void recvSuccess();
|
||||
void recvError();
|
||||
|
||||
boost::shared_ptr<transport::TMemoryBuffer> compressedBuffer_;
|
||||
transport::TZlibTransport zlibTransport_;
|
||||
VoidCallback recvSuccess_;
|
||||
VoidCallback recvError_;
|
||||
|
||||
VoidCallback callback_;
|
||||
VoidCallback errorCallback_;
|
||||
transport::TMemoryBuffer *callbackBuffer_;
|
||||
};
|
||||
|
||||
boost::shared_ptr<TAsyncEventChannel> channel_;
|
||||
|
||||
// TODO: support multiple pending send requests
|
||||
SendRequest sendRequest_;
|
||||
RecvRequest recvRequest_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::async
|
||||
|
||||
#endif // THRIFT_ASYNC_TZLIBASYNCCHANNEL_H_
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef _THRIFT_CONCURRENCY_EXCEPTION_H_
|
||||
#define _THRIFT_CONCURRENCY_EXCEPTION_H_ 1
|
||||
|
||||
#include <exception>
|
||||
#include "thrift/lib/cpp/Thrift.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace concurrency {
|
||||
|
||||
class NoSuchTaskException : public apache::thrift::TLibraryException {};
|
||||
|
||||
class UncancellableTaskException : public apache::thrift::TLibraryException {};
|
||||
|
||||
class InvalidArgumentException : public apache::thrift::TLibraryException {};
|
||||
|
||||
class IllegalStateException : public apache::thrift::TLibraryException {
|
||||
public:
|
||||
IllegalStateException() {}
|
||||
IllegalStateException(const std::string& message) : TLibraryException(message) {}
|
||||
};
|
||||
|
||||
class TimedOutException : public apache::thrift::TLibraryException {
|
||||
public:
|
||||
TimedOutException():TLibraryException("TimedOutException"){};
|
||||
TimedOutException(const std::string& message ) :
|
||||
TLibraryException(message) {}
|
||||
};
|
||||
|
||||
class TooManyPendingTasksException : public apache::thrift::TLibraryException {
|
||||
public:
|
||||
TooManyPendingTasksException():TLibraryException("TooManyPendingTasksException"){};
|
||||
TooManyPendingTasksException(const std::string& message ) :
|
||||
TLibraryException(message) {}
|
||||
};
|
||||
|
||||
class SystemResourceException : public apache::thrift::TLibraryException {
|
||||
public:
|
||||
SystemResourceException() {}
|
||||
|
||||
SystemResourceException(const std::string& message) :
|
||||
TLibraryException(message) {}
|
||||
};
|
||||
|
||||
}}} // apache::thrift::concurrency
|
||||
|
||||
#endif // #ifndef _THRIFT_CONCURRENCY_EXCEPTION_H_
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef _THRIFT_CONCURRENCY_FUNCTION_RUNNER_H
|
||||
#define _THRIFT_CONCURRENCY_FUNCTION_RUNNER_H 1
|
||||
|
||||
#include <tr1/functional>
|
||||
#include "thrift/lib/cpp/concurrency/Thread.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace concurrency {
|
||||
|
||||
/**
|
||||
* Convenient implementation of Runnable that will execute arbitrary callbacks.
|
||||
* Interfaces are provided to accept both a generic 'void(void)' callback, and
|
||||
* a 'void* (void*)' pthread_create-style callback.
|
||||
*
|
||||
* Example use:
|
||||
* void* my_thread_main(void* arg);
|
||||
* shared_ptr<ThreadFactory> factory = ...;
|
||||
* // To create a thread that executes my_thread_main once:
|
||||
* shared_ptr<Thread> thread = factory->newThread(
|
||||
* FunctionRunner::create(my_thread_main, some_argument));
|
||||
* thread->start();
|
||||
*
|
||||
* bool A::foo();
|
||||
* A* a = new A();
|
||||
* // To create a thread that executes a.foo() every 100 milliseconds:
|
||||
* factory->newThread(FunctionRunner::create(
|
||||
* std::tr1::bind(&A::foo, a), 100))->start();
|
||||
*
|
||||
*/
|
||||
|
||||
class FunctionRunner : public Runnable {
|
||||
public:
|
||||
// This is the type of callback 'pthread_create()' expects.
|
||||
typedef void* (*PthreadFuncPtr)(void *arg);
|
||||
// This a fully-generic void(void) callback for custom bindings.
|
||||
typedef std::tr1::function<void()> VoidFunc;
|
||||
|
||||
typedef std::tr1::function<bool()> BoolFunc;
|
||||
|
||||
/**
|
||||
* Syntactic sugar to make it easier to create new FunctionRunner
|
||||
* objects wrapped in shared_ptr.
|
||||
*/
|
||||
static boost::shared_ptr<FunctionRunner> create(const VoidFunc& cob) {
|
||||
return boost::shared_ptr<FunctionRunner>(new FunctionRunner(cob));
|
||||
}
|
||||
|
||||
static boost::shared_ptr<FunctionRunner> create(PthreadFuncPtr func,
|
||||
void* arg) {
|
||||
return boost::shared_ptr<FunctionRunner>(new FunctionRunner(func, arg));
|
||||
}
|
||||
|
||||
static boost::shared_ptr<FunctionRunner> create(const BoolFunc& cob,
|
||||
int intervalMs) {
|
||||
return boost::shared_ptr<FunctionRunner>(new FunctionRunner(cob,
|
||||
intervalMs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a 'pthread_create' style callback, this FunctionRunner will
|
||||
* execute the given callback. Note that the 'void*' return value is ignored.
|
||||
*/
|
||||
FunctionRunner(PthreadFuncPtr func, void* arg)
|
||||
: func_(std::tr1::bind(func, arg)), repFunc_(0), initFunc_(0)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Given a generic callback, this FunctionRunner will execute it.
|
||||
*/
|
||||
FunctionRunner(const VoidFunc& cob)
|
||||
: func_(cob), repFunc_(0), initFunc_(0)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Given a bool foo(...) type callback, FunctionRunner will execute
|
||||
* the callback repeatedly with 'intervalMs' milliseconds between the calls,
|
||||
* until it returns false. Note that the actual interval between calls will
|
||||
* be intervalMs plus execution time of the callback.
|
||||
*/
|
||||
FunctionRunner(const BoolFunc& cob, int intervalMs)
|
||||
: func_(0), repFunc_(cob), intervalMs_(intervalMs), initFunc_(0)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Set a callback to be called when the thread is started.
|
||||
*/
|
||||
void setInitFunc(const VoidFunc& initFunc) {
|
||||
initFunc_ = initFunc;
|
||||
}
|
||||
|
||||
void run() {
|
||||
if (initFunc_) {
|
||||
initFunc_();
|
||||
}
|
||||
if (repFunc_) {
|
||||
while(repFunc_()) {
|
||||
usleep(intervalMs_*1000);
|
||||
}
|
||||
} else {
|
||||
func_();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
VoidFunc func_;
|
||||
BoolFunc repFunc_;
|
||||
int intervalMs_;
|
||||
VoidFunc initFunc_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::concurrency
|
||||
|
||||
#endif // #ifndef _THRIFT_CONCURRENCY_FUNCTION_RUNNER_H
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef _THRIFT_CONCURRENCY_MONITOR_H_
|
||||
#define _THRIFT_CONCURRENCY_MONITOR_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/concurrency/Exception.h"
|
||||
#include "thrift/lib/cpp/concurrency/Mutex.h"
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
|
||||
namespace apache { namespace thrift { namespace concurrency {
|
||||
|
||||
/**
|
||||
* A monitor is a combination mutex and condition-event. Waiting and
|
||||
* notifying condition events requires that the caller own the mutex. Mutex
|
||||
* lock and unlock operations can be performed independently of condition
|
||||
* events. This is more or less analogous to java.lang.Object multi-thread
|
||||
* operations.
|
||||
*
|
||||
* Note the Monitor can create a new, internal mutex; alternatively, a
|
||||
* separate Mutex can be passed in and the Monitor will re-use it without
|
||||
* taking ownership. It's the user's responsibility to make sure that the
|
||||
* Mutex is not deallocated before the Monitor.
|
||||
*
|
||||
* Note that all methods are const. Monitors implement logical constness, not
|
||||
* bit constness. This allows const methods to call monitor methods without
|
||||
* needing to cast away constness or change to non-const signatures.
|
||||
*
|
||||
* @version $Id:$
|
||||
*/
|
||||
class Monitor : boost::noncopyable {
|
||||
public:
|
||||
/** Creates a new mutex, and takes ownership of it. */
|
||||
Monitor();
|
||||
|
||||
/** Uses the provided mutex without taking ownership. */
|
||||
explicit Monitor(Mutex* mutex);
|
||||
|
||||
/** Uses the mutex inside the provided Monitor without taking ownership. */
|
||||
explicit Monitor(Monitor* monitor);
|
||||
|
||||
/** Deallocates the mutex only if we own it. */
|
||||
virtual ~Monitor();
|
||||
|
||||
Mutex& mutex() const;
|
||||
|
||||
virtual void lock() const;
|
||||
|
||||
virtual void unlock() const;
|
||||
|
||||
/**
|
||||
* Waits a maximum of the specified timeout in milliseconds for the condition
|
||||
* to occur, or waits forever if timeout_ms == 0.
|
||||
*
|
||||
* Returns 0 if condition occurs, ETIMEDOUT on timeout, or an error code.
|
||||
*/
|
||||
int waitForTimeRelative(int64_t timeout_ms) const;
|
||||
|
||||
/**
|
||||
* Waits until the absolute time specified using struct timespec.
|
||||
* Returns 0 if condition occurs, ETIMEDOUT on timeout, or an error code.
|
||||
*/
|
||||
int waitForTime(const timespec* abstime) const;
|
||||
|
||||
/**
|
||||
* Waits forever until the condition occurs.
|
||||
* Returns 0 if condition occurs, or an error code otherwise.
|
||||
*/
|
||||
int waitForever() const;
|
||||
|
||||
/**
|
||||
* Exception-throwing version of waitForTimeRelative(), called simply
|
||||
* wait(int64) for historical reasons. Timeout is in milliseconds.
|
||||
*
|
||||
* If the condition occurs, this function returns cleanly; on timeout or
|
||||
* error an exception is thrown.
|
||||
*/
|
||||
void wait(int64_t timeout_ms = 0LL) const;
|
||||
|
||||
|
||||
/** Wakes up one thread waiting on this monitor. */
|
||||
virtual void notify() const;
|
||||
|
||||
/** Wakes up all waiting threads on this monitor. */
|
||||
virtual void notifyAll() const;
|
||||
|
||||
private:
|
||||
|
||||
class Impl;
|
||||
|
||||
Impl* impl_;
|
||||
};
|
||||
|
||||
class Synchronized {
|
||||
public:
|
||||
Synchronized(const Monitor* monitor) : g(monitor->mutex()) { }
|
||||
Synchronized(const Monitor& monitor) : g(monitor.mutex()) { }
|
||||
|
||||
private:
|
||||
Guard g;
|
||||
};
|
||||
|
||||
|
||||
}}} // apache::thrift::concurrency
|
||||
|
||||
#endif // #ifndef _THRIFT_CONCURRENCY_MONITOR_H_
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef THRIFT_CONCURRENCY_MUTEX_H_
|
||||
#define THRIFT_CONCURRENCY_MUTEX_H_ 1
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
namespace apache { namespace thrift { namespace concurrency {
|
||||
|
||||
#ifndef THRIFT_NO_CONTENTION_PROFILING
|
||||
|
||||
/**
|
||||
* Determines if the Thrift Mutex and ReadWriteMutex classes will attempt to
|
||||
* profile their blocking acquire methods. If this value is set to non-zero,
|
||||
* Thrift will attempt to invoke the callback once every profilingSampleRate
|
||||
* times. However, as the sampling is not synchronized the rate is not
|
||||
* guaranteed, and could be subject to big bursts and swings. Please ensure
|
||||
* your sampling callback is as performant as your application requires.
|
||||
*
|
||||
* The callback will get called with the wait time taken to lock the mutex in
|
||||
* usec and a (void*) that uniquely identifies the Mutex (or ReadWriteMutex)
|
||||
* being locked.
|
||||
*
|
||||
* The enableMutexProfiling() function is unsynchronized; calling this function
|
||||
* while profiling is already enabled may result in race conditions. On
|
||||
* architectures where a pointer assignment is atomic, this is safe but there
|
||||
* is no guarantee threads will agree on a single callback within any
|
||||
* particular time period.
|
||||
*/
|
||||
typedef void (*MutexWaitCallback)(const void* id, int64_t waitTimeMicros);
|
||||
void enableMutexProfiling(int32_t profilingSampleRate,
|
||||
MutexWaitCallback callback);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A simple mutex class
|
||||
*
|
||||
* @version $Id:$
|
||||
*/
|
||||
class Mutex {
|
||||
public:
|
||||
typedef void (*Initializer)(void*);
|
||||
|
||||
// Specifying the type of the mutex with one of the static Initializer
|
||||
// methods defined in this class.
|
||||
explicit Mutex(Initializer init = DEFAULT_INITIALIZER);
|
||||
|
||||
// Specifying the type of the mutex with an integer. The value has
|
||||
// to be supported by the underlying implementation, currently
|
||||
// pthread_mutex. So the possible values are PTHREAD_MUTEX_NORMAL,
|
||||
// PTHREAD_MUTEX_ERRORCHECK, PTHREAD_MUTEX_RECURSIVE and
|
||||
// PTHREAD_MUTEX_DEFAULT.
|
||||
explicit Mutex(int type);
|
||||
|
||||
virtual ~Mutex() {}
|
||||
virtual void lock() const;
|
||||
virtual bool trylock() const;
|
||||
virtual bool timedlock(int64_t milliseconds) const;
|
||||
virtual void unlock() const;
|
||||
|
||||
/**
|
||||
* Determine if the mutex is locked.
|
||||
*
|
||||
* This is intended to be used primarily as a debugging aid, and is not
|
||||
* guaranteed to be a fast operation. For example, a common use case is to
|
||||
* assert(mutex.isLocked()) in functions that may only be called with a
|
||||
* particular mutex already locked.
|
||||
*
|
||||
* TODO: This method currently always returns false for recursive mutexes.
|
||||
* Avoid calling this method on recursive mutexes.
|
||||
*/
|
||||
virtual bool isLocked() const;
|
||||
|
||||
void* getUnderlyingImpl() const;
|
||||
|
||||
static void DEFAULT_INITIALIZER(void*);
|
||||
static void ADAPTIVE_INITIALIZER(void*);
|
||||
static void RECURSIVE_INITIALIZER(void*);
|
||||
|
||||
private:
|
||||
|
||||
class impl;
|
||||
boost::shared_ptr<impl> impl_;
|
||||
};
|
||||
|
||||
class ReadWriteMutex {
|
||||
public:
|
||||
ReadWriteMutex();
|
||||
virtual ~ReadWriteMutex() {}
|
||||
|
||||
// these get the lock and block until it is done successfully
|
||||
virtual void acquireRead() const;
|
||||
virtual void acquireWrite() const;
|
||||
|
||||
// these get the lock and block until it is done successfully
|
||||
// or run out of time
|
||||
virtual bool timedRead(int64_t milliseconds) const;
|
||||
virtual bool timedWrite(int64_t milliseconds) const;
|
||||
|
||||
// these attempt to get the lock, returning false immediately if they fail
|
||||
virtual bool attemptRead() const;
|
||||
virtual bool attemptWrite() const;
|
||||
|
||||
// this releases both read and write locks
|
||||
virtual void release() const;
|
||||
|
||||
private:
|
||||
|
||||
class impl;
|
||||
boost::shared_ptr<impl> impl_;
|
||||
};
|
||||
|
||||
/**
|
||||
* A ReadWriteMutex that guarantees writers will not be starved by readers:
|
||||
* When a writer attempts to acquire the mutex, all new readers will be
|
||||
* blocked from acquiring the mutex until the writer has acquired and
|
||||
* released it. In some operating systems, this may already be guaranteed
|
||||
* by a regular ReadWriteMutex.
|
||||
*/
|
||||
class NoStarveReadWriteMutex : public ReadWriteMutex {
|
||||
public:
|
||||
NoStarveReadWriteMutex();
|
||||
|
||||
virtual void acquireRead() const;
|
||||
virtual void acquireWrite() const;
|
||||
|
||||
// these get the lock and block until it is done successfully
|
||||
// or run out of time
|
||||
virtual bool timedRead(int64_t milliseconds) const;
|
||||
virtual bool timedWrite(int64_t milliseconds) const;
|
||||
|
||||
private:
|
||||
Mutex mutex_;
|
||||
mutable volatile bool writerWaiting_;
|
||||
};
|
||||
|
||||
class Guard : boost::noncopyable {
|
||||
public:
|
||||
explicit Guard(const Mutex& value, int64_t timeout = 0) : mutex_(&value) {
|
||||
if (timeout == 0) {
|
||||
value.lock();
|
||||
} else if (timeout < 0) {
|
||||
if (!value.trylock()) {
|
||||
mutex_ = NULL;
|
||||
}
|
||||
} else {
|
||||
if (!value.timedlock(timeout)) {
|
||||
mutex_ = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
~Guard() {
|
||||
if (mutex_) {
|
||||
mutex_->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is really operator bool. However, implementing it to return
|
||||
* bool is actually harmful. See
|
||||
* www.artima.com/cppsource/safebool.html for the details; in brief,
|
||||
* converting to bool allows a lot of nonsensical operations in
|
||||
* addition to simple testing. To avoid that, we return a pointer to
|
||||
* member which can only be used for testing.
|
||||
*/
|
||||
typedef const Mutex*const Guard::*const pBoolMember;
|
||||
inline operator pBoolMember() const {
|
||||
return mutex_ != NULL ? &Guard::mutex_ : NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
const Mutex* mutex_;
|
||||
};
|
||||
|
||||
// Can be used as second argument to RWGuard to make code more readable
|
||||
// as to whether we're doing acquireRead() or acquireWrite().
|
||||
enum RWGuardType {
|
||||
RW_READ = 0,
|
||||
RW_WRITE = 1,
|
||||
};
|
||||
|
||||
|
||||
class RWGuard : boost::noncopyable {
|
||||
public:
|
||||
explicit RWGuard(const ReadWriteMutex& value, bool write = false,
|
||||
int64_t timeout=0)
|
||||
: rw_mutex_(value), locked_(true) {
|
||||
if (write) {
|
||||
if (timeout) {
|
||||
locked_ = rw_mutex_.timedWrite(timeout);
|
||||
} else {
|
||||
rw_mutex_.acquireWrite();
|
||||
}
|
||||
} else {
|
||||
if (timeout) {
|
||||
locked_ = rw_mutex_.timedRead(timeout);
|
||||
} else {
|
||||
rw_mutex_.acquireRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RWGuard(const ReadWriteMutex& value, RWGuardType type, int64_t timeout = 0)
|
||||
: rw_mutex_(value), locked_(true) {
|
||||
if (type == RW_WRITE) {
|
||||
if (timeout) {
|
||||
locked_ = rw_mutex_.timedWrite(timeout);
|
||||
} else {
|
||||
rw_mutex_.acquireWrite();
|
||||
}
|
||||
} else {
|
||||
if (timeout) {
|
||||
locked_ = rw_mutex_.timedRead(timeout);
|
||||
} else {
|
||||
rw_mutex_.acquireRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
~RWGuard() {
|
||||
if (locked_) {
|
||||
rw_mutex_.release();
|
||||
}
|
||||
}
|
||||
|
||||
typedef const bool RWGuard::*const pBoolMember;
|
||||
operator pBoolMember() const {
|
||||
return locked_ ? &RWGuard::locked_ : NULL;
|
||||
}
|
||||
|
||||
bool operator!() const {
|
||||
return !locked_;
|
||||
}
|
||||
|
||||
bool release() {
|
||||
if (!locked_) return false;
|
||||
rw_mutex_.release();
|
||||
locked_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const ReadWriteMutex& rw_mutex_;
|
||||
mutable bool locked_;
|
||||
};
|
||||
|
||||
|
||||
// A little hack to prevent someone from trying to do "Guard(m);"
|
||||
// Such a use is invalid because the temporary Guard object is
|
||||
// destroyed at the end of the line, releasing the lock.
|
||||
// Sorry for polluting the global namespace, but I think it's worth it.
|
||||
#define Guard(m) incorrect_use_of_Guard(m)
|
||||
#define RWGuard(m) incorrect_use_of_RWGuard(m)
|
||||
|
||||
|
||||
}}} // apache::thrift::concurrency
|
||||
|
||||
#endif // #ifndef THRIFT_CONCURRENCY_MUTEX_H_
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
|
||||
*/
|
||||
#ifndef THRIFT_CONCURRENCY_POSIXTHREADFACTORY_H_
|
||||
#define THRIFT_CONCURRENCY_POSIXTHREADFACTORY_H_ 1
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "thrift/lib/cpp/concurrency/Thread.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace apache { namespace thrift { namespace concurrency {
|
||||
|
||||
void getLiveThreadIds(std::set<pthread_t>* tids);
|
||||
|
||||
/**
|
||||
* A thread factory to create posix threads
|
||||
*
|
||||
* @version $Id:$
|
||||
*/
|
||||
class PosixThreadFactory : public ThreadFactory {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* POSIX Thread scheduler policies
|
||||
*/
|
||||
enum POLICY {
|
||||
OTHER,
|
||||
FIFO,
|
||||
ROUND_ROBIN
|
||||
};
|
||||
|
||||
/**
|
||||
* POSIX Thread scheduler relative priorities,
|
||||
*
|
||||
* Absolute priority is determined by scheduler policy and OS. This
|
||||
* enumeration specifies relative priorities such that one can specify a
|
||||
* priority within a giving scheduler policy without knowing the absolute
|
||||
* value of the priority.
|
||||
*/
|
||||
enum PRIORITY {
|
||||
LOWEST = 0,
|
||||
LOWER = 1,
|
||||
LOW = 2,
|
||||
NORMAL = 3,
|
||||
HIGH = 4,
|
||||
HIGHER = 5,
|
||||
HIGHEST = 6,
|
||||
INCREMENT = 7,
|
||||
DECREMENT = 8
|
||||
};
|
||||
|
||||
/**
|
||||
* Posix thread (pthread) factory. All threads created by a factory are reference-counted
|
||||
* via boost::shared_ptr and boost::weak_ptr. The factory guarantees that threads and
|
||||
* the Runnable tasks they host will be properly cleaned up once the last strong reference
|
||||
* to both is given up.
|
||||
*
|
||||
* Threads are created with the specified policy, priority, stack-size and detachable-mode
|
||||
* detached means the thread is free-running and will release all system resources the
|
||||
* when it completes. A detachable thread is not joinable. The join method
|
||||
* of a detachable thread will return immediately with no error.
|
||||
*
|
||||
* By default threads are not joinable.
|
||||
*/
|
||||
|
||||
explicit PosixThreadFactory(POLICY policy=ROUND_ROBIN,
|
||||
PRIORITY priority=NORMAL,
|
||||
int stackSize=1,
|
||||
bool detached=true);
|
||||
|
||||
// From ThreadFactory;
|
||||
boost::shared_ptr<Thread> newThread(boost::shared_ptr<Runnable> runnable) const;
|
||||
|
||||
// From ThreadFactory;
|
||||
Thread::id_t getCurrentThreadId() const;
|
||||
|
||||
/**
|
||||
* Gets stack size for created threads
|
||||
*
|
||||
* @return int size in megabytes
|
||||
*/
|
||||
virtual int getStackSize() const;
|
||||
|
||||
/**
|
||||
* Sets stack size for created threads
|
||||
*
|
||||
* @param value size in megabytes
|
||||
*/
|
||||
virtual void setStackSize(int value);
|
||||
|
||||
/**
|
||||
* Gets priority relative to current policy
|
||||
*/
|
||||
virtual PRIORITY getPriority() const;
|
||||
|
||||
/**
|
||||
* Sets priority relative to current policy
|
||||
*/
|
||||
virtual void setPriority(PRIORITY priority);
|
||||
|
||||
/**
|
||||
* Sets detached mode of threads
|
||||
*/
|
||||
virtual void setDetached(bool detached);
|
||||
|
||||
/**
|
||||
* Gets current detached mode
|
||||
*/
|
||||
virtual bool isDetached() const;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
boost::shared_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::concurrency
|
||||
|
||||
#endif // #ifndef _THRIFT_CONCURRENCY_POSIXTHREADFACTORY_H_
|
|
@ -0,0 +1,17 @@
|
|||
# TARGETS file for thrift/lib/cpp/concurrency
|
||||
|
||||
cpp_library (
|
||||
name = "concurrency",
|
||||
srcs = [
|
||||
"Util.cpp",
|
||||
"Monitor.cpp",
|
||||
"Mutex.cpp",
|
||||
"PosixThreadFactory.cpp",
|
||||
"ThreadManager.cpp",
|
||||
"TimerManager.cpp"
|
||||
],
|
||||
deps = [ '@/common/base:profiler',
|
||||
'@/thrift/lib/cpp:thrift_base' ],
|
||||
)
|
||||
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef _THRIFT_CONCURRENCY_THREAD_H_
|
||||
#define _THRIFT_CONCURRENCY_THREAD_H_ 1
|
||||
|
||||
#include <stdint.h>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
|
||||
namespace apache { namespace thrift { namespace concurrency {
|
||||
|
||||
class Thread;
|
||||
|
||||
/**
|
||||
* Minimal runnable class. More or less analogous to java.lang.Runnable.
|
||||
*
|
||||
* @version $Id:$
|
||||
*/
|
||||
class Runnable {
|
||||
|
||||
public:
|
||||
virtual ~Runnable() {};
|
||||
virtual void run() = 0;
|
||||
|
||||
/**
|
||||
* Gets the thread object that is hosting this runnable object - can return
|
||||
* an empty boost::shared pointer if no references remain on the thread object
|
||||
*/
|
||||
virtual boost::shared_ptr<Thread> thread() { return thread_.lock(); }
|
||||
|
||||
/**
|
||||
* Sets the thread that is executing this object. This is only meant for
|
||||
* use by concrete implementations of Thread.
|
||||
*/
|
||||
virtual void thread(boost::shared_ptr<Thread> value) { thread_ = value; }
|
||||
|
||||
private:
|
||||
boost::weak_ptr<Thread> thread_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Minimal thread class. Returned by thread factory bound to a Runnable object
|
||||
* and ready to start execution. More or less analogous to java.lang.Thread
|
||||
* (minus all the thread group, priority, mode and other baggage, since that
|
||||
* is difficult to abstract across platforms and is left for platform-specific
|
||||
* ThreadFactory implementations to deal with
|
||||
*
|
||||
* @see apache::thrift::concurrency::ThreadFactory)
|
||||
*/
|
||||
class Thread {
|
||||
|
||||
public:
|
||||
|
||||
typedef uint64_t id_t;
|
||||
|
||||
virtual ~Thread() {};
|
||||
|
||||
/**
|
||||
* Starts the thread. Does platform specific thread creation and
|
||||
* configuration then invokes the run method of the Runnable object bound
|
||||
* to this thread.
|
||||
*/
|
||||
virtual void start() = 0;
|
||||
|
||||
/**
|
||||
* Join this thread. Current thread blocks until this target thread
|
||||
* completes.
|
||||
*/
|
||||
virtual void join() = 0;
|
||||
|
||||
/**
|
||||
* Gets the thread's platform-specific ID
|
||||
*/
|
||||
virtual id_t getId() = 0;
|
||||
|
||||
/**
|
||||
* Gets the runnable object this thread is hosting
|
||||
*/
|
||||
virtual boost::shared_ptr<Runnable> runnable() const { return _runnable; }
|
||||
|
||||
protected:
|
||||
virtual void runnable(boost::shared_ptr<Runnable> value) { _runnable = value; }
|
||||
|
||||
private:
|
||||
boost::shared_ptr<Runnable> _runnable;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Factory to create platform-specific thread object and bind them to Runnable
|
||||
* object for execution
|
||||
*/
|
||||
class ThreadFactory {
|
||||
|
||||
public:
|
||||
virtual ~ThreadFactory() {}
|
||||
virtual boost::shared_ptr<Thread> newThread(boost::shared_ptr<Runnable> runnable) const = 0;
|
||||
|
||||
/** Gets the current thread id or unknown_thread_id if the current thread is not a thrift thread */
|
||||
|
||||
static const Thread::id_t unknown_thread_id;
|
||||
|
||||
virtual Thread::id_t getCurrentThreadId() const = 0;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::concurrency
|
||||
|
||||
#endif // #ifndef _THRIFT_CONCURRENCY_THREAD_H_
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_CONCURRENCY_THREADLOCAL_H_
|
||||
#define THRIFT_CONCURRENCY_THREADLOCAL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/Thrift.h"
|
||||
#include <pthread.h>
|
||||
|
||||
namespace apache { namespace thrift { namespace concurrency {
|
||||
|
||||
template <typename T>
|
||||
class DefaultThreadLocalManager;
|
||||
|
||||
/**
|
||||
* ThreadLocal manages thread-local storage for a particular object type.
|
||||
*
|
||||
* Each ThreadLocal object contains a separate instance of an object for each
|
||||
* thread that accesses the ThreadLocal object.
|
||||
*
|
||||
* Note that you should avoid creating too many ThreadLocal objects (e.g., such
|
||||
* as keeping a ThreadLocal member variable in commonly allocated objects).
|
||||
* The number of ThreadLocal objects cannot be larger than the value of
|
||||
* PTHREAD_KEYS_MAX, which is 1024 on many systems.
|
||||
*
|
||||
* The ManagerT template parameter controls how object allocation and
|
||||
* deallocation should be performed. When get() is called from a thread that
|
||||
* does not already have an instance of the object, Manager::allocate() is
|
||||
* called. When a thread exits, Manager::destroy() is called if the thread has
|
||||
* an instance of this object.
|
||||
*/
|
||||
template <typename T, typename ManagerT = DefaultThreadLocalManager<T> >
|
||||
class ThreadLocal {
|
||||
public:
|
||||
typedef T DataType;
|
||||
typedef ManagerT Manager;
|
||||
|
||||
/**
|
||||
* Create a new ThreadLocal object.
|
||||
*/
|
||||
ThreadLocal() {
|
||||
int ret = pthread_key_create(&key_, &ThreadLocal::onThreadExit);
|
||||
if (ret != 0) {
|
||||
throw TLibraryException("failed to allocate new thread-local key", ret);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access this thread's local instance of the object.
|
||||
*
|
||||
* If there is no instance of the object in this thread, Manager::allocate()
|
||||
* will be called to allocate a new instance. (Though some Manager
|
||||
* implementations may return NULL, if each thread's instance must be
|
||||
* expilcitly initialized.)
|
||||
*/
|
||||
T *get() const {
|
||||
T *obj = getNoAlloc();
|
||||
if (obj == NULL) {
|
||||
Manager m;
|
||||
obj = m.allocate();
|
||||
if (obj != NULL) {
|
||||
setImpl(obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access this thread's local instance of the object.
|
||||
*
|
||||
* If there is no instance of the object in this thread, NULL will be
|
||||
* returned. Manager::allocate() will never be called.
|
||||
*/
|
||||
T *getNoAlloc() const {
|
||||
return static_cast<T*>(pthread_getspecific(key_));
|
||||
}
|
||||
|
||||
/**
|
||||
* Operator overload to perform get()
|
||||
*/
|
||||
T *operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Operator overload to perform get()
|
||||
*/
|
||||
T &operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the instance of the object to be used by this thread.
|
||||
*/
|
||||
void set(T* obj) {
|
||||
T *old = getNoAlloc();
|
||||
Manager m;
|
||||
m.replace(old, obj);
|
||||
setImpl(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the instance of the object used by this thread.
|
||||
*
|
||||
* If this thread had a non-NULL object, Manager::destroy() will be called.
|
||||
*/
|
||||
void clear() {
|
||||
T *obj = getNoAlloc();
|
||||
if (obj != NULL) {
|
||||
Manager m;
|
||||
m.destroy(obj);
|
||||
setImpl(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setImpl(T* obj) const {
|
||||
int ret = pthread_setspecific(key_, obj);
|
||||
if (ret != 0) {
|
||||
throw TLibraryException("failed to update thread-local key", ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void onThreadExit(void* arg) {
|
||||
T *obj = static_cast<T*>(arg);
|
||||
if (obj != NULL) {
|
||||
Manager m;
|
||||
m.destroy(obj);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_key_t key_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class DefaultThreadLocalManager {
|
||||
public:
|
||||
T* allocate() {
|
||||
return new T;
|
||||
}
|
||||
|
||||
void destroy(T* t) {
|
||||
delete t;
|
||||
}
|
||||
|
||||
void replace(T* oldObj, T* newObj) {
|
||||
if (oldObj != newObj) {
|
||||
delete oldObj;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class DestroyOnlyThreadLocalManager {
|
||||
public:
|
||||
T* allocate() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void destroy(T* t) {
|
||||
delete t;
|
||||
}
|
||||
|
||||
void replace(T* oldObj, T* newObj) {
|
||||
if (oldObj != newObj) {
|
||||
delete oldObj;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class NoopThreadLocalManager {
|
||||
public:
|
||||
T* allocate() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void destroy(T*) {
|
||||
}
|
||||
|
||||
void replace(T*, T*) {
|
||||
}
|
||||
};
|
||||
|
||||
}}} // apache::thrift::concurrency
|
||||
|
||||
#endif // THRIFT_CONCURRENCY_THREADLOCAL_H_
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef _THRIFT_CONCURRENCY_THREADMANAGER_H_
|
||||
#define _THRIFT_CONCURRENCY_THREADMANAGER_H_ 1
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <tr1/functional>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace apache { namespace thrift { namespace concurrency {
|
||||
|
||||
class Runnable;
|
||||
class ThreadFactory;
|
||||
|
||||
/**
|
||||
* ThreadManager class
|
||||
*
|
||||
* This class manages a pool of threads. It uses a ThreadFactory to create
|
||||
* threads. It never actually creates or destroys worker threads, rather
|
||||
* It maintains statistics on number of idle threads, number of active threads,
|
||||
* task backlog, and average wait and service times and informs the PoolPolicy
|
||||
* object bound to instances of this manager of interesting transitions. It is
|
||||
* then up the PoolPolicy object to decide if the thread pool size needs to be
|
||||
* adjusted and call this object addWorker and removeWorker methods to make
|
||||
* changes.
|
||||
*
|
||||
* This design allows different policy implementations to used this code to
|
||||
* handle basic worker thread management and worker task execution and focus on
|
||||
* policy issues. The simplest policy, StaticPolicy, does nothing other than
|
||||
* create a fixed number of threads.
|
||||
*/
|
||||
class ThreadManager {
|
||||
|
||||
protected:
|
||||
ThreadManager() {}
|
||||
|
||||
public:
|
||||
class Task;
|
||||
typedef std::tr1::function<void(boost::shared_ptr<Runnable>)> ExpireCallback;
|
||||
typedef std::tr1::function<void()> InitCallback;
|
||||
|
||||
virtual ~ThreadManager() {}
|
||||
|
||||
/**
|
||||
* Starts the thread manager. Verifies all attributes have been properly
|
||||
* initialized, then allocates necessary resources to begin operation
|
||||
*/
|
||||
virtual void start() = 0;
|
||||
|
||||
/**
|
||||
* Stops the thread manager. Aborts all remaining unprocessed task, shuts
|
||||
* down all created worker threads, and releases all allocated resources.
|
||||
* This method blocks for all worker threads to complete, thus it can
|
||||
* potentially block forever if a worker thread is running a task that
|
||||
* won't terminate.
|
||||
*/
|
||||
virtual void stop() = 0;
|
||||
|
||||
/**
|
||||
* Joins the thread manager. This is the same as stop, except that it will
|
||||
* wait until all the tasks have finished, rather than aborting the tasks.
|
||||
*/
|
||||
virtual void join() = 0;
|
||||
|
||||
enum STATE {
|
||||
UNINITIALIZED,
|
||||
STARTING,
|
||||
STARTED,
|
||||
JOINING,
|
||||
STOPPING,
|
||||
STOPPED
|
||||
};
|
||||
|
||||
virtual const STATE state() const = 0;
|
||||
|
||||
virtual boost::shared_ptr<ThreadFactory> threadFactory() const = 0;
|
||||
|
||||
virtual void threadFactory(boost::shared_ptr<ThreadFactory> value) = 0;
|
||||
|
||||
virtual void addWorker(size_t value=1) = 0;
|
||||
|
||||
virtual void removeWorker(size_t value=1) = 0;
|
||||
|
||||
/**
|
||||
* Gets the current number of idle worker threads
|
||||
*/
|
||||
virtual size_t idleWorkerCount() const = 0;
|
||||
|
||||
/**
|
||||
* Gets the current number of total worker threads
|
||||
*/
|
||||
virtual size_t workerCount() const = 0;
|
||||
|
||||
/**
|
||||
* Gets the current number of pending tasks
|
||||
*/
|
||||
virtual size_t pendingTaskCount() const = 0;
|
||||
|
||||
/**
|
||||
* Gets the current number of pending and executing tasks
|
||||
*/
|
||||
virtual size_t totalTaskCount() const = 0;
|
||||
|
||||
/**
|
||||
* Gets the maximum pending task count. 0 indicates no maximum
|
||||
*/
|
||||
virtual size_t pendingTaskCountMax() const = 0;
|
||||
|
||||
/**
|
||||
* Gets the number of tasks which have been expired without being run.
|
||||
*/
|
||||
virtual size_t expiredTaskCount() = 0;
|
||||
|
||||
/**
|
||||
* Adds a task to be executed at some time in the future by a worker thread.
|
||||
*
|
||||
* This method will block if pendingTaskCountMax() in not zero and
|
||||
* pendingTaskCount() is greater than or equal to pendingTaskCountMax(). If
|
||||
* this method is called in the context of a ThreadManager worker thread it
|
||||
* will throw a TooManyPendingTasksException
|
||||
*
|
||||
* @param task The task to queue for execution
|
||||
*
|
||||
* @param timeout Time to wait in milliseconds to add a task when a
|
||||
* pending-task-count is specified. Specific cases:
|
||||
* timeout = 0 : Wait forever to queue task.
|
||||
* timeout = -1 : Return immediately if pending task count exceeds specified
|
||||
* max
|
||||
* @param expiration when nonzero, the number of milliseconds the task is
|
||||
* valid to be run; if exceeded, the task will be dropped off the queue and
|
||||
* not run.
|
||||
*
|
||||
* @throws TooManyPendingTasksException Pending task count exceeds max
|
||||
* pending task count
|
||||
*/
|
||||
virtual void add(boost::shared_ptr<Runnable>task,
|
||||
int64_t timeout=0LL,
|
||||
int64_t expiration=0LL) = 0;
|
||||
|
||||
/**
|
||||
* Removes a pending task
|
||||
*/
|
||||
virtual void remove(boost::shared_ptr<Runnable> task) = 0;
|
||||
|
||||
/**
|
||||
* Remove the next pending task which would be run.
|
||||
*
|
||||
* @return the task removed.
|
||||
*/
|
||||
virtual boost::shared_ptr<Runnable> removeNextPending() = 0;
|
||||
|
||||
/**
|
||||
* Set a callback to be called when a task is expired and not run.
|
||||
*
|
||||
* @param expireCallback a function called with the shared_ptr<Runnable> for
|
||||
* the expired task.
|
||||
*/
|
||||
virtual void setExpireCallback(ExpireCallback expireCallback) = 0;
|
||||
|
||||
/**
|
||||
* Set a callback to be called when a worker thread is created.
|
||||
*/
|
||||
virtual void setThreadInitCallback(InitCallback initCallback) = 0;
|
||||
|
||||
static boost::shared_ptr<ThreadManager> newThreadManager();
|
||||
|
||||
/**
|
||||
* Creates a simple thread manager the uses count number of worker threads
|
||||
* and has a pendingTaskCountMax maximum pending tasks. The default, 0,
|
||||
* specified no limit on pending tasks
|
||||
*/
|
||||
static boost::shared_ptr<ThreadManager>
|
||||
newSimpleThreadManager(size_t count = 4,
|
||||
size_t pendingTaskCountMax = 0,
|
||||
bool enableTaskStats = false);
|
||||
|
||||
/**
|
||||
* Get an internal statistics.
|
||||
*
|
||||
* @param waitTimeUs - average time (us) task spent in a queue
|
||||
* @param runTimeUs - average time (us) task spent running
|
||||
* @param maxItems - max items collected for stats
|
||||
*/
|
||||
virtual void getStats(int64_t& waitTimeUs, int64_t& runTimeUs,
|
||||
int64_t maxItems) {
|
||||
waitTimeUs = 0;
|
||||
runTimeUs = 0;
|
||||
}
|
||||
|
||||
class Task;
|
||||
|
||||
class Worker;
|
||||
|
||||
class Impl;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::concurrency
|
||||
|
||||
#endif // #ifndef _THRIFT_CONCURRENCY_THREADMANAGER_H_
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef _THRIFT_CONCURRENCY_TIMERMANAGER_H_
|
||||
#define _THRIFT_CONCURRENCY_TIMERMANAGER_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/concurrency/Exception.h"
|
||||
#include "thrift/lib/cpp/concurrency/Monitor.h"
|
||||
#include "thrift/lib/cpp/concurrency/Thread.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <map>
|
||||
#include <time.h>
|
||||
|
||||
namespace apache { namespace thrift { namespace concurrency {
|
||||
|
||||
/**
|
||||
* Timer Manager
|
||||
*
|
||||
* This class dispatches timer tasks when they fall due.
|
||||
*
|
||||
* @version $Id:$
|
||||
*/
|
||||
class TimerManager {
|
||||
|
||||
public:
|
||||
|
||||
TimerManager();
|
||||
|
||||
virtual ~TimerManager();
|
||||
|
||||
virtual boost::shared_ptr<const ThreadFactory> threadFactory() const;
|
||||
|
||||
virtual void threadFactory(boost::shared_ptr<const ThreadFactory> value);
|
||||
|
||||
/**
|
||||
* Starts the timer manager service
|
||||
*
|
||||
* @throws IllegalArgumentException Missing thread factory attribute
|
||||
*/
|
||||
virtual void start();
|
||||
|
||||
/**
|
||||
* Stops the timer manager service
|
||||
*/
|
||||
virtual void stop();
|
||||
|
||||
virtual size_t taskCount() const ;
|
||||
|
||||
/**
|
||||
* Adds a task to be executed at some time in the future by a worker thread.
|
||||
*
|
||||
* @param task The task to execute
|
||||
* @param timeout Time in milliseconds to delay before executing task
|
||||
*/
|
||||
virtual void add(boost::shared_ptr<Runnable> task, int64_t timeout);
|
||||
|
||||
/**
|
||||
* Adds a task to be executed at some time in the future by a worker thread.
|
||||
*
|
||||
* @param task The task to execute
|
||||
* @param timeout Absolute time in the future to execute task.
|
||||
*/
|
||||
virtual void add(boost::shared_ptr<Runnable> task, const struct timespec& timeout);
|
||||
|
||||
/**
|
||||
* Removes a pending task
|
||||
*
|
||||
* @throws NoSuchTaskException Specified task doesn't exist. It was either
|
||||
* processed already or this call was made for a
|
||||
* task that was never added to this timer
|
||||
*
|
||||
* @throws UncancellableTaskException Specified task is already being
|
||||
* executed or has completed execution.
|
||||
*/
|
||||
virtual void remove(boost::shared_ptr<Runnable> task);
|
||||
|
||||
enum STATE {
|
||||
UNINITIALIZED,
|
||||
STARTING,
|
||||
STARTED,
|
||||
STOPPING,
|
||||
STOPPED
|
||||
};
|
||||
|
||||
virtual const STATE state() const;
|
||||
|
||||
private:
|
||||
boost::shared_ptr<const ThreadFactory> threadFactory_;
|
||||
class Task;
|
||||
friend class Task;
|
||||
std::multimap<int64_t, boost::shared_ptr<Task> > taskMap_;
|
||||
size_t taskCount_;
|
||||
Monitor monitor_;
|
||||
STATE state_;
|
||||
class Dispatcher;
|
||||
friend class Dispatcher;
|
||||
boost::shared_ptr<Dispatcher> dispatcher_;
|
||||
boost::shared_ptr<Thread> dispatcherThread_;
|
||||
typedef std::multimap<int64_t, boost::shared_ptr<TimerManager::Task> >::iterator task_iterator;
|
||||
typedef std::pair<task_iterator, task_iterator> task_range;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::concurrency
|
||||
|
||||
#endif // #ifndef _THRIFT_CONCURRENCY_TIMERMANAGER_H_
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef _THRIFT_CONCURRENCY_UTIL_H_
|
||||
#define _THRIFT_CONCURRENCY_UTIL_H_ 1
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
namespace apache { namespace thrift { namespace concurrency {
|
||||
|
||||
/**
|
||||
* Utility methods
|
||||
*
|
||||
* This class contains basic utility methods for converting time formats,
|
||||
* and other common platform-dependent concurrency operations.
|
||||
* It should not be included in API headers for other concurrency library
|
||||
* headers, since it will, by definition, pull in all sorts of horrid
|
||||
* platform dependent crap. Rather it should be included directly in
|
||||
* concurrency library implementation source.
|
||||
*
|
||||
* @version $Id:$
|
||||
*/
|
||||
class Util {
|
||||
public:
|
||||
|
||||
static const int64_t NS_PER_S = 1000000000LL;
|
||||
static const int64_t US_PER_S = 1000000LL;
|
||||
static const int64_t MS_PER_S = 1000LL;
|
||||
|
||||
static const int64_t NS_PER_MS = NS_PER_S / MS_PER_S;
|
||||
static const int64_t NS_PER_US = NS_PER_S / US_PER_S;
|
||||
static const int64_t US_PER_MS = US_PER_S / MS_PER_S;
|
||||
|
||||
/**
|
||||
* Converts millisecond timestamp into a timespec struct
|
||||
*
|
||||
* @param struct timespec& result
|
||||
* @param time or duration in milliseconds
|
||||
*/
|
||||
static void toTimespec(struct timespec& result, int64_t value) {
|
||||
result.tv_sec = value / MS_PER_S; // ms to s
|
||||
result.tv_nsec = (value % MS_PER_S) * NS_PER_MS; // ms to ns
|
||||
}
|
||||
|
||||
static void toTimeval(struct timeval& result, int64_t value) {
|
||||
result.tv_sec = value / MS_PER_S; // ms to s
|
||||
result.tv_usec = (value % MS_PER_S) * US_PER_MS; // ms to us
|
||||
}
|
||||
|
||||
static const void toTicks(int64_t& result, int64_t secs, int64_t oldTicks,
|
||||
int64_t oldTicksPerSec, int64_t newTicksPerSec) {
|
||||
result = secs * newTicksPerSec;
|
||||
result += oldTicks * newTicksPerSec / oldTicksPerSec;
|
||||
|
||||
int64_t oldPerNew = oldTicksPerSec / newTicksPerSec;
|
||||
if (oldPerNew && ((oldTicks % oldPerNew) >= (oldPerNew / 2))) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Converts struct timespec to arbitrary-sized ticks since epoch
|
||||
*/
|
||||
static const void toTicks(int64_t& result,
|
||||
const struct timespec& value,
|
||||
int64_t ticksPerSec) {
|
||||
return toTicks(result, value.tv_sec, value.tv_nsec, NS_PER_S, ticksPerSec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts struct timeval to arbitrary-sized ticks since epoch
|
||||
*/
|
||||
static const void toTicks(int64_t& result,
|
||||
const struct timeval& value,
|
||||
int64_t ticksPerSec) {
|
||||
return toTicks(result, value.tv_sec, value.tv_usec, US_PER_S, ticksPerSec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts struct timespec to milliseconds
|
||||
*/
|
||||
static const void toMilliseconds(int64_t& result,
|
||||
const struct timespec& value) {
|
||||
return toTicks(result, value, MS_PER_S);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts struct timeval to milliseconds
|
||||
*/
|
||||
static const void toMilliseconds(int64_t& result,
|
||||
const struct timeval& value) {
|
||||
return toTicks(result, value, MS_PER_S);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts struct timespec to microseconds
|
||||
*/
|
||||
static const void toUsec(int64_t& result, const struct timespec& value) {
|
||||
return toTicks(result, value, US_PER_S);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts struct timeval to microseconds
|
||||
*/
|
||||
static const void toUsec(int64_t& result, const struct timeval& value) {
|
||||
return toTicks(result, value, US_PER_S);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current time as a number of arbitrary-size ticks from epoch
|
||||
*/
|
||||
static const int64_t currentTimeTicks(int64_t ticksPerSec);
|
||||
|
||||
/**
|
||||
* Get current time as milliseconds from epoch
|
||||
*/
|
||||
static const int64_t currentTime() { return currentTimeTicks(MS_PER_S); }
|
||||
|
||||
/**
|
||||
* Get current time as micros from epoch
|
||||
*/
|
||||
static const int64_t currentTimeUsec() { return currentTimeTicks(US_PER_S); }
|
||||
|
||||
/**
|
||||
* Get monotonic time as a number of arbitrary-size ticks from some
|
||||
* unspecified starting point.
|
||||
*
|
||||
* This may fall back to the current time (potentially non-monotonic) on
|
||||
* systems that do not support monotonic time.
|
||||
*/
|
||||
static const int64_t monotonicTimeTicks(int64_t ticksPerSec);
|
||||
|
||||
/**
|
||||
* Get monotonic time as milliseconds.
|
||||
*/
|
||||
static const int64_t monotonicTime() { return monotonicTimeTicks(MS_PER_S); }
|
||||
|
||||
/**
|
||||
* Get current time as micros from epoch
|
||||
*/
|
||||
static const int64_t monotonicTimeUsec() {
|
||||
return monotonicTimeTicks(US_PER_S);
|
||||
}
|
||||
};
|
||||
|
||||
}}} // apache::thrift::concurrency
|
||||
|
||||
#endif // #ifndef _THRIFT_CONCURRENCY_UTIL_H_
|
|
@ -0,0 +1,45 @@
|
|||
# TARGETS file for thrift/lib/cpp/concurrency/test
|
||||
|
||||
cpp_binary (
|
||||
name = "test",
|
||||
srcs = [
|
||||
"Tests.cpp"
|
||||
],
|
||||
deps = [
|
||||
"@/thrift/lib/cpp/concurrency"
|
||||
]
|
||||
)
|
||||
|
||||
cpp_unittest (
|
||||
name = "test_mutex",
|
||||
srcs = [
|
||||
"RWMutexTest.cpp"
|
||||
],
|
||||
deps = [
|
||||
"@/thrift/lib/cpp/concurrency",
|
||||
"@/thrift/lib/cpp:thrift",
|
||||
],
|
||||
external_deps = [
|
||||
('boost', '>= 1.37', 'boost_unit_test_framework'),
|
||||
],
|
||||
type = 'boost',
|
||||
owner = 'putivsky',
|
||||
emails = ['adback-dev@lists.facebook.com',
|
||||
'thrift-team@lists.facebook.com'],
|
||||
|
||||
)
|
||||
|
||||
cpp_unittest(
|
||||
name = 'ThreadManagerTests',
|
||||
srcs = [
|
||||
'ThreadManagerTests.cpp',
|
||||
],
|
||||
deps = [
|
||||
'@/thrift/lib/cpp/concurrency',
|
||||
],
|
||||
external_deps = [
|
||||
('boost', '>= 1.37', 'boost_unit_test_framework'),
|
||||
],
|
||||
type = 'boost',
|
||||
emails = ['thrift-team@lists.facebook.com'],
|
||||
)
|
|
@ -0,0 +1,354 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "thrift/lib/cpp/config.h"
|
||||
#include "thrift/lib/cpp/concurrency/Thread.h"
|
||||
#include "thrift/lib/cpp/concurrency/PosixThreadFactory.h"
|
||||
#include "thrift/lib/cpp/concurrency/Monitor.h"
|
||||
#include "thrift/lib/cpp/concurrency/Util.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
namespace apache { namespace thrift { namespace concurrency { namespace test {
|
||||
|
||||
using boost::shared_ptr;
|
||||
using namespace apache::thrift::concurrency;
|
||||
|
||||
/**
|
||||
* ThreadManagerTests class
|
||||
*
|
||||
* @version $Id:$
|
||||
*/
|
||||
class ThreadFactoryTests {
|
||||
|
||||
public:
|
||||
|
||||
static const double ERROR;
|
||||
|
||||
class Task: public Runnable {
|
||||
|
||||
public:
|
||||
|
||||
Task() {}
|
||||
|
||||
void run() {
|
||||
std::cout << "\t\t\tHello World" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hello world test
|
||||
*/
|
||||
bool helloWorldTest() {
|
||||
|
||||
PosixThreadFactory threadFactory = PosixThreadFactory();
|
||||
|
||||
shared_ptr<Task> task = shared_ptr<Task>(new ThreadFactoryTests::Task());
|
||||
|
||||
shared_ptr<Thread> thread = threadFactory.newThread(task);
|
||||
|
||||
thread->start();
|
||||
|
||||
thread->join();
|
||||
|
||||
std::cout << "\t\t\tSuccess!" << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reap N threads
|
||||
*/
|
||||
class ReapNTask: public Runnable {
|
||||
|
||||
public:
|
||||
|
||||
ReapNTask(Monitor& monitor, int& activeCount) :
|
||||
_monitor(monitor),
|
||||
_count(activeCount) {}
|
||||
|
||||
void run() {
|
||||
Synchronized s(_monitor);
|
||||
|
||||
_count--;
|
||||
|
||||
//std::cout << "\t\t\tthread count: " << _count << std::endl;
|
||||
|
||||
if (_count == 0) {
|
||||
_monitor.notify();
|
||||
}
|
||||
}
|
||||
|
||||
Monitor& _monitor;
|
||||
|
||||
int& _count;
|
||||
};
|
||||
|
||||
bool reapNThreads(int loop=1, int count=10) {
|
||||
|
||||
PosixThreadFactory threadFactory = PosixThreadFactory();
|
||||
|
||||
Monitor* monitor = new Monitor();
|
||||
|
||||
for(int lix = 0; lix < loop; lix++) {
|
||||
|
||||
int* activeCount = new int(count);
|
||||
|
||||
std::set<shared_ptr<Thread> > threads;
|
||||
|
||||
int tix;
|
||||
|
||||
for (tix = 0; tix < count; tix++) {
|
||||
try {
|
||||
threads.insert(threadFactory.newThread(shared_ptr<Runnable>(new ReapNTask(*monitor, *activeCount))));
|
||||
} catch(SystemResourceException& e) {
|
||||
std::cout << "\t\t\tfailed to create " << lix * count + tix << " thread " << e.what() << std::endl;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
tix = 0;
|
||||
for (std::set<shared_ptr<Thread> >::const_iterator thread = threads.begin(); thread != threads.end(); tix++, ++thread) {
|
||||
|
||||
try {
|
||||
(*thread)->start();
|
||||
} catch(SystemResourceException& e) {
|
||||
std::cout << "\t\t\tfailed to start " << lix * count + tix << " thread " << e.what() << std::endl;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Synchronized s(*monitor);
|
||||
while (*activeCount > 0) {
|
||||
monitor->wait(1000);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\t\t\treaped " << lix * count << " threads" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "\t\t\tSuccess!" << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class SynchStartTask: public Runnable {
|
||||
|
||||
public:
|
||||
|
||||
enum STATE {
|
||||
UNINITIALIZED,
|
||||
STARTING,
|
||||
STARTED,
|
||||
STOPPING,
|
||||
STOPPED
|
||||
};
|
||||
|
||||
SynchStartTask(Monitor& monitor, volatile STATE& state) :
|
||||
_monitor(monitor),
|
||||
_state(state) {}
|
||||
|
||||
void run() {
|
||||
{
|
||||
Synchronized s(_monitor);
|
||||
if (_state == SynchStartTask::STARTING) {
|
||||
_state = SynchStartTask::STARTED;
|
||||
_monitor.notify();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Synchronized s(_monitor);
|
||||
while (_state == SynchStartTask::STARTED) {
|
||||
_monitor.wait();
|
||||
}
|
||||
|
||||
if (_state == SynchStartTask::STOPPING) {
|
||||
_state = SynchStartTask::STOPPED;
|
||||
_monitor.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Monitor& _monitor;
|
||||
volatile STATE& _state;
|
||||
};
|
||||
|
||||
bool synchStartTest() {
|
||||
|
||||
Monitor monitor;
|
||||
|
||||
SynchStartTask::STATE state = SynchStartTask::UNINITIALIZED;
|
||||
|
||||
shared_ptr<SynchStartTask> task = shared_ptr<SynchStartTask>(new SynchStartTask(monitor, state));
|
||||
|
||||
PosixThreadFactory threadFactory = PosixThreadFactory();
|
||||
|
||||
shared_ptr<Thread> thread = threadFactory.newThread(task);
|
||||
|
||||
if (state == SynchStartTask::UNINITIALIZED) {
|
||||
|
||||
state = SynchStartTask::STARTING;
|
||||
|
||||
thread->start();
|
||||
}
|
||||
|
||||
{
|
||||
Synchronized s(monitor);
|
||||
while (state == SynchStartTask::STARTING) {
|
||||
monitor.wait();
|
||||
}
|
||||
}
|
||||
|
||||
assert(state != SynchStartTask::STARTING);
|
||||
|
||||
{
|
||||
Synchronized s(monitor);
|
||||
|
||||
try {
|
||||
monitor.wait(100);
|
||||
} catch(TimedOutException& e) {
|
||||
}
|
||||
|
||||
if (state == SynchStartTask::STARTED) {
|
||||
|
||||
state = SynchStartTask::STOPPING;
|
||||
|
||||
monitor.notify();
|
||||
}
|
||||
|
||||
while (state == SynchStartTask::STOPPING) {
|
||||
monitor.wait();
|
||||
}
|
||||
}
|
||||
|
||||
assert(state == SynchStartTask::STOPPED);
|
||||
|
||||
bool success = true;
|
||||
|
||||
std::cout << "\t\t\t" << (success ? "Success" : "Failure") << "!" << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** See how accurate monitor timeout is. */
|
||||
|
||||
bool monitorTimeoutTest(size_t count=1000, int64_t timeout=10) {
|
||||
|
||||
Monitor monitor;
|
||||
|
||||
int64_t startTime = Util::currentTime();
|
||||
|
||||
for (size_t ix = 0; ix < count; ix++) {
|
||||
{
|
||||
Synchronized s(monitor);
|
||||
try {
|
||||
monitor.wait(timeout);
|
||||
} catch(TimedOutException& e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t endTime = Util::currentTime();
|
||||
|
||||
double error = ((endTime - startTime) - (count * timeout)) / (double)(count * timeout);
|
||||
|
||||
if (error < 0.0) {
|
||||
|
||||
error *= 1.0;
|
||||
}
|
||||
|
||||
bool success = error < ThreadFactoryTests::ERROR;
|
||||
|
||||
std::cout << "\t\t\t" << (success ? "Success" : "Failure") << "! expected time: " << count * timeout << "ms elapsed time: "<< endTime - startTime << "ms error%: " << error * 100.0 << std::endl;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
class FloodTask : public Runnable {
|
||||
public:
|
||||
|
||||
FloodTask(const size_t id) :_id(id) {}
|
||||
~FloodTask(){
|
||||
if(_id % 1000 == 0) {
|
||||
std::cout << "\t\tthread " << _id << " done" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void run(){
|
||||
if(_id % 1000 == 0) {
|
||||
std::cout << "\t\tthread " << _id << " started" << std::endl;
|
||||
}
|
||||
|
||||
usleep(1);
|
||||
}
|
||||
const size_t _id;
|
||||
};
|
||||
|
||||
void foo(PosixThreadFactory *tf) {
|
||||
}
|
||||
|
||||
bool floodNTest(size_t loop=1, size_t count=100000) {
|
||||
|
||||
bool success = false;
|
||||
|
||||
for(size_t lix = 0; lix < loop; lix++) {
|
||||
|
||||
PosixThreadFactory threadFactory = PosixThreadFactory();
|
||||
threadFactory.setDetached(true);
|
||||
|
||||
for(size_t tix = 0; tix < count; tix++) {
|
||||
|
||||
try {
|
||||
|
||||
shared_ptr<FloodTask> task(new FloodTask(lix * count + tix ));
|
||||
|
||||
shared_ptr<Thread> thread = threadFactory.newThread(task);
|
||||
|
||||
thread->start();
|
||||
|
||||
usleep(1);
|
||||
|
||||
} catch (TException& e) {
|
||||
|
||||
std::cout << "\t\t\tfailed to start " << lix * count + tix << " thread " << e.what() << std::endl;
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\t\t\tflooded " << (lix + 1) * count << " threads" << std::endl;
|
||||
|
||||
success = true;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
};
|
||||
|
||||
const double ThreadFactoryTests::ERROR = .20;
|
||||
|
||||
}}}} // apache::thrift::concurrency::test
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "thrift/lib/cpp/concurrency/TimerManager.h"
|
||||
#include "thrift/lib/cpp/concurrency/PosixThreadFactory.h"
|
||||
#include "thrift/lib/cpp/concurrency/Monitor.h"
|
||||
#include "thrift/lib/cpp/concurrency/Util.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace apache { namespace thrift { namespace concurrency { namespace test {
|
||||
|
||||
using namespace apache::thrift::concurrency;
|
||||
|
||||
/**
|
||||
* ThreadManagerTests class
|
||||
*
|
||||
* @version $Id:$
|
||||
*/
|
||||
class TimerManagerTests {
|
||||
|
||||
public:
|
||||
|
||||
static const double ERROR;
|
||||
|
||||
class Task: public Runnable {
|
||||
public:
|
||||
|
||||
Task(Monitor& monitor, int64_t timeout) :
|
||||
_timeout(timeout),
|
||||
_startTime(Util::currentTime()),
|
||||
_monitor(monitor),
|
||||
_success(false),
|
||||
_done(false) {}
|
||||
|
||||
~Task() { std::cerr << this << std::endl; }
|
||||
|
||||
void run() {
|
||||
|
||||
_endTime = Util::currentTime();
|
||||
|
||||
// Figure out error percentage
|
||||
|
||||
int64_t delta = _endTime - _startTime;
|
||||
|
||||
|
||||
delta = delta > _timeout ? delta - _timeout : _timeout - delta;
|
||||
|
||||
float error = delta / _timeout;
|
||||
|
||||
if(error < ERROR) {
|
||||
_success = true;
|
||||
}
|
||||
|
||||
_done = true;
|
||||
|
||||
std::cout << "\t\t\tTimerManagerTests::Task[" << this << "] done" << std::endl; //debug
|
||||
|
||||
{Synchronized s(_monitor);
|
||||
_monitor.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t _timeout;
|
||||
int64_t _startTime;
|
||||
int64_t _endTime;
|
||||
Monitor& _monitor;
|
||||
bool _success;
|
||||
bool _done;
|
||||
};
|
||||
|
||||
/**
|
||||
* This test creates two tasks and waits for the first to expire within 10%
|
||||
* of the expected expiration time. It then verifies that the timer manager
|
||||
* properly clean up itself and the remaining orphaned timeout task when the
|
||||
* manager goes out of scope and its destructor is called.
|
||||
*/
|
||||
bool test00(int64_t timeout=1000LL) {
|
||||
|
||||
shared_ptr<TimerManagerTests::Task> orphanTask = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, 10 * timeout));
|
||||
|
||||
{
|
||||
|
||||
TimerManager timerManager;
|
||||
|
||||
timerManager.threadFactory(shared_ptr<PosixThreadFactory>(new PosixThreadFactory()));
|
||||
|
||||
timerManager.start();
|
||||
|
||||
assert(timerManager.state() == TimerManager::STARTED);
|
||||
|
||||
shared_ptr<TimerManagerTests::Task> task = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout));
|
||||
|
||||
{
|
||||
Synchronized s(_monitor);
|
||||
|
||||
timerManager.add(orphanTask, 10 * timeout);
|
||||
|
||||
timerManager.add(task, timeout);
|
||||
|
||||
_monitor.wait();
|
||||
}
|
||||
|
||||
assert(task->_done);
|
||||
|
||||
|
||||
std::cout << "\t\t\t" << (task->_success ? "Success" : "Failure") << "!" << std::endl;
|
||||
}
|
||||
|
||||
// timerManager.stop(); This is where it happens via destructor
|
||||
|
||||
assert(!orphanTask->_done);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend class TestTask;
|
||||
|
||||
Monitor _monitor;
|
||||
};
|
||||
|
||||
const double TimerManagerTests::ERROR = .20;
|
||||
|
||||
}}}} // apache::thrift::concurrency
|
||||
|
|
@ -0,0 +1,316 @@
|
|||
/* config.h. Generated by configure. */
|
||||
/*
|
||||
* Generated by simpkins on 2009-11-18,
|
||||
* on an x86_64 CentOS 5.2 system.
|
||||
*/
|
||||
/* config.hin. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define if the AI_ADDRCONFIG symbol is unavailable */
|
||||
/* #undef AI_ADDRCONFIG */
|
||||
|
||||
/* Possible value for SIGNED_RIGHT_SHIFT_IS */
|
||||
#define ARITHMETIC_RIGHT_SHIFT 1
|
||||
|
||||
/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
|
||||
systems. This function is required for `alloca.c' support on those systems.
|
||||
*/
|
||||
/* #undef CRAY_STACKSEG_END */
|
||||
|
||||
/* Define to 1 if using `alloca.c'. */
|
||||
/* #undef C_ALLOCA */
|
||||
|
||||
/* Define to 1 if you have `alloca', as a function or macro. */
|
||||
#define HAVE_ALLOCA 1
|
||||
|
||||
/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
|
||||
*/
|
||||
#define HAVE_ALLOCA_H 1
|
||||
|
||||
/* Define to 1 if you have the <arpa/inet.h> header file. */
|
||||
#define HAVE_ARPA_INET_H 1
|
||||
|
||||
/* define if the Boost library is available */
|
||||
#define HAVE_BOOST
|
||||
|
||||
/* Define to 1 if you have the `bzero' function. */
|
||||
#define HAVE_BZERO 1
|
||||
|
||||
/* Define to 1 if you have the `clock_gettime' function. */
|
||||
#define HAVE_CLOCK_GETTIME 1
|
||||
|
||||
/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
|
||||
don't. */
|
||||
#define HAVE_DECL_STRERROR_R 1
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#define HAVE_DLFCN_H 1
|
||||
|
||||
/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
|
||||
/* #undef HAVE_DOPRNT */
|
||||
|
||||
/* Define to 1 if you have the <endian.h> header file. */
|
||||
#define HAVE_ENDIAN_H 1
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#define HAVE_FCNTL_H 1
|
||||
|
||||
/* Define to 1 if you have the `ftruncate' function. */
|
||||
#define HAVE_FTRUNCATE 1
|
||||
|
||||
/* Define to 1 if you have the `gethostbyname' function. */
|
||||
#define HAVE_GETHOSTBYNAME 1
|
||||
|
||||
/* Define to 1 if you have the `gettimeofday' function. */
|
||||
#define HAVE_GETTIMEOFDAY 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* define if libevent is available */
|
||||
#define HAVE_LIBEVENT
|
||||
|
||||
/* Define to 1 if you have the <libintl.h> header file. */
|
||||
#define HAVE_LIBINTL_H 1
|
||||
|
||||
/* Define to 1 if you have the `pthread' library (-lpthread). */
|
||||
#define HAVE_LIBPTHREAD 1
|
||||
|
||||
/* Define to 1 if you have the `rt' library (-lrt). */
|
||||
#define HAVE_LIBRT 1
|
||||
|
||||
/* Define to 1 if you have the `socket' library (-lsocket). */
|
||||
/* #undef HAVE_LIBSOCKET */
|
||||
|
||||
/* Define to 1 if you have the <limits.h> header file. */
|
||||
#define HAVE_LIMITS_H 1
|
||||
|
||||
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
|
||||
to 0 otherwise. */
|
||||
#define HAVE_MALLOC 1
|
||||
|
||||
/* Define to 1 if you have the <malloc.h> header file. */
|
||||
#define HAVE_MALLOC_H 1
|
||||
|
||||
/* Define to 1 if you have the `memmove' function. */
|
||||
#define HAVE_MEMMOVE 1
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the `memset' function. */
|
||||
#define HAVE_MEMSET 1
|
||||
|
||||
/* Define to 1 if you have the `mkdir' function. */
|
||||
#define HAVE_MKDIR 1
|
||||
|
||||
/* Define to 1 if you have the <netdb.h> header file. */
|
||||
#define HAVE_NETDB_H 1
|
||||
|
||||
/* Define to 1 if you have the <netinet/in.h> header file. */
|
||||
#define HAVE_NETINET_IN_H 1
|
||||
|
||||
/* Define to 1 if you have the <pthread.h> header file. */
|
||||
#define HAVE_PTHREAD_H 1
|
||||
|
||||
/* Define to 1 if the system has the type `ptrdiff_t'. */
|
||||
#define HAVE_PTRDIFF_T 1
|
||||
|
||||
/* Define to 1 if your system has a GNU libc compatible `realloc' function,
|
||||
and to 0 otherwise. */
|
||||
#define HAVE_REALLOC 1
|
||||
|
||||
/* Define to 1 if you have the `realpath' function. */
|
||||
#define HAVE_REALPATH 1
|
||||
|
||||
/* Define to 1 if you have the `sched_get_priority_max' function. */
|
||||
#define HAVE_SCHED_GET_PRIORITY_MAX 1
|
||||
|
||||
/* Define to 1 if you have the `sched_get_priority_min' function. */
|
||||
#define HAVE_SCHED_GET_PRIORITY_MIN 1
|
||||
|
||||
/* Define to 1 if you have the `select' function. */
|
||||
#define HAVE_SELECT 1
|
||||
|
||||
/* Define to 1 if you have the `socket' function. */
|
||||
#define HAVE_SOCKET 1
|
||||
|
||||
/* Define to 1 if you have the `sqrt' function. */
|
||||
#define HAVE_SQRT 1
|
||||
|
||||
/* Define to 1 if `stat' has the bug that it succeeds when given the
|
||||
zero-length file name argument. */
|
||||
/* #undef HAVE_STAT_EMPTY_STRING_BUG */
|
||||
|
||||
/* Define to 1 if stdbool.h conforms to C99. */
|
||||
#define HAVE_STDBOOL_H 1
|
||||
|
||||
/* Define to 1 if you have the <stddef.h> header file. */
|
||||
#define HAVE_STDDEF_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the `strchr' function. */
|
||||
#define HAVE_STRCHR 1
|
||||
|
||||
/* Define to 1 if you have the `strdup' function. */
|
||||
#define HAVE_STRDUP 1
|
||||
|
||||
/* Define to 1 if you have the `strerror' function. */
|
||||
#define HAVE_STRERROR 1
|
||||
|
||||
/* Define to 1 if you have the `strerror_r' function. */
|
||||
#define HAVE_STRERROR_R 1
|
||||
|
||||
/* Define to 1 if you have the `strftime' function. */
|
||||
#define HAVE_STRFTIME 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the `strstr' function. */
|
||||
#define HAVE_STRSTR 1
|
||||
|
||||
/* Define to 1 if you have the `strtol' function. */
|
||||
#define HAVE_STRTOL 1
|
||||
|
||||
/* Define to 1 if you have the `strtoul' function. */
|
||||
#define HAVE_STRTOUL 1
|
||||
|
||||
/* Define to 1 if you have the <sys/select.h> header file. */
|
||||
#define HAVE_SYS_SELECT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#define HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
#define HAVE_SYS_TIME_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the `vprintf' function. */
|
||||
#define HAVE_VPRINTF 1
|
||||
|
||||
/* define if zlib is available */
|
||||
#define HAVE_ZLIB
|
||||
|
||||
/* Define to 1 if the system has the type `_Bool'. */
|
||||
/* #undef HAVE__BOOL */
|
||||
|
||||
/* Possible value for SIGNED_RIGHT_SHIFT_IS */
|
||||
#define LOGICAL_RIGHT_SHIFT 2
|
||||
|
||||
/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
|
||||
slash. */
|
||||
#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "thrift"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT ""
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "thrift"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "thrift 20080411"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "thrift"
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "20080411"
|
||||
|
||||
/* Define to the type of arg 1 for `select'. */
|
||||
#define SELECT_TYPE_ARG1 int
|
||||
|
||||
/* Define to the type of args 2, 3 and 4 for `select'. */
|
||||
#define SELECT_TYPE_ARG234 (fd_set *)
|
||||
|
||||
/* Define to the type of arg 5 for `select'. */
|
||||
#define SELECT_TYPE_ARG5 (struct timeval *)
|
||||
|
||||
/* Indicates the effect of the right shift operator on negative signed
|
||||
integers */
|
||||
#define SIGNED_RIGHT_SHIFT_IS 1
|
||||
|
||||
/* If using the C implementation of alloca, define if you know the
|
||||
direction of stack growth for your system; otherwise it will be
|
||||
automatically deduced at run-time.
|
||||
STACK_DIRECTION > 0 => grows toward higher addresses
|
||||
STACK_DIRECTION < 0 => grows toward lower addresses
|
||||
STACK_DIRECTION = 0 => direction of growth unknown */
|
||||
/* #undef STACK_DIRECTION */
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Define to 1 if strerror_r returns char *. */
|
||||
#define STRERROR_R_CHAR_P 1
|
||||
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#define TIME_WITH_SYS_TIME 1
|
||||
|
||||
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
|
||||
/* #undef TM_IN_SYS_TIME */
|
||||
|
||||
/* Possible value for SIGNED_RIGHT_SHIFT_IS */
|
||||
#define UNKNOWN_RIGHT_SHIFT 3
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "20080411"
|
||||
|
||||
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
|
||||
`char[]'. */
|
||||
#define YYTEXT_POINTER 1
|
||||
|
||||
/* Calls Google Perftools ProfilerRegisterThread() on pthread creation
|
||||
|
||||
Set define to 0. It is exposing a linux 6.12 posix bug when an
|
||||
application receives a SIGKILL, which causes the machine to crash.
|
||||
*/
|
||||
#ifndef GOOGLE_PERFTOOLS_REGISTER_THREAD
|
||||
# define GOOGLE_PERFTOOLS_REGISTER_THREAD 0
|
||||
#endif
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
/* #undef const */
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef __cplusplus
|
||||
/* #undef inline */
|
||||
#endif
|
||||
|
||||
/* Define to rpl_malloc if the replacement function should be used. */
|
||||
/* #undef malloc */
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
/* #undef mode_t */
|
||||
|
||||
/* Define to `long' if <sys/types.h> does not define. */
|
||||
/* #undef off_t */
|
||||
|
||||
/* Define to rpl_realloc if the replacement function should be used. */
|
||||
/* #undef realloc */
|
||||
|
||||
/* Define to `unsigned' if <sys/types.h> does not define. */
|
||||
/* #undef size_t */
|
||||
|
||||
/* Define to empty if the keyword `volatile' does not work. Warning: valid
|
||||
code using `volatile' can become incorrect without. Disable with care. */
|
||||
/* #undef volatile */
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef PEEKPROCESSOR_H
|
||||
#define PEEKPROCESSOR_H
|
||||
|
||||
#include <string>
|
||||
#include "thrift/lib/cpp/TProcessor.h"
|
||||
#include "thrift/lib/cpp/transport/TTransport.h"
|
||||
#include "thrift/lib/cpp/transport/TTransportUtils.h"
|
||||
#include "thrift/lib/cpp/transport/TBufferTransports.h"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace apache { namespace thrift { namespace processor {
|
||||
|
||||
namespace server {
|
||||
class TConnectionContext;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class for peeking at the raw data that is being processed by another processor
|
||||
* and gives the derived class a chance to change behavior accordingly
|
||||
*
|
||||
*/
|
||||
class PeekProcessor : public apache::thrift::TProcessor {
|
||||
|
||||
public:
|
||||
PeekProcessor();
|
||||
virtual ~PeekProcessor();
|
||||
|
||||
// Input here: actualProcessor - the underlying processor
|
||||
// protocolFactory - the protocol factory used to wrap the memory buffer
|
||||
// transportFactory - this TPipedTransportFactory is used to wrap the source transport
|
||||
// via a call to getPipedTransport
|
||||
void initialize(boost::shared_ptr<apache::thrift::TProcessor> actualProcessor,
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocolFactory> protocolFactory,
|
||||
boost::shared_ptr<apache::thrift::transport::TPipedTransportFactory> transportFactory);
|
||||
|
||||
boost::shared_ptr<apache::thrift::transport::TTransport> getPipedTransport(boost::shared_ptr<apache::thrift::transport::TTransport> in);
|
||||
|
||||
void setTargetTransport(boost::shared_ptr<apache::thrift::transport::TTransport> targetTransport);
|
||||
|
||||
virtual bool process(boost::shared_ptr<apache::thrift::protocol::TProtocol> in,
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol> out,
|
||||
TConnectionContext* connectionContext);
|
||||
|
||||
// The following three functions can be overloaded by child classes to
|
||||
// achieve desired peeking behavior
|
||||
virtual void peekName(const std::string& fname);
|
||||
virtual void peekBuffer(uint8_t* buffer, uint32_t size);
|
||||
virtual void peek(boost::shared_ptr<apache::thrift::protocol::TProtocol> in,
|
||||
apache::thrift::protocol::TType ftype,
|
||||
int16_t fid);
|
||||
virtual void peekEnd();
|
||||
|
||||
private:
|
||||
boost::shared_ptr<apache::thrift::TProcessor> actualProcessor_;
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol> pipedProtocol_;
|
||||
boost::shared_ptr<apache::thrift::transport::TPipedTransportFactory> transportFactory_;
|
||||
boost::shared_ptr<apache::thrift::transport::TMemoryBuffer> memoryBuffer_;
|
||||
boost::shared_ptr<apache::thrift::transport::TTransport> targetTransport_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::processor
|
||||
|
||||
#endif
|
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef STATSPROCESSOR_H
|
||||
#define STATSPROCESSOR_H
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "thrift/lib/cpp/transport/TTransport.h"
|
||||
#include "thrift/lib/cpp/protocol/TProtocol.h"
|
||||
#include "thrift/lib/cpp/TProcessor.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace processor {
|
||||
|
||||
/*
|
||||
* Class for keeping track of function call statistics and printing them if desired
|
||||
*
|
||||
*/
|
||||
class StatsProcessor : public apache::thrift::TProcessor {
|
||||
public:
|
||||
StatsProcessor(bool print, bool frequency)
|
||||
: print_(print),
|
||||
frequency_(frequency)
|
||||
{}
|
||||
virtual ~StatsProcessor() {};
|
||||
|
||||
virtual bool process(boost::shared_ptr<apache::thrift::protocol::TProtocol> piprot,
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol> poprot,
|
||||
void* serverContext) {
|
||||
|
||||
piprot_ = piprot;
|
||||
|
||||
std::string fname;
|
||||
apache::thrift::protocol::TMessageType mtype;
|
||||
int32_t seqid;
|
||||
|
||||
piprot_->readMessageBegin(fname, mtype, seqid);
|
||||
if (mtype != apache::thrift::protocol::T_CALL) {
|
||||
if (print_) {
|
||||
printf("Unknown message type\n");
|
||||
}
|
||||
throw apache::thrift::TLibraryException("Unexpected message type");
|
||||
}
|
||||
if (print_) {
|
||||
printf("%s (", fname.c_str());
|
||||
}
|
||||
if (frequency_) {
|
||||
if (frequency_map_.find(fname) != frequency_map_.end()) {
|
||||
frequency_map_[fname]++;
|
||||
} else {
|
||||
frequency_map_[fname] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
apache::thrift::protocol::TType ftype;
|
||||
int16_t fid;
|
||||
|
||||
while (true) {
|
||||
piprot_->readFieldBegin(fname, ftype, fid);
|
||||
if (ftype == apache::thrift::protocol::T_STOP) {
|
||||
break;
|
||||
}
|
||||
|
||||
printAndPassToBuffer(ftype);
|
||||
if (print_) {
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
|
||||
if (print_) {
|
||||
printf("\b\b)\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::map<std::string, int64_t>& get_frequency_map() {
|
||||
return frequency_map_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void printAndPassToBuffer(apache::thrift::protocol::TType ftype) {
|
||||
switch (ftype) {
|
||||
case apache::thrift::protocol::T_BOOL:
|
||||
{
|
||||
bool boolv;
|
||||
piprot_->readBool(boolv);
|
||||
if (print_) {
|
||||
printf("%d", boolv);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case apache::thrift::protocol::T_BYTE:
|
||||
{
|
||||
int8_t bytev;
|
||||
piprot_->readByte(bytev);
|
||||
if (print_) {
|
||||
printf("%d", bytev);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case apache::thrift::protocol::T_I16:
|
||||
{
|
||||
int16_t i16;
|
||||
piprot_->readI16(i16);
|
||||
if (print_) {
|
||||
printf("%d", i16);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case apache::thrift::protocol::T_I32:
|
||||
{
|
||||
int32_t i32;
|
||||
piprot_->readI32(i32);
|
||||
if (print_) {
|
||||
printf("%d", i32);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case apache::thrift::protocol::T_I64:
|
||||
{
|
||||
int64_t i64;
|
||||
piprot_->readI64(i64);
|
||||
if (print_) {
|
||||
printf("%ld", i64);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case apache::thrift::protocol::T_DOUBLE:
|
||||
{
|
||||
double dub;
|
||||
piprot_->readDouble(dub);
|
||||
if (print_) {
|
||||
printf("%f", dub);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case apache::thrift::protocol::T_STRING:
|
||||
{
|
||||
std::string str;
|
||||
piprot_->readString(str);
|
||||
if (print_) {
|
||||
printf("%s", str.c_str());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case apache::thrift::protocol::T_STRUCT:
|
||||
{
|
||||
std::string name;
|
||||
int16_t fid;
|
||||
apache::thrift::protocol::TType ftype;
|
||||
piprot_->readStructBegin(name);
|
||||
if (print_) {
|
||||
printf("<");
|
||||
}
|
||||
while (true) {
|
||||
piprot_->readFieldBegin(name, ftype, fid);
|
||||
if (ftype == apache::thrift::protocol::T_STOP) {
|
||||
break;
|
||||
}
|
||||
printAndPassToBuffer(ftype);
|
||||
if (print_) {
|
||||
printf(",");
|
||||
}
|
||||
piprot_->readFieldEnd();
|
||||
}
|
||||
piprot_->readStructEnd();
|
||||
if (print_) {
|
||||
printf("\b>");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case apache::thrift::protocol::T_MAP:
|
||||
{
|
||||
apache::thrift::protocol::TType keyType;
|
||||
apache::thrift::protocol::TType valType;
|
||||
uint32_t i, size;
|
||||
piprot_->readMapBegin(keyType, valType, size);
|
||||
if (print_) {
|
||||
printf("{");
|
||||
}
|
||||
for (i = 0; i < size; i++) {
|
||||
printAndPassToBuffer(keyType);
|
||||
if (print_) {
|
||||
printf("=>");
|
||||
}
|
||||
printAndPassToBuffer(valType);
|
||||
if (print_) {
|
||||
printf(",");
|
||||
}
|
||||
}
|
||||
piprot_->readMapEnd();
|
||||
if (print_) {
|
||||
printf("\b}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case apache::thrift::protocol::T_SET:
|
||||
{
|
||||
apache::thrift::protocol::TType elemType;
|
||||
uint32_t i, size;
|
||||
piprot_->readSetBegin(elemType, size);
|
||||
if (print_) {
|
||||
printf("{");
|
||||
}
|
||||
for (i = 0; i < size; i++) {
|
||||
printAndPassToBuffer(elemType);
|
||||
if (print_) {
|
||||
printf(",");
|
||||
}
|
||||
}
|
||||
piprot_->readSetEnd();
|
||||
if (print_) {
|
||||
printf("\b}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case apache::thrift::protocol::T_LIST:
|
||||
{
|
||||
apache::thrift::protocol::TType elemType;
|
||||
uint32_t i, size;
|
||||
piprot_->readListBegin(elemType, size);
|
||||
if (print_) {
|
||||
printf("[");
|
||||
}
|
||||
for (i = 0; i < size; i++) {
|
||||
printAndPassToBuffer(elemType);
|
||||
if (print_) {
|
||||
printf(",");
|
||||
}
|
||||
}
|
||||
piprot_->readListEnd();
|
||||
if (print_) {
|
||||
printf("\b]");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<apache::thrift::protocol::TProtocol> piprot_;
|
||||
std::map<std::string, int64_t> frequency_map_;
|
||||
|
||||
bool print_;
|
||||
bool frequency_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::processor
|
||||
|
||||
#endif
|
|
@ -0,0 +1,9 @@
|
|||
# TARGETS file for thrift/lib/cpp/processor
|
||||
|
||||
cpp_library (
|
||||
name = "processor",
|
||||
srcs = [
|
||||
"PeekProcessor.cpp"
|
||||
],
|
||||
deps = [ "@/thrift/lib/cpp/transport" ]
|
||||
)
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef _THRIFT_TEST_EVENTLOG_H_
|
||||
#define _THRIFT_TEST_EVENTLOG_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/concurrency/Monitor.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace test {
|
||||
|
||||
// Initially I made EventType an enum, but using char* results
|
||||
// in much more readable error messages when there is a mismatch.
|
||||
// It also lets users of EventLog easily define their own new types.
|
||||
// Comparing the literal pointer values should be safe, barring any strange
|
||||
// linking setup that results in duplicate symbols.
|
||||
typedef const char* EventType;
|
||||
|
||||
struct Event {
|
||||
Event(EventType type, uint32_t connectionId, uint32_t callId,
|
||||
const std::string& message) :
|
||||
type(type),
|
||||
connectionId(connectionId),
|
||||
callId(callId),
|
||||
message(message) {}
|
||||
|
||||
EventType type;
|
||||
uint32_t connectionId;
|
||||
uint32_t callId;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
class EventLog {
|
||||
public:
|
||||
static EventType ET_LOG_END;
|
||||
static EventType ET_CONN_CREATED;
|
||||
static EventType ET_CONN_DESTROYED;
|
||||
static EventType ET_CALL_STARTED;
|
||||
static EventType ET_CALL_FINISHED;
|
||||
static EventType ET_PROCESS;
|
||||
static EventType ET_PRE_READ;
|
||||
static EventType ET_POST_READ;
|
||||
static EventType ET_PRE_WRITE;
|
||||
static EventType ET_POST_WRITE;
|
||||
static EventType ET_ASYNC_COMPLETE;
|
||||
static EventType ET_HANDLER_ERROR;
|
||||
|
||||
static EventType ET_CALL_INCREMENT_GENERATION;
|
||||
static EventType ET_CALL_GET_GENERATION;
|
||||
static EventType ET_CALL_ADD_STRING;
|
||||
static EventType ET_CALL_GET_STRINGS;
|
||||
static EventType ET_CALL_GET_DATA_WAIT;
|
||||
static EventType ET_CALL_ONEWAY_WAIT;
|
||||
static EventType ET_CALL_UNEXPECTED_EXCEPTION_WAIT;
|
||||
static EventType ET_CALL_EXCEPTION_WAIT;
|
||||
static EventType ET_WAIT_RETURN;
|
||||
static EventType ET_CALL_SET_VALUE;
|
||||
static EventType ET_CALL_GET_VALUE;
|
||||
|
||||
EventLog();
|
||||
|
||||
void append(EventType type, uint32_t connectionId, uint32_t callId,
|
||||
const std::string& message = "");
|
||||
|
||||
Event waitForEvent(int64_t timeout = 500);
|
||||
Event waitForConnEvent(uint32_t connId, int64_t timeout = 500);
|
||||
|
||||
protected:
|
||||
typedef std::list<Event> EventList;
|
||||
|
||||
concurrency::Monitor monitor_;
|
||||
EventList events_;
|
||||
uint32_t id_;
|
||||
|
||||
static uint32_t nextId_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::test
|
||||
|
||||
#endif // _THRIFT_TEST_EVENTLOG_H_
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef _THRIFT_PROCESSOR_TEST_HANDLERS_H_
|
||||
#define _THRIFT_PROCESSOR_TEST_HANDLERS_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/processor/test/EventLog.h"
|
||||
#include "thrift/lib/cpp/processor/test/gen-cpp/ChildService.h"
|
||||
|
||||
#include "thrift/lib/cpp/server/TConnectionContext.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace test {
|
||||
|
||||
class ParentHandler : virtual public ParentServiceIf {
|
||||
public:
|
||||
ParentHandler(const boost::shared_ptr<EventLog>& log) :
|
||||
triggerMonitor(&mutex_),
|
||||
generation_(0),
|
||||
wait_(false),
|
||||
log_(log) { }
|
||||
|
||||
int32_t incrementGeneration() {
|
||||
concurrency::Guard g(mutex_);
|
||||
log_->append(EventLog::ET_CALL_INCREMENT_GENERATION, 0, 0);
|
||||
return ++generation_;
|
||||
}
|
||||
|
||||
int32_t getGeneration() {
|
||||
concurrency::Guard g(mutex_);
|
||||
log_->append(EventLog::ET_CALL_GET_GENERATION, 0, 0);
|
||||
return generation_;
|
||||
}
|
||||
|
||||
void addString(const std::string& s) {
|
||||
concurrency::Guard g(mutex_);
|
||||
log_->append(EventLog::ET_CALL_ADD_STRING, 0, 0);
|
||||
strings_.push_back(s);
|
||||
}
|
||||
|
||||
void getStrings(std::vector<std::string>& _return) {
|
||||
concurrency::Guard g(mutex_);
|
||||
log_->append(EventLog::ET_CALL_GET_STRINGS, 0, 0);
|
||||
_return = strings_;
|
||||
}
|
||||
|
||||
void getDataWait(std::string& _return, int32_t length) {
|
||||
concurrency::Guard g(mutex_);
|
||||
log_->append(EventLog::ET_CALL_GET_DATA_WAIT, 0, 0);
|
||||
|
||||
blockUntilTriggered();
|
||||
|
||||
_return.append(length, 'a');
|
||||
}
|
||||
|
||||
void onewayWait() {
|
||||
concurrency::Guard g(mutex_);
|
||||
log_->append(EventLog::ET_CALL_ONEWAY_WAIT, 0, 0);
|
||||
|
||||
blockUntilTriggered();
|
||||
}
|
||||
|
||||
void exceptionWait(const std::string& message) {
|
||||
concurrency::Guard g(mutex_);
|
||||
log_->append(EventLog::ET_CALL_EXCEPTION_WAIT, 0, 0);
|
||||
|
||||
blockUntilTriggered();
|
||||
|
||||
MyError e;
|
||||
e.message = message;
|
||||
throw e;
|
||||
}
|
||||
|
||||
void unexpectedExceptionWait(const std::string& message) {
|
||||
concurrency::Guard g(mutex_);
|
||||
log_->append(EventLog::ET_CALL_UNEXPECTED_EXCEPTION_WAIT, 0, 0);
|
||||
|
||||
blockUntilTriggered();
|
||||
|
||||
MyError e;
|
||||
e.message = message;
|
||||
throw e;
|
||||
}
|
||||
|
||||
/**
|
||||
* After prepareTriggeredCall() is invoked, calls to any of the *Wait()
|
||||
* functions won't return until triggerPendingCalls() is invoked
|
||||
*
|
||||
* This has to be a separate function invoked by the main test thread
|
||||
* in order to to avoid race conditions.
|
||||
*/
|
||||
void prepareTriggeredCall() {
|
||||
concurrency::Guard g(mutex_);
|
||||
wait_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wake up all calls waiting in blockUntilTriggered()
|
||||
*/
|
||||
void triggerPendingCalls() {
|
||||
concurrency::Guard g(mutex_);
|
||||
wait_ = false;
|
||||
triggerMonitor.notifyAll();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* blockUntilTriggered() won't return until triggerPendingCalls() is invoked
|
||||
* in another thread.
|
||||
*
|
||||
* This should only be called when already holding mutex_.
|
||||
*/
|
||||
void blockUntilTriggered() {
|
||||
while (wait_) {
|
||||
triggerMonitor.waitForever();
|
||||
}
|
||||
|
||||
// Log an event when we return
|
||||
log_->append(EventLog::ET_WAIT_RETURN, 0, 0);
|
||||
}
|
||||
|
||||
concurrency::Mutex mutex_;
|
||||
concurrency::Monitor triggerMonitor;
|
||||
int32_t generation_;
|
||||
bool wait_;
|
||||
std::vector<std::string> strings_;
|
||||
boost::shared_ptr<EventLog> log_;
|
||||
};
|
||||
|
||||
class ChildHandler : public ParentHandler, virtual public ChildServiceIf {
|
||||
public:
|
||||
ChildHandler(const boost::shared_ptr<EventLog>& log) :
|
||||
ParentHandler(log),
|
||||
value_(0) {}
|
||||
|
||||
int32_t setValue(int32_t value) {
|
||||
concurrency::Guard g(mutex_);
|
||||
log_->append(EventLog::ET_CALL_SET_VALUE, 0, 0);
|
||||
|
||||
int32_t oldValue = value_;
|
||||
value_ = value;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
int32_t getValue() {
|
||||
concurrency::Guard g(mutex_);
|
||||
log_->append(EventLog::ET_CALL_GET_VALUE, 0, 0);
|
||||
|
||||
return value_;
|
||||
}
|
||||
|
||||
protected:
|
||||
int32_t value_;
|
||||
};
|
||||
|
||||
struct ConnContext {
|
||||
public:
|
||||
ConnContext(server::TConnectionContext* ctx, uint32_t id) :
|
||||
ctx(ctx),
|
||||
id(id) {}
|
||||
|
||||
server::TConnectionContext* ctx;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
struct CallContext {
|
||||
public:
|
||||
CallContext(ConnContext *context, uint32_t id, const std::string& name) :
|
||||
connContext(context),
|
||||
name(name),
|
||||
id(id) {}
|
||||
|
||||
ConnContext *connContext;
|
||||
std::string name;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
class ServerEventHandler : public server::TServerEventHandler {
|
||||
public:
|
||||
ServerEventHandler(const boost::shared_ptr<EventLog>& log) :
|
||||
nextId_(1),
|
||||
log_(log) {}
|
||||
|
||||
virtual void preServe(const transport::TSocketAddress*) {}
|
||||
|
||||
virtual void newConnection(server::TConnectionContext* ctx) {
|
||||
ConnContext* context = new ConnContext(ctx, nextId_);
|
||||
++nextId_;
|
||||
ctx->setUserData(context);
|
||||
log_->append(EventLog::ET_CONN_CREATED, context->id, 0);
|
||||
}
|
||||
|
||||
virtual void connectionDestroyed(server::TConnectionContext* ctx) {
|
||||
ConnContext* context = static_cast<ConnContext*>(ctx->getUserData());
|
||||
|
||||
if (ctx != context->ctx) {
|
||||
abort();
|
||||
}
|
||||
|
||||
log_->append(EventLog::ET_CONN_DESTROYED, context->id, 0);
|
||||
|
||||
delete context;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t nextId_;
|
||||
boost::shared_ptr<EventLog> log_;
|
||||
};
|
||||
|
||||
class ProcessorEventHandler : public TProcessorEventHandler {
|
||||
public:
|
||||
ProcessorEventHandler(const boost::shared_ptr<EventLog>& log) :
|
||||
nextId_(1),
|
||||
log_(log) {}
|
||||
|
||||
void* getContext(const char* fnName, TConnectionContext* serverContext) {
|
||||
ConnContext* connContext =
|
||||
reinterpret_cast<ConnContext*>(serverContext->getUserData());
|
||||
|
||||
CallContext* context = new CallContext(connContext, nextId_, fnName);
|
||||
++nextId_;
|
||||
|
||||
log_->append(EventLog::ET_CALL_STARTED, connContext->id, context->id,
|
||||
fnName);
|
||||
return context;
|
||||
}
|
||||
|
||||
void freeContext(void* ctx, const char* fnName) {
|
||||
CallContext* context = reinterpret_cast<CallContext*>(ctx);
|
||||
checkName(context, fnName);
|
||||
log_->append(EventLog::ET_CALL_FINISHED, context->connContext->id,
|
||||
context->id, fnName);
|
||||
delete context;
|
||||
}
|
||||
|
||||
void preRead(void* ctx, const char* fnName) {
|
||||
CallContext* context = reinterpret_cast<CallContext*>(ctx);
|
||||
checkName(context, fnName);
|
||||
log_->append(EventLog::ET_PRE_READ, context->connContext->id, context->id,
|
||||
fnName);
|
||||
}
|
||||
|
||||
void postRead(void* ctx, const char* fnName, uint32_t bytes) {
|
||||
CallContext* context = reinterpret_cast<CallContext*>(ctx);
|
||||
checkName(context, fnName);
|
||||
log_->append(EventLog::ET_POST_READ, context->connContext->id, context->id,
|
||||
fnName);
|
||||
}
|
||||
|
||||
void preWrite(void* ctx, const char* fnName) {
|
||||
CallContext* context = reinterpret_cast<CallContext*>(ctx);
|
||||
checkName(context, fnName);
|
||||
log_->append(EventLog::ET_PRE_WRITE, context->connContext->id, context->id,
|
||||
fnName);
|
||||
}
|
||||
|
||||
void postWrite(void* ctx, const char* fnName, uint32_t bytes) {
|
||||
CallContext* context = reinterpret_cast<CallContext*>(ctx);
|
||||
checkName(context, fnName);
|
||||
log_->append(EventLog::ET_POST_WRITE, context->connContext->id,
|
||||
context->id, fnName);
|
||||
}
|
||||
|
||||
void asyncComplete(void* ctx, const char* fnName) {
|
||||
CallContext* context = reinterpret_cast<CallContext*>(ctx);
|
||||
checkName(context, fnName);
|
||||
log_->append(EventLog::ET_ASYNC_COMPLETE, context->connContext->id,
|
||||
context->id, fnName);
|
||||
}
|
||||
|
||||
void handlerError(void* ctx, const char* fnName) {
|
||||
CallContext* context = reinterpret_cast<CallContext*>(ctx);
|
||||
checkName(context, fnName);
|
||||
log_->append(EventLog::ET_HANDLER_ERROR, context->connContext->id,
|
||||
context->id, fnName);
|
||||
}
|
||||
|
||||
protected:
|
||||
void checkName(const CallContext* context, const char* fnName) {
|
||||
// Note: we can't use BOOST_CHECK_EQUAL here, since the handler runs in a
|
||||
// different thread from the test functions. Just abort if the names are
|
||||
// different
|
||||
if (context->name != fnName) {
|
||||
fprintf(stderr, "call context name mismatch: \"%s\" != \"%s\"\n",
|
||||
context->name.c_str(), fnName);
|
||||
fflush(stderr);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t nextId_;
|
||||
boost::shared_ptr<EventLog> log_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::test
|
||||
|
||||
#endif // _THRIFT_PROCESSOR_TEST_HANDLERS_H_
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef _THRIFT_TEST_SERVERTHREAD_H_
|
||||
#define _THRIFT_TEST_SERVERTHREAD_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/TProcessor.h"
|
||||
#include "thrift/lib/cpp/protocol/TProtocol.h"
|
||||
#include "thrift/lib/cpp/server/TServer.h"
|
||||
#include "thrift/lib/cpp/transport/TTransport.h"
|
||||
|
||||
#include "thrift/lib/cpp/processor/test/EventLog.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace test {
|
||||
|
||||
/**
|
||||
* A helper class to tell ServerThread how to create the server
|
||||
*/
|
||||
class ServerState {
|
||||
public:
|
||||
virtual ~ServerState() {}
|
||||
|
||||
/**
|
||||
* Create a server to listen on the specified port.
|
||||
*
|
||||
* If the server returned fails to bind to the specified port when serve() is
|
||||
* called on it, createServer() may be called again on a different port.
|
||||
*/
|
||||
virtual boost::shared_ptr<server::TServer> createServer(uint16_t port) = 0;
|
||||
|
||||
/**
|
||||
* Get the TServerEventHandler to set on the server.
|
||||
*
|
||||
* This is only called after the server successfully binds and is about to
|
||||
* start serving traffic. It is invoked from the server thread, rather than
|
||||
* the main thread.
|
||||
*/
|
||||
virtual boost::shared_ptr<server::TServerEventHandler>
|
||||
getServerEventHandler() {
|
||||
return boost::shared_ptr<server::TServerEventHandler>();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called in the server thread after server binding succeeds.
|
||||
*
|
||||
* Subclasses may override this method if they wish to record the final
|
||||
* port that was used for the server.
|
||||
*/
|
||||
virtual void bindSuccessful(uint16_t port) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* ServerThread starts a thrift server running in a separate thread.
|
||||
*/
|
||||
class ServerThread {
|
||||
public:
|
||||
ServerThread(const boost::shared_ptr<ServerState>& state, bool autoStart) :
|
||||
helper_(new Helper(this)),
|
||||
port_(0),
|
||||
running_(false),
|
||||
serving_(false),
|
||||
error_(false),
|
||||
serverState_(state) {
|
||||
if (autoStart) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
uint16_t getPort() const {
|
||||
return port_;
|
||||
}
|
||||
|
||||
~ServerThread() {
|
||||
if (running_) {
|
||||
try {
|
||||
stop();
|
||||
} catch (...) {
|
||||
GlobalOutput.printf("error shutting down server");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// Annoying. thrift forces us to use shared_ptr, so we have to use
|
||||
// a helper class that we can allocate on the heap and give to thrift.
|
||||
// It would be simpler if we could just make Runnable and TServerEventHandler
|
||||
// private base classes of ServerThread.
|
||||
class Helper : public concurrency::Runnable,
|
||||
public server::TServerEventHandler {
|
||||
public:
|
||||
Helper(ServerThread* serverThread)
|
||||
: serverThread_(serverThread) {}
|
||||
|
||||
void run() {
|
||||
serverThread_->run();
|
||||
}
|
||||
|
||||
void preServe(const transport::TSocketAddress* address) {
|
||||
serverThread_->preServe(address);
|
||||
}
|
||||
|
||||
private:
|
||||
ServerThread* serverThread_;
|
||||
};
|
||||
|
||||
void run();
|
||||
void preServe(const transport::TSocketAddress* address);
|
||||
|
||||
boost::shared_ptr<Helper> helper_;
|
||||
|
||||
uint16_t port_;
|
||||
bool running_;
|
||||
bool serving_;
|
||||
bool error_;
|
||||
concurrency::Monitor serverMonitor_;
|
||||
|
||||
boost::shared_ptr<ServerState> serverState_;
|
||||
boost::shared_ptr<server::TServer> server_;
|
||||
boost::shared_ptr<concurrency::Thread> thread_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::test
|
||||
|
||||
#endif // _THRIFT_TEST_SERVERTHREAD_H_
|
|
@ -0,0 +1,24 @@
|
|||
cpp_unittest(
|
||||
name = 'ProcessorTest',
|
||||
srcs = [
|
||||
'EventLog.cpp',
|
||||
'ProcessorTest.cpp',
|
||||
'ServerThread.cpp',
|
||||
],
|
||||
deps = [
|
||||
':thrift',
|
||||
'@/thrift/lib/cpp/server:nonblocking',
|
||||
],
|
||||
external_deps = [
|
||||
# Versions of boost lower than 1.37 don't support BOOST_AUTO_TEST_CASE.
|
||||
('boost', '>= 1.37', 'boost_unit_test_framework'),
|
||||
],
|
||||
type = 'boost',
|
||||
emails = ['thrift-team@lists.facebook.com'],
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
name = 'thrift',
|
||||
thrift_srcs = { 'proc.thrift' : ['ParentService', 'ChildService']},
|
||||
thrift_cpp_options = 'templates',
|
||||
)
|
|
@ -0,0 +1,22 @@
|
|||
namespace cpp apache.thrift.test
|
||||
|
||||
exception MyError {
|
||||
1: string message
|
||||
}
|
||||
|
||||
service ParentService {
|
||||
i32 incrementGeneration()
|
||||
i32 getGeneration()
|
||||
void addString(1: string s)
|
||||
list<string> getStrings()
|
||||
|
||||
binary getDataWait(1: i32 length)
|
||||
oneway void onewayWait()
|
||||
void exceptionWait(1: string message) throws (2: MyError error)
|
||||
void unexpectedExceptionWait(1: string message)
|
||||
}
|
||||
|
||||
service ChildService extends ParentService {
|
||||
i32 setValue(1: i32 value)
|
||||
i32 getValue()
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
# TARGETS file for thrift/lib/cpp/protocol
|
||||
|
||||
cpp_library (
|
||||
name = "protocol",
|
||||
srcs = [
|
||||
"TBase64Utils.cpp",
|
||||
"TDebugProtocol.cpp",
|
||||
"TJSONProtocol.cpp",
|
||||
"TSimpleJSONProtocol.cpp",
|
||||
"THeaderProtocol.cpp",
|
||||
"TPhpSerializeProtocol.cpp",
|
||||
],
|
||||
deps = [
|
||||
"@/thrift/lib/cpp/transport",
|
||||
"@/thrift/lib/cpp/transport:header",
|
||||
"@/thrift/lib/cpp:thrift_exception",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef _THRIFT_PROTOCOL_TBASE64UTILS_H_
|
||||
#define _THRIFT_PROTOCOL_TBASE64UTILS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
namespace apache { namespace thrift { namespace protocol {
|
||||
|
||||
// in must be at least len bytes
|
||||
// len must be 1, 2, or 3
|
||||
// buf must be a buffer of at least 4 bytes and may not overlap in
|
||||
// the data is not padded with '='; the caller can do this if desired
|
||||
void base64_encode(const uint8_t *in, uint32_t len, uint8_t *buf);
|
||||
|
||||
// buf must be a buffer of at least 4 bytes and contain base64 encoded values
|
||||
// buf will be changed to contain output bytes
|
||||
// len is number of bytes to consume from input (must be 2, 3, or 4)
|
||||
// no '=' padding should be included in the input
|
||||
void base64_decode(uint8_t *buf, uint32_t len);
|
||||
|
||||
}}} // apache::thrift::protocol
|
||||
|
||||
#endif // #define _THRIFT_PROTOCOL_TBASE64UTILS_H_
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef THRIFT_PROTOCOL_TBINARYPROTOCOL_H_
|
||||
#define THRIFT_PROTOCOL_TBINARYPROTOCOL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/protocol/TProtocol.h"
|
||||
#include "thrift/lib/cpp/protocol/TVirtualProtocol.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace apache { namespace thrift { namespace protocol {
|
||||
|
||||
/**
|
||||
* The default binary protocol for thrift. Writes all data in a very basic
|
||||
* binary format, essentially just spitting out the raw bytes.
|
||||
*
|
||||
*/
|
||||
template <class Transport_>
|
||||
class TBinaryProtocolT
|
||||
: public TVirtualProtocol< TBinaryProtocolT<Transport_> > {
|
||||
|
||||
public:
|
||||
static const int32_t VERSION_MASK = 0xffff0000;
|
||||
static const int32_t VERSION_1 = 0x80010000;
|
||||
// VERSION_2 (0x80020000) is taken by TDenseProtocol.
|
||||
|
||||
TBinaryProtocolT(const boost::shared_ptr<Transport_>& trans) :
|
||||
TVirtualProtocol< TBinaryProtocolT<Transport_> >(trans),
|
||||
trans_(trans.get()),
|
||||
string_limit_(0),
|
||||
container_limit_(0),
|
||||
strict_read_(false),
|
||||
strict_write_(true),
|
||||
string_buf_(NULL),
|
||||
string_buf_size_(0) {}
|
||||
|
||||
TBinaryProtocolT(const boost::shared_ptr<Transport_>& trans,
|
||||
int32_t string_limit,
|
||||
int32_t container_limit,
|
||||
bool strict_read,
|
||||
bool strict_write) :
|
||||
TVirtualProtocol< TBinaryProtocolT<Transport_> >(trans),
|
||||
trans_(trans.get()),
|
||||
string_limit_(string_limit),
|
||||
container_limit_(container_limit),
|
||||
strict_read_(strict_read),
|
||||
strict_write_(strict_write),
|
||||
string_buf_(NULL),
|
||||
string_buf_size_(0) {}
|
||||
|
||||
/**
|
||||
* Construct a TBinaryProtocolT using a raw pointer to the transport.
|
||||
*
|
||||
* The caller is responsible for ensuring that the transport remains valid
|
||||
* for the lifetime of the protocol.
|
||||
*/
|
||||
TBinaryProtocolT(Transport_* trans) :
|
||||
TVirtualProtocol< TBinaryProtocolT<Transport_> >(trans),
|
||||
trans_(trans),
|
||||
string_limit_(0),
|
||||
container_limit_(0),
|
||||
strict_read_(false),
|
||||
strict_write_(true),
|
||||
string_buf_(NULL),
|
||||
string_buf_size_(0) {}
|
||||
|
||||
~TBinaryProtocolT() {
|
||||
if (string_buf_ != NULL) {
|
||||
std::free(string_buf_);
|
||||
string_buf_size_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void setStringSizeLimit(int32_t string_limit) {
|
||||
string_limit_ = string_limit;
|
||||
}
|
||||
|
||||
void setContainerSizeLimit(int32_t container_limit) {
|
||||
container_limit_ = container_limit;
|
||||
}
|
||||
|
||||
void setStrict(bool strict_read, bool strict_write) {
|
||||
strict_read_ = strict_read;
|
||||
strict_write_ = strict_write;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writing functions.
|
||||
*/
|
||||
|
||||
/*ol*/ uint32_t writeMessageBegin(const std::string& name,
|
||||
const TMessageType messageType,
|
||||
const int32_t seqid);
|
||||
|
||||
/*ol*/ uint32_t writeMessageEnd();
|
||||
|
||||
|
||||
inline uint32_t writeStructBegin(const char* name);
|
||||
|
||||
inline uint32_t writeStructEnd();
|
||||
|
||||
inline uint32_t writeFieldBegin(const char* name,
|
||||
const TType fieldType,
|
||||
const int16_t fieldId);
|
||||
|
||||
inline uint32_t writeFieldEnd();
|
||||
|
||||
inline uint32_t writeFieldStop();
|
||||
|
||||
inline uint32_t writeMapBegin(const TType keyType,
|
||||
const TType valType,
|
||||
const uint32_t size);
|
||||
|
||||
inline uint32_t writeMapEnd();
|
||||
|
||||
inline uint32_t writeListBegin(const TType elemType, const uint32_t size);
|
||||
|
||||
inline uint32_t writeListEnd();
|
||||
|
||||
inline uint32_t writeSetBegin(const TType elemType, const uint32_t size);
|
||||
|
||||
inline uint32_t writeSetEnd();
|
||||
|
||||
inline uint32_t writeBool(const bool value);
|
||||
|
||||
inline uint32_t writeByte(const int8_t byte);
|
||||
|
||||
inline uint32_t writeI16(const int16_t i16);
|
||||
|
||||
inline uint32_t writeI32(const int32_t i32);
|
||||
|
||||
inline uint32_t writeI64(const int64_t i64);
|
||||
|
||||
inline uint32_t writeDouble(const double dub);
|
||||
|
||||
template <typename StrType>
|
||||
inline uint32_t writeString(const StrType& str);
|
||||
|
||||
inline uint32_t writeBinary(const std::string& str);
|
||||
|
||||
/**
|
||||
* Reading functions
|
||||
*/
|
||||
|
||||
|
||||
/*ol*/ uint32_t readMessageBegin(std::string& name,
|
||||
TMessageType& messageType,
|
||||
int32_t& seqid);
|
||||
|
||||
/*ol*/ uint32_t readMessageEnd();
|
||||
|
||||
inline uint32_t readStructBegin(std::string& name);
|
||||
|
||||
inline uint32_t readStructEnd();
|
||||
|
||||
inline uint32_t readFieldBegin(std::string& name,
|
||||
TType& fieldType,
|
||||
int16_t& fieldId);
|
||||
|
||||
inline uint32_t readFieldEnd();
|
||||
|
||||
inline uint32_t readMapBegin(TType& keyType,
|
||||
TType& valType,
|
||||
uint32_t& size);
|
||||
|
||||
inline uint32_t readMapEnd();
|
||||
|
||||
inline uint32_t readListBegin(TType& elemType, uint32_t& size);
|
||||
|
||||
inline uint32_t readListEnd();
|
||||
|
||||
inline uint32_t readSetBegin(TType& elemType, uint32_t& size);
|
||||
|
||||
inline uint32_t readSetEnd();
|
||||
|
||||
inline uint32_t readBool(bool& value);
|
||||
// Provide the default readBool() implementation for std::vector<bool>
|
||||
using TVirtualProtocol< TBinaryProtocolT<Transport_> >::readBool;
|
||||
|
||||
inline uint32_t readByte(int8_t& byte);
|
||||
|
||||
inline uint32_t readI16(int16_t& i16);
|
||||
|
||||
inline uint32_t readI32(int32_t& i32);
|
||||
|
||||
inline uint32_t readI64(int64_t& i64);
|
||||
|
||||
inline uint32_t readDouble(double& dub);
|
||||
|
||||
template<typename StrType>
|
||||
inline uint32_t readString(StrType& str);
|
||||
|
||||
inline uint32_t readBinary(std::string& str);
|
||||
|
||||
protected:
|
||||
template<typename StrType>
|
||||
uint32_t readStringBody(StrType& str, int32_t sz);
|
||||
|
||||
Transport_* trans_;
|
||||
|
||||
int32_t string_limit_;
|
||||
int32_t container_limit_;
|
||||
|
||||
// Enforce presence of version identifier
|
||||
bool strict_read_;
|
||||
bool strict_write_;
|
||||
|
||||
// Buffer for reading strings, save for the lifetime of the protocol to
|
||||
// avoid memory churn allocating memory on every string read
|
||||
uint8_t* string_buf_;
|
||||
int32_t string_buf_size_;
|
||||
|
||||
};
|
||||
|
||||
typedef TBinaryProtocolT<transport::TTransport> TBinaryProtocol;
|
||||
|
||||
|
||||
class TBinaryProtocolFactoryBase : public TProtocolFactory {};
|
||||
|
||||
/**
|
||||
* Constructs binary protocol handlers
|
||||
*/
|
||||
template <class Transport_>
|
||||
class TBinaryProtocolFactoryT : public TBinaryProtocolFactoryBase {
|
||||
public:
|
||||
TBinaryProtocolFactoryT() :
|
||||
string_limit_(0),
|
||||
container_limit_(0),
|
||||
strict_read_(false),
|
||||
strict_write_(true) {}
|
||||
|
||||
TBinaryProtocolFactoryT(int32_t string_limit, int32_t container_limit,
|
||||
bool strict_read, bool strict_write) :
|
||||
string_limit_(string_limit),
|
||||
container_limit_(container_limit),
|
||||
strict_read_(strict_read),
|
||||
strict_write_(strict_write) {}
|
||||
|
||||
virtual ~TBinaryProtocolFactoryT() {}
|
||||
|
||||
void setStringSizeLimit(int32_t string_limit) {
|
||||
string_limit_ = string_limit;
|
||||
}
|
||||
|
||||
void setContainerSizeLimit(int32_t container_limit) {
|
||||
container_limit_ = container_limit;
|
||||
}
|
||||
|
||||
void setStrict(bool strict_read, bool strict_write) {
|
||||
strict_read_ = strict_read;
|
||||
strict_write_ = strict_write;
|
||||
}
|
||||
|
||||
boost::shared_ptr<TProtocol> getProtocol(
|
||||
boost::shared_ptr<transport::TTransport> trans) {
|
||||
boost::shared_ptr<Transport_> specific_trans =
|
||||
boost::dynamic_pointer_cast<Transport_>(trans);
|
||||
TProtocol* prot;
|
||||
if (specific_trans) {
|
||||
prot = new TBinaryProtocolT<Transport_>(specific_trans, string_limit_,
|
||||
container_limit_, strict_read_,
|
||||
strict_write_);
|
||||
} else {
|
||||
prot = new TBinaryProtocol(trans, string_limit_, container_limit_,
|
||||
strict_read_, strict_write_);
|
||||
}
|
||||
|
||||
return boost::shared_ptr<TProtocol>(prot);
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t string_limit_;
|
||||
int32_t container_limit_;
|
||||
bool strict_read_;
|
||||
bool strict_write_;
|
||||
|
||||
};
|
||||
|
||||
typedef TBinaryProtocolFactoryT<transport::TTransport> TBinaryProtocolFactory;
|
||||
|
||||
}}} // apache::thrift::protocol
|
||||
|
||||
#include "TBinaryProtocol.tcc"
|
||||
|
||||
#endif // #ifndef THRIFT_PROTOCOL_TBINARYPROTOCOL_H_
|
|
@ -0,0 +1,441 @@
|
|||
// Copyright (c) 2006- Facebook
|
||||
// Distributed under the Thrift Software License
|
||||
//
|
||||
// See accompanying file LICENSE or visit the Thrift site at:
|
||||
// http://developers.facebook.com/thrift/
|
||||
|
||||
#ifndef _THRIFT_PROTOCOL_TBINARYPROTOCOL_TCC_
|
||||
#define _THRIFT_PROTOCOL_TBINARYPROTOCOL_TCC_ 1
|
||||
|
||||
#include "thrift/lib/cpp/protocol/TBinaryProtocol.h"
|
||||
|
||||
#include <limits>
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
|
||||
namespace apache { namespace thrift { namespace protocol {
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeMessageBegin(const std::string& name,
|
||||
const TMessageType messageType,
|
||||
const int32_t seqid) {
|
||||
if (this->strict_write_) {
|
||||
int32_t version = (VERSION_1) | ((int32_t)messageType);
|
||||
uint32_t wsize = 0;
|
||||
wsize += writeI32(version);
|
||||
wsize += writeString(name);
|
||||
wsize += writeI32(seqid);
|
||||
return wsize;
|
||||
} else {
|
||||
uint32_t wsize = 0;
|
||||
wsize += writeString(name);
|
||||
wsize += writeByte((int8_t)messageType);
|
||||
wsize += writeI32(seqid);
|
||||
return wsize;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeMessageEnd() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeStructBegin(const char* name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeStructEnd() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeFieldBegin(const char* name,
|
||||
const TType fieldType,
|
||||
const int16_t fieldId) {
|
||||
uint32_t wsize = 0;
|
||||
wsize += writeByte((int8_t)fieldType);
|
||||
wsize += writeI16(fieldId);
|
||||
return wsize;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeFieldEnd() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeFieldStop() {
|
||||
return
|
||||
writeByte((int8_t)T_STOP);
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeMapBegin(const TType keyType,
|
||||
const TType valType,
|
||||
const uint32_t size) {
|
||||
uint32_t wsize = 0;
|
||||
wsize += writeByte((int8_t)keyType);
|
||||
wsize += writeByte((int8_t)valType);
|
||||
wsize += writeI32((int32_t)size);
|
||||
return wsize;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeMapEnd() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeListBegin(const TType elemType,
|
||||
const uint32_t size) {
|
||||
uint32_t wsize = 0;
|
||||
wsize += writeByte((int8_t) elemType);
|
||||
wsize += writeI32((int32_t)size);
|
||||
return wsize;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeListEnd() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeSetBegin(const TType elemType,
|
||||
const uint32_t size) {
|
||||
uint32_t wsize = 0;
|
||||
wsize += writeByte((int8_t)elemType);
|
||||
wsize += writeI32((int32_t)size);
|
||||
return wsize;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeSetEnd() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeBool(const bool value) {
|
||||
uint8_t tmp = value ? 1 : 0;
|
||||
this->trans_->write(&tmp, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeByte(const int8_t byte) {
|
||||
this->trans_->write((uint8_t*)&byte, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeI16(const int16_t i16) {
|
||||
int16_t net = (int16_t)htons(i16);
|
||||
this->trans_->write((uint8_t*)&net, 2);
|
||||
return 2;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeI32(const int32_t i32) {
|
||||
int32_t net = (int32_t)htonl(i32);
|
||||
this->trans_->write((uint8_t*)&net, 4);
|
||||
return 4;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeI64(const int64_t i64) {
|
||||
int64_t net = (int64_t)htonll(i64);
|
||||
this->trans_->write((uint8_t*)&net, 8);
|
||||
return 8;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeDouble(const double dub) {
|
||||
BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
|
||||
BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);
|
||||
|
||||
uint64_t bits = bitwise_cast<uint64_t>(dub);
|
||||
bits = htonll(bits);
|
||||
this->trans_->write((uint8_t*)&bits, 8);
|
||||
return 8;
|
||||
}
|
||||
|
||||
|
||||
template <class Transport_>
|
||||
template<typename StrType>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeString(const StrType& str) {
|
||||
uint32_t size = str.size();
|
||||
uint32_t result = writeI32((int32_t)size);
|
||||
if (size > 0) {
|
||||
this->trans_->write((uint8_t*)str.data(), size);
|
||||
}
|
||||
return result + size;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::writeBinary(const std::string& str) {
|
||||
return TBinaryProtocolT<Transport_>::writeString(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reading functions
|
||||
*/
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readMessageBegin(std::string& name,
|
||||
TMessageType& messageType,
|
||||
int32_t& seqid) {
|
||||
uint32_t result = 0;
|
||||
int32_t sz;
|
||||
result += readI32(sz);
|
||||
|
||||
if (sz < 0) {
|
||||
// Check for correct version number
|
||||
int32_t version = sz & VERSION_MASK;
|
||||
if (version != VERSION_1) {
|
||||
throw TProtocolException(TProtocolException::BAD_VERSION, "Bad version identifier");
|
||||
}
|
||||
messageType = (TMessageType)(sz & 0x000000ff);
|
||||
result += readString(name);
|
||||
result += readI32(seqid);
|
||||
} else {
|
||||
if (this->strict_read_) {
|
||||
throw TProtocolException(TProtocolException::BAD_VERSION, "No version identifier... old protocol client in strict mode?");
|
||||
} else {
|
||||
// Handle pre-versioned input
|
||||
int8_t type;
|
||||
result += readStringBody(name, sz);
|
||||
result += readByte(type);
|
||||
messageType = (TMessageType)type;
|
||||
result += readI32(seqid);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readMessageEnd() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readStructBegin(std::string& name) {
|
||||
name = "";
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readStructEnd() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readFieldBegin(std::string& name,
|
||||
TType& fieldType,
|
||||
int16_t& fieldId) {
|
||||
uint32_t result = 0;
|
||||
int8_t type;
|
||||
result += readByte(type);
|
||||
fieldType = (TType)type;
|
||||
if (fieldType == T_STOP) {
|
||||
fieldId = 0;
|
||||
return result;
|
||||
}
|
||||
result += readI16(fieldId);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readFieldEnd() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readMapBegin(TType& keyType,
|
||||
TType& valType,
|
||||
uint32_t& size) {
|
||||
int8_t k, v;
|
||||
uint32_t result = 0;
|
||||
int32_t sizei;
|
||||
result += readByte(k);
|
||||
keyType = (TType)k;
|
||||
result += readByte(v);
|
||||
valType = (TType)v;
|
||||
result += readI32(sizei);
|
||||
if (sizei < 0) {
|
||||
throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
|
||||
} else if (this->container_limit_ && sizei > this->container_limit_) {
|
||||
throw TProtocolException(TProtocolException::SIZE_LIMIT);
|
||||
}
|
||||
size = (uint32_t)sizei;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readMapEnd() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readListBegin(TType& elemType,
|
||||
uint32_t& size) {
|
||||
int8_t e;
|
||||
uint32_t result = 0;
|
||||
int32_t sizei;
|
||||
result += readByte(e);
|
||||
elemType = (TType)e;
|
||||
result += readI32(sizei);
|
||||
if (sizei < 0) {
|
||||
throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
|
||||
} else if (this->container_limit_ && sizei > this->container_limit_) {
|
||||
throw TProtocolException(TProtocolException::SIZE_LIMIT);
|
||||
}
|
||||
size = (uint32_t)sizei;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readListEnd() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readSetBegin(TType& elemType,
|
||||
uint32_t& size) {
|
||||
int8_t e;
|
||||
uint32_t result = 0;
|
||||
int32_t sizei;
|
||||
result += readByte(e);
|
||||
elemType = (TType)e;
|
||||
result += readI32(sizei);
|
||||
if (sizei < 0) {
|
||||
throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
|
||||
} else if (this->container_limit_ && sizei > this->container_limit_) {
|
||||
throw TProtocolException(TProtocolException::SIZE_LIMIT);
|
||||
}
|
||||
size = (uint32_t)sizei;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readSetEnd() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readBool(bool& value) {
|
||||
uint8_t b[1];
|
||||
this->trans_->readAll(b, 1);
|
||||
value = *(int8_t*)b != 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readByte(int8_t& byte) {
|
||||
uint8_t b[1];
|
||||
this->trans_->readAll(b, 1);
|
||||
byte = *(int8_t*)b;
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readI16(int16_t& i16) {
|
||||
uint8_t b[2];
|
||||
this->trans_->readAll(b, 2);
|
||||
i16 = *bitwise_cast<int16_t*>(&b);
|
||||
i16 = (int16_t)ntohs(i16);
|
||||
return 2;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readI32(int32_t& i32) {
|
||||
uint8_t b[4];
|
||||
this->trans_->readAll(b, 4);
|
||||
i32 = *bitwise_cast<int32_t*>(&b);
|
||||
i32 = (int32_t)ntohl(i32);
|
||||
return 4;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readI64(int64_t& i64) {
|
||||
uint8_t b[8];
|
||||
this->trans_->readAll(b, 8);
|
||||
i64 = *bitwise_cast<int64_t*>(&b);
|
||||
i64 = (int64_t)ntohll(i64);
|
||||
return 8;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readDouble(double& dub) {
|
||||
BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
|
||||
BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);
|
||||
|
||||
uint64_t bits;
|
||||
uint8_t b[8];
|
||||
this->trans_->readAll(b, 8);
|
||||
bits = *bitwise_cast<uint64_t*>(&b);
|
||||
bits = ntohll(bits);
|
||||
dub = bitwise_cast<double>(bits);
|
||||
return 8;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
template<typename StrType>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readString(StrType& str) {
|
||||
uint32_t result;
|
||||
int32_t size;
|
||||
result = readI32(size);
|
||||
return result + readStringBody(str, size);
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readBinary(std::string& str) {
|
||||
return TBinaryProtocolT<Transport_>::readString(str);
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
template<typename StrType>
|
||||
uint32_t TBinaryProtocolT<Transport_>::readStringBody(StrType& str,
|
||||
int32_t size) {
|
||||
uint32_t result = 0;
|
||||
|
||||
// Catch error cases
|
||||
if (size < 0) {
|
||||
throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
|
||||
}
|
||||
if (this->string_limit_ > 0 && size > this->string_limit_) {
|
||||
throw TProtocolException(TProtocolException::SIZE_LIMIT);
|
||||
}
|
||||
|
||||
// Catch empty string case
|
||||
if (size == 0) {
|
||||
str.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Try to borrow first
|
||||
const uint8_t* borrow_buf;
|
||||
uint32_t got = size;
|
||||
if ((borrow_buf = this->trans_->borrow(NULL, &got))) {
|
||||
str.assign((const char*)borrow_buf, size);
|
||||
this->trans_->consume(size);
|
||||
return size;
|
||||
}
|
||||
|
||||
// Use the heap here to prevent stack overflow for v. large strings
|
||||
if (size > this->string_buf_size_ || this->string_buf_ == NULL) {
|
||||
void* new_string_buf = std::realloc(this->string_buf_, (uint32_t)size);
|
||||
if (new_string_buf == NULL) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
this->string_buf_ = (uint8_t*)new_string_buf;
|
||||
this->string_buf_size_ = size;
|
||||
}
|
||||
this->trans_->readAll(this->string_buf_, size);
|
||||
str.assign((char*)this->string_buf_, size);
|
||||
return (uint32_t)size;
|
||||
}
|
||||
|
||||
}}} // apache::thrift::protocol
|
||||
|
||||
#endif // #ifndef _THRIFT_PROTOCOL_TBINARYPROTOCOL_TCC_
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef THRIFT_PROTOCOL_TCOMPACTPROTOCOL_H_
|
||||
#define THRIFT_PROTOCOL_TCOMPACTPROTOCOL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/protocol/TVirtualProtocol.h"
|
||||
|
||||
#include <stack>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace apache { namespace thrift { namespace protocol {
|
||||
|
||||
/**
|
||||
* C++ Implementation of the Compact Protocol as described in THRIFT-110
|
||||
*/
|
||||
template <class Transport_>
|
||||
class TCompactProtocolT
|
||||
: public TVirtualProtocol< TCompactProtocolT<Transport_> > {
|
||||
public:
|
||||
static const int8_t VERSION_N = 2;
|
||||
static const int8_t VERSION_LOW = 1;
|
||||
static const int8_t VERSION_DOUBLE_BE = 2;
|
||||
|
||||
protected:
|
||||
// Normally we can define static const data members of integral
|
||||
// type here. However there appears to be a gcc issue when the
|
||||
// high bit is set (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49896)
|
||||
// unless we cast to a value that fits in an int8_t (0x82 and 0xE0 are
|
||||
// uint8_t)
|
||||
static const int8_t PROTOCOL_ID = static_cast<int8_t>(0x82);
|
||||
static const int8_t TYPE_MASK = static_cast<int8_t>(0xE0);
|
||||
|
||||
static const int8_t VERSION_MASK = 0x1f; // 0001 1111
|
||||
static const int32_t TYPE_SHIFT_AMOUNT = 5;
|
||||
|
||||
Transport_* trans_;
|
||||
|
||||
/**
|
||||
* (Writing) If we encounter a boolean field begin, save the TField here
|
||||
* so it can have the value incorporated.
|
||||
*/
|
||||
struct {
|
||||
const char* name;
|
||||
TType fieldType;
|
||||
int16_t fieldId;
|
||||
} booleanField_;
|
||||
|
||||
/**
|
||||
* (Reading) If we read a field header, and it's a boolean field, save
|
||||
* the boolean value here so that readBool can use it.
|
||||
*/
|
||||
struct {
|
||||
bool hasBoolValue;
|
||||
bool boolValue;
|
||||
} boolValue_;
|
||||
|
||||
/**
|
||||
* Used to keep track of the last field for the current and previous structs,
|
||||
* so we can do the delta stuff.
|
||||
*/
|
||||
|
||||
std::stack<int16_t> lastField_;
|
||||
int16_t lastFieldId_;
|
||||
int8_t version_;
|
||||
|
||||
public:
|
||||
explicit TCompactProtocolT(boost::shared_ptr<Transport_> trans) :
|
||||
TVirtualProtocol< TCompactProtocolT<Transport_> >(trans),
|
||||
trans_(trans.get()),
|
||||
lastFieldId_(0),
|
||||
version_(VERSION_N),
|
||||
string_limit_(0),
|
||||
string_buf_(NULL),
|
||||
string_buf_size_(0),
|
||||
container_limit_(0) {
|
||||
booleanField_.name = NULL;
|
||||
boolValue_.hasBoolValue = false;
|
||||
}
|
||||
|
||||
TCompactProtocolT(boost::shared_ptr<Transport_> trans,
|
||||
int32_t string_limit,
|
||||
int32_t container_limit) :
|
||||
TVirtualProtocol< TCompactProtocolT<Transport_> >(trans),
|
||||
trans_(trans.get()),
|
||||
lastFieldId_(0),
|
||||
version_(VERSION_N),
|
||||
string_limit_(string_limit),
|
||||
string_buf_(NULL),
|
||||
string_buf_size_(0),
|
||||
container_limit_(container_limit) {
|
||||
booleanField_.name = NULL;
|
||||
boolValue_.hasBoolValue = false;
|
||||
}
|
||||
|
||||
virtual ~TCompactProtocolT() {
|
||||
if (string_buf_) {
|
||||
free(string_buf_);
|
||||
}
|
||||
}
|
||||
|
||||
/** Set this if you need backwards compatibility with an old version */
|
||||
void setVersion(const int8_t version) {
|
||||
version_ = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writing functions
|
||||
*/
|
||||
|
||||
virtual uint32_t writeMessageBegin(const std::string& name,
|
||||
const TMessageType messageType,
|
||||
const int32_t seqid);
|
||||
|
||||
uint32_t writeStructBegin(const char* name);
|
||||
|
||||
uint32_t writeStructEnd();
|
||||
|
||||
uint32_t writeFieldBegin(const char* name,
|
||||
const TType fieldType,
|
||||
const int16_t fieldId);
|
||||
|
||||
uint32_t writeFieldStop();
|
||||
|
||||
uint32_t writeListBegin(const TType elemType,
|
||||
const uint32_t size);
|
||||
|
||||
uint32_t writeSetBegin(const TType elemType,
|
||||
const uint32_t size);
|
||||
|
||||
virtual uint32_t writeMapBegin(const TType keyType,
|
||||
const TType valType,
|
||||
const uint32_t size);
|
||||
|
||||
uint32_t writeBool(const bool value);
|
||||
|
||||
uint32_t writeByte(const int8_t byte);
|
||||
|
||||
uint32_t writeI16(const int16_t i16);
|
||||
|
||||
uint32_t writeI32(const int32_t i32);
|
||||
|
||||
uint32_t writeI64(const int64_t i64);
|
||||
|
||||
uint32_t writeDouble(const double dub);
|
||||
|
||||
uint32_t writeString(const char* str);
|
||||
|
||||
template <class String_>
|
||||
uint32_t writeString(const String_& str);
|
||||
|
||||
template <class String_>
|
||||
uint32_t writeBinary(const String_& str);
|
||||
|
||||
/**
|
||||
* These methods are called by structs, but don't actually have any wired
|
||||
* output or purpose
|
||||
*/
|
||||
virtual uint32_t writeMessageEnd() { return 0; }
|
||||
uint32_t writeMapEnd() { return 0; }
|
||||
uint32_t writeListEnd() { return 0; }
|
||||
uint32_t writeSetEnd() { return 0; }
|
||||
uint32_t writeFieldEnd() { return 0; }
|
||||
|
||||
protected:
|
||||
int32_t writeFieldBeginInternal(const char* name,
|
||||
const TType fieldType,
|
||||
const int16_t fieldId,
|
||||
int8_t typeOverride);
|
||||
uint32_t writeCollectionBegin(int8_t elemType, int32_t size);
|
||||
uint32_t writeVarint32(uint32_t n);
|
||||
uint32_t writeVarint64(uint64_t n);
|
||||
uint64_t i64ToZigzag(const int64_t l);
|
||||
uint32_t i32ToZigzag(const int32_t n);
|
||||
inline int8_t getCompactType(int8_t ttype);
|
||||
|
||||
public:
|
||||
uint32_t readMessageBegin(std::string& name,
|
||||
TMessageType& messageType,
|
||||
int32_t& seqid);
|
||||
|
||||
uint32_t readStructBegin(std::string& name);
|
||||
|
||||
uint32_t readStructEnd();
|
||||
|
||||
uint32_t readFieldBegin(std::string& name,
|
||||
TType& fieldType,
|
||||
int16_t& fieldId);
|
||||
|
||||
uint32_t readMapBegin(TType& keyType,
|
||||
TType& valType,
|
||||
uint32_t& size);
|
||||
|
||||
uint32_t readListBegin(TType& elemType,
|
||||
uint32_t& size);
|
||||
|
||||
uint32_t readSetBegin(TType& elemType,
|
||||
uint32_t& size);
|
||||
|
||||
uint32_t readBool(bool& value);
|
||||
// Provide the default readBool() implementation for std::vector<bool>
|
||||
using TVirtualProtocol< TCompactProtocolT<Transport_> >::readBool;
|
||||
|
||||
uint32_t readByte(int8_t& byte);
|
||||
|
||||
uint32_t readI16(int16_t& i16);
|
||||
|
||||
uint32_t readI32(int32_t& i32);
|
||||
|
||||
uint32_t readI64(int64_t& i64);
|
||||
|
||||
uint32_t readDouble(double& dub);
|
||||
|
||||
template <class String_>
|
||||
uint32_t readString(String_& str);
|
||||
|
||||
template <class String_>
|
||||
uint32_t readBinary(String_& str);
|
||||
|
||||
/*
|
||||
*These methods are here for the struct to call, but don't have any wire
|
||||
* encoding.
|
||||
*/
|
||||
uint32_t readMessageEnd() { return 0; }
|
||||
uint32_t readFieldEnd() { return 0; }
|
||||
uint32_t readMapEnd() { return 0; }
|
||||
uint32_t readListEnd() { return 0; }
|
||||
uint32_t readSetEnd() { return 0; }
|
||||
|
||||
protected:
|
||||
uint32_t readVarint32(int32_t& i32);
|
||||
uint32_t readVarint64(int64_t& i64);
|
||||
int32_t zigzagToI32(uint32_t n);
|
||||
int64_t zigzagToI64(uint64_t n);
|
||||
TType getTType(int8_t type);
|
||||
|
||||
// Buffer for reading strings, save for the lifetime of the protocol to
|
||||
// avoid memory churn allocating memory on every string read
|
||||
int32_t string_limit_;
|
||||
uint8_t* string_buf_;
|
||||
int32_t string_buf_size_;
|
||||
int32_t container_limit_;
|
||||
};
|
||||
|
||||
typedef TCompactProtocolT<TTransport> TCompactProtocol;
|
||||
|
||||
/**
|
||||
* Constructs compact protocol handlers
|
||||
*/
|
||||
template <class Transport_>
|
||||
class TCompactProtocolFactoryT : public TProtocolFactory {
|
||||
public:
|
||||
TCompactProtocolFactoryT() :
|
||||
string_limit_(0),
|
||||
container_limit_(0) {}
|
||||
|
||||
TCompactProtocolFactoryT(int32_t string_limit, int32_t container_limit) :
|
||||
string_limit_(string_limit),
|
||||
container_limit_(container_limit) {}
|
||||
|
||||
virtual ~TCompactProtocolFactoryT() {}
|
||||
|
||||
void setStringSizeLimit(int32_t string_limit) {
|
||||
string_limit_ = string_limit;
|
||||
}
|
||||
|
||||
void setContainerSizeLimit(int32_t container_limit) {
|
||||
container_limit_ = container_limit;
|
||||
}
|
||||
|
||||
boost::shared_ptr<TProtocol> getProtocol(boost::shared_ptr<TTransport> trans) {
|
||||
boost::shared_ptr<Transport_> specific_trans =
|
||||
boost::dynamic_pointer_cast<Transport_>(trans);
|
||||
TProtocol* prot;
|
||||
if (specific_trans) {
|
||||
prot = new TCompactProtocolT<Transport_>(specific_trans, string_limit_,
|
||||
container_limit_);
|
||||
} else {
|
||||
prot = new TCompactProtocol(trans, string_limit_, container_limit_);
|
||||
}
|
||||
|
||||
return boost::shared_ptr<TProtocol>(prot);
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t string_limit_;
|
||||
int32_t container_limit_;
|
||||
|
||||
};
|
||||
|
||||
typedef TCompactProtocolFactoryT<TTransport> TCompactProtocolFactory;
|
||||
|
||||
}}} // apache::thrift::protocol
|
||||
|
||||
#include "TCompactProtocol.tcc"
|
||||
|
||||
#endif
|
|
@ -0,0 +1,820 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_
|
||||
#define THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_ 1
|
||||
|
||||
#include "thrift/lib/cpp/thrift_config.h"
|
||||
#include "folly/Likely.h"
|
||||
#include <limits>
|
||||
|
||||
/*
|
||||
* TCompactProtocol::i*ToZigzag depend on the fact that the right shift
|
||||
* operator on a signed integer is an arithmetic (sign-extending) shift.
|
||||
* If this is not the case, the current implementation will not work.
|
||||
* If anyone encounters this error, we can try to figure out the best
|
||||
* way to implement an arithmetic right shift on their platform.
|
||||
*/
|
||||
#if !defined(SIGNED_RIGHT_SHIFT_IS) || !defined(ARITHMETIC_RIGHT_SHIFT)
|
||||
# error "Unable to determine the behavior of a signed right shift"
|
||||
#endif
|
||||
#if SIGNED_RIGHT_SHIFT_IS != ARITHMETIC_RIGHT_SHIFT
|
||||
# error "TCompactProtocol currently only works if a signed right shift is arithmetic"
|
||||
#endif
|
||||
|
||||
namespace apache { namespace thrift { namespace protocol {
|
||||
|
||||
namespace detail { namespace compact {
|
||||
|
||||
enum Types {
|
||||
CT_STOP = 0x00,
|
||||
CT_BOOLEAN_TRUE = 0x01,
|
||||
CT_BOOLEAN_FALSE = 0x02,
|
||||
CT_BYTE = 0x03,
|
||||
CT_I16 = 0x04,
|
||||
CT_I32 = 0x05,
|
||||
CT_I64 = 0x06,
|
||||
CT_DOUBLE = 0x07,
|
||||
CT_BINARY = 0x08,
|
||||
CT_LIST = 0x09,
|
||||
CT_SET = 0x0A,
|
||||
CT_MAP = 0x0B,
|
||||
CT_STRUCT = 0x0C,
|
||||
};
|
||||
|
||||
const int8_t TTypeToCType[16] = {
|
||||
CT_STOP, // T_STOP
|
||||
0, // unused
|
||||
CT_BOOLEAN_TRUE, // T_BOOL
|
||||
CT_BYTE, // T_BYTE
|
||||
CT_DOUBLE, // T_DOUBLE
|
||||
0, // unused
|
||||
CT_I16, // T_I16
|
||||
0, // unused
|
||||
CT_I32, // T_I32
|
||||
0, // unused
|
||||
CT_I64, // T_I64
|
||||
CT_BINARY, // T_STRING
|
||||
CT_STRUCT, // T_STRUCT
|
||||
CT_MAP, // T_MAP
|
||||
CT_SET, // T_SET
|
||||
CT_LIST, // T_LIST
|
||||
};
|
||||
|
||||
}} // end detail::compact namespace
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeMessageBegin(
|
||||
const std::string& name,
|
||||
const TMessageType messageType,
|
||||
const int32_t seqid) {
|
||||
uint32_t wsize = 0;
|
||||
wsize += writeByte(PROTOCOL_ID);
|
||||
wsize += writeByte((version_ & VERSION_MASK) | (((int32_t)messageType << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
|
||||
wsize += writeVarint32(seqid);
|
||||
wsize += writeString(name);
|
||||
return wsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a field header containing the field id and field type. If the
|
||||
* difference between the current field id and the last one is small (< 15),
|
||||
* then the field id will be encoded in the 4 MSB as a delta. Otherwise, the
|
||||
* field id will follow the type header as a zigzag varint.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeFieldBegin(const char* name,
|
||||
const TType fieldType,
|
||||
const int16_t fieldId) {
|
||||
if (fieldType == T_BOOL) {
|
||||
booleanField_.name = name;
|
||||
booleanField_.fieldType = fieldType;
|
||||
booleanField_.fieldId = fieldId;
|
||||
} else {
|
||||
return writeFieldBeginInternal(name, fieldType, fieldId, -1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the STOP symbol so we know there are no more fields in this struct.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeFieldStop() {
|
||||
return writeByte(T_STOP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a struct begin. This doesn't actually put anything on the wire. We
|
||||
* use it as an opportunity to put special placeholder markers on the field
|
||||
* stack so we can get the field id deltas correct.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeStructBegin(const char* name) {
|
||||
lastField_.push(lastFieldId_);
|
||||
lastFieldId_ = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a struct end. This doesn't actually put anything on the wire. We use
|
||||
* this as an opportunity to pop the last field from the current struct off
|
||||
* of the field stack.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeStructEnd() {
|
||||
lastFieldId_ = lastField_.top();
|
||||
lastField_.pop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a List header.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeListBegin(const TType elemType,
|
||||
const uint32_t size) {
|
||||
return writeCollectionBegin(elemType, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a set header.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeSetBegin(const TType elemType,
|
||||
const uint32_t size) {
|
||||
return writeCollectionBegin(elemType, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a map header. If the map is empty, omit the key and value type
|
||||
* headers, as we don't need any additional information to skip it.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeMapBegin(const TType keyType,
|
||||
const TType valType,
|
||||
const uint32_t size) {
|
||||
uint32_t wsize = 0;
|
||||
|
||||
if (size == 0) {
|
||||
wsize += writeByte(0);
|
||||
} else {
|
||||
wsize += writeVarint32(size);
|
||||
wsize += writeByte(getCompactType(keyType) << 4 | getCompactType(valType));
|
||||
}
|
||||
return wsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a boolean value. Potentially, this could be a boolean field, in
|
||||
* which case the field header info isn't written yet. If so, decide what the
|
||||
* right type header is for the value and then write the field header.
|
||||
* Otherwise, write a single byte.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeBool(const bool value) {
|
||||
uint32_t wsize = 0;
|
||||
|
||||
if (booleanField_.name != NULL) {
|
||||
// we haven't written the field header yet
|
||||
wsize += writeFieldBeginInternal(booleanField_.name,
|
||||
booleanField_.fieldType,
|
||||
booleanField_.fieldId,
|
||||
value ? detail::compact::CT_BOOLEAN_TRUE :
|
||||
detail::compact::CT_BOOLEAN_FALSE);
|
||||
booleanField_.name = NULL;
|
||||
} else {
|
||||
// we're not part of a field, so just write the value
|
||||
wsize += writeByte(value ? detail::compact::CT_BOOLEAN_TRUE :
|
||||
detail::compact::CT_BOOLEAN_FALSE);
|
||||
}
|
||||
return wsize;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeByte(const int8_t byte) {
|
||||
trans_->write((uint8_t*)&byte, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an i16 as a zigzag varint.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeI16(const int16_t i16) {
|
||||
return writeVarint32(i32ToZigzag(i16));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an i32 as a zigzag varint.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeI32(const int32_t i32) {
|
||||
return writeVarint32(i32ToZigzag(i32));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an i64 as a zigzag varint.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeI64(const int64_t i64) {
|
||||
return writeVarint64(i64ToZigzag(i64));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a double to the wire as 8 bytes.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeDouble(const double dub) {
|
||||
BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
|
||||
BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);
|
||||
|
||||
uint64_t bits = bitwise_cast<uint64_t>(dub);
|
||||
if (version_ >= VERSION_DOUBLE_BE) {
|
||||
bits = htonll(bits);
|
||||
} else {
|
||||
bits = htolell(bits);
|
||||
}
|
||||
trans_->write((uint8_t*)&bits, 8);
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a string to the wire with a varint size preceding.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeString(const char* str) {
|
||||
return writeString(std::string(str));
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
template <class String_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeString(const String_& str) {
|
||||
return writeBinary(str);
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
template <class String_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeBinary(const String_& str) {
|
||||
uint32_t ssize = str.size();
|
||||
uint32_t wsize = writeVarint32(ssize) + ssize;
|
||||
trans_->write((uint8_t*)str.data(), ssize);
|
||||
return wsize;
|
||||
}
|
||||
|
||||
//
|
||||
// Internal Writing methods
|
||||
//
|
||||
|
||||
/**
|
||||
* The workhorse of writeFieldBegin. It has the option of doing a
|
||||
* 'type override' of the type header. This is used specifically in the
|
||||
* boolean field case.
|
||||
*/
|
||||
template <class Transport_>
|
||||
int32_t TCompactProtocolT<Transport_>::writeFieldBeginInternal(
|
||||
const char* name,
|
||||
const TType fieldType,
|
||||
const int16_t fieldId,
|
||||
int8_t typeOverride) {
|
||||
uint32_t wsize = 0;
|
||||
|
||||
// if there's a type override, use that.
|
||||
int8_t typeToWrite = (typeOverride == -1 ? getCompactType(fieldType) : typeOverride);
|
||||
|
||||
// check if we can use delta encoding for the field id
|
||||
if (fieldId > lastFieldId_ && fieldId - lastFieldId_ <= 15) {
|
||||
// write them together
|
||||
wsize += writeByte((fieldId - lastFieldId_) << 4 | typeToWrite);
|
||||
} else {
|
||||
// write them separate
|
||||
wsize += writeByte(typeToWrite);
|
||||
wsize += writeI16(fieldId);
|
||||
}
|
||||
|
||||
lastFieldId_ = fieldId;
|
||||
return wsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method for writing the start of lists and sets. List and sets on
|
||||
* the wire differ only by the type indicator.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeCollectionBegin(int8_t elemType,
|
||||
int32_t size) {
|
||||
uint32_t wsize = 0;
|
||||
if (size <= 14) {
|
||||
wsize += writeByte(size << 4 | getCompactType(elemType));
|
||||
} else {
|
||||
wsize += writeByte(0xf0 | getCompactType(elemType));
|
||||
wsize += writeVarint32(size);
|
||||
}
|
||||
return wsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an i32 as a varint. Results in 1-5 bytes on the wire.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeVarint32(uint32_t n) {
|
||||
uint8_t buf[5];
|
||||
uint32_t wsize = 0;
|
||||
|
||||
while (true) {
|
||||
if ((n & ~0x7F) == 0) {
|
||||
buf[wsize++] = (int8_t)n;
|
||||
break;
|
||||
} else {
|
||||
buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
|
||||
n >>= 7;
|
||||
}
|
||||
}
|
||||
trans_->write(buf, wsize);
|
||||
return wsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an i64 as a varint. Results in 1-10 bytes on the wire.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::writeVarint64(uint64_t n) {
|
||||
uint8_t buf[10];
|
||||
uint32_t wsize = 0;
|
||||
|
||||
while (true) {
|
||||
if ((n & ~0x7FL) == 0) {
|
||||
buf[wsize++] = (int8_t)n;
|
||||
break;
|
||||
} else {
|
||||
buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
|
||||
n >>= 7;
|
||||
}
|
||||
}
|
||||
trans_->write(buf, wsize);
|
||||
return wsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert l into a zigzag long. This allows negative numbers to be
|
||||
* represented compactly as a varint.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint64_t TCompactProtocolT<Transport_>::i64ToZigzag(const int64_t l) {
|
||||
return (l << 1) ^ (l >> 63);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert n into a zigzag int. This allows negative numbers to be
|
||||
* represented compactly as a varint.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::i32ToZigzag(const int32_t n) {
|
||||
return (n << 1) ^ (n >> 31);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a TType value, find the appropriate detail::compact::Types value
|
||||
*/
|
||||
template <class Transport_>
|
||||
int8_t TCompactProtocolT<Transport_>::getCompactType(int8_t ttype) {
|
||||
return detail::compact::TTypeToCType[ttype];
|
||||
}
|
||||
|
||||
//
|
||||
// Reading Methods
|
||||
//
|
||||
|
||||
/**
|
||||
* Read a message header.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readMessageBegin(
|
||||
std::string& name,
|
||||
TMessageType& messageType,
|
||||
int32_t& seqid) {
|
||||
uint32_t rsize = 0;
|
||||
int8_t protocolId;
|
||||
int8_t versionAndType;
|
||||
|
||||
rsize += readByte(protocolId);
|
||||
if (protocolId != PROTOCOL_ID) {
|
||||
throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol identifier");
|
||||
}
|
||||
|
||||
rsize += readByte(versionAndType);
|
||||
version_ = (int8_t)(versionAndType & VERSION_MASK);
|
||||
if (!(version_ <= VERSION_N && version_ >= VERSION_LOW)) {
|
||||
throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol version");
|
||||
}
|
||||
|
||||
messageType = (TMessageType)((versionAndType >> TYPE_SHIFT_AMOUNT) & 0x03);
|
||||
rsize += readVarint32(seqid);
|
||||
rsize += readString(name);
|
||||
|
||||
return rsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a struct begin. There's nothing on the wire for this, but it is our
|
||||
* opportunity to push a new struct begin marker on the field stack.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readStructBegin(std::string& name) {
|
||||
name = "";
|
||||
lastField_.push(lastFieldId_);
|
||||
lastFieldId_ = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Doesn't actually consume any wire data, just removes the last field for
|
||||
* this struct from the field stack.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readStructEnd() {
|
||||
lastFieldId_ = lastField_.top();
|
||||
lastField_.pop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a field header off the wire.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readFieldBegin(std::string& name,
|
||||
TType& fieldType,
|
||||
int16_t& fieldId) {
|
||||
uint32_t rsize = 0;
|
||||
int8_t byte;
|
||||
int8_t type;
|
||||
|
||||
rsize += readByte(byte);
|
||||
type = (byte & 0x0f);
|
||||
|
||||
// if it's a stop, then we can return immediately, as the struct is over.
|
||||
if (type == T_STOP) {
|
||||
fieldType = T_STOP;
|
||||
fieldId = 0;
|
||||
return rsize;
|
||||
}
|
||||
|
||||
// mask off the 4 MSB of the type header. it could contain a field id delta.
|
||||
int16_t modifier = (int16_t)(((uint8_t)byte & 0xf0) >> 4);
|
||||
if (modifier == 0) {
|
||||
// not a delta, look ahead for the zigzag varint field id.
|
||||
rsize += readI16(fieldId);
|
||||
} else {
|
||||
fieldId = (int16_t)(lastFieldId_ + modifier);
|
||||
}
|
||||
fieldType = getTType(type);
|
||||
|
||||
// if this happens to be a boolean field, the value is encoded in the type
|
||||
if (type == detail::compact::CT_BOOLEAN_TRUE ||
|
||||
type == detail::compact::CT_BOOLEAN_FALSE) {
|
||||
// save the boolean value in a special instance variable.
|
||||
boolValue_.hasBoolValue = true;
|
||||
boolValue_.boolValue =
|
||||
(type == detail::compact::CT_BOOLEAN_TRUE ? true : false);
|
||||
}
|
||||
|
||||
// push the new field onto the field stack so we can keep the deltas going.
|
||||
lastFieldId_ = fieldId;
|
||||
return rsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a map header off the wire. If the size is zero, skip reading the key
|
||||
* and value type. This means that 0-length maps will yield TMaps without the
|
||||
* "correct" types.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readMapBegin(TType& keyType,
|
||||
TType& valType,
|
||||
uint32_t& size) {
|
||||
uint32_t rsize = 0;
|
||||
int8_t kvType = 0;
|
||||
int32_t msize = 0;
|
||||
|
||||
rsize += readVarint32(msize);
|
||||
if (msize != 0)
|
||||
rsize += readByte(kvType);
|
||||
|
||||
if (msize < 0) {
|
||||
throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
|
||||
} else if (container_limit_ && msize > container_limit_) {
|
||||
throw TProtocolException(TProtocolException::SIZE_LIMIT);
|
||||
}
|
||||
|
||||
keyType = getTType((int8_t)((uint8_t)kvType >> 4));
|
||||
valType = getTType((int8_t)((uint8_t)kvType & 0xf));
|
||||
size = (uint32_t)msize;
|
||||
|
||||
return rsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a list header off the wire. If the list size is 0-14, the size will
|
||||
* be packed into the element type header. If it's a longer list, the 4 MSB
|
||||
* of the element type header will be 0xF, and a varint will follow with the
|
||||
* true size.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readListBegin(TType& elemType,
|
||||
uint32_t& size) {
|
||||
int8_t size_and_type;
|
||||
uint32_t rsize = 0;
|
||||
int32_t lsize;
|
||||
|
||||
rsize += readByte(size_and_type);
|
||||
|
||||
lsize = ((uint8_t)size_and_type >> 4) & 0x0f;
|
||||
if (lsize == 15) {
|
||||
rsize += readVarint32(lsize);
|
||||
}
|
||||
|
||||
if (lsize < 0) {
|
||||
throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
|
||||
} else if (container_limit_ && lsize > container_limit_) {
|
||||
throw TProtocolException(TProtocolException::SIZE_LIMIT);
|
||||
}
|
||||
|
||||
elemType = getTType((int8_t)(size_and_type & 0x0f));
|
||||
size = (uint32_t)lsize;
|
||||
|
||||
return rsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a set header off the wire. If the set size is 0-14, the size will
|
||||
* be packed into the element type header. If it's a longer set, the 4 MSB
|
||||
* of the element type header will be 0xF, and a varint will follow with the
|
||||
* true size.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readSetBegin(TType& elemType,
|
||||
uint32_t& size) {
|
||||
return readListBegin(elemType, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a boolean off the wire. If this is a boolean field, the value should
|
||||
* already have been read during readFieldBegin, so we'll just consume the
|
||||
* pre-stored value. Otherwise, read a byte.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readBool(bool& value) {
|
||||
if (boolValue_.hasBoolValue == true) {
|
||||
value = boolValue_.boolValue;
|
||||
boolValue_.hasBoolValue = false;
|
||||
return 0;
|
||||
} else {
|
||||
int8_t val;
|
||||
readByte(val);
|
||||
value = (val == detail::compact::CT_BOOLEAN_TRUE);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a single byte off the wire. Nothing interesting here.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readByte(int8_t& byte) {
|
||||
uint8_t b[1];
|
||||
trans_->readAll(b, 1);
|
||||
byte = *(int8_t*)b;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an i16 from the wire as a zigzag varint.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readI16(int16_t& i16) {
|
||||
int32_t value;
|
||||
uint32_t rsize = readVarint32(value);
|
||||
i16 = (int16_t)zigzagToI32(value);
|
||||
return rsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an i32 from the wire as a zigzag varint.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readI32(int32_t& i32) {
|
||||
int32_t value;
|
||||
uint32_t rsize = readVarint32(value);
|
||||
i32 = zigzagToI32(value);
|
||||
return rsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an i64 from the wire as a zigzag varint.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readI64(int64_t& i64) {
|
||||
int64_t value;
|
||||
uint32_t rsize = readVarint64(value);
|
||||
i64 = zigzagToI64(value);
|
||||
return rsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* No magic here - just read a double off the wire.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readDouble(double& dub) {
|
||||
BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
|
||||
BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);
|
||||
|
||||
union {
|
||||
uint64_t bits;
|
||||
uint8_t b[8];
|
||||
} u;
|
||||
trans_->readAll(u.b, 8);
|
||||
if (version_ >= VERSION_DOUBLE_BE) {
|
||||
u.bits = ntohll(u.bits);
|
||||
} else {
|
||||
u.bits = letohll(u.bits);
|
||||
}
|
||||
dub = bitwise_cast<double>(u.bits);
|
||||
return 8;
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
template <class String_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readString(String_& str) {
|
||||
return readBinary(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a byte[] from the wire.
|
||||
*/
|
||||
template <class Transport_>
|
||||
template <class String_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readBinary(String_& str) {
|
||||
int32_t rsize = 0;
|
||||
int32_t size;
|
||||
|
||||
rsize += readVarint32(size);
|
||||
// Catch empty string case
|
||||
if (size == 0) {
|
||||
str = "";
|
||||
return rsize;
|
||||
}
|
||||
|
||||
// Catch error cases
|
||||
if (size < 0) {
|
||||
throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
|
||||
}
|
||||
if (string_limit_ > 0 && size > string_limit_) {
|
||||
throw TProtocolException(TProtocolException::SIZE_LIMIT);
|
||||
}
|
||||
|
||||
// Use the heap here to prevent stack overflow for v. large strings
|
||||
if (size > string_buf_size_ || string_buf_ == NULL) {
|
||||
void* new_string_buf = std::realloc(string_buf_, (uint32_t)size);
|
||||
if (new_string_buf == NULL) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
string_buf_ = (uint8_t*)new_string_buf;
|
||||
string_buf_size_ = size;
|
||||
}
|
||||
trans_->readAll(string_buf_, size);
|
||||
str.assign((char*)string_buf_, size);
|
||||
|
||||
return rsize + (uint32_t)size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an i32 from the wire as a varint. The MSB of each byte is set
|
||||
* if there is another byte to follow. This can read up to 5 bytes.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readVarint32(int32_t& i32) {
|
||||
int64_t val;
|
||||
uint32_t rsize = readVarint64(val);
|
||||
i32 = (int32_t)val;
|
||||
return rsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an i64 from the wire as a proper varint. The MSB of each byte is set
|
||||
* if there is another byte to follow. This can read up to 10 bytes.
|
||||
*/
|
||||
template <class Transport_>
|
||||
uint32_t TCompactProtocolT<Transport_>::readVarint64(int64_t& i64) {
|
||||
uint32_t rsize = 0;
|
||||
uint64_t val = 0;
|
||||
int shift = 0;
|
||||
uint8_t buf[10]; // 64 bits / (7 bits/byte) = 10 bytes.
|
||||
uint32_t buf_size = sizeof(buf);
|
||||
const uint8_t* borrowed = trans_->borrow(buf, &buf_size);
|
||||
|
||||
// Fast path.
|
||||
if (borrowed != NULL) {
|
||||
while (true) {
|
||||
uint8_t byte = borrowed[rsize];
|
||||
rsize++;
|
||||
val |= (uint64_t)(byte & 0x7f) << shift;
|
||||
shift += 7;
|
||||
if (!(byte & 0x80)) {
|
||||
i64 = val;
|
||||
trans_->consume(rsize);
|
||||
return rsize;
|
||||
}
|
||||
// Have to check for invalid data so we don't crash.
|
||||
if (UNLIKELY(rsize == sizeof(buf))) {
|
||||
throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slow path.
|
||||
else {
|
||||
while (true) {
|
||||
uint8_t byte;
|
||||
rsize += trans_->readAll(&byte, 1);
|
||||
val |= (uint64_t)(byte & 0x7f) << shift;
|
||||
shift += 7;
|
||||
if (!(byte & 0x80)) {
|
||||
i64 = val;
|
||||
return rsize;
|
||||
}
|
||||
// Might as well check for invalid data on the slow path too.
|
||||
if (UNLIKELY(rsize >= sizeof(buf))) {
|
||||
throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from zigzag int to int.
|
||||
*/
|
||||
template <class Transport_>
|
||||
int32_t TCompactProtocolT<Transport_>::zigzagToI32(uint32_t n) {
|
||||
return (n >> 1) ^ -(n & 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from zigzag long to long.
|
||||
*/
|
||||
template <class Transport_>
|
||||
int64_t TCompactProtocolT<Transport_>::zigzagToI64(uint64_t n) {
|
||||
return (n >> 1) ^ -(n & 1);
|
||||
}
|
||||
|
||||
template <class Transport_>
|
||||
TType TCompactProtocolT<Transport_>::getTType(int8_t type) {
|
||||
switch (type) {
|
||||
case T_STOP:
|
||||
return T_STOP;
|
||||
case detail::compact::CT_BOOLEAN_FALSE:
|
||||
case detail::compact::CT_BOOLEAN_TRUE:
|
||||
return T_BOOL;
|
||||
case detail::compact::CT_BYTE:
|
||||
return T_BYTE;
|
||||
case detail::compact::CT_I16:
|
||||
return T_I16;
|
||||
case detail::compact::CT_I32:
|
||||
return T_I32;
|
||||
case detail::compact::CT_I64:
|
||||
return T_I64;
|
||||
case detail::compact::CT_DOUBLE:
|
||||
return T_DOUBLE;
|
||||
case detail::compact::CT_BINARY:
|
||||
return T_STRING;
|
||||
case detail::compact::CT_LIST:
|
||||
return T_LIST;
|
||||
case detail::compact::CT_SET:
|
||||
return T_SET;
|
||||
case detail::compact::CT_MAP:
|
||||
return T_MAP;
|
||||
case detail::compact::CT_STRUCT:
|
||||
return T_STRUCT;
|
||||
default:
|
||||
throw TLibraryException("don't know what type: " + type);
|
||||
}
|
||||
return T_STOP;
|
||||
}
|
||||
|
||||
}}} // apache::thrift::protocol
|
||||
|
||||
#endif // THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef THRIFT_PROTOCOL_TDEBUGPROTOCOL_H
|
||||
#define THRIFT_PROTOCOL_TDEBUGPROTOCOL_H
|
||||
|
||||
#include "thrift/lib/cpp/protocol/TVirtualProtocol.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace apache { namespace thrift { namespace protocol {
|
||||
|
||||
/*
|
||||
|
||||
!!! EXPERIMENTAL CODE !!!
|
||||
|
||||
This protocol is very much a work in progress.
|
||||
It doesn't handle many cases properly.
|
||||
It throws exceptions in many cases.
|
||||
It probably segfaults in many cases.
|
||||
Bug reports and feature requests are welcome.
|
||||
Complaints are not. :R
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Protocol that prints the payload in a nice human-readable format.
|
||||
* Reading from this protocol is not supported.
|
||||
*
|
||||
*/
|
||||
class TDebugProtocol : public TVirtualProtocol<TDebugProtocol> {
|
||||
private:
|
||||
enum write_state_t
|
||||
{ UNINIT
|
||||
, STRUCT
|
||||
, LIST
|
||||
, SET
|
||||
, MAP_KEY
|
||||
, MAP_VALUE
|
||||
};
|
||||
|
||||
public:
|
||||
TDebugProtocol(boost::shared_ptr<TTransport> trans)
|
||||
: TVirtualProtocol<TDebugProtocol>(trans)
|
||||
, trans_(trans.get())
|
||||
, string_limit_(DEFAULT_STRING_LIMIT)
|
||||
, string_prefix_size_(DEFAULT_STRING_PREFIX_SIZE)
|
||||
{
|
||||
write_state_.push_back(UNINIT);
|
||||
}
|
||||
|
||||
static const int32_t DEFAULT_STRING_LIMIT = 256;
|
||||
static const int32_t DEFAULT_STRING_PREFIX_SIZE = 16;
|
||||
|
||||
void setStringSizeLimit(int32_t string_limit) {
|
||||
string_limit_ = string_limit;
|
||||
}
|
||||
|
||||
void setStringPrefixSize(int32_t string_prefix_size) {
|
||||
string_prefix_size_ = string_prefix_size;
|
||||
}
|
||||
|
||||
|
||||
uint32_t writeMessageBegin(const std::string& name,
|
||||
const TMessageType messageType,
|
||||
const int32_t seqid);
|
||||
|
||||
uint32_t writeMessageEnd();
|
||||
|
||||
|
||||
uint32_t writeStructBegin(const char* name);
|
||||
|
||||
uint32_t writeStructEnd();
|
||||
|
||||
uint32_t writeFieldBegin(const char* name,
|
||||
const TType fieldType,
|
||||
const int16_t fieldId);
|
||||
|
||||
uint32_t writeFieldEnd();
|
||||
|
||||
uint32_t writeFieldStop();
|
||||
|
||||
uint32_t writeMapBegin(const TType keyType,
|
||||
const TType valType,
|
||||
const uint32_t size);
|
||||
|
||||
uint32_t writeMapEnd();
|
||||
|
||||
uint32_t writeListBegin(const TType elemType,
|
||||
const uint32_t size);
|
||||
|
||||
uint32_t writeListEnd();
|
||||
|
||||
uint32_t writeSetBegin(const TType elemType,
|
||||
const uint32_t size);
|
||||
|
||||
uint32_t writeSetEnd();
|
||||
|
||||
uint32_t writeBool(const bool value);
|
||||
|
||||
uint32_t writeByte(const int8_t byte);
|
||||
|
||||
uint32_t writeI16(const int16_t i16);
|
||||
|
||||
uint32_t writeI32(const int32_t i32);
|
||||
|
||||
uint32_t writeI64(const int64_t i64);
|
||||
|
||||
uint32_t writeDouble(const double dub);
|
||||
|
||||
uint32_t writeString(const std::string& str);
|
||||
|
||||
template <class StrType>
|
||||
uint32_t writeString(const StrType& str) {
|
||||
return writeString(std::string(str.data(), str.size()));
|
||||
}
|
||||
|
||||
uint32_t writeBinary(const std::string& str);
|
||||
|
||||
|
||||
private:
|
||||
void indentUp();
|
||||
void indentDown();
|
||||
uint32_t writePlain(const std::string& str);
|
||||
uint32_t writeIndented(const std::string& str);
|
||||
uint32_t startItem();
|
||||
uint32_t endItem();
|
||||
uint32_t writeItem(const std::string& str);
|
||||
|
||||
static std::string fieldTypeName(TType type);
|
||||
|
||||
TTransport* trans_;
|
||||
|
||||
int32_t string_limit_;
|
||||
int32_t string_prefix_size_;
|
||||
|
||||
std::string indent_str_;
|
||||
static const int indent_inc = 2;
|
||||
|
||||
std::vector<write_state_t> write_state_;
|
||||
std::vector<int> list_idx_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs debug protocol handlers
|
||||
*/
|
||||
class TDebugProtocolFactory : public TProtocolFactory {
|
||||
public:
|
||||
TDebugProtocolFactory() {}
|
||||
virtual ~TDebugProtocolFactory() {}
|
||||
|
||||
boost::shared_ptr<TProtocol> getProtocol(boost::shared_ptr<TTransport> trans) {
|
||||
return boost::shared_ptr<TProtocol>(new TDebugProtocol(trans));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}}} // apache::thrift::protocol
|
||||
|
||||
|
||||
// TODO(dreiss): Move (part of) ThriftDebugString into a .cpp file and remove this.
|
||||
#include "thrift/lib/cpp/transport/TBufferTransports.h"
|
||||
|
||||
namespace apache { namespace thrift {
|
||||
|
||||
template <typename T>
|
||||
struct ThriftTypeTraits {
|
||||
static const apache::thrift::protocol::TType fieldType_ =
|
||||
apache::thrift::protocol::TType(99);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ThriftTypeTraits<bool> {
|
||||
static const apache::thrift::protocol::TType fieldType_ =
|
||||
apache::thrift::protocol::T_BOOL;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ThriftTypeTraits<int8_t> {
|
||||
static const apache::thrift::protocol::TType fieldType_ =
|
||||
apache::thrift::protocol::T_I08;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ThriftTypeTraits<int16_t> {
|
||||
static const apache::thrift::protocol::TType fieldType_ =
|
||||
apache::thrift::protocol::T_I16;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ThriftTypeTraits<int32_t> {
|
||||
static const apache::thrift::protocol::TType fieldType_ =
|
||||
apache::thrift::protocol::T_I32;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ThriftTypeTraits<int64_t> {
|
||||
static const apache::thrift::protocol::TType fieldType_ =
|
||||
apache::thrift::protocol::T_I64;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ThriftTypeTraits<double> {
|
||||
static const apache::thrift::protocol::TType fieldType_ =
|
||||
apache::thrift::protocol::T_DOUBLE;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ThriftTypeTraits<std::string> {
|
||||
static const apache::thrift::protocol::TType fieldType_ =
|
||||
apache::thrift::protocol::T_STRING;
|
||||
};
|
||||
|
||||
class TDebugProtocolEx : public apache::thrift::protocol::TDebugProtocol {
|
||||
public:
|
||||
TDebugProtocolEx(
|
||||
boost::shared_ptr<apache::thrift::protocol::TTransport> trans
|
||||
)
|
||||
: TDebugProtocol(trans) {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write(const T& t) {
|
||||
t.write(this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write(const std::vector<T>& c) {
|
||||
writeListBegin(ThriftTypeTraits<T>::fieldType_, c.size());
|
||||
typeof(c.begin()) it = c.begin();
|
||||
for (; it != c.end(); it++) {
|
||||
write(*it);
|
||||
}
|
||||
writeListEnd();
|
||||
}
|
||||
|
||||
template<typename K, typename V>
|
||||
void write(const std::map<K, V>& c) {
|
||||
writeMapBegin(
|
||||
ThriftTypeTraits<K>::fieldType_,
|
||||
ThriftTypeTraits<V>::fieldType_,
|
||||
c.size()
|
||||
);
|
||||
typeof(c.begin()) it = c.begin();
|
||||
for (; it != c.end(); it++) {
|
||||
write(it->first);
|
||||
write(it->second);
|
||||
}
|
||||
writeMapEnd();
|
||||
}
|
||||
|
||||
template<typename K, typename V>
|
||||
void write(const std::multimap<K, V>& c) {
|
||||
writeMapBegin(
|
||||
ThriftTypeTraits<K>::fieldType_,
|
||||
ThriftTypeTraits<V>::fieldType_,
|
||||
c.size()
|
||||
);
|
||||
typeof(c.begin()) it = c.begin();
|
||||
for (; it != c.end(); it++) {
|
||||
write(it->first);
|
||||
write(it->second);
|
||||
}
|
||||
writeMapEnd();
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void write(const std::set<T>& c) {
|
||||
writeSetBegin(
|
||||
ThriftTypeTraits<T>::fieldType_,
|
||||
c.size()
|
||||
);
|
||||
typeof(c.begin()) it = c.begin();
|
||||
for (; it != c.end(); it++) {
|
||||
write(*it);
|
||||
}
|
||||
writeSetEnd();
|
||||
}
|
||||
|
||||
void write(const bool value) {
|
||||
writeBool(value);
|
||||
}
|
||||
|
||||
void write(const int8_t byte) {
|
||||
writeByte(byte);
|
||||
}
|
||||
|
||||
void write(const int16_t i16) {
|
||||
writeI16(i16);
|
||||
}
|
||||
|
||||
void write(const int32_t i32) {
|
||||
writeI32(i32);
|
||||
}
|
||||
|
||||
void write(const int64_t i64) {
|
||||
writeI64(i64);
|
||||
}
|
||||
|
||||
void write(const double dub) {
|
||||
writeDouble(dub);
|
||||
}
|
||||
|
||||
void write(const std::string& str) {
|
||||
writeString(str);
|
||||
}
|
||||
|
||||
template<typename K, typename V>
|
||||
void write(const std::pair<K, V>& p) {
|
||||
writeStructBegin("pair");
|
||||
writeFieldBegin("first", ThriftTypeTraits<K>::fieldType_, 1);
|
||||
write(p.first);
|
||||
writeFieldEnd();
|
||||
writeFieldBegin("second", ThriftTypeTraits<V>::fieldType_, 2);
|
||||
write(p.second);
|
||||
writeFieldEnd();
|
||||
writeStructEnd();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
std::string ThriftDebugString(const T& ts) {
|
||||
using namespace apache::thrift::transport;
|
||||
using namespace apache::thrift::protocol;
|
||||
TMemoryBuffer* buffer = new TMemoryBuffer;
|
||||
boost::shared_ptr<TTransport> trans(buffer);
|
||||
TDebugProtocolEx protocol(trans);
|
||||
|
||||
protocol.write(ts);
|
||||
|
||||
uint8_t* buf;
|
||||
uint32_t size;
|
||||
buffer->getBuffer(&buf, &size);
|
||||
return std::string((char*)buf, (unsigned int)size);
|
||||
}
|
||||
|
||||
}} // apache::thrift
|
||||
|
||||
|
||||
#endif // #ifndef _THRIFT_PROTOCOL_TDEBUGPROTOCOL_H_
|
||||
|
||||
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef THRIFT_PROTOCOL_THEADERPROTOCOL_H_
|
||||
#define THRIFT_PROTOCOL_THEADERPROTOCOL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/protocol/TProtocol.h"
|
||||
#include "thrift/lib/cpp/protocol/TProtocolTypes.h"
|
||||
#include "thrift/lib/cpp/protocol/TVirtualProtocol.h"
|
||||
#include "thrift/lib/cpp/transport/THeaderTransport.h"
|
||||
#include "thrift/lib/cpp/util/shared_ptr_util.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <bitset>
|
||||
|
||||
using apache::thrift::transport::THeaderTransport;
|
||||
using apache::thrift::transport::TTransportPair;
|
||||
|
||||
namespace apache { namespace thrift { namespace protocol {
|
||||
|
||||
/**
|
||||
* The header protocol for thrift. Reads unframed, framed, header format,
|
||||
* and http
|
||||
*
|
||||
*/
|
||||
class THeaderProtocol
|
||||
: public TVirtualProtocol<THeaderProtocol> {
|
||||
public:
|
||||
explicit THeaderProtocol(const boost::shared_ptr<TTransport>& trans,
|
||||
std::bitset<CLIENT_TYPES_LEN>* clientTypes = NULL,
|
||||
uint16_t protoId = T_COMPACT_PROTOCOL) :
|
||||
TVirtualProtocol<THeaderProtocol>(getTransportWrapper(trans,
|
||||
clientTypes))
|
||||
, trans_(boost::dynamic_pointer_cast<THeaderTransport, TTransport>(
|
||||
this->getTransport()))
|
||||
, protoId_(protoId)
|
||||
{
|
||||
trans_->setProtocolId(protoId);
|
||||
resetProtocol();
|
||||
}
|
||||
|
||||
THeaderProtocol(const boost::shared_ptr<TTransport>& inTrans,
|
||||
const boost::shared_ptr<TTransport>& outTrans,
|
||||
std::bitset<CLIENT_TYPES_LEN>* clientTypes = NULL,
|
||||
uint16_t protoId = T_COMPACT_PROTOCOL) :
|
||||
TVirtualProtocol<THeaderProtocol>(getInOutTransportWrapper(inTrans,
|
||||
outTrans,
|
||||
clientTypes))
|
||||
, trans_(boost::dynamic_pointer_cast<THeaderTransport, TTransport>(
|
||||
this->getTransport()))
|
||||
, protoId_(protoId)
|
||||
{
|
||||
trans_->setProtocolId(protoId);
|
||||
resetProtocol();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a THeaderProtocol using a raw pointer to the transport.
|
||||
*
|
||||
* The caller is responsible for ensuring that the transport remains valid
|
||||
* for the lifetime of the protocol.
|
||||
*/
|
||||
THeaderProtocol(TTransport* trans,
|
||||
std::bitset<CLIENT_TYPES_LEN>* clientTypes,
|
||||
uint16_t protoId = T_COMPACT_PROTOCOL) :
|
||||
TVirtualProtocol<THeaderProtocol>(
|
||||
getTransportWrapper(
|
||||
boost::shared_ptr<TTransport>(trans,
|
||||
NoopPtrDestructor<TTransport>()),
|
||||
clientTypes))
|
||||
, trans_(boost::dynamic_pointer_cast<THeaderTransport, TTransport>(
|
||||
this->getTransport()))
|
||||
, protoId_(protoId)
|
||||
{
|
||||
trans_->setProtocolId(protoId);
|
||||
resetProtocol();
|
||||
}
|
||||
|
||||
~THeaderProtocol() {}
|
||||
|
||||
/**
|
||||
* Functions to work with headers by calling into THeaderTransport
|
||||
*/
|
||||
void setProtocolId(uint16_t protoId) {
|
||||
trans_->setProtocolId(protoId);
|
||||
resetProtocol();
|
||||
}
|
||||
|
||||
void resetProtocol();
|
||||
|
||||
typedef THeaderTransport::StringToStringMap StringToStringMap;
|
||||
|
||||
// these work with write headers
|
||||
void setHeader(const std::string& key, const std::string& value) {
|
||||
trans_->setHeader(key, value);
|
||||
}
|
||||
|
||||
void setPersistentHeader(const std::string& key, const std::string& value) {
|
||||
trans_->setPersistentHeader(key, value);
|
||||
}
|
||||
|
||||
void clearHeaders() {
|
||||
trans_->clearHeaders();
|
||||
}
|
||||
|
||||
void clearPersistentHeaders() {
|
||||
trans_->clearPersistentHeaders();
|
||||
}
|
||||
|
||||
StringToStringMap& getWriteHeaders() {
|
||||
return trans_->getWriteHeaders();
|
||||
}
|
||||
|
||||
StringToStringMap& getPersistentWriteHeaders() {
|
||||
return trans_->getPersistentWriteHeaders();
|
||||
}
|
||||
|
||||
// these work with read headers
|
||||
const StringToStringMap& getHeaders() const {
|
||||
return trans_->getHeaders();
|
||||
}
|
||||
|
||||
void setTransform(uint16_t trans) {
|
||||
trans_->setTransform(trans);
|
||||
}
|
||||
|
||||
std::string getPeerIdentity() const {
|
||||
return trans_->getPeerIdentity();
|
||||
}
|
||||
void setIdentity(const std::string& identity) {
|
||||
trans_->setIdentity(identity);
|
||||
}
|
||||
|
||||
void setHmac(THeaderTransport::MacCallback macCb,
|
||||
THeaderTransport::VerifyMacCallback verifyCb) {
|
||||
trans_->setHmac(macCb, verifyCb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writing functions.
|
||||
*/
|
||||
|
||||
/*ol*/ uint32_t writeMessageBegin(const std::string& name,
|
||||
const TMessageType messageType,
|
||||
const int32_t seqId);
|
||||
|
||||
/*ol*/ uint32_t writeMessageEnd();
|
||||
|
||||
|
||||
uint32_t writeStructBegin(const char* name);
|
||||
|
||||
uint32_t writeStructEnd();
|
||||
|
||||
uint32_t writeFieldBegin(const char* name,
|
||||
const TType fieldType,
|
||||
const int16_t fieldId);
|
||||
|
||||
uint32_t writeFieldEnd();
|
||||
|
||||
uint32_t writeFieldStop();
|
||||
|
||||
uint32_t writeMapBegin(const TType keyType,
|
||||
const TType valType,
|
||||
const uint32_t size);
|
||||
|
||||
uint32_t writeMapEnd();
|
||||
|
||||
uint32_t writeListBegin(const TType elemType, const uint32_t size);
|
||||
|
||||
uint32_t writeListEnd();
|
||||
|
||||
uint32_t writeSetBegin(const TType elemType, const uint32_t size);
|
||||
|
||||
uint32_t writeSetEnd();
|
||||
|
||||
uint32_t writeBool(const bool value);
|
||||
|
||||
uint32_t writeByte(const int8_t byte);
|
||||
|
||||
uint32_t writeI16(const int16_t i16);
|
||||
|
||||
uint32_t writeI32(const int32_t i32);
|
||||
|
||||
uint32_t writeI64(const int64_t i64);
|
||||
|
||||
uint32_t writeDouble(const double dub);
|
||||
|
||||
uint32_t writeString(const std::string& str);
|
||||
|
||||
uint32_t writeBinary(const std::string& str);
|
||||
|
||||
/**
|
||||
* Reading functions
|
||||
*/
|
||||
|
||||
|
||||
/*ol*/ uint32_t readMessageBegin(std::string& name,
|
||||
TMessageType& messageType,
|
||||
int32_t& seqId);
|
||||
|
||||
/*ol*/ uint32_t readMessageEnd();
|
||||
|
||||
uint32_t readStructBegin(std::string& name);
|
||||
|
||||
uint32_t readStructEnd();
|
||||
|
||||
uint32_t readFieldBegin(std::string& name,
|
||||
TType& fieldType,
|
||||
int16_t& fieldId);
|
||||
|
||||
uint32_t readFieldEnd();
|
||||
|
||||
uint32_t readMapBegin(TType& keyType,
|
||||
TType& valType,
|
||||
uint32_t& size);
|
||||
|
||||
uint32_t readMapEnd();
|
||||
|
||||
uint32_t readListBegin(TType& elemType, uint32_t& size);
|
||||
|
||||
uint32_t readListEnd();
|
||||
|
||||
uint32_t readSetBegin(TType& elemType, uint32_t& size);
|
||||
|
||||
uint32_t readSetEnd();
|
||||
|
||||
uint32_t readBool(bool& value);
|
||||
// Provide the default readBool() implementation for std::vector<bool>
|
||||
using TVirtualProtocol< THeaderProtocol >::readBool;
|
||||
|
||||
uint32_t readByte(int8_t& byte);
|
||||
|
||||
uint32_t readI16(int16_t& i16);
|
||||
|
||||
uint32_t readI32(int32_t& i32);
|
||||
|
||||
uint32_t readI64(int64_t& i64);
|
||||
|
||||
uint32_t readDouble(double& dub);
|
||||
|
||||
uint32_t readString(std::string& str);
|
||||
|
||||
uint32_t readBinary(std::string& binary);
|
||||
|
||||
protected:
|
||||
template<typename StrType>
|
||||
uint32_t readStringBody(StrType& str, int32_t sz);
|
||||
|
||||
boost::shared_ptr<TTransport> getTransportWrapper(
|
||||
const boost::shared_ptr<TTransport>& trans,
|
||||
std::bitset<CLIENT_TYPES_LEN>* clientTypes) {
|
||||
if (dynamic_cast<THeaderTransport*>(trans.get()) != NULL) {
|
||||
return trans;
|
||||
} else {
|
||||
return boost::shared_ptr<THeaderTransport>(
|
||||
new THeaderTransport(trans, clientTypes));
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<TTransport> getInOutTransportWrapper(
|
||||
const boost::shared_ptr<TTransport>& inTrans,
|
||||
const boost::shared_ptr<TTransport>& outTrans,
|
||||
std::bitset<CLIENT_TYPES_LEN>* clientTypes) {
|
||||
assert(dynamic_cast<THeaderTransport*>(inTrans.get()) == NULL
|
||||
&& dynamic_cast<THeaderTransport*>(outTrans.get()) == NULL);
|
||||
|
||||
return boost::shared_ptr<THeaderTransport>(
|
||||
new THeaderTransport(inTrans, outTrans, clientTypes)
|
||||
);
|
||||
}
|
||||
|
||||
boost::shared_ptr<THeaderTransport> trans_;
|
||||
|
||||
boost::shared_ptr<TProtocol> proto_;
|
||||
uint32_t protoId_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs header protocol handlers
|
||||
*/
|
||||
class THeaderProtocolFactory : public TDuplexProtocolFactory {
|
||||
public:
|
||||
explicit THeaderProtocolFactory(uint16_t protoId = T_COMPACT_PROTOCOL,
|
||||
bool disableIdentity = false) {
|
||||
protoId_ = protoId;
|
||||
setIdentity_ = disableIdentity;
|
||||
}
|
||||
|
||||
virtual ~THeaderProtocolFactory() {}
|
||||
|
||||
void setClientTypes(std::bitset<CLIENT_TYPES_LEN>& clientTypes) {
|
||||
for (int i = 0; i < CLIENT_TYPES_LEN; i++) {
|
||||
this->clientTypes[i] = clientTypes[i];
|
||||
}
|
||||
}
|
||||
|
||||
void setIdentity(const std::string& identity) {
|
||||
identity_ = identity;
|
||||
setIdentity_ = true;
|
||||
}
|
||||
|
||||
void setTransform(uint16_t trans) {
|
||||
trans_.push_back(trans);
|
||||
}
|
||||
|
||||
virtual TProtocolPair getProtocol(
|
||||
boost::shared_ptr<transport::TTransport> trans) {
|
||||
THeaderProtocol* prot = new THeaderProtocol(trans, &clientTypes, protoId_);
|
||||
|
||||
if(setIdentity_) {
|
||||
prot->setIdentity(identity_);
|
||||
}
|
||||
|
||||
for (auto& t : trans_) {
|
||||
prot->setTransform(t);
|
||||
}
|
||||
|
||||
boost::shared_ptr<TProtocol> pprot(prot);
|
||||
return TProtocolPair(pprot, pprot);
|
||||
}
|
||||
|
||||
virtual TProtocolPair getProtocol(TTransportPair transports) {
|
||||
THeaderProtocol* prot = new THeaderProtocol(transports.first,
|
||||
transports.second,
|
||||
&clientTypes,
|
||||
protoId_);
|
||||
|
||||
if(setIdentity_) {
|
||||
prot->setIdentity(identity_);
|
||||
}
|
||||
|
||||
for (auto& t : trans_) {
|
||||
prot->setTransform(t);
|
||||
}
|
||||
|
||||
boost::shared_ptr<TProtocol> pprot(prot);
|
||||
return TProtocolPair(pprot, pprot);
|
||||
}
|
||||
|
||||
// No implementation of getInputProtocolFactory/getOutputProtocolFactory
|
||||
// Using base class implementation which return NULL.
|
||||
|
||||
private:
|
||||
std::bitset<CLIENT_TYPES_LEN> clientTypes;
|
||||
uint16_t protoId_;
|
||||
bool setIdentity_;
|
||||
std::vector<uint16_t> trans_;
|
||||
std::string identity_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::protocol
|
||||
|
||||
#endif // #ifndef THRIFT_PROTOCOL_THEADERPROTOCOL_H_
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef THRIFT_PROTOCOL_TJSONPROTOCOL_H_
|
||||
#define THRIFT_PROTOCOL_TJSONPROTOCOL_H_ 1
|
||||
|
||||
#include "TVirtualProtocol.h"
|
||||
|
||||
#include <stack>
|
||||
|
||||
namespace apache { namespace thrift { namespace protocol {
|
||||
|
||||
// Forward declaration
|
||||
class TJSONContext;
|
||||
|
||||
/**
|
||||
* JSON protocol for Thrift.
|
||||
*
|
||||
* Implements a protocol which uses JSON as the wire-format.
|
||||
*
|
||||
* Thrift types are represented as described below:
|
||||
*
|
||||
* 1. Every Thrift integer type is represented as a JSON number.
|
||||
*
|
||||
* 2. Thrift doubles are represented as JSON numbers. Some special values are
|
||||
* represented as strings:
|
||||
* a. "NaN" for not-a-number values
|
||||
* b. "Infinity" for positive infinity
|
||||
* c. "-Infinity" for negative infinity
|
||||
*
|
||||
* 3. Thrift string values are emitted as JSON strings, with appropriate
|
||||
* escaping.
|
||||
*
|
||||
* 4. Thrift binary values are encoded into Base64 and emitted as JSON strings.
|
||||
* The readBinary() method is written such that it will properly skip if
|
||||
* called on a Thrift string (although it will decode garbage data).
|
||||
*
|
||||
* 5. Thrift structs are represented as JSON objects, with the field ID as the
|
||||
* key, and the field value represented as a JSON object with a single
|
||||
* key-value pair. The key is a short string identifier for that type,
|
||||
* followed by the value. The valid type identifiers are: "tf" for bool,
|
||||
* "i8" for byte, "i16" for 16-bit integer, "i32" for 32-bit integer, "i64"
|
||||
* for 64-bit integer, "dbl" for double-precision floating point, "str" for
|
||||
* string (including binary), "rec" for struct ("records"), "map" for map,
|
||||
* "lst" for list, "set" for set.
|
||||
*
|
||||
* 6. Thrift lists and sets are represented as JSON arrays, with the first
|
||||
* element of the JSON array being the string identifier for the Thrift
|
||||
* element type and the second element of the JSON array being the count of
|
||||
* the Thrift elements. The Thrift elements then follow.
|
||||
*
|
||||
* 7. Thrift maps are represented as JSON arrays, with the first two elements
|
||||
* of the JSON array being the string identifiers for the Thrift key type
|
||||
* and value type, followed by the count of the Thrift pairs, followed by a
|
||||
* JSON object containing the key-value pairs. Note that JSON keys can only
|
||||
* be strings, which means that the key type of the Thrift map should be
|
||||
* restricted to numeric or string types -- in the case of numerics, they
|
||||
* are serialized as strings.
|
||||
*
|
||||
* 8. Thrift messages are represented as JSON arrays, with the protocol
|
||||
* version #, the message name, the message type, and the sequence ID as
|
||||
* the first 4 elements.
|
||||
*
|
||||
* More discussion of the double handling is probably warranted. The aim of
|
||||
* the current implementation is to match as closely as possible the behavior
|
||||
* of Java's Double.toString(), which has no precision loss. Implementors in
|
||||
* other languages should strive to achieve that where possible. I have not
|
||||
* yet verified whether boost:lexical_cast, which is doing that work for me in
|
||||
* C++, loses any precision, but I am leaving this as a future improvement. I
|
||||
* may try to provide a C component for this, so that other languages could
|
||||
* bind to the same underlying implementation for maximum consistency.
|
||||
*
|
||||
* Note further that JavaScript itself is not capable of representing
|
||||
* floating point infinities -- presumably when we have a JavaScript Thrift
|
||||
* client, this would mean that infinities get converted to not-a-number in
|
||||
* transmission. I don't know of any work-around for this issue.
|
||||
*
|
||||
*/
|
||||
class TJSONProtocol : public TVirtualProtocol<TJSONProtocol> {
|
||||
public:
|
||||
|
||||
TJSONProtocol(boost::shared_ptr<TTransport> ptrans);
|
||||
|
||||
~TJSONProtocol();
|
||||
|
||||
private:
|
||||
|
||||
void pushContext(boost::shared_ptr<TJSONContext> c);
|
||||
|
||||
void popContext();
|
||||
|
||||
protected:
|
||||
|
||||
uint32_t writeJSONEscapeChar(uint8_t ch);
|
||||
|
||||
uint32_t writeJSONChar(uint8_t ch);
|
||||
|
||||
uint32_t writeJSONString(const std::string &str);
|
||||
|
||||
uint32_t writeJSONBase64(const std::string &str);
|
||||
|
||||
template <typename NumberType>
|
||||
uint32_t writeJSONInteger(NumberType num);
|
||||
|
||||
uint32_t writeJSONBool(bool value);
|
||||
|
||||
uint32_t writeJSONDouble(double num);
|
||||
|
||||
uint32_t writeJSONObjectStart() ;
|
||||
|
||||
uint32_t writeJSONObjectEnd();
|
||||
|
||||
uint32_t writeJSONArrayStart();
|
||||
|
||||
uint32_t writeJSONArrayEnd();
|
||||
|
||||
uint32_t readJSONSyntaxChar(uint8_t ch);
|
||||
|
||||
uint32_t readJSONEscapeChar(uint8_t *out);
|
||||
|
||||
uint32_t readJSONString(std::string &str, bool skipContext = false);
|
||||
|
||||
uint32_t readJSONBase64(std::string &str);
|
||||
|
||||
uint32_t readJSONNumericChars(std::string &str);
|
||||
|
||||
template <typename NumberType>
|
||||
uint32_t readJSONInteger(NumberType &num);
|
||||
|
||||
uint32_t readJSONDouble(double &num);
|
||||
|
||||
uint32_t readJSONObjectStart();
|
||||
|
||||
uint32_t readJSONObjectEnd();
|
||||
|
||||
uint32_t readJSONArrayStart();
|
||||
|
||||
uint32_t readJSONArrayEnd();
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Writing functions.
|
||||
*/
|
||||
|
||||
uint32_t writeMessageBegin(const std::string& name,
|
||||
const TMessageType messageType,
|
||||
const int32_t seqid);
|
||||
|
||||
uint32_t writeMessageEnd();
|
||||
|
||||
uint32_t writeStructBegin(const char* name);
|
||||
|
||||
uint32_t writeStructEnd();
|
||||
|
||||
uint32_t writeFieldBegin(const char* name,
|
||||
const TType fieldType,
|
||||
const int16_t fieldId);
|
||||
|
||||
uint32_t writeFieldEnd();
|
||||
|
||||
uint32_t writeFieldStop();
|
||||
|
||||
uint32_t writeMapBegin(const TType keyType,
|
||||
const TType valType,
|
||||
const uint32_t size);
|
||||
|
||||
uint32_t writeMapEnd();
|
||||
|
||||
uint32_t writeListBegin(const TType elemType,
|
||||
const uint32_t size);
|
||||
|
||||
uint32_t writeListEnd();
|
||||
|
||||
uint32_t writeSetBegin(const TType elemType,
|
||||
const uint32_t size);
|
||||
|
||||
uint32_t writeSetEnd();
|
||||
|
||||
uint32_t writeBool(const bool value);
|
||||
|
||||
uint32_t writeByte(const int8_t byte);
|
||||
|
||||
uint32_t writeI16(const int16_t i16);
|
||||
|
||||
uint32_t writeI32(const int32_t i32);
|
||||
|
||||
uint32_t writeI64(const int64_t i64);
|
||||
|
||||
uint32_t writeDouble(const double dub);
|
||||
|
||||
uint32_t writeString(const std::string& str);
|
||||
|
||||
uint32_t writeBinary(const std::string& str);
|
||||
|
||||
/**
|
||||
* Reading functions
|
||||
*/
|
||||
|
||||
uint32_t readMessageBegin(std::string& name,
|
||||
TMessageType& messageType,
|
||||
int32_t& seqid);
|
||||
|
||||
uint32_t readMessageEnd();
|
||||
|
||||
uint32_t readStructBegin(std::string& name);
|
||||
|
||||
uint32_t readStructEnd();
|
||||
|
||||
uint32_t readFieldBegin(std::string& name,
|
||||
TType& fieldType,
|
||||
int16_t& fieldId);
|
||||
|
||||
uint32_t readFieldEnd();
|
||||
|
||||
uint32_t readMapBegin(TType& keyType,
|
||||
TType& valType,
|
||||
uint32_t& size);
|
||||
|
||||
uint32_t readMapEnd();
|
||||
|
||||
uint32_t readListBegin(TType& elemType,
|
||||
uint32_t& size);
|
||||
|
||||
uint32_t readListEnd();
|
||||
|
||||
uint32_t readSetBegin(TType& elemType,
|
||||
uint32_t& size);
|
||||
|
||||
uint32_t readSetEnd();
|
||||
|
||||
uint32_t readBool(bool& value);
|
||||
|
||||
// Provide the default readBool() implementation for std::vector<bool>
|
||||
using TVirtualProtocol<TJSONProtocol>::readBool;
|
||||
|
||||
uint32_t readByte(int8_t& byte);
|
||||
|
||||
uint32_t readI16(int16_t& i16);
|
||||
|
||||
uint32_t readI32(int32_t& i32);
|
||||
|
||||
uint32_t readI64(int64_t& i64);
|
||||
|
||||
uint32_t readDouble(double& dub);
|
||||
|
||||
uint32_t readString(std::string& str);
|
||||
|
||||
uint32_t readBinary(std::string& str);
|
||||
|
||||
class LookaheadReader {
|
||||
|
||||
public:
|
||||
|
||||
LookaheadReader(TTransport &trans) :
|
||||
trans_(&trans),
|
||||
hasData_(false) {
|
||||
}
|
||||
|
||||
uint8_t read() {
|
||||
if (hasData_) {
|
||||
hasData_ = false;
|
||||
}
|
||||
else {
|
||||
trans_->readAll(&data_, 1);
|
||||
}
|
||||
return data_;
|
||||
}
|
||||
|
||||
uint8_t peek() {
|
||||
if (!hasData_) {
|
||||
trans_->readAll(&data_, 1);
|
||||
}
|
||||
hasData_ = true;
|
||||
return data_;
|
||||
}
|
||||
|
||||
private:
|
||||
TTransport *trans_;
|
||||
bool hasData_;
|
||||
uint8_t data_;
|
||||
};
|
||||
|
||||
private:
|
||||
TTransport* trans_;
|
||||
|
||||
std::stack<boost::shared_ptr<TJSONContext> > contexts_;
|
||||
boost::shared_ptr<TJSONContext> context_;
|
||||
LookaheadReader reader_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs input and output protocol objects given transports.
|
||||
*/
|
||||
class TJSONProtocolFactory : public TProtocolFactory {
|
||||
public:
|
||||
TJSONProtocolFactory() {}
|
||||
|
||||
virtual ~TJSONProtocolFactory() {}
|
||||
|
||||
boost::shared_ptr<TProtocol> getProtocol(boost::shared_ptr<TTransport> trans) {
|
||||
return boost::shared_ptr<TProtocol>(new TJSONProtocol(trans));
|
||||
}
|
||||
};
|
||||
|
||||
}}} // apache::thrift::protocol
|
||||
|
||||
|
||||
// TODO(dreiss): Move part of ThriftJSONString into a .cpp file and remove this.
|
||||
#include "thrift/lib/cpp/transport/TBufferTransports.h"
|
||||
|
||||
/*namespace apache { namespace thrift {
|
||||
|
||||
template<typename ThriftStruct>
|
||||
std::string ThriftJSONString(const ThriftStruct& ts) {
|
||||
using namespace apache::thrift::transport;
|
||||
using namespace apache::thrift::protocol;
|
||||
TMemoryBuffer* buffer = new TMemoryBuffer;
|
||||
boost::shared_ptr<TTransport> trans(buffer);
|
||||
TJSONProtocol protocol(trans);
|
||||
|
||||
ts.write(&protocol);
|
||||
|
||||
uint8_t* buf;
|
||||
uint32_t size;
|
||||
buffer->getBuffer(&buf, &size);
|
||||
return std::string((char*)buf, (unsigned int)size);
|
||||
}
|
||||
|
||||
}} // apache::thrift
|
||||
*/
|
||||
#endif // #define THRIFT_PROTOCOL_TJSONPROTOCOL_H_ 1
|
|
@ -0,0 +1,287 @@
|
|||
/**
|
||||
* Copyright 2012 Facebook
|
||||
* @author Tudor Bosman (tudorb@fb.com)
|
||||
*/
|
||||
|
||||
#ifndef THRIFT_LIB_CPP_PROTOCOL_TNEUTRONIUMPROTOCOL_H_
|
||||
#define THRIFT_LIB_CPP_PROTOCOL_TNEUTRONIUMPROTOCOL_H_
|
||||
|
||||
#include "thrift/lib/cpp/protocol/TProtocol.h"
|
||||
#include "thrift/lib/cpp/protocol/TVirtualProtocol.h"
|
||||
#include "thrift/lib/cpp/protocol/neutronium/Encoder.h"
|
||||
#include "thrift/lib/cpp/protocol/neutronium/Decoder.h"
|
||||
|
||||
namespace apache { namespace thrift { namespace protocol {
|
||||
|
||||
class TNeutroniumProtocol
|
||||
: public TVirtualProtocol<TNeutroniumProtocol> {
|
||||
|
||||
public:
|
||||
TNeutroniumProtocol(const neutronium::Schema* schema,
|
||||
neutronium::InternTable* internTable,
|
||||
folly::IOBuf* buf)
|
||||
: TVirtualProtocol<TNeutroniumProtocol>(nullptr),
|
||||
enc_(schema, internTable, buf),
|
||||
dec_(schema, internTable, buf) {
|
||||
}
|
||||
|
||||
void setRootType(int64_t rootType) {
|
||||
enc_.setRootType(rootType);
|
||||
dec_.setRootType(rootType);
|
||||
}
|
||||
|
||||
uint32_t writeMessageBegin(const std::string& name,
|
||||
const TMessageType messageType,
|
||||
const int32_t seqid) {
|
||||
LOG(FATAL) << "Message encoding / decoding not implemented";
|
||||
}
|
||||
|
||||
uint32_t writeMessageEnd() {
|
||||
LOG(FATAL) << "Message encoding / decoding not implemented";
|
||||
}
|
||||
|
||||
uint32_t writeStructBegin(const char* name) {
|
||||
enc_.writeStructBegin(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeStructEnd() {
|
||||
enc_.writeStructEnd();
|
||||
return enc_.bytesWritten();
|
||||
}
|
||||
|
||||
uint32_t writeFieldBegin(const char* name,
|
||||
const TType fieldType,
|
||||
const int16_t fieldId) {
|
||||
enc_.writeFieldBegin(name, fieldType, fieldId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeFieldEnd() {
|
||||
enc_.writeFieldEnd();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeFieldStop() {
|
||||
enc_.writeFieldStop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeMapBegin(const TType keyType,
|
||||
const TType valType,
|
||||
const uint32_t size) {
|
||||
enc_.writeMapBegin(keyType, valType, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeMapEnd() {
|
||||
enc_.writeMapEnd();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeListBegin(const TType elemType, const uint32_t size) {
|
||||
enc_.writeListBegin(elemType, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeListEnd() {
|
||||
enc_.writeListEnd();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeSetBegin(const TType elemType, const uint32_t size) {
|
||||
enc_.writeSetBegin(elemType, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeSetEnd() {
|
||||
enc_.writeSetEnd();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeBool(const bool value) {
|
||||
enc_.writeBool(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeByte(const int8_t byte) {
|
||||
enc_.writeByte(byte);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeI16(const int16_t i16) {
|
||||
enc_.writeI16(i16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeI32(const int32_t i32) {
|
||||
enc_.writeI32(i32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeI64(const int64_t i64) {
|
||||
enc_.writeI64(i64);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeDouble(const double dub) {
|
||||
enc_.writeDouble(dub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename StrType>
|
||||
uint32_t writeString(const StrType& str) {
|
||||
enc_.writeString(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t writeBinary(const std::string& str) {
|
||||
enc_.writeBinary(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reading functions
|
||||
*/
|
||||
|
||||
|
||||
uint32_t readMessageBegin(std::string& name,
|
||||
TMessageType& messageType,
|
||||
int32_t& seqid) {
|
||||
LOG(FATAL) << "Message encoding / decoding not implemented";
|
||||
}
|
||||
|
||||
uint32_t readMessageEnd() {
|
||||
LOG(FATAL) << "Message encoding / decoding not implemented";
|
||||
}
|
||||
|
||||
uint32_t readStructBegin(std::string& name) {
|
||||
dec_.readStructBegin();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readStructEnd() {
|
||||
dec_.readStructEnd();
|
||||
return dec_.bytesRead();
|
||||
}
|
||||
|
||||
uint32_t readFieldBegin(std::string& name,
|
||||
TType& fieldType,
|
||||
int16_t& fieldId) {
|
||||
dec_.readFieldBegin(fieldType, fieldId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readFieldEnd() {
|
||||
dec_.readFieldEnd();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readMapBegin(TType& keyType, TType& valType, uint32_t& size) {
|
||||
dec_.readMapBegin(keyType, valType, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readMapEnd() {
|
||||
dec_.readMapEnd();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readListBegin(TType& elemType, uint32_t& size) {
|
||||
dec_.readListBegin(elemType, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readListEnd() {
|
||||
dec_.readListEnd();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readSetBegin(TType& elemType, uint32_t& size) {
|
||||
dec_.readSetBegin(elemType, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readSetEnd() {
|
||||
dec_.readSetEnd();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readBool(bool& value) {
|
||||
dec_.readBool(value);
|
||||
return 0;
|
||||
}
|
||||
// Provide the default readBool() implementation for std::vector<bool>
|
||||
using TVirtualProtocol<TNeutroniumProtocol>::readBool;
|
||||
|
||||
uint32_t readByte(int8_t& byte) {
|
||||
dec_.readByte(byte);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readI16(int16_t& i16) {
|
||||
dec_.readI16(i16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readI32(int32_t& i32) {
|
||||
dec_.readI32(i32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readI64(int64_t& i64) {
|
||||
dec_.readI64(i64);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readDouble(double& dub) {
|
||||
dec_.readDouble(dub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename StrType>
|
||||
uint32_t readString(StrType& str) {
|
||||
dec_.readString(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t readBinary(std::string& str) {
|
||||
dec_.readBinary(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
neutronium::Encoder enc_;
|
||||
neutronium::Decoder dec_;
|
||||
};
|
||||
|
||||
class Neutronium {
|
||||
public:
|
||||
explicit Neutronium(const neutronium::Schema* schema,
|
||||
neutronium::InternTable* internTable,
|
||||
folly::IOBuf* buf)
|
||||
: proto_(schema, internTable, buf) {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint32_t serialize(const T& obj) {
|
||||
proto_.setRootType(T::_reflection_id);
|
||||
return obj.write(&proto_);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint32_t deserialize(T& obj) {
|
||||
proto_.setRootType(T::_reflection_id);
|
||||
return obj.read(&proto_);
|
||||
}
|
||||
|
||||
private:
|
||||
TNeutroniumProtocol proto_;
|
||||
};
|
||||
|
||||
|
||||
}}} // apache::thrift::protocol
|
||||
|
||||
#endif /* THRIFT_LIB_CPP_PROTOCOL_TNEUTRONIUMPROTOCOL_H_ */
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef _THRIFT_PROTOCOL_TPHPSERIALIZEPROTOCOL_H_
|
||||
#define _THRIFT_PROTOCOL_TPHPSERIALIZEPROTOCOL_H_ 1
|
||||
|
||||
#include "thrift/lib/cpp/protocol/TVirtualProtocol.h"
|
||||
#include "thrift/lib/cpp/transport/TBufferTransports.h"
|
||||
|
||||
#include <stack>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace apache { namespace thrift { namespace protocol {
|
||||
|
||||
|
||||
/**
|
||||
* A Thrift protocol for serializing Thrift objects into PHP's
|
||||
* "serialize" format. Should work properly for objects that
|
||||
* PHP can properly express. Currently, it can silently corrupt
|
||||
* data that PHP cannot properly express (lists or bools as map keys,
|
||||
* very large integers on 32-bit systems, and possibly others).
|
||||
*/
|
||||
|
||||
class TPhpSerializeProtocol : public TVirtualProtocol<TPhpSerializeProtocol> {
|
||||
public:
|
||||
TPhpSerializeProtocol(boost::shared_ptr<TTransport> trans)
|
||||
: TVirtualProtocol<TPhpSerializeProtocol>(trans)
|
||||
, trans_(trans.get())
|
||||
{}
|
||||
|
||||
uint32_t writeMessageBegin(const std::string& name,
|
||||
const TMessageType messageType,
|
||||
const int32_t seqid);
|
||||
|
||||
uint32_t writeMessageEnd();
|
||||
|
||||
uint32_t writeStructBegin(const char* name);
|
||||
|
||||
uint32_t writeStructEnd();
|
||||
|
||||
uint32_t writeFieldBegin(const char* name,
|
||||
const TType fieldType,
|
||||
const int16_t fieldId);
|
||||
|
||||
uint32_t writeFieldEnd();
|
||||
|
||||
uint32_t writeFieldStop();
|
||||
|
||||
uint32_t writeListBegin(const TType elemType,
|
||||
const uint32_t size);
|
||||
|
||||
uint32_t writeListEnd();
|
||||
|
||||
uint32_t writeSetBegin(const TType elemType,
|
||||
const uint32_t size);
|
||||
|
||||
uint32_t writeSetEnd();
|
||||
|
||||
uint32_t writeMapBegin(const TType keyType,
|
||||
const TType valType,
|
||||
const uint32_t size);
|
||||
|
||||
uint32_t writeMapEnd();
|
||||
|
||||
uint32_t writeBool(const bool value);
|
||||
|
||||
uint32_t writeByte(const int8_t byte);
|
||||
|
||||
uint32_t writeI16(const int16_t i16);
|
||||
|
||||
uint32_t writeI32(const int32_t i32);
|
||||
|
||||
uint32_t writeI64(const int64_t i64);
|
||||
|
||||
uint32_t writeDouble(const double dub);
|
||||
|
||||
uint32_t writeString(const std::string& str);
|
||||
|
||||
uint32_t writeBinary(const std::string& str);
|
||||
|
||||
protected:
|
||||
uint32_t doWriteInt(const int64_t i64);
|
||||
uint32_t doWriteInt(const std::string& val);
|
||||
uint32_t doWriteString(const std::string& str, bool is_class);
|
||||
uint32_t doWriteListBegin(uint32_t size, bool is_map);
|
||||
uint32_t listKey();
|
||||
uint32_t write3(const char* v1, const char* v2, const char* v3);
|
||||
uint32_t write(const char* buf, uint32_t len);
|
||||
|
||||
std::stack<int> listPosStack_;
|
||||
std::stack<int> structSizeStack_;
|
||||
std::stack< boost::shared_ptr<apache::thrift::transport::TMemoryBuffer> > structBufferStack_;
|
||||
|
||||
TTransport* trans_;
|
||||
};
|
||||
|
||||
}}} // apache::thrift::protocol
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue