Summary: Test Plan: Reviewers: CC: Task ID: # Blame Rev:main
parent
4abf94eeb4
commit
b40ad060e0
@ -0,0 +1,450 @@ |
||||
/**
|
||||
* Thrift server that supports operations on the
|
||||
* Facebook TAO Graph database |
||||
* @author Dhruba Borthakur (dhruba@gmail.com) |
||||
* Copyright 2012 Facebook |
||||
*/ |
||||
#ifndef THRIFT_LEVELDB_ASSOC_SERVER_H_ |
||||
#define THRIFT_LEVELDB_ASSOC_SERVER_H_ |
||||
|
||||
#include <AssocService.h> |
||||
#include <leveldb_types.h> |
||||
#include "openhandles.h" |
||||
#include "server_options.h" |
||||
|
||||
#include "leveldb/db.h" |
||||
#include "leveldb/write_batch.h" |
||||
#include "util/testharness.h" |
||||
|
||||
using namespace apache::thrift; |
||||
using namespace apache::thrift::protocol; |
||||
using namespace apache::thrift::transport; |
||||
using namespace apache::thrift::server; |
||||
|
||||
using boost::shared_ptr; |
||||
|
||||
using namespace ::Tleveldb; |
||||
|
||||
//
|
||||
// These are the service methods that processes Association Data.
|
||||
|
||||
class AssocServiceHandler : virtual public AssocServiceIf { |
||||
public: |
||||
|
||||
AssocServiceHandler(OpenHandles* openhandles) { |
||||
openhandles_ = openhandles; |
||||
} |
||||
|
||||
int64_t taoAssocPut(const Text& tableName, int64_t assocType, int64_t id1,
|
||||
int64_t id2, int64_t id1Type, int64_t id2Type,
|
||||
int64_t timestamp, AssocVisibility visibility,
|
||||
bool update_count, int64_t dataVersion, const Text& data,
|
||||
const Text& wormhole_comment) { |
||||
leveldb::DB* db = openhandles_->get(tableName, NULL); |
||||
if (db == NULL) { |
||||
return Code::kNotFound; |
||||
} |
||||
int64_t ret = assocPutInternal(tableName, |
||||
db, assocType, id1, id2, id1Type, id2Type, |
||||
timestamp, visibility, update_count, dataVersion, |
||||
data, wormhole_comment); |
||||
return ret; |
||||
} |
||||
|
||||
int64_t taoAssocDelete(const Text& tableName, int64_t assocType, int64_t id1,
|
||||
int64_t id2, AssocVisibility visibility, bool update_count,
|
||||
const Text& wormhole_comment) { |
||||
printf("taoAssocDelete\n"); |
||||
} |
||||
|
||||
void taoAssocRangeGet(std::vector<TaoAssocGetResult> & _return,
|
||||
const Text& tableName, int64_t assocType, int64_t id1,
|
||||
int64_t start_time, int64_t end_time, int64_t offset,
|
||||
int64_t limit) { |
||||
printf("taoAssocRangeGet\n"); |
||||
} |
||||
|
||||
void taoAssocGet(std::vector<TaoAssocGetResult> & _return,
|
||||
const Text& tableName, int64_t assocType, int64_t id1,
|
||||
const std::vector<int64_t> & id2s) { |
||||
leveldb::DB* db = openhandles_->get(tableName, NULL); |
||||
if (db == NULL) { |
||||
throw generate_exception(tableName, Code::kNotFound, |
||||
"Unable to database " , |
||||
assocType, id1, 0, 0, 0, 0, Tleveldb::UNUSED1); |
||||
} |
||||
taoAssocGetInternal(_return, tableName, db, assocType, id1, id2s); |
||||
} |
||||
|
||||
int64_t taoAssocCount(const Text& tableName, int64_t assocType, int64_t id1) { |
||||
leveldb::DB* db = openhandles_->get(tableName, NULL); |
||||
if (db == NULL) { |
||||
return Code::kNotFound; |
||||
} |
||||
return taoAssocCountInternal(tableName, db, assocType, id1); |
||||
} |
||||
|
||||
private: |
||||
OpenHandles* openhandles_; |
||||
|
||||
//
|
||||
// inserts an assoc
|
||||
// Returns true if the iinsertion is successful, otherwise return false.
|
||||
//
|
||||
bool assocPutInternal(const Text& tableName, leveldb::DB* db, |
||||
int64_t assocType, int64_t id1,
|
||||
int64_t id2, int64_t id1Type, int64_t id2Type,
|
||||
int64_t ts, AssocVisibility vis,
|
||||
bool update_count, int64_t dataVersion, const Text& data,
|
||||
const Text& wormhole_comment) { |
||||
leveldb::WriteOptions woptions; |
||||
woptions.sync = true; |
||||
|
||||
// create the payload for this assoc
|
||||
int payloadsize = sizeof(id1Type) + sizeof(id2Type) + |
||||
sizeof(dataVersion) + data.size() + wormhole_comment.size(); |
||||
std::string payload; |
||||
payload.reserve(payloadsize); |
||||
payload.resize(payloadsize); |
||||
makePayload(&payload[0], id1Type, id2Type, dataVersion, data,
|
||||
wormhole_comment); |
||||
leveldb::Slice payload_slice(payload); |
||||
|
||||
// create RowKey with plenty of space at the end to query
|
||||
// all columns 'c', 'm', 'p, etc.
|
||||
int maxkeysize = sizeof(id1) + sizeof(assocType) + |
||||
1 + // 'c', 'p' or 'm'
|
||||
sizeof(ts) + |
||||
sizeof(id2); |
||||
std::string dummy; |
||||
dummy.reserve(maxkeysize); |
||||
dummy.resize(maxkeysize); |
||||
char* keybuf = &dummy[0]; |
||||
int rowkeysize = makeRowKey(keybuf, id1, assocType); |
||||
leveldb::ReadOptions roptions; |
||||
leveldb::Status status; |
||||
std::string value; |
||||
int keysize; |
||||
|
||||
int64_t count = 0; |
||||
int64_t oldts; |
||||
int8_t oldvis; |
||||
bool newassoc = false; // is this assoc new or an overwrite
|
||||
|
||||
// make a key for count
|
||||
keysize = appendRowKeyForCount(rowkeysize, keybuf); |
||||
leveldb::Slice ckey(keybuf, keysize); |
||||
|
||||
// Scan 'c' to get $count if $update_count == true
|
||||
if (update_count) { |
||||
status = db->Get(roptions, ckey, &value); |
||||
if (status.IsNotFound()) { |
||||
// nothing to do
|
||||
} else if (!status.ok() || (value.size() != sizeof(int64_t))) { |
||||
throw generate_exception(tableName, Code::kNotFound, |
||||
"Unable to find count ",
|
||||
assocType, id1, id2, id1Type, id2Type, ts, vis); |
||||
|
||||
} else { |
||||
extract_int64(&count, (char *)value.c_str()); |
||||
} |
||||
} |
||||
|
||||
// Scan 'm'$id2 to get $ts and $vis
|
||||
keysize = appendRowKeyForMeta(rowkeysize, keybuf, id2); |
||||
leveldb::Slice mkey(keybuf, keysize); |
||||
status = db->Get(roptions, mkey, &value); |
||||
if (status.IsNotFound()) { |
||||
newassoc = true; |
||||
} else if (!status.ok() ||
|
||||
(value.size() != sizeof(int64_t) + sizeof(int8_t))) { |
||||
throw generate_exception(tableName, Code::kNotFound, |
||||
"Unable to find m$id2 ",
|
||||
assocType, id1, id2, id1Type, id2Type, ts, vis); |
||||
} |
||||
|
||||
// make the key 'p'$old_ts$id2
|
||||
keysize = appendRowKeyForPayload(rowkeysize, keybuf, oldts, id2); |
||||
leveldb::Slice pkey(keybuf, keysize); |
||||
|
||||
// if ts != oldts, then delete 'p'$old_ts$id2
|
||||
if (!newassoc) { |
||||
char* val = (char *)value.c_str(); |
||||
extract_int64(&oldts, val); |
||||
extract_int8(&oldvis, val + sizeof(int64_t)); |
||||
|
||||
if (ts != oldts) { |
||||
if (!db->Delete(woptions, pkey).ok()) { |
||||
throw generate_exception(tableName, Code::kNotFound, |
||||
"Unable to delete from p$oldts$id2 ",
|
||||
assocType, id1, id2, id1Type, id2Type, ts, vis); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// store in m$id2 the value of $ts$vis
|
||||
std::string myvalue; |
||||
appendRowKeyForMeta(rowkeysize, keybuf, id2); |
||||
myvalue.reserve(sizeof(int64_t) + sizeof(int8_t)); |
||||
myvalue.resize(sizeof(int64_t) + sizeof(int8_t)); |
||||
makeTsVisString(&myvalue[0], ts, vis); |
||||
leveldb::Slice sl(myvalue); |
||||
if (!db->Put(woptions, mkey, sl).ok()) { |
||||
// throw exception;
|
||||
throw generate_exception(tableName, Code::kNotFound, |
||||
"Unable to put into m$id2",
|
||||
assocType, id1, id2, id1Type, id2Type, ts, vis); |
||||
} |
||||
|
||||
// store in p$ts$id2 the payload
|
||||
appendRowKeyForPayload(rowkeysize, keybuf, ts, id2); |
||||
if (!db->Put(woptions, pkey, payload_slice).ok()) { |
||||
throw generate_exception(tableName, Code::kNotFound, |
||||
"Unable to put into p$ts$id2",
|
||||
assocType, id1, id2, id1Type, id2Type, ts, vis); |
||||
} |
||||
|
||||
// increment count
|
||||
if (update_count && (newassoc || oldvis != VISIBLE)) { |
||||
assert(count >= 0); |
||||
count++; |
||||
appendRowKeyForCount(rowkeysize, keybuf); |
||||
myvalue.reserve(sizeof(int64_t)); |
||||
myvalue.resize(sizeof(int64_t)); |
||||
makeCountString(&myvalue[0], count); |
||||
leveldb::Slice count_slice(myvalue); |
||||
if (!db->Put(woptions, ckey, count_slice).ok()) { |
||||
throw generate_exception(tableName, Code::kNotFound, |
||||
"Unable to insert into count",
|
||||
assocType, id1, id2, id1Type, id2Type, ts, vis); |
||||
} |
||||
} |
||||
if (update_count) { |
||||
assert(count > 0); |
||||
return count; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
int64_t taoAssocCountInternal(const Text& tableName, leveldb::DB* db, |
||||
int64_t assocType, int64_t id1) { |
||||
// create key to query
|
||||
int maxkeysize = sizeof(id1) + sizeof(assocType) + 1; |
||||
std::string dummy; |
||||
dummy.reserve(maxkeysize); |
||||
dummy.resize(maxkeysize); |
||||
char* keybuf = &dummy[0]; |
||||
int rowkeysize = makeRowKey(keybuf, id1, assocType); |
||||
int keysize = appendRowKeyForCount(rowkeysize, keybuf); // column 'c'
|
||||
leveldb::Slice ckey(keybuf, keysize); |
||||
|
||||
// query database to find value
|
||||
leveldb::ReadOptions roptions; |
||||
leveldb::Status status; |
||||
std::string value; |
||||
int64_t count; |
||||
status = db->Get(roptions, ckey, &value); |
||||
|
||||
// parse results retrieved from database
|
||||
if (status.IsNotFound()) { |
||||
return 0; // non existant assoc
|
||||
} else if (!status.ok()) { |
||||
throw generate_exception(tableName, Code::kNotFound, |
||||
"Unable to find count ", |
||||
assocType, id1, 0, 0, 0, 0, Tleveldb::UNUSED1); |
||||
} |
||||
if (value.size() != sizeof(int64_t)) { |
||||
printf("expected %lld got %lld\n", sizeof(int64_t), value.size()); |
||||
throw generate_exception(tableName, Code::kNotFound, |
||||
"Bad sizes for count ", |
||||
assocType, id1, 0, 0, 0, 0, Tleveldb::UNUSED1); |
||||
} |
||||
extract_int64(&count, (char *)value.c_str()); |
||||
return count; |
||||
} |
||||
|
||||
void taoAssocGetInternal(std::vector<TaoAssocGetResult> & _return,
|
||||
const Text& tableName,
|
||||
leveldb::DB* db, |
||||
int64_t assocType, int64_t id1,
|
||||
const std::vector<int64_t> & id2s) { |
||||
int64_t ts, id2; |
||||
int8_t oldvis; |
||||
leveldb::ReadOptions roptions; |
||||
leveldb::Status status; |
||||
std::string value; |
||||
int numout = 0; |
||||
|
||||
// create max key to query
|
||||
int maxkeysize = sizeof(id1) + sizeof(assocType) + 1 + sizeof(ts) + |
||||
sizeof(id2); |
||||
std::string dummy; |
||||
dummy.reserve(maxkeysize); |
||||
dummy.resize(maxkeysize); |
||||
|
||||
// create rowkey
|
||||
char* keybuf = &dummy[0]; |
||||
int rowkeysize = makeRowKey(keybuf, id1, assocType); |
||||
|
||||
for (int index = 0; index < id2s.size(); index++) { |
||||
int64_t id2 = id2s[index]; |
||||
|
||||
// query column 'm'$id2
|
||||
int keysize = appendRowKeyForMeta(rowkeysize, keybuf, id2);
|
||||
leveldb::Slice ckey(keybuf, keysize); |
||||
status = db->Get(roptions, ckey, &value); |
||||
|
||||
// parse results retrieved from database
|
||||
if (status.IsNotFound()) { |
||||
continue; // non existant assoc
|
||||
} else if (!status.ok() || |
||||
value.size() != sizeof(int64_t) + sizeof(int8_t)) { |
||||
throw generate_exception(tableName, Code::kNotFound, |
||||
"Unable to find m$id2 ", |
||||
assocType, id1, id2, 0, 0, 0, Tleveldb::UNUSED1); |
||||
} |
||||
extractTsVisString(&ts, &oldvis, &value[0]); |
||||
if(oldvis != AssocVisibility::VISIBLE) { |
||||
continue; |
||||
} |
||||
ASSERT_NE(ts, 0); |
||||
printf("XXX ts = %lld\n", ts); |
||||
|
||||
// this assoc is visible, scan 'p'$ts$id2 to retrieve payload.
|
||||
keysize = appendRowKeyForPayload(rowkeysize, keybuf, ts, id2);
|
||||
leveldb::Slice pkey(keybuf, keysize); |
||||
status = db->Get(roptions, pkey, &value); |
||||
|
||||
// parse results retrieved from database
|
||||
if (status.IsNotFound()) { |
||||
printf("XXX2"); |
||||
continue; // non existant assoc
|
||||
} else if (!status.ok()) { |
||||
throw generate_exception(tableName, Code::kNotFound, |
||||
"Unable to find m$id2 ", |
||||
assocType, id1, id2, 0, 0, 0, Tleveldb::UNUSED1); |
||||
} |
||||
|
||||
// update return values
|
||||
_return[numout].id2 = id2; |
||||
_return[numout].data = value; |
||||
|
||||
// un-encoded from the payload XXX
|
||||
_return[numout].id1Type = 0; |
||||
_return[numout].id2Type = 0; |
||||
_return[numout].dataVersion = 0; |
||||
numout++; |
||||
} |
||||
} |
||||
|
||||
// fill the row key and returns the size of the key
|
||||
inline int makeRowKey(char* dest, int64_t id1, int64_t assocType) { |
||||
dest = copy_int64_switch_endian(dest, id1); |
||||
dest = copy_int64_switch_endian(dest, assocType); |
||||
return sizeof(id1) + sizeof(assocType); |
||||
} |
||||
|
||||
// fill the row key +'c' and returns the size of the key
|
||||
inline int appendRowKeyForCount(int rowkeysize, char* dest) { |
||||
dest += rowkeysize; |
||||
*dest = 'c'; |
||||
return rowkeysize + 1; |
||||
} |
||||
|
||||
// fill the row key +'p' + $ts$id2 and returns the size of the key
|
||||
inline int appendRowKeyForPayload(int rowkeysize, char* dest, |
||||
int64_t ts, int64_t id2) { |
||||
dest += rowkeysize; |
||||
*dest++ = 'p'; |
||||
dest = copy_int64_switch_endian(dest, ts); |
||||
dest = copy_int64_switch_endian(dest, id2); |
||||
return rowkeysize + sizeof(ts) + sizeof(id2) + 1; |
||||
} |
||||
// fill the row key +'m' + id2 and returns the size of the key
|
||||
inline int appendRowKeyForMeta(int rowkeysize, char* dest,
|
||||
int64_t id2) { |
||||
dest += rowkeysize; |
||||
*dest++ = 'm'; |
||||
dest = copy_int64_switch_endian(dest, id2); |
||||
return rowkeysize + sizeof(id2) + 1; |
||||
} |
||||
|
||||
//
|
||||
// encode id1Type, id2Type, dataversion, etc into the payload
|
||||
inline void makePayload(char* dest, int64_t id1Type, int64_t id2Type, |
||||
int64_t dataVersion, const Text& data,
|
||||
const Text& wormhole_comment) { |
||||
dest = copy_int64_switch_endian(dest, id1Type); |
||||
dest = copy_int64_switch_endian(dest, id2Type); |
||||
dest = copy_int64_switch_endian(dest, dataVersion); |
||||
memcpy(dest, data.data(), data.size()); |
||||
dest += data.size(); |
||||
memcpy(dest, wormhole_comment.data(), wormhole_comment.size()); |
||||
dest += wormhole_comment.size(); |
||||
} |
||||
|
||||
// fill the timestamp and visibility
|
||||
inline void makeTsVisString(char* dest, int64_t ts, int8_t vis) { |
||||
dest = copy_int64_switch_endian(dest, ts); |
||||
*dest = vis; |
||||
} |
||||
|
||||
// extracts the timestamp and visibility from a byte stream
|
||||
inline void extractTsVisString(int64_t* ts, int8_t* vis, char* src) { |
||||
extract_int64(ts, src); |
||||
extract_int8(vis, src + sizeof(*ts)); |
||||
} |
||||
|
||||
// fill the count value
|
||||
inline void makeCountString(char* dest, int64_t count) { |
||||
dest = copy_int64_switch_endian(dest, count); |
||||
} |
||||
|
||||
//
|
||||
// Switch endianess of the id and copy it to dest.
|
||||
// Returns the updated destination address
|
||||
//
|
||||
inline char* copy_int64_switch_endian(char* dest, int64_t id) { |
||||
char* src = (char *)&id + sizeof(id) - 1; |
||||
for (int i = 0; i < sizeof(id); i++) { |
||||
*dest++ = *src--;
|
||||
} |
||||
return dest; |
||||
} |
||||
|
||||
// extracts a int64 type from the char stream. Swaps endianness.
|
||||
inline void extract_int64(int64_t* dest, char* src) { |
||||
src += sizeof(int64_t) - 1; |
||||
for (int i = 0; i < sizeof(uint64_t); i++) { |
||||
*dest++ = *src--;
|
||||
} |
||||
} |
||||
|
||||
// extracts a 1 byte integer from the char stream.
|
||||
inline void extract_int8(int8_t* dest, char* src) { |
||||
*dest = *(int8_t *)src; |
||||
} |
||||
|
||||
|
||||
// generate an exception message
|
||||
LeveldbException generate_exception(const Text& tableName, |
||||
Code errorCode, const char* message, |
||||
int64_t assocType, int64_t id1,
|
||||
int64_t id2, int64_t id1Type, int64_t id2Type,
|
||||
int64_t ts, AssocVisibility vis) { |
||||
char result[1024]; |
||||
sprintf(result,
|
||||
"id1=%d assocType=%d id2=%d id1Type=%d id2Type=%d ts=%d vis=%d ",
|
||||
id1, assocType, id2, id1Type, id2Type, ts, vis); |
||||
fprintf(stderr, "assoc_server error table %s: %s errorCode=%d %s", |
||||
tableName.c_str(), message, errorCode, result); |
||||
|
||||
LeveldbException e; |
||||
e.errorCode = errorCode; |
||||
e.message = message; |
||||
throw e; |
||||
} |
||||
|
||||
}; |
||||
|
||||
#endif // THRIFT_LEVELDB_ASSOC_SERVER_H_
|
@ -0,0 +1,70 @@ |
||||
/*
|
||||
* 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. |
||||
*/ |
||||
|
||||
// @author: Andrei Alexandrescu
|
||||
|
||||
#ifndef FOLLY_PREPROCESSOR_ |
||||
#define FOLLY_PREPROCESSOR_ |
||||
|
||||
/**
|
||||
* Necessarily evil preprocessor-related amenities. |
||||
*/ |
||||
|
||||
/**
|
||||
* FB_ONE_OR_NONE(hello, world) expands to hello and |
||||
* FB_ONE_OR_NONE(hello) expands to nothing. This macro is used to |
||||
* insert or eliminate text based on the presence of another argument. |
||||
*/ |
||||
#define FB_ONE_OR_NONE(a, ...) FB_THIRD(a, ## __VA_ARGS__, a) |
||||
#define FB_THIRD(a, b, ...) __VA_ARGS__ |
||||
|
||||
/**
|
||||
* Helper macro that extracts the first argument out of a list of any |
||||
* number of arguments. |
||||
*/ |
||||
#define FB_ARG_1(a, ...) a |
||||
|
||||
/**
|
||||
* Helper macro that extracts the second argument out of a list of any |
||||
* number of arguments. If only one argument is given, it returns |
||||
* that. |
||||
*/ |
||||
#define FB_ARG_2_OR_1(...) FB_ARG_2_OR_1_IMPL(__VA_ARGS__, __VA_ARGS__) |
||||
// Support macro for the above
|
||||
#define FB_ARG_2_OR_1_IMPL(a, b, ...) b |
||||
|
||||
/**
|
||||
* FB_ANONYMOUS_VARIABLE(str) introduces an identifier starting with |
||||
* str and ending with a number that varies with the line. |
||||
*/ |
||||
#ifndef FB_ANONYMOUS_VARIABLE |
||||
#define FB_CONCATENATE_IMPL(s1, s2) s1##s2 |
||||
#define FB_CONCATENATE(s1, s2) FB_CONCATENATE_IMPL(s1, s2) |
||||
#ifdef __COUNTER__ |
||||
#define FB_ANONYMOUS_VARIABLE(str) FB_CONCATENATE(str, __COUNTER__) |
||||
#else |
||||
#define FB_ANONYMOUS_VARIABLE(str) FB_CONCATENATE(str, __LINE__) |
||||
#endif |
||||
#endif |
||||
|
||||
/**
|
||||
* Use FB_STRINGIZE(name) when you'd want to do what #name does inside |
||||
* another macro expansion. |
||||
*/ |
||||
#define FB_STRINGIZE(name) #name |
||||
|
||||
|
||||
#endif // FOLLY_PREPROCESSOR_
|
@ -0,0 +1,158 @@ |
||||
/*
|
||||
* 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_SCOPEGUARD_H_ |
||||
#define FOLLY_SCOPEGUARD_H_ |
||||
|
||||
#include <cstddef> |
||||
#include <iostream> |
||||
#include <functional> |
||||
#include <new> |
||||
//#include <glog/logging.h>
|
||||
|
||||
#include "folly/Preprocessor.h" |
||||
|
||||
namespace folly { |
||||
|
||||
/**
|
||||
* ScopeGuard is a general implementation of the "Initilization is |
||||
* Resource Acquisition" idiom. Basically, it guarantees that a function |
||||
* is executed upon leaving the currrent scope unless otherwise told. |
||||
* |
||||
* The makeGuard() function is used to create a new ScopeGuard object. |
||||
* It can be instantiated with a lambda function, a std::function<void()>, |
||||
* a functor, or a void(*)() function pointer. |
||||
* |
||||
* |
||||
* Usage example: Add a friend to memory iff it is also added to the db. |
||||
* |
||||
* void User::addFriend(User& newFriend) { |
||||
* // add the friend to memory
|
||||
* friends_.push_back(&newFriend); |
||||
* |
||||
* // If the db insertion that follows fails, we should
|
||||
* // remove it from memory.
|
||||
* // (You could also declare this as "auto guard = makeGuard(...)")
|
||||
* ScopeGuard guard = makeGuard([&] { friends_.pop_back(); }); |
||||
* |
||||
* // this will throw an exception upon error, which
|
||||
* // makes the ScopeGuard execute UserCont::pop_back()
|
||||
* // once the Guard's destructor is called.
|
||||
* db_->addFriend(GetName(), newFriend.GetName()); |
||||
* |
||||
* // an exception was not thrown, so don't execute
|
||||
* // the Guard.
|
||||
* guard.dismiss(); |
||||
* } |
||||
* |
||||
* Examine ScopeGuardTest.cpp for some more sample usage. |
||||
* |
||||
* Stolen from: |
||||
* Andrei's and Petru Marginean's CUJ article: |
||||
* http://drdobbs.com/184403758
|
||||
* and the loki library: |
||||
* http://loki-lib.sourceforge.net/index.php?n=Idioms.ScopeGuardPointer
|
||||
* and triendl.kj article: |
||||
* http://www.codeproject.com/KB/cpp/scope_guard.aspx
|
||||
*/ |
||||
class ScopeGuardImplBase { |
||||
public: |
||||
void dismiss() noexcept { |
||||
dismissed_ = true; |
||||
} |
||||
|
||||
protected: |
||||
ScopeGuardImplBase() |
||||
: dismissed_(false) {} |
||||
|
||||
ScopeGuardImplBase(ScopeGuardImplBase&& other) |
||||
: dismissed_(other.dismissed_) { |
||||
other.dismissed_ = true; |
||||
} |
||||
|
||||
bool dismissed_; |
||||
}; |
||||
|
||||
template<typename FunctionType> |
||||
class ScopeGuardImpl : public ScopeGuardImplBase { |
||||
public: |
||||
explicit ScopeGuardImpl(const FunctionType& fn) |
||||
: function_(fn) {} |
||||
|
||||
explicit ScopeGuardImpl(FunctionType&& fn) |
||||
: function_(std::move(fn)) {} |
||||
|
||||
ScopeGuardImpl(ScopeGuardImpl&& other) |
||||
: ScopeGuardImplBase(std::move(other)), |
||||
function_(std::move(other.function_)) { |
||||
} |
||||
|
||||
~ScopeGuardImpl() noexcept { |
||||
if (!dismissed_) { |
||||
execute(); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
void* operator new(size_t) = delete; |
||||
|
||||
void execute() noexcept { |
||||
try { |
||||
function_(); |
||||
} catch (const std::exception& ex) { |
||||
std::cout << "ScopeGuard cleanup function threw a " << |
||||
typeid(ex).name() << "exception: " << ex.what(); |
||||
} catch (...) { |
||||
std::cout << "ScopeGuard cleanup function threw a non-exception object"; |
||||
} |
||||
} |
||||
|
||||
FunctionType function_; |
||||
}; |
||||
|
||||
template<typename FunctionType> |
||||
ScopeGuardImpl<typename std::decay<FunctionType>::type> |
||||
makeGuard(FunctionType&& fn) { |
||||
return ScopeGuardImpl<typename std::decay<FunctionType>::type>( |
||||
std::forward<FunctionType>(fn)); |
||||
} |
||||
|
||||
/**
|
||||
* This is largely unneeded if you just use auto for your guards. |
||||
*/ |
||||
typedef ScopeGuardImplBase&& ScopeGuard; |
||||
|
||||
namespace detail { |
||||
/**
|
||||
* Internal use for the macro SCOPE_EXIT below |
||||
*/ |
||||
enum class ScopeGuardOnExit {}; |
||||
|
||||
template <typename FunctionType> |
||||
ScopeGuardImpl<typename std::decay<FunctionType>::type> |
||||
operator+(detail::ScopeGuardOnExit, FunctionType&& fn) { |
||||
return ScopeGuardImpl<typename std::decay<FunctionType>::type>( |
||||
std::forward<FunctionType>(fn)); |
||||
} |
||||
} // namespace detail
|
||||
|
||||
} // folly
|
||||
|
||||
#define SCOPE_EXIT \ |
||||
auto FB_ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
|
||||
= ::folly::detail::ScopeGuardOnExit() + [&] |
||||
|
||||
#endif // FOLLY_SCOPEGUARD_H_
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@ |
||||
!<arch> |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,11 +0,0 @@ |
||||
/**
|
||||
* Options for the Thrift leveldb server. |
||||
* @author Dhruba Borthakur (dhruba@gmail.com) |
||||
* Copyright 2012 Facebook |
||||
**/ |
||||
#include <DB.h> |
||||
#include "server_options.h" |
||||
|
||||
const std::string ServerOptions::DEFAULT_HOST = "hostname"; |
||||
const std::string ServerOptions::DEFAULT_ROOTDIR = "/tmp/ldb/"; |
||||
|
Loading…
Reference in new issue