1
0
Fork 0

fix premature unlock (#3802)

* fix some deadlocks found by evil lock manager (tm)

* fix duplicate lock

* fix indentation

* ensure proper lock dependencies

* fix lock acquisition

* removed useless comment

* do not lock twice

* create either a V8 transaction context or a standalone transaction context, depending on if we are called from within V8 or not

* AQL micro optimizations

* use explicit constructor

* only use V8DealerFeature's ConditionLocker for acquiring a free V8 context

entering and exiting the selected context is then done later on without having to hold the ConditionLocker

* remove some recursive locks

* Disable custom deadlock detection when Thread Sanitizer is enabled

* Changing ifdef's

* grr

* broke gcc

* Using atomic for ApplicationServer::_server

* fix premature unlock

* add some asserts

* honor collection locking in cluster

* yet one more lock fix

* removed assertion

* some more bugfixes

* Fixing assert

(cherry picked from commit 1155df173bfb67303077fbe04ee8d909517bfd21)
This commit is contained in:
Jan 2017-12-13 13:27:42 +01:00 committed by GitHub
parent 3143805c6f
commit 9c76613e63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 578 additions and 367 deletions

View File

@ -74,7 +74,7 @@ config_t::config_t(
_lock() {}
config_t::config_t(config_t const& other) {
READ_LOCKER(readLocker, other._lock);
// will call operator=, which will ensure proper locking
*this = other;
}

View File

@ -88,7 +88,7 @@ void RestAgencyHandler::redirectRequest(std::string const& leaderId) {
_response->setResponseCode(rest::ResponseCode::TEMPORARY_REDIRECT);
_response->setHeaderNC(StaticStrings::Location, url);
LOG_TOPIC(DEBUG, Logger::AGENCY) << "Sending 307 redirect to " << url;
} catch (std::exception const& e) {
} catch (std::exception const&) {
reportMessage(rest::ResponseCode::SERVICE_UNAVAILABLE, "No leader");
}
}

View File

@ -122,7 +122,7 @@ std::shared_ptr<VPackBuilder> Ast::toVelocyPack(bool verbose) const {
return builder;
}
/// @brief destroy the AST
/// @brief add an operation to the AST
void Ast::addOperation(AstNode* node) {
TRI_ASSERT(_root != nullptr);
@ -840,6 +840,7 @@ AstNode* Ast::createNodeValueInt(int64_t value) {
AstNode* node = createNode(NODE_TYPE_VALUE);
node->setValueType(VALUE_TYPE_INT);
node->setIntValue(value);
node->setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
return node;
}
@ -849,6 +850,7 @@ AstNode* Ast::createNodeValueDouble(double value) {
AstNode* node = createNode(NODE_TYPE_VALUE);
node->setValueType(VALUE_TYPE_DOUBLE);
node->setDoubleValue(value);
node->setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
return node;
}
@ -869,6 +871,7 @@ AstNode* Ast::createNodeValueString(char const* value, size_t length) {
AstNode* node = createNode(NODE_TYPE_VALUE);
node->setValueType(VALUE_TYPE_STRING);
node->setStringValue(value, length);
node->setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
return node;
}
@ -1247,13 +1250,13 @@ AstNode* Ast::createNodeShortestPath(
}
/// @brief create an AST function call node
AstNode* Ast::createNodeFunctionCall(char const* functionName,
AstNode* Ast::createNodeFunctionCall(char const* functionName, size_t length,
AstNode const* arguments) {
if (functionName == nullptr) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
}
auto normalized = normalizeFunctionName(functionName);
auto normalized = normalizeFunctionName(functionName, length);
AstNode* node;
@ -2946,7 +2949,7 @@ AstNode* Ast::optimizeFunctionCall(AstNode* node) {
auto countArgs = createNodeArray();
countArgs->addMember(createNodeValueString(arg->getStringValue(),
arg->getStringLength()));
return createNodeFunctionCall("COLLECTION_COUNT", countArgs);
return createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("COLLECTION_COUNT"), countArgs);
}
}
} else if (func->name == "IS_NULL") {
@ -3180,6 +3183,11 @@ AstNode* Ast::nodeFromVPack(VPackSlice const& slice, bool copyStringValues) {
}
if (slice.isNumber()) {
if (slice.isSmallInt() || slice.isInt()) {
// integer value
return createNodeValueInt(slice.getInt());
}
// floating point value
return createNodeValueDouble(slice.getNumber<double>());
}
@ -3394,10 +3402,10 @@ void Ast::traverseReadOnly(AstNode const* node,
}
/// @brief normalize a function name
std::pair<std::string, bool> Ast::normalizeFunctionName(char const* name) {
std::pair<std::string, bool> Ast::normalizeFunctionName(char const* name, size_t length) {
TRI_ASSERT(name != nullptr);
std::string functionName(name);
std::string functionName(name, length);
// convert name to upper case
std::transform(functionName.begin(), functionName.end(), functionName.begin(), ::toupper);

View File

@ -344,7 +344,11 @@ class Ast {
AstNode const*, AstNode const*);
/// @brief create an AST function call node
AstNode* createNodeFunctionCall(char const*, AstNode const*);
AstNode* createNodeFunctionCall(char const* functionName, AstNode const* arguments) {
return createNodeFunctionCall(functionName, strlen(functionName), arguments);
}
AstNode* createNodeFunctionCall(char const* functionName, size_t length, AstNode const* arguments);
/// @brief create an AST range node
AstNode* createNodeRange(AstNode const*, AstNode const*);
@ -512,9 +516,9 @@ public:
static void traverseReadOnly(AstNode const*,
std::function<void(AstNode const*, void*)>,
void*);
private:
private:
/// @brief normalize a function name
std::pair<std::string, bool> normalizeFunctionName(char const*);
std::pair<std::string, bool> normalizeFunctionName(char const* functionName, size_t length);
/// @brief create a node of the specified type
AstNode* createNode(AstNodeType);

View File

@ -397,7 +397,17 @@ std::pair<bool, bool> Condition::findIndexes(
transaction::Methods* trx = _ast->query()->trx();
size_t const itemsInIndex = node->collection()->count(trx);
size_t itemsInIndex;
if (!collectionName.empty() && collectionName[0] == '_' &&
collectionName.substr(0, 11) == "_statistics") {
// use hard-coded number of items in index, because we are dealing with
// the statistics collection here. this saves a roundtrip to the DB servers
// for statistics queries that do not need a fully accurate collection count
itemsInIndex = 1024;
} else {
// actually count number of items in index
itemsInIndex = node->collection()->count(trx);
}
if (_root == nullptr) {
size_t dummy;
return trx->getIndexForSortCondition(collectionName, sortCondition,

View File

@ -107,10 +107,10 @@ arangodb::aql::AstNode* IndexBlock::makeUnique(
if (isSparse) {
// the index is sorted. we need to use SORTED_UNIQUE to get the
// result back in index order
return ast->createNodeFunctionCall("SORTED_UNIQUE", array);
return ast->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("SORTED_UNIQUE"), array);
}
// a regular UNIQUE will do
return ast->createNodeFunctionCall("UNIQUE", array);
return ast->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("UNIQUE"), array);
}
// presumably an array with no or a single member

View File

@ -226,7 +226,7 @@ void arangodb::aql::sortInValuesRule(Optimizer* opt,
auto args = ast->createNodeArray();
args->addMember(originalArg);
auto sorted = ast->createNodeFunctionCall("SORTED_UNIQUE", args);
auto sorted = ast->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("SORTED_UNIQUE"), args);
auto outVar = ast->variables()->createTemporaryVariable();
ExecutionNode* calculationNode = nullptr;
@ -4983,10 +4983,10 @@ std::unique_ptr<Condition> buildGeoCondition(ExecutionPlan* plan,
args->addMember(info.range);
auto lessValue = ast->createNodeValueBool(info.lessgreaterequal);
args->addMember(lessValue);
cond = ast->createNodeFunctionCall("WITHIN", args);
cond = ast->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("WITHIN"), args);
} else {
// NEAR
cond = ast->createNodeFunctionCall("NEAR", args);
cond = ast->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("NEAR"), args);
}
TRI_ASSERT(cond != nullptr);

View File

@ -56,8 +56,8 @@ QueryResources::~QueryResources() {
_resourceMonitor->decreaseMemoryUsage(_nodes.size() * sizeof(AstNode) + _nodes.capacity() * sizeof(AstNode*));
}
// TODO: FIXME
void QueryResources::steal() {
// we are not responsible for freeing any data, so we delete our inventory
_strings.clear();
_nodes.clear();
}
@ -103,7 +103,7 @@ char* QueryResources::registerString(char const* p, size_t length) {
return const_cast<char*>(EmptyString);
}
if (length < ShortStringStorage::MaxStringLength) {
if (length < ShortStringStorage::maxStringLength()) {
return _shortStringStorage.registerString(p, length);
}
@ -148,10 +148,15 @@ char* QueryResources::registerLongString(char* copy, size_t length) {
TRI_ASSERT(capacity >= _strings.capacity());
// reserve space
_resourceMonitor->increaseMemoryUsage((capacity - _strings.size()) * sizeof(char*));
_strings.reserve(capacity);
_resourceMonitor->increaseMemoryUsage(((capacity - _strings.size()) * sizeof(char*)) + length);
try {
_strings.reserve(capacity);
} catch (...) {
// revert change in memory increase
_resourceMonitor->decreaseMemoryUsage(((capacity - _strings.size()) * sizeof(char*)) + length);
throw;
}
_resourceMonitor->increaseMemoryUsage(length);
// will not fail
_strings.emplace_back(copy);
_stringsLength += length;

View File

@ -49,17 +49,17 @@ class QueryResources {
/// @brief register a string
/// the string is freed when the query is destroyed
char* registerString(char const*, size_t);
char* registerString(char const* p, size_t length);
/// @brief register a string
/// the string is freed when the query is destroyed
char* registerString(std::string const& p) {
return registerString(p.c_str(), p.length());
return registerString(p.data(), p.size());
}
/// @brief register a potentially UTF-8-escaped string
/// the string is freed when the query is destroyed
char* registerEscapedString(char const*, size_t, size_t&);
char* registerEscapedString(char const* p, size_t length, size_t& outLength);
private:
char* registerLongString(char* copy, size_t length);

View File

@ -27,9 +27,6 @@
using namespace arangodb::aql;
/// @brief maximum length of a "short" string
size_t const ShortStringStorage::MaxStringLength = 127;
/// @brief create a short string storage instance
ShortStringStorage::ShortStringStorage(ResourceMonitor* resourceMonitor, size_t blockSize)
: _resourceMonitor(resourceMonitor), _blocks(), _blockSize(blockSize), _current(nullptr), _end(nullptr) {
@ -46,7 +43,7 @@ ShortStringStorage::~ShortStringStorage() {
/// @brief register a short string
char* ShortStringStorage::registerString(char const* p, size_t length) {
TRI_ASSERT(length <= MaxStringLength);
TRI_ASSERT(length <= maxStringLength());
if (_current == nullptr || (_current + length + 1 > _end)) {
allocateBlock();

View File

@ -48,7 +48,7 @@ class ShortStringStorage {
public:
/// @brief maximum length of strings in short string storage
static size_t const MaxStringLength;
static constexpr size_t maxStringLength() { return 127; }
private:
ResourceMonitor* _resourceMonitor;

View File

@ -3244,7 +3244,7 @@ yyreduce:
#line 1111 "Aql/grammar.y" /* yacc.c:1646 */
{
auto list = static_cast<AstNode const*>(parser->popStack());
(yyval.node) = parser->ast()->createNodeFunctionCall("LIKE", list);
(yyval.node) = parser->ast()->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("LIKE"), list);
}
#line 3250 "Aql/grammar.cpp" /* yacc.c:1646 */
break;
@ -3399,7 +3399,7 @@ yyreduce:
AstNode* arguments = parser->ast()->createNodeArray(2);
arguments->addMember((yyvsp[-2].node));
arguments->addMember((yyvsp[0].node));
(yyval.node) = parser->ast()->createNodeFunctionCall("LIKE", arguments);
(yyval.node) = parser->ast()->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("LIKE"), arguments);
}
#line 3405 "Aql/grammar.cpp" /* yacc.c:1646 */
break;
@ -3410,7 +3410,7 @@ yyreduce:
AstNode* arguments = parser->ast()->createNodeArray(2);
arguments->addMember((yyvsp[-2].node));
arguments->addMember((yyvsp[0].node));
(yyval.node) = parser->ast()->createNodeFunctionCall("REGEX_TEST", arguments);
(yyval.node) = parser->ast()->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("REGEX_TEST"), arguments);
}
#line 3416 "Aql/grammar.cpp" /* yacc.c:1646 */
break;
@ -3421,7 +3421,7 @@ yyreduce:
AstNode* arguments = parser->ast()->createNodeArray(2);
arguments->addMember((yyvsp[-2].node));
arguments->addMember((yyvsp[0].node));
AstNode* node = parser->ast()->createNodeFunctionCall("REGEX_TEST", arguments);
AstNode* node = parser->ast()->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("REGEX_TEST"), arguments);
(yyval.node) = parser->ast()->createNodeUnaryOperator(NODE_TYPE_OPERATOR_UNARY_NOT, node);
}
#line 3428 "Aql/grammar.cpp" /* yacc.c:1646 */

View File

@ -1115,7 +1115,7 @@ function_call:
parser->pushStack(node);
} optional_function_call_arguments T_CLOSE %prec FUNCCALL {
auto list = static_cast<AstNode const*>(parser->popStack());
$$ = parser->ast()->createNodeFunctionCall("LIKE", list);
$$ = parser->ast()->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("LIKE"), list);
}
;
@ -1181,19 +1181,19 @@ operator_binary:
AstNode* arguments = parser->ast()->createNodeArray(2);
arguments->addMember($1);
arguments->addMember($3);
$$ = parser->ast()->createNodeFunctionCall("LIKE", arguments);
$$ = parser->ast()->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("LIKE"), arguments);
}
| expression T_REGEX_MATCH expression {
AstNode* arguments = parser->ast()->createNodeArray(2);
arguments->addMember($1);
arguments->addMember($3);
$$ = parser->ast()->createNodeFunctionCall("REGEX_TEST", arguments);
$$ = parser->ast()->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("REGEX_TEST"), arguments);
}
| expression T_REGEX_NON_MATCH expression {
AstNode* arguments = parser->ast()->createNodeArray(2);
arguments->addMember($1);
arguments->addMember($3);
AstNode* node = parser->ast()->createNodeFunctionCall("REGEX_TEST", arguments);
AstNode* node = parser->ast()->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("REGEX_TEST"), arguments);
$$ = parser->ast()->createNodeUnaryOperator(NODE_TYPE_OPERATOR_UNARY_NOT, node);
}
| expression quantifier T_EQ expression {

View File

@ -539,7 +539,7 @@ class ClusterComm {
protected: // protected members are for unit test purposes
/// @brief Constructor for test cases.
ClusterComm(bool);
explicit ClusterComm(bool);
// code below this point used to be "private". now "protected" to
// enable unit test wrapper class

View File

@ -826,7 +826,7 @@ bool ServerState::storeRole(RoleEnum role) {
AgencyComm comm; // should not throw anything
AgencyCommResult res = comm.sendTransactionWithFailover(*trx.get(), 1.0);
if (!res.successful()) {
return false;
return false;
}
} catch (...) {
LOG_TOPIC(FATAL, arangodb::Logger::FIXME) << __FUNCTION__

View File

@ -166,13 +166,16 @@ bool BaseEngine::lockCollection(std::string const& shard) {
return false;
}
_trx->pinData(cid); // will throw when it fails
Result res = _trx->lock(cid, AccessMode::Type::READ);
if (!res.ok()) {
Result lockResult = _trx->lockRecursive(cid, AccessMode::Type::READ);
if (!lockResult.ok() && !lockResult.is(TRI_ERROR_LOCKED)) {
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
<< "Logging Shard " << shard << " lead to exception '"
<< res.errorNumber() << "' (" << res.errorMessage() << ") ";
<< "Locking shard " << shard << " lead to exception '"
<< lockResult.errorNumber() << "' (" << lockResult.errorMessage() << ") ";
return false;
}
return true;
}

View File

@ -89,7 +89,6 @@
#include "Ssl/SslServerFeature.h"
#include "StorageEngine/EngineSelectorFeature.h"
#include "StorageEngine/StorageEngine.h"
#include "V8Server/V8DealerFeature.h"
using namespace arangodb;
using namespace arangodb::rest;

View File

@ -43,9 +43,10 @@
using namespace arangodb;
MMFilesCollectionKeys::MMFilesCollectionKeys(TRI_vocbase_t* vocbase, std::string const& name,
MMFilesCollectionKeys::MMFilesCollectionKeys(TRI_vocbase_t* vocbase, std::unique_ptr<CollectionGuard> guard,
TRI_voc_tick_t blockerId, double ttl)
: CollectionKeys(vocbase, name, ttl),
: CollectionKeys(vocbase, ttl),
_guard(std::move(guard)),
_ditch(nullptr),
_resolver(vocbase),
_blockerId(blockerId) {
@ -53,8 +54,6 @@ MMFilesCollectionKeys::MMFilesCollectionKeys(TRI_vocbase_t* vocbase, std::string
// prevent the collection from being unloaded while the export is ongoing
// this may throw
_guard.reset(new arangodb::CollectionGuard(vocbase, _name.c_str(), false));
_collection = _guard->collection();
TRI_ASSERT(_collection != nullptr);
}
@ -95,7 +94,10 @@ void MMFilesCollectionKeys::create(TRI_voc_tick_t maxTick) {
// copy all document tokens into the result under the read-lock
{
auto ctx = transaction::StandaloneContext::Create(_collection->vocbase());
SingleCollectionTransaction trx(ctx, _name, AccessMode::Type::READ);
SingleCollectionTransaction trx(ctx, _collection->cid(), AccessMode::Type::READ);
// already locked by _guard
trx.addHint(transaction::Hints::Hint::NO_USAGE_LOCK);
Result res = trx.begin();
if (!res.ok()) {

View File

@ -46,7 +46,7 @@ class MMFilesCollectionKeys final : public CollectionKeys {
MMFilesCollectionKeys(MMFilesCollectionKeys const&) = delete;
MMFilesCollectionKeys& operator=(MMFilesCollectionKeys const&) = delete;
MMFilesCollectionKeys(TRI_vocbase_t*, std::string const& name,
MMFilesCollectionKeys(TRI_vocbase_t*, std::unique_ptr<CollectionGuard> guard,
TRI_voc_tick_t blockerId, double ttl);
~MMFilesCollectionKeys();

View File

@ -440,6 +440,9 @@ void MMFilesCompactorThread::compactDatafiles(LogicalCollection* collection,
trx.addHint(transaction::Hints::Hint::NO_ABORT_MARKER);
trx.addHint(transaction::Hints::Hint::NO_COMPACTION_LOCK);
trx.addHint(transaction::Hints::Hint::NO_THROTTLING);
// when we get into this function, the caller has already acquired the
// collection's status lock - so we better do not lock it again
trx.addHint(transaction::Hints::Hint::NO_USAGE_LOCK);
CompactionInitialContext initial = getCompactionContext(&trx, collection, toCompact);
@ -999,6 +1002,9 @@ uint64_t MMFilesCompactorThread::getNumberOfDocuments(LogicalCollection* collect
// if lock acquisition fails, we go on and report an (arbitrary) positive number
trx.addHint(transaction::Hints::Hint::TRY_LOCK);
trx.addHint(transaction::Hints::Hint::NO_THROTTLING);
// when we get into this function, the caller has already acquired the
// collection's status lock - so we better do not lock it again
trx.addHint(transaction::Hints::Hint::NO_USAGE_LOCK);
Result res = trx.begin();

View File

@ -704,7 +704,7 @@ MMFilesWalSlotInfo MMFilesLogfileManager::allocate(uint32_t size) {
// allocate space in a logfile for later writing
MMFilesWalSlotInfo MMFilesLogfileManager::allocate(TRI_voc_tick_t databaseId,
TRI_voc_cid_t collectionId, uint32_t size) {
TRI_voc_cid_t collectionId, uint32_t size) {
TRI_ASSERT(size >= sizeof(MMFilesMarker));
if (!_allowWrites) {
@ -768,10 +768,10 @@ MMFilesWalSlotInfoCopy MMFilesLogfileManager::allocateAndWrite(MMFilesWalMarker
// memcpy the data into the WAL region and return the filled slot
// to the WAL logfile manager
MMFilesWalSlotInfoCopy MMFilesLogfileManager::writeSlot(MMFilesWalSlotInfo& slotInfo,
MMFilesWalMarker const* marker,
bool wakeUpSynchronizer,
bool waitForSyncRequested,
bool waitUntilSyncDone) {
MMFilesWalMarker const* marker,
bool wakeUpSynchronizer,
bool waitForSyncRequested,
bool waitUntilSyncDone) {
TRI_ASSERT(slotInfo.slot != nullptr);
TRI_ASSERT(marker != nullptr);
@ -2207,6 +2207,9 @@ int MMFilesLogfileManager::inspectLogfiles() {
// update the tick with the max tick we found in the WAL
TRI_UpdateTickServer(_recoverState->lastTick);
// return the lock here to ensure proper locking order
writeLocker.unlock();
TRI_ASSERT(_slots != nullptr);
// set the last ticks we found in existing logfile data
_slots->setLastTick(_recoverState->lastTick);

View File

@ -652,9 +652,9 @@ void MMFilesRestReplicationHandler::handleCommandCreateKeys() {
return;
}
arangodb::CollectionGuard guard(_vocbase, c->cid(), false);
auto guard = std::make_unique<arangodb::CollectionGuard>(_vocbase, c->cid(), false);
arangodb::LogicalCollection* col = guard.collection();
arangodb::LogicalCollection* col = guard->collection();
TRI_ASSERT(col != nullptr);
// turn off the compaction for the collection
@ -665,10 +665,10 @@ void MMFilesRestReplicationHandler::handleCommandCreateKeys() {
if (res != TRI_ERROR_NO_ERROR) {
THROW_ARANGO_EXCEPTION(res);
}
// initialize a container with the keys
auto keys =
std::make_unique<MMFilesCollectionKeys>(_vocbase, col->name(), id, 300.0);
std::make_unique<MMFilesCollectionKeys>(_vocbase, std::move(guard), id, 300.0);
std::string const idString(std::to_string(keys->id()));

View File

@ -46,13 +46,17 @@ MMFilesTransactionCollection::MMFilesTransactionCollection(TransactionState* trx
MMFilesTransactionCollection::~MMFilesTransactionCollection() {}
/// @brief request a main-level lock for a collection
int MMFilesTransactionCollection::lock() {
return lock(_accessType, 0);
}
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
int MMFilesTransactionCollection::lockRecursive() { return lockRecursive(_accessType, 0); }
/// @brief request a lock for a collection
int MMFilesTransactionCollection::lock(AccessMode::Type accessType,
int nestingLevel) {
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
int MMFilesTransactionCollection::lockRecursive(AccessMode::Type accessType,
int nestingLevel) {
if (AccessMode::isWriteOrExclusive(accessType) && !AccessMode::isWriteOrExclusive(_accessType)) {
// wrong lock type
return TRI_ERROR_INTERNAL;
@ -67,8 +71,8 @@ int MMFilesTransactionCollection::lock(AccessMode::Type accessType,
}
/// @brief request an unlock for a collection
int MMFilesTransactionCollection::unlock(AccessMode::Type accessType,
int nestingLevel) {
int MMFilesTransactionCollection::unlockRecursive(AccessMode::Type accessType,
int nestingLevel) {
if (AccessMode::isWriteOrExclusive(accessType) && !AccessMode::isWriteOrExclusive(_accessType)) {
// wrong lock type: write-unlock requested but collection is read-only
return TRI_ERROR_INTERNAL;
@ -95,6 +99,13 @@ bool MMFilesTransactionCollection::isLocked(AccessMode::Type accessType, int nes
/// @brief check whether a collection is locked at all
bool MMFilesTransactionCollection::isLocked() const {
if (CollectionLockState::_noLockHeaders != nullptr) {
std::string collName(_collection->name());
auto it = CollectionLockState::_noLockHeaders->find(collName);
if (it != CollectionLockState::_noLockHeaders->end()) {
return true;
}
}
return (_lockType != AccessMode::Type::NONE);
}
@ -151,8 +162,8 @@ bool MMFilesTransactionCollection::canAccess(AccessMode::Type accessType) const
}
// check if access type matches
if (AccessMode::AccessMode::isWriteOrExclusive(accessType) &&
!AccessMode::AccessMode::isWriteOrExclusive(_accessType)) {
if (AccessMode::isWriteOrExclusive(accessType) &&
!AccessMode::isWriteOrExclusive(_accessType)) {
// type doesn't match. probably also a mistake by the caller
return false;
}
@ -161,8 +172,8 @@ bool MMFilesTransactionCollection::canAccess(AccessMode::Type accessType) const
}
int MMFilesTransactionCollection::updateUsage(AccessMode::Type accessType, int nestingLevel) {
if (AccessMode::AccessMode::isWriteOrExclusive(accessType) &&
!AccessMode::AccessMode::isWriteOrExclusive(_accessType)) {
if (AccessMode::isWriteOrExclusive(accessType) &&
!AccessMode::isWriteOrExclusive(_accessType)) {
if (nestingLevel > 0) {
// trying to write access a collection that is only marked with
// read-access
@ -226,7 +237,7 @@ int MMFilesTransactionCollection::use(int nestingLevel) {
TRI_ASSERT(physical != nullptr);
if (nestingLevel == 0 &&
AccessMode::AccessMode::isWriteOrExclusive(_accessType)) {
AccessMode::isWriteOrExclusive(_accessType)) {
// read-lock the compaction lock
if (!_transaction->hasHint(transaction::Hints::Hint::NO_COMPACTION_LOCK)) {
if (!_compactionLocked) {
@ -239,19 +250,23 @@ int MMFilesTransactionCollection::use(int nestingLevel) {
bool shouldLock = _transaction->hasHint(transaction::Hints::Hint::LOCK_ENTIRELY);
if (!shouldLock) {
shouldLock = (AccessMode::AccessMode::isWriteOrExclusive(_accessType) && !_transaction->hasHint(transaction::Hints::Hint::SINGLE_OPERATION));
shouldLock = (!AccessMode::isNone(_accessType) && !_transaction->hasHint(transaction::Hints::Hint::SINGLE_OPERATION));
}
if (shouldLock && !isLocked()) {
// r/w lock the collection
int res = doLock(_accessType, nestingLevel);
if (res != TRI_ERROR_NO_ERROR) {
if (res == TRI_ERROR_LOCKED) {
// TRI_ERROR_LOCKED is not an error, but it indicates that the lock operation has actually acquired the lock
// (and that the lock has not been held before)
res = TRI_ERROR_NO_ERROR;
} else if (res != TRI_ERROR_NO_ERROR) {
return res;
}
}
if (AccessMode::AccessMode::isWriteOrExclusive(_accessType) && _originalRevision == 0) {
if (AccessMode::isWriteOrExclusive(_accessType) && _originalRevision == 0) {
// store original revision at transaction start
_originalRevision = physical->revision();
}
@ -269,7 +284,7 @@ void MMFilesTransactionCollection::unuse(int nestingLevel) {
// the top level transaction releases all collections
if (nestingLevel == 0 && _collection != nullptr) {
if (!_transaction->hasHint(transaction::Hints::Hint::NO_COMPACTION_LOCK)) {
if (AccessMode::AccessMode::isWriteOrExclusive(_accessType) && _compactionLocked) {
if (AccessMode::isWriteOrExclusive(_accessType) && _compactionLocked) {
auto physical = static_cast<MMFilesCollection*>(_collection->getPhysical());
TRI_ASSERT(physical != nullptr);
// read-unlock the compaction lock
@ -294,6 +309,9 @@ void MMFilesTransactionCollection::release() {
}
/// @brief lock a collection
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
int MMFilesTransactionCollection::doLock(AccessMode::Type type, int nestingLevel) {
if (_transaction->hasHint(transaction::Hints::Hint::LOCK_NEVER)) {
// never lock
@ -339,7 +357,11 @@ int MMFilesTransactionCollection::doLock(AccessMode::Type type, int nestingLevel
if (res == TRI_ERROR_NO_ERROR) {
_lockType = type;
} else if (res == TRI_ERROR_LOCK_TIMEOUT && timeout >= 0.1) {
// not an error, but we use TRI_ERROR_LOCKED to indicate that we actually acquired the lock ourselves
return TRI_ERROR_LOCKED;
}
if (res == TRI_ERROR_LOCK_TIMEOUT && timeout >= 0.1) {
LOG_TOPIC(WARN, Logger::QUERIES) << "timed out after " << timeout << " s waiting for " << AccessMode::typeString(type) << "-lock on collection '" << _collection->name() << "'";
} else if (res == TRI_ERROR_DEADLOCK) {
LOG_TOPIC(WARN, Logger::QUERIES) << "deadlock detected while trying to acquire " << AccessMode::typeString(type) << "-lock on collection '" << _collection->name() << "'";

View File

@ -45,13 +45,19 @@ class MMFilesTransactionCollection final : public TransactionCollection {
~MMFilesTransactionCollection();
/// @brief request a main-level lock for a collection
int lock() override;
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
int lockRecursive() override;
/// @brief request a lock for a collection
int lock(AccessMode::Type, int nestingLevel) override;
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
int lockRecursive(AccessMode::Type, int nestingLevel) override;
/// @brief request an unlock for a collection
int unlock(AccessMode::Type, int nestingLevel) override;
int unlockRecursive(AccessMode::Type, int nestingLevel) override;
/// @brief check whether a collection is locked in a specific mode in a transaction
bool isLocked(AccessMode::Type, int nestingLevel) const override;
@ -74,6 +80,9 @@ class MMFilesTransactionCollection final : public TransactionCollection {
private:
/// @brief request a lock for a collection
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
int doLock(AccessMode::Type, int nestingLevel);
/// @brief request an unlock for a collection

View File

@ -526,6 +526,10 @@ Result Syncer::createCollection(TRI_vocbase_t* vocbase,
}
SingleCollectionTransaction trx(transaction::StandaloneContext::Create(vocbase),
guard.collection()->cid(), AccessMode::Type::WRITE);
// already locked by guard above
trx.addHint(transaction::Hints::Hint::NO_USAGE_LOCK);
Result res = trx.begin();
if (!res.ok()) {
return res;
@ -626,6 +630,9 @@ Result Syncer::createIndex(VPackSlice const& slice) {
SingleCollectionTransaction trx(transaction::StandaloneContext::Create(vocbase),
guard.collection()->cid(), AccessMode::Type::WRITE);
// already locked by guard above
trx.addHint(transaction::Hints::Hint::NO_USAGE_LOCK);
Result res = trx.begin();
if (!res.ok()) {

View File

@ -133,7 +133,7 @@ void RestCollectionHandler::handleCommandGet() {
VPackObjectBuilder obj(&builder, true);
obj->add("checksum", result.slice().get("checksum"));
obj->add("revision", result.slice().get("revision"));
collectionRepresentation(builder, name, /*showProperties*/ false,
collectionRepresentation(builder, coll, /*showProperties*/ false,
/*showFigures*/ false, /*showCount*/ false,
/*aggregateCount*/ false);
} else {
@ -142,18 +142,18 @@ void RestCollectionHandler::handleCommandGet() {
}
} else if (sub == "figures") {
// /_api/collection/<identifier>/figures
collectionRepresentation(builder, name, /*showProperties*/ true,
collectionRepresentation(builder, coll, /*showProperties*/ true,
/*showFigures*/ true, /*showCount*/ true,
/*aggregateCount*/ true);
} else if (sub == "count") {
// /_api/collection/<identifier>/count
bool details = _request->parsedValue("details", false);
collectionRepresentation(builder, name, /*showProperties*/ true,
collectionRepresentation(builder, coll, /*showProperties*/ true,
/*showFigures*/ false, /*showCount*/ true,
/*aggregateCount*/ !details);
} else if (sub == "properties") {
// /_api/collection/<identifier>/properties
collectionRepresentation(builder, name, /*showProperties*/ true,
collectionRepresentation(builder, coll, /*showProperties*/ true,
/*showFigures*/ false, /*showCount*/ false,
/*aggregateCount*/ false);
} else if (sub == "revision") {
@ -166,7 +166,7 @@ void RestCollectionHandler::handleCommandGet() {
}
VPackObjectBuilder obj(&builder, true);
obj->add("revision", VPackValue(StringUtils::itoa(revisionId)));
collectionRepresentation(builder, name, /*showProperties*/ true,
collectionRepresentation(builder, coll, /*showProperties*/ true,
/*showFigures*/ false, /*showCount*/ false,
/*aggregateCount*/ false);
@ -179,7 +179,7 @@ void RestCollectionHandler::handleCommandGet() {
}
VPackObjectBuilder obj(&builder, true); // need to open object
collectionRepresentation(builder, name, /*showProperties*/ true,
collectionRepresentation(builder, coll, /*showProperties*/ true,
/*showFigures*/ false, /*showCount*/ false,
/*aggregateCount*/ false);
auto shards =

View File

@ -1537,6 +1537,11 @@ int RestReplicationHandler::processRestoreIndexes(VPackSlice const& collection,
auto ctx = transaction::StandaloneContext::Create(_vocbase);
SingleCollectionTransaction trx(ctx, collection->cid(),
AccessMode::Type::EXCLUSIVE);
// collection status lock was already acquired by collection guard
// above
trx.addHint(transaction::Hints::Hint::NO_USAGE_LOCK);
Result res = trx.begin();
if (!res.ok()) {

View File

@ -21,16 +21,14 @@
/// @author Jan Christoph Uhde
////////////////////////////////////////////////////////////////////////////////
#include "Basics/WriteLocker.h"
#include "Basics/ReadLocker.h"
#include "RestTransactionHandler.h"
#include "ApplicationFeatures/ApplicationServer.h"
#include "VocBase/Methods/Transactions.h"
#include "Basics/ReadLocker.h"
#include "Basics/WriteLocker.h"
#include "Rest/HttpRequest.h"
#include "Basics/voc-errors.h"
#include "V8Server/V8Context.h"
#include "V8Server/V8DealerFeature.h"
#include "VocBase/Methods/Transactions.h"
#include <velocypack/Builder.h>
#include <velocypack/velocypack-aliases.h>
@ -42,13 +40,12 @@ using namespace arangodb::rest;
RestTransactionHandler::RestTransactionHandler(GeneralRequest* request, GeneralResponse* response)
: RestVocbaseBaseHandler(request, response)
, _v8Context(nullptr)
, _lock()
{}
, _lock() {}
void RestTransactionHandler::returnContext(){
WRITE_LOCKER(writeLock, _lock);
V8DealerFeature::DEALER->exitContext(_v8Context);
_v8Context = nullptr;
void RestTransactionHandler::returnContext() {
WRITE_LOCKER(writeLock, _lock);
V8DealerFeature::DEALER->exitContext(_v8Context);
_v8Context = nullptr;
}
RestStatus RestTransactionHandler::execute() {
@ -58,7 +55,7 @@ RestStatus RestTransactionHandler::execute() {
}
auto slice = _request->payload();
if(!slice.isObject()){
if (!slice.isObject()) {
generateError(Result(TRI_ERROR_BAD_PARAMETER, "could not acquire v8 context"));
return RestStatus::DONE;
}
@ -77,14 +74,14 @@ RestStatus RestTransactionHandler::execute() {
try {
{
WRITE_LOCKER(lock, _lock);
if(_canceled){
if (_canceled) {
generateCanceled();
return RestStatus::DONE;
}
}
Result res = executeTransaction(_v8Context->_isolate, _lock, _canceled, slice , portType, result);
if (res.ok()){
if (res.ok()) {
VPackSlice slice = result.slice();
if (slice.isNone()) {
generateOk(rest::ResponseCode::OK, VPackSlice::nullSlice());
@ -111,7 +108,7 @@ bool RestTransactionHandler::cancel() {
_canceled.store(true);
auto isolate = _v8Context->_isolate;
if (!v8::V8::IsExecutionTerminating(isolate)) {
v8::V8::TerminateExecution(isolate);
v8::V8::TerminateExecution(isolate);
}
return true;
}

View File

@ -24,6 +24,7 @@
#ifndef ARANGOD_REST_HANDLER_REST_TRANSACTION_HANDLER_H
#define ARANGOD_REST_HANDLER_REST_TRANSACTION_HANDLER_H 1
#include "Basics/Common.h"
#include "Basics/ReadWriteLock.h"
#include "RestHandler/RestVocbaseBaseHandler.h"

View File

@ -85,9 +85,9 @@ void RocksDBReplicationContext::bind(TRI_vocbase_t* vocbase) {
if (!_trx || !_guard || (_guard->database() != vocbase)) {
rocksdb::Snapshot const* snap = nullptr;
if (_trx) {
_trx->abort();
auto state = RocksDBTransactionState::toState(_trx.get());
snap = state->stealSnapshot();
_trx->abort();
_trx.reset();
}

View File

@ -50,11 +50,17 @@ RocksDBTransactionCollection::RocksDBTransactionCollection(
RocksDBTransactionCollection::~RocksDBTransactionCollection() {}
/// @brief request a main-level lock for a collection
int RocksDBTransactionCollection::lock() { return lock(_accessType, 0); }
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
int RocksDBTransactionCollection::lockRecursive() { return lockRecursive(_accessType, 0); }
/// @brief request a lock for a collection
int RocksDBTransactionCollection::lock(AccessMode::Type accessType,
int nestingLevel) {
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
int RocksDBTransactionCollection::lockRecursive(AccessMode::Type accessType,
int nestingLevel) {
if (AccessMode::isWriteOrExclusive(accessType) &&
!AccessMode::isWriteOrExclusive(_accessType)) {
// wrong lock type
@ -70,8 +76,8 @@ int RocksDBTransactionCollection::lock(AccessMode::Type accessType,
}
/// @brief request an unlock for a collection
int RocksDBTransactionCollection::unlock(AccessMode::Type accessType,
int nestingLevel) {
int RocksDBTransactionCollection::unlockRecursive(AccessMode::Type accessType,
int nestingLevel) {
if (AccessMode::isWriteOrExclusive(accessType) &&
!AccessMode::isWriteOrExclusive(_accessType)) {
// wrong lock type: write-unlock requested but collection is read-only
@ -102,6 +108,13 @@ bool RocksDBTransactionCollection::isLocked(AccessMode::Type accessType,
/// @brief check whether a collection is locked at all
bool RocksDBTransactionCollection::isLocked() const {
if (CollectionLockState::_noLockHeaders != nullptr) {
std::string collName(_collection->name());
auto it = CollectionLockState::_noLockHeaders->find(collName);
if (it != CollectionLockState::_noLockHeaders->end()) {
return true;
}
}
return (_lockType != AccessMode::Type::NONE);
}
@ -200,8 +213,12 @@ int RocksDBTransactionCollection::use(int nestingLevel) {
if (AccessMode::isWriteOrExclusive(_accessType) && !isLocked()) {
// r/w lock the collection
int res = doLock(_accessType, nestingLevel);
if (res != TRI_ERROR_NO_ERROR) {
if (res == TRI_ERROR_LOCKED) {
// TRI_ERROR_LOCKED is not an error, but it indicates that the lock operation has actually acquired the lock
// (and that the lock has not been held before)
res = TRI_ERROR_NO_ERROR;
} else if (res != TRI_ERROR_NO_ERROR) {
return res;
}
}
@ -273,9 +290,13 @@ void RocksDBTransactionCollection::commitCounts() {
}
/// @brief lock a collection
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
int RocksDBTransactionCollection::doLock(AccessMode::Type type,
int nestingLevel) {
if (!AccessMode::isWriteOrExclusive(type)) {
_lockType = type;
return TRI_ERROR_NO_ERROR;
}
@ -323,7 +344,11 @@ int RocksDBTransactionCollection::doLock(AccessMode::Type type,
if (res == TRI_ERROR_NO_ERROR) {
_lockType = type;
} else if (res == TRI_ERROR_LOCK_TIMEOUT && timeout >= 0.1) {
// not an error, but we use TRI_ERROR_LOCKED to indicate that we actually acquired the lock ourselves
return TRI_ERROR_LOCKED;
}
if (res == TRI_ERROR_LOCK_TIMEOUT && timeout >= 0.1) {
LOG_TOPIC(WARN, Logger::QUERIES)
<< "timed out after " << timeout << " s waiting for "
<< AccessMode::typeString(type) << "-lock on collection '"
@ -338,6 +363,7 @@ int RocksDBTransactionCollection::doUnlock(AccessMode::Type type,
int nestingLevel) {
if (!AccessMode::isWriteOrExclusive(type) ||
!AccessMode::isWriteOrExclusive(_lockType)) {
_lockType = AccessMode::Type::NONE;
return TRI_ERROR_NO_ERROR;
}

View File

@ -44,13 +44,19 @@ class RocksDBTransactionCollection final : public TransactionCollection {
~RocksDBTransactionCollection();
/// @brief request a main-level lock for a collection
int lock() override;
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
int lockRecursive() override;
/// @brief request a lock for a collection
int lock(AccessMode::Type, int nestingLevel) override;
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
int lockRecursive(AccessMode::Type, int nestingLevel) override;
/// @brief request an unlock for a collection
int unlock(AccessMode::Type, int nestingLevel) override;
int unlockRecursive(AccessMode::Type, int nestingLevel) override;
/// @brief check whether a collection is locked in a specific mode in a
/// transaction
@ -86,6 +92,9 @@ class RocksDBTransactionCollection final : public TransactionCollection {
private:
/// @brief request a lock for a collection
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
int doLock(AccessMode::Type, int nestingLevel);
/// @brief request an unlock for a collection

View File

@ -501,7 +501,7 @@ void RocksDBTransactionState::donateSnapshot(rocksdb::Snapshot const* snap) {
rocksdb::Snapshot const* RocksDBTransactionState::stealSnapshot() {
TRI_ASSERT(_snapshot != nullptr);
TRI_ASSERT(isReadOnlyTransaction());
TRI_ASSERT(_status == transaction::Status::COMMITTED || _status == transaction::Status::ABORTED);
TRI_ASSERT(_status == transaction::Status::RUNNING);
rocksdb::Snapshot const* snap = _snapshot;
_snapshot = nullptr;
return snap;

View File

@ -404,7 +404,7 @@ void Scheduler::rebalanceThreads() {
uint64_t const nrWorking = numWorking(counters);
uint64_t const nrBlocked = numBlocked(counters);
if (nrRunning >= std::max(_nrMinimum, nrWorking + nrQueued)) {
if (nrRunning >= std::max(_nrMinimum, nrWorking + nrBlocked + nrQueued + 1)) {
// all threads are working, and none are blocked. so there is no
// need to start a new thread now
if (nrWorking == nrRunning) {

View File

@ -58,13 +58,19 @@ class TransactionCollection {
AccessMode::Type accessType() const { return _accessType; }
/// @brief request a main-level lock for a collection
virtual int lock() = 0;
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
virtual int lockRecursive() = 0;
/// @brief request a lock for a collection
virtual int lock(AccessMode::Type, int nestingLevel) = 0;
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and no other error occurred
/// returns any other error code otherwise
virtual int lockRecursive(AccessMode::Type, int nestingLevel) = 0;
/// @brief request an unlock for a collection
virtual int unlock(AccessMode::Type, int nestingLevel) = 0;
virtual int unlockRecursive(AccessMode::Type, int nestingLevel) = 0;
/// @brief check whether a collection is locked in a specific mode in a transaction
virtual bool isLocked(AccessMode::Type, int nestingLevel) const = 0;

View File

@ -214,9 +214,9 @@ int TransactionState::unuseCollections(int nestingLevel) {
int TransactionState::lockCollections() {
for (auto& trxCollection : _collections) {
int res = trxCollection->lock();
int res = trxCollection->lockRecursive();
if (res != TRI_ERROR_NO_ERROR) {
if (res != TRI_ERROR_NO_ERROR && res != TRI_ERROR_LOCKED) {
return res;
}
}

View File

@ -819,18 +819,18 @@ OperationResult transaction::Methods::anyLocal(
if (cid == 0) {
throwCollectionNotFound(collectionName.c_str());
}
pinData(cid); // will throw when it fails
Result res = lock(cid, AccessMode::Type::READ);
if (!res.ok()) {
return OperationResult(res);
}
VPackBuilder resultBuilder;
resultBuilder.openArray();
Result lockResult = lockRecursive(cid, AccessMode::Type::READ);
if (!lockResult.ok() && !lockResult.is(TRI_ERROR_LOCKED)) {
return OperationResult(lockResult);
}
ManagedDocumentResult mmdr;
std::unique_ptr<OperationCursor> cursor =
indexScan(collectionName, transaction::Methods::CursorType::ANY, &mmdr, false);
@ -839,13 +839,15 @@ OperationResult transaction::Methods::anyLocal(
resultBuilder.add(slice);
});
resultBuilder.close();
if (lockResult.is(TRI_ERROR_LOCKED)) {
Result res = unlockRecursive(cid, AccessMode::Type::READ);
res = unlock(cid, AccessMode::Type::READ);
if (!res.ok()) {
return OperationResult(res);
if (!res.ok()) {
return OperationResult(res);
}
}
resultBuilder.close();
return OperationResult(Result(), resultBuilder.steal(), _transactionContextPtr->orderCustomTypeHandler(), false);
}
@ -934,21 +936,25 @@ void transaction::Methods::invokeOnAllElements(
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName);
TransactionCollection* trxCol = trxCollection(cid, AccessMode::Type::READ);
LogicalCollection* logical = documentCollection(trxCol);
TRI_ASSERT(logical != nullptr);
_transactionContextPtr->pinData(logical);
LogicalCollection* collection = documentCollection(trxCol);
TRI_ASSERT(collection != nullptr);
_transactionContextPtr->pinData(collection);
Result res = trxCol->lock(AccessMode::Type::READ, _state->nestingLevel());
if (!res.ok()) {
THROW_ARANGO_EXCEPTION(res);
Result lockResult = trxCol->lockRecursive(AccessMode::Type::READ, _state->nestingLevel());
if (!lockResult.ok() && !lockResult.is(TRI_ERROR_LOCKED)) {
THROW_ARANGO_EXCEPTION(lockResult);
}
TRI_ASSERT(isLocked(collection, AccessMode::Type::READ));
logical->invokeOnAllElements(this, callback);
collection->invokeOnAllElements(this, callback);
res = trxCol->unlock(AccessMode::Type::READ, _state->nestingLevel());
if (lockResult.is(TRI_ERROR_LOCKED)) {
Result res = trxCol->unlockRecursive(AccessMode::Type::READ, _state->nestingLevel());
if (!res.ok()) {
THROW_ARANGO_EXCEPTION(res);
if (!res.ok()) {
THROW_ARANGO_EXCEPTION(res);
}
}
}
@ -1708,12 +1714,12 @@ OperationResult transaction::Methods::modifyLocal(
// Update/replace are a read and a write, let's get the write lock already
// for the read operation:
Result res = lock(cid, AccessMode::Type::WRITE);
Result lockResult = lockRecursive(cid, AccessMode::Type::WRITE);
if (!res.ok()) {
return OperationResult(res);
if (!lockResult.ok() && !lockResult.is(TRI_ERROR_LOCKED)) {
return OperationResult(lockResult);
}
VPackBuilder resultBuilder; // building the complete result
TRI_voc_tick_t maxTick = 0;
@ -1775,6 +1781,7 @@ OperationResult transaction::Methods::modifyLocal(
bool multiCase = newValue.isArray();
std::unordered_map<int, size_t> errorCounter;
Result res;
if (multiCase) {
{
VPackArrayBuilder guard(&resultBuilder);
@ -2228,16 +2235,16 @@ OperationResult transaction::Methods::allLocal(
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName);
pinData(cid); // will throw when it fails
Result res = lock(cid, AccessMode::Type::READ);
if (!res.ok()) {
return OperationResult(res);
}
VPackBuilder resultBuilder;
resultBuilder.openArray();
Result lockResult = lockRecursive(cid, AccessMode::Type::READ);
if (!lockResult.ok() && !lockResult.is(TRI_ERROR_LOCKED)) {
return OperationResult(lockResult);
}
ManagedDocumentResult mmdr;
std::unique_ptr<OperationCursor> cursor =
indexScan(collectionName, transaction::Methods::CursorType::ALL, &mmdr, false);
@ -2251,13 +2258,15 @@ OperationResult transaction::Methods::allLocal(
};
cursor->allDocuments(cb);
resultBuilder.close();
if (lockResult.is(TRI_ERROR_LOCKED)) {
Result res = unlockRecursive(cid, AccessMode::Type::READ);
res = unlock(cid, AccessMode::Type::READ);
if (res.ok()) {
return OperationResult(res);
if (res.ok()) {
return OperationResult(res);
}
}
resultBuilder.close();
return OperationResult(Result(), resultBuilder.steal(), _transactionContextPtr->orderCustomTypeHandler(), false);
}
@ -2317,19 +2326,25 @@ OperationResult transaction::Methods::truncateLocal(
pinData(cid); // will throw when it fails
Result res = lock(cid, AccessMode::Type::WRITE);
Result lockResult = lockRecursive(cid, AccessMode::Type::WRITE);
if (!res.ok()) {
return OperationResult(res);
if (!lockResult.ok() && !lockResult.is(TRI_ERROR_LOCKED)) {
return OperationResult(lockResult);
}
TRI_ASSERT(isLocked(collection, AccessMode::Type::WRITE));
try {
collection->truncate(this, options);
} catch (basics::Exception const& ex) {
unlock(cid, AccessMode::Type::WRITE);
if (lockResult.is(TRI_ERROR_LOCKED)) {
unlockRecursive(cid, AccessMode::Type::WRITE);
}
return OperationResult(Result(ex.code(), ex.what()));
} catch (std::exception const& ex) {
unlock(cid, AccessMode::Type::WRITE);
if (lockResult.is(TRI_ERROR_LOCKED)) {
unlockRecursive(cid, AccessMode::Type::WRITE);
}
return OperationResult(Result(TRI_ERROR_INTERNAL, ex.what()));
}
@ -2400,7 +2415,10 @@ OperationResult transaction::Methods::truncateLocal(
}
}
res = unlock(cid, AccessMode::Type::WRITE);
Result res;
if (lockResult.is(TRI_ERROR_LOCKED)) {
res = unlockRecursive(cid, AccessMode::Type::WRITE);
}
return OperationResult(res);
}
@ -2480,18 +2498,22 @@ OperationResult transaction::Methods::countLocal(
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName);
LogicalCollection* collection = documentCollection(trxCollection(cid));
Result res = lock(cid, AccessMode::Type::READ);
Result lockResult = lockRecursive(cid, AccessMode::Type::READ);
if (!res.ok()) {
return OperationResult(res);
if (!lockResult.ok() && !lockResult.is(TRI_ERROR_LOCKED)) {
return OperationResult(lockResult);
}
TRI_ASSERT(isLocked(collection, AccessMode::Type::READ));
uint64_t num = collection->numberDocuments(this);
res = unlock(cid, AccessMode::Type::READ);
if (lockResult.is(TRI_ERROR_LOCKED)) {
Result res = unlockRecursive(cid, AccessMode::Type::READ);
if (!res.ok()) {
return OperationResult(res);
if (!res.ok()) {
return OperationResult(res);
}
}
VPackBuilder resultBuilder;
@ -2832,25 +2854,25 @@ bool transaction::Methods::isLocked(LogicalCollection* document,
}
/// @brief read- or write-lock a collection
Result transaction::Methods::lock(TRI_voc_cid_t cid,
AccessMode::Type type) {
Result transaction::Methods::lockRecursive(TRI_voc_cid_t cid,
AccessMode::Type type) {
if (_state == nullptr || _state->status() != transaction::Status::RUNNING) {
return Result(TRI_ERROR_TRANSACTION_INTERNAL, "transaction not running on lock");
}
TransactionCollection* trxColl = trxCollection(cid, type);
TRI_ASSERT(trxColl != nullptr);
return trxColl->lock(type, _state->nestingLevel());
return Result(trxColl->lockRecursive(type, _state->nestingLevel()));
}
/// @brief read- or write-unlock a collection
Result transaction::Methods::unlock(TRI_voc_cid_t cid,
AccessMode::Type type) {
Result transaction::Methods::unlockRecursive(TRI_voc_cid_t cid,
AccessMode::Type type) {
if (_state == nullptr || _state->status() != transaction::Status::RUNNING) {
return Result(TRI_ERROR_TRANSACTION_INTERNAL, "transaction not running on unlock");
}
TransactionCollection* trxColl = trxCollection(cid, type);
TRI_ASSERT(trxColl != nullptr);
return trxColl->unlock(type, _state->nestingLevel());
return Result(trxColl->unlockRecursive(type, _state->nestingLevel()));
}
/// @brief get list of indexes for a collection

View File

@ -67,6 +67,7 @@ class BaseEngine;
namespace transaction {
class CallbackInvoker;
class Context;
struct Options;
}
/// @brief forward declarations
@ -80,8 +81,6 @@ class TransactionState;
class TransactionCollection;
namespace transaction {
struct Options;
#ifdef USE_ENTERPRISE
#define ENTERPRISE_VIRT virtual
#else
@ -497,10 +496,10 @@ class Methods {
Result addCollection(std::string const&, AccessMode::Type);
/// @brief read- or write-lock a collection
ENTERPRISE_VIRT Result lock(TRI_voc_cid_t, AccessMode::Type);
ENTERPRISE_VIRT Result lockRecursive(TRI_voc_cid_t, AccessMode::Type);
/// @brief read- or write-unlock a collection
ENTERPRISE_VIRT Result unlock(TRI_voc_cid_t, AccessMode::Type);
ENTERPRISE_VIRT Result unlockRecursive(TRI_voc_cid_t, AccessMode::Type);
private:

View File

@ -23,6 +23,7 @@
#include "V8Context.h"
#include "StorageEngine/TransactionState.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/CollectionNameResolver.h"
#include <v8.h>
@ -137,3 +138,11 @@ std::shared_ptr<transaction::V8Context> transaction::V8Context::Create(
TRI_vocbase_t* vocbase, bool embeddable) {
return std::make_shared<transaction::V8Context>(vocbase, embeddable);
}
std::shared_ptr<transaction::Context> transaction::V8Context::CreateWhenRequired(
TRI_vocbase_t* vocbase, bool embeddable) {
if (v8::Isolate::GetCurrent() != nullptr) {
return Create(vocbase, embeddable);
}
return transaction::StandaloneContext::Create(vocbase);
}

View File

@ -75,8 +75,12 @@ class V8Context final : public Context {
/// @brief check whether the transaction is embedded
static bool isEmbedded();
/// @brief create a context, returned in a shared ptr
static std::shared_ptr<transaction::V8Context> Create(TRI_vocbase_t*, bool);
/// @brief create a context
static std::shared_ptr<transaction::V8Context> Create(TRI_vocbase_t*, bool embeddable);
/// @brief create a V8 transaction context if we are in a V8 isolate, and a standlone
/// transaction context otherwise
static std::shared_ptr<transaction::Context> CreateWhenRequired(TRI_vocbase_t*, bool embeddable);
private:

View File

@ -121,6 +121,14 @@ class CollectionGuard {
}
public:
/// @brief prematurely release the usage lock
void release() {
if (_collection != nullptr) {
_vocbase->releaseCollection(_collection);
_collection = nullptr;
}
}
/// @brief return the collection pointer
inline arangodb::LogicalCollection* collection() const { return _collection; }

View File

@ -26,10 +26,9 @@
using namespace arangodb;
CollectionKeys::CollectionKeys(TRI_vocbase_t* vocbase, std::string const& name, double ttl)
CollectionKeys::CollectionKeys(TRI_vocbase_t* vocbase, double ttl)
: _vocbase(vocbase),
_collection(nullptr),
_name(name),
_id(0),
_ttl(ttl),
_expires(0.0),

View File

@ -47,8 +47,7 @@ class CollectionKeys {
CollectionKeys(CollectionKeys const&) = delete;
CollectionKeys& operator=(CollectionKeys const&) = delete;
CollectionKeys(TRI_vocbase_t*, std::string const& name,
double ttl);
CollectionKeys(TRI_vocbase_t*, double ttl);
virtual ~CollectionKeys() = default;
@ -110,7 +109,6 @@ class CollectionKeys {
protected:
TRI_vocbase_t* _vocbase;
arangodb::LogicalCollection* _collection;
std::string const _name;
CollectionKeysId _id;
double _ttl;
double _expires;

View File

@ -57,17 +57,15 @@ void V8Context::lockAndEnter() {
TRI_ASSERT(_locker == nullptr);
_locker = new v8::Locker(_isolate);
_isolate->Enter();
TRI_ASSERT(_locker->IsLocked(_isolate));
TRI_ASSERT(v8::Locker::IsLocked(_isolate));
assertLocked();
++_invocations;
++_invocationsSinceLastGc;
}
void V8Context::unlockAndExit() {
TRI_ASSERT(_locker != nullptr);
TRI_ASSERT(_isolate != nullptr);
assertLocked();
_isolate->Exit();
delete _locker;
@ -75,6 +73,13 @@ void V8Context::unlockAndExit() {
TRI_ASSERT(!v8::Locker::IsLocked(_isolate));
}
void V8Context::assertLocked() const {
TRI_ASSERT(_locker != nullptr);
TRI_ASSERT(_isolate != nullptr);
TRI_ASSERT(_locker->IsLocked(_isolate));
TRI_ASSERT(v8::Locker::IsLocked(_isolate));
}
bool V8Context::hasGlobalMethodsQueued() {
MUTEX_LOCKER(mutexLocker, _globalMethodsLock);

View File

@ -114,7 +114,7 @@ class V8Context {
size_t id() const { return _id; }
bool isDefault() const { return _id == 0; }
bool isUsed() const { return _locker != nullptr; }
void assertLocked() const;
double age() const;
void lockAndEnter();
void unlockAndExit();
@ -125,7 +125,6 @@ class V8Context {
void setCleaned(double stamp);
size_t const _id;
v8::Persistent<v8::Context> _context;
v8::Isolate* _isolate;
v8::Locker* _locker;
@ -147,6 +146,8 @@ class V8Context {
class V8ContextGuard {
public:
explicit V8ContextGuard(V8Context* context);
V8ContextGuard(V8ContextGuard const&) = delete;
V8ContextGuard& operator=(V8ContextGuard const&) = delete;
~V8ContextGuard();
private:

View File

@ -101,11 +101,10 @@ V8DealerFeature::V8DealerFeature(
_nrInflightContexts(0),
_maxContextInvocations(0),
_allowAdminExecute(false),
_ok(false),
_nextId(0),
_stopping(false),
_gcFinished(false),
_contextsModificationBlockers(0) {
_dynamicContextCreationBlockers(0) {
setOptional(false);
requiresElevatedPrivileges(false);
startsAfter("Action");
@ -258,7 +257,7 @@ void V8DealerFeature::start() {
CONDITION_LOCKER(guard, _contextCondition);
_contexts.reserve(static_cast<size_t>(_nrMaxContexts));
_busyContexts.reserve(static_cast<size_t>(_nrMaxContexts));
_freeContexts.reserve(static_cast<size_t>(_nrMaxContexts));
_idleContexts.reserve(static_cast<size_t>(_nrMaxContexts));
_dirtyContexts.reserve(static_cast<size_t>(_nrMaxContexts));
for (size_t i = 0; i < _nrMinContexts; ++i) {
@ -277,7 +276,7 @@ void V8DealerFeature::start() {
// apply context update is only run on contexts that no other
// threads can see (yet)
applyContextUpdate(context);
_freeContexts.push_back(context);
_idleContexts.push_back(context);
}
}
@ -367,7 +366,7 @@ void V8DealerFeature::collectGarbage() {
uint64_t const reducedWaitTime =
static_cast<uint64_t>(_gcFrequency * 1000.0 * 200.0);
while (_stopping == 0) {
while (!_stopping) {
try {
V8Context* context = nullptr;
bool wasDirty = false;
@ -384,7 +383,7 @@ void V8DealerFeature::collectGarbage() {
gotSignal = guard.wait(waitTime);
}
if (preferFree && !_freeContexts.empty()) {
if (preferFree && !_idleContexts.empty()) {
context = pickFreeContextForGc();
}
@ -394,7 +393,7 @@ void V8DealerFeature::collectGarbage() {
if (context->invocationsSinceLastGc() < 50 && !context->_hasActiveExternals) {
// don't collect this one yet. it doesn't have externals, so there
// is no urge for garbage collection
_freeContexts.emplace_back(context);
_idleContexts.emplace_back(context);
context = nullptr;
} else {
wasDirty = true;
@ -402,7 +401,7 @@ void V8DealerFeature::collectGarbage() {
}
if (context == nullptr && !preferFree && !gotSignal &&
!_freeContexts.empty()) {
!_idleContexts.empty()) {
// we timed out waiting for a signal, so we have idle time that we can
// spend on running the GC pro-actively
// We'll pick one of the free contexts and clean it up
@ -444,8 +443,7 @@ void V8DealerFeature::collectGarbage() {
{
v8::Context::Scope contextScope(localContext);
TRI_ASSERT(context->_locker->IsLocked(isolate));
TRI_ASSERT(v8::Locker::IsLocked(isolate));
context->assertLocked();
TRI_GET_GLOBALS();
TRI_RunGarbageCollectionV8(isolate, 1.0);
@ -464,7 +462,7 @@ void V8DealerFeature::collectGarbage() {
if (_contexts.size() > _nrMinContexts &&
!context->isDefault() &&
context->shouldBeRemoved(_maxContextAge, _maxContextInvocations) &&
_contextsModificationBlockers == 0) {
_dynamicContextCreationBlockers == 0) {
// remove the extra context as it is not needed anymore
_contexts.erase(std::remove_if(_contexts.begin(), _contexts.end(), [&context](V8Context* c) {
return (c->id() == context->id());
@ -477,9 +475,9 @@ void V8DealerFeature::collectGarbage() {
} else {
// put it back into the free list
if (wasDirty) {
_freeContexts.emplace_back(context);
_idleContexts.emplace_back(context);
} else {
_freeContexts.insert(_freeContexts.begin(), context);
_idleContexts.insert(_idleContexts.begin(), context);
}
guard.broadcast();
}
@ -496,11 +494,11 @@ void V8DealerFeature::collectGarbage() {
_gcFinished = true;
}
void V8DealerFeature::unblockContextsModification() {
void V8DealerFeature::unblockDynamicContextCreation() {
CONDITION_LOCKER(guard, _contextCondition);
TRI_ASSERT(_contextsModificationBlockers > 0);
--_contextsModificationBlockers;
TRI_ASSERT(_dynamicContextCreationBlockers > 0);
--_dynamicContextCreationBlockers;
}
void V8DealerFeature::loadJavaScriptFileInAllContexts(TRI_vocbase_t* vocbase,
@ -517,27 +515,69 @@ void V8DealerFeature::loadJavaScriptFileInAllContexts(TRI_vocbase_t* vocbase,
{
CONDITION_LOCKER(guard, _contextCondition);
// block the addition or removal of contexts
++_contextsModificationBlockers;
while (_nrInflightContexts > 0) {
// wait until all pending context creation requests have been satisified
guard.wait(10000);
}
// copy the list of contexts into a local variable
contexts = _contexts;
// block the addition or removal of contexts
++_dynamicContextCreationBlockers;
}
TRI_DEFER(unblockContextsModification());
TRI_DEFER(unblockDynamicContextCreation());
LOG_TOPIC(TRACE, Logger::V8) << "loading JavaScript file '" << file << "' in all (" << contexts.size() << ") V8 context";
// now safely scan the local copy of the contexts
for (auto& context : contexts) {
CONDITION_LOCKER(guard, _contextCondition);
while (context->isUsed()) {
while (_busyContexts.find(context) != _busyContexts.end()) {
// we must not enter the context if another thread is also using it...
guard.wait(10000);
}
TRI_ASSERT(!context->isUsed());
loadJavaScriptFileInContext(vocbase, file, context, builder);
TRI_ASSERT(!context->isUsed());
auto it = std::find(_dirtyContexts.begin(), _dirtyContexts.end(), context);
if (it != _dirtyContexts.end()) {
// context is in _dirtyContexts
// remove it from there
_dirtyContexts.erase(it);
guard.unlock();
try {
loadJavaScriptFileInContext(vocbase, file, context, builder);
} catch (...) {
guard.lock();
_dirtyContexts.push_back(context);
throw;
}
// and re-insert it after we are done
guard.lock();
_dirtyContexts.push_back(context);
} else {
// if the context is neither busy nor dirty, it must be idle
auto it = std::find(_idleContexts.begin(), _idleContexts.end(), context);
if (it != _idleContexts.end()) {
// remove it from there
_idleContexts.erase(it);
guard.unlock();
try {
loadJavaScriptFileInContext(vocbase, file, context, builder);
} catch (...) {
guard.lock();
_idleContexts.push_back(context);
throw;
}
// and re-insert it after we are done
guard.lock();
_idleContexts.push_back(context);
} else {
LOG_TOPIC(WARN, Logger::V8) << "v8 context #" << context->id() << " has disappeared";
}
}
}
if (builder != nullptr) {
@ -577,22 +617,13 @@ void V8DealerFeature::startGarbageCollection() {
_gcFinished = false;
}
void V8DealerFeature::enterContextInternal(TRI_vocbase_t* vocbase,
void V8DealerFeature::prepareLockedContext(TRI_vocbase_t* vocbase,
V8Context* context,
bool allowUseDatabase) {
context->lockAndEnter();
enterLockedContext(vocbase, context, allowUseDatabase);
}
void V8DealerFeature::enterLockedContext(TRI_vocbase_t* vocbase,
V8Context* context,
bool allowUseDatabase) {
TRI_ASSERT(vocbase != nullptr);
// when we get here, we should have a context and an isolate
TRI_ASSERT(context != nullptr);
TRI_ASSERT(context->_isolate != nullptr);
TRI_ASSERT(context->isUsed());
context->assertLocked();
auto isolate = context->_isolate;
@ -604,8 +635,7 @@ void V8DealerFeature::enterLockedContext(TRI_vocbase_t* vocbase,
{
v8::Context::Scope contextScope(localContext);
TRI_ASSERT(context->_locker->IsLocked(isolate));
TRI_ASSERT(v8::Locker::IsLocked(isolate));
context->assertLocked();
TRI_GET_GLOBALS();
// initialize the context data
@ -663,10 +693,10 @@ V8Context* V8DealerFeature::enterContext(TRI_vocbase_t* vocbase,
break;
}
for (auto it = _freeContexts.begin(); it != _freeContexts.end(); ++it) {
for (auto it = _idleContexts.begin(); it != _idleContexts.end(); ++it) {
if ((*it)->id() == id) {
context = (*it);
_freeContexts.erase(it);
_idleContexts.erase(it);
_busyContexts.emplace(context);
break;
}
@ -687,7 +717,6 @@ V8Context* V8DealerFeature::enterContext(TRI_vocbase_t* vocbase,
if (context != nullptr) {
// found the context
TRI_ASSERT(guard.isLocked());
context->lockAndEnter();
break;
}
@ -702,7 +731,7 @@ V8Context* V8DealerFeature::enterContext(TRI_vocbase_t* vocbase,
if (!found) {
vocbase->release();
LOG_TOPIC(WARN, arangodb::Logger::V8) << "specified context #" << id << " not found";
LOG_TOPIC(WARN, arangodb::Logger::V8) << "specified V8 context #" << id << " not found";
return nullptr;
}
}
@ -721,15 +750,14 @@ V8Context* V8DealerFeature::enterContext(TRI_vocbase_t* vocbase,
else {
CONDITION_LOCKER(guard, _contextCondition);
while (_freeContexts.empty() && !_stopping) {
while (_idleContexts.empty() && !_stopping) {
TRI_ASSERT(guard.isLocked());
LOG_TOPIC(TRACE, arangodb::Logger::V8) << "waiting for unused V8 context";
if (!_dirtyContexts.empty()) {
// we'll use a dirty context in this case
V8Context* context = _dirtyContexts.back();
_freeContexts.push_back(context);
_idleContexts.push_back(_dirtyContexts.back());
_dirtyContexts.pop_back();
break;
}
@ -739,7 +767,7 @@ V8Context* V8DealerFeature::enterContext(TRI_vocbase_t* vocbase,
(forceContext == ANY_CONTEXT_OR_PRIORITY && (_contexts.size() + _nrInflightContexts <= _nrMaxContexts)));
if (contextLimitNotExceeded &&
_contextsModificationBlockers == 0 &&
_dynamicContextCreationBlockers == 0 &&
!MaxMapCountFeature::isNearMaxMappings()) {
++_nrInflightContexts;
@ -753,6 +781,7 @@ V8Context* V8DealerFeature::enterContext(TRI_vocbase_t* vocbase,
} catch (...) {
guard.lock();
// clean up state
--_nrInflightContexts;
throw;
}
@ -771,8 +800,9 @@ V8Context* V8DealerFeature::enterContext(TRI_vocbase_t* vocbase,
continue;
}
TRI_ASSERT(guard.isLocked());
try {
_freeContexts.push_back(context);
_idleContexts.push_back(context);
LOG_TOPIC(DEBUG, Logger::V8) << "created additional V8 context #" << context->id() << ", number of contexts is now " << _contexts.size();
} catch (...) {
TRI_ASSERT(!_contexts.empty());
@ -808,49 +838,39 @@ V8Context* V8DealerFeature::enterContext(TRI_vocbase_t* vocbase,
return nullptr;
}
TRI_ASSERT(!_freeContexts.empty());
TRI_ASSERT(!_idleContexts.empty());
context = _freeContexts.back();
context = _idleContexts.back();
LOG_TOPIC(TRACE, arangodb::Logger::V8) << "found unused V8 context #" << context->id();
TRI_ASSERT(context != nullptr);
_freeContexts.pop_back();
_idleContexts.pop_back();
// should not fail because we reserved enough space beforehand
_busyContexts.emplace(context);
context->lockAndEnter();
}
TRI_ASSERT(context != nullptr);
TRI_ASSERT(context->isUsed());
context->lockAndEnter();
context->assertLocked();
enterLockedContext(vocbase, context, allowUseDatabase);
prepareLockedContext(vocbase, context, allowUseDatabase);
return context;
}
void V8DealerFeature::exitContextInternal(V8Context* context) {
try {
exitLockedContext(context);
context->unlockAndExit();
} catch (...) {
// make sure the context will be exited
context->unlockAndExit();
throw;
}
TRI_DEFER(context->unlockAndExit());
cleanupLockedContext(context);
}
void V8DealerFeature::exitLockedContext(V8Context* context) {
void V8DealerFeature::cleanupLockedContext(V8Context* context) {
TRI_ASSERT(context != nullptr);
LOG_TOPIC(TRACE, arangodb::Logger::V8) << "leaving V8 context #" << context->id();
auto isolate = context->_isolate;
TRI_ASSERT(isolate != nullptr);
TRI_ASSERT(context->_locker != nullptr);
TRI_ASSERT(context->_locker->IsLocked(isolate));
TRI_ASSERT(v8::Locker::IsLocked(isolate));
TRI_ASSERT(context->isUsed());
context->assertLocked();
bool canceled = false;
@ -903,8 +923,7 @@ void V8DealerFeature::exitLockedContext(V8Context* context) {
// run global context methods
if (runGlobal) {
TRI_ASSERT(context->_locker->IsLocked(isolate));
TRI_ASSERT(v8::Locker::IsLocked(isolate));
context->assertLocked();
try {
context->handleGlobalContextMethods();
@ -927,7 +946,7 @@ void V8DealerFeature::exitLockedContext(V8Context* context) {
}
void V8DealerFeature::exitContext(V8Context* context) {
exitLockedContext(context);
cleanupLockedContext(context);
V8GcThread* gc = static_cast<V8GcThread*>(_gcThread.get());
@ -956,10 +975,10 @@ void V8DealerFeature::exitContext(V8Context* context) {
performGarbageCollection = true;
}
CONDITION_LOCKER(guard, _contextCondition);
context->unlockAndExit();
CONDITION_LOCKER(guard, _contextCondition);
if (performGarbageCollection && (forceGarbageCollection || !_freeContexts.empty())) {
if (performGarbageCollection && (forceGarbageCollection || !_idleContexts.empty())) {
// only add the context to the dirty list if there is at least one other
// free context
@ -969,7 +988,7 @@ void V8DealerFeature::exitContext(V8Context* context) {
} else {
// note that re-adding the context here should not fail as we reserved
// enough room for all contexts during startup
_freeContexts.emplace_back(context);
_idleContexts.emplace_back(context);
}
_busyContexts.erase(context);
@ -977,13 +996,13 @@ void V8DealerFeature::exitContext(V8Context* context) {
LOG_TOPIC(TRACE, arangodb::Logger::V8) << "returned dirty V8 context #" << context->id();
guard.broadcast();
} else {
CONDITION_LOCKER(guard, _contextCondition);
context->unlockAndExit();
CONDITION_LOCKER(guard, _contextCondition);
_busyContexts.erase(context);
// note that re-adding the context here should not fail as we reserved
// enough room for all contexts during startup
_freeContexts.emplace_back(context);
_idleContexts.emplace_back(context);
LOG_TOPIC(TRACE, arangodb::Logger::V8) << "returned dirty V8 context #" << context->id() << " back into free";
guard.broadcast();
@ -1011,7 +1030,8 @@ void V8DealerFeature::applyContextUpdate(V8Context* context) {
continue;
}
enterContextInternal(vocbase, context, true);
context->lockAndEnter();
prepareLockedContext(vocbase, context, true);
TRI_DEFER(exitContextInternal(context));
{
@ -1091,7 +1111,7 @@ void V8DealerFeature::shutdownContexts() {
std::this_thread::sleep_for(std::chrono::microseconds(10000));
}
LOG_TOPIC(DEBUG, arangodb::Logger::V8) << "commanding GC Thread to terminate";
LOG_TOPIC(DEBUG, arangodb::Logger::V8) << "commanding V8 GC thread to terminate";
}
// shutdown all instances
@ -1109,7 +1129,7 @@ void V8DealerFeature::shutdownContexts() {
}
V8Context* V8DealerFeature::pickFreeContextForGc() {
int const n = (int)_freeContexts.size();
int const n = static_cast<int>(_idleContexts.size());
if (n == 0) {
// this is easy...
@ -1126,15 +1146,15 @@ V8Context* V8DealerFeature::pickFreeContextForGc() {
for (int i = n - 1; i > 0; --i) {
// check if there's actually anything to clean up in the context
if (_freeContexts[i]->invocationsSinceLastGc() < 50 &&
!_freeContexts[i]->_hasActiveExternals) {
if (_idleContexts[i]->invocationsSinceLastGc() < 50 &&
!_idleContexts[i]->_hasActiveExternals) {
continue;
}
// compare last GC stamp
if (pickedContextNr == -1 ||
_freeContexts[i]->_lastGcStamp <=
_freeContexts[pickedContextNr]->_lastGcStamp) {
_idleContexts[i]->_lastGcStamp <=
_idleContexts[pickedContextNr]->_lastGcStamp) {
pickedContextNr = i;
}
}
@ -1147,7 +1167,7 @@ V8Context* V8DealerFeature::pickFreeContextForGc() {
}
// this is the context to clean up
V8Context* context = _freeContexts[pickedContextNr];
V8Context* context = _idleContexts[pickedContextNr];
TRI_ASSERT(context != nullptr);
// now compare its last GC timestamp with the last global GC stamp
@ -1161,10 +1181,10 @@ V8Context* V8DealerFeature::pickFreeContextForGc() {
// around
if (n > 1) {
for (int i = pickedContextNr; i < n - 1; ++i) {
_freeContexts[i] = _freeContexts[i + 1];
_idleContexts[i] = _idleContexts[i + 1];
}
}
_freeContexts.pop_back();
_idleContexts.pop_back();
return context;
}
@ -1187,7 +1207,7 @@ V8Context* V8DealerFeature::buildContext(size_t id) {
// and automatically exit and unlock it when it runs out of scope
V8ContextGuard contextGuard(context.get());
v8::HandleScope handleScope(isolate);
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
@ -1255,7 +1275,7 @@ V8Context* V8DealerFeature::buildContext(size_t id) {
v8::ReadOnly);
}
for (auto j : _definedStrings) {
for (auto const& j : _definedStrings) {
localContext->Global()->ForceSet(TRI_V8_STD_STRING(isolate, j.first),
TRI_V8_STD_STRING(isolate, j.second),
v8::ReadOnly);
@ -1291,7 +1311,7 @@ V8DealerFeature::stats V8DealerFeature::getCurrentContextNumbers() {
_contexts.size(),
_busyContexts.size(),
_dirtyContexts.size(),
_freeContexts.size(),
_idleContexts.size(),
_nrMaxContexts
};
}
@ -1311,17 +1331,17 @@ bool V8DealerFeature::loadJavaScriptFileInContext(TRI_vocbase_t* vocbase,
return false;
}
enterContextInternal(vocbase, context, true);
context->lockAndEnter();
prepareLockedContext(vocbase, context, true);
TRI_DEFER(exitContextInternal(context));
try {
loadJavaScriptFileInternal(file, context, builder);
} catch (...) {
LOG_TOPIC(WARN, Logger::V8) << "caught exception while executing JavaScript file '" << file << "' in context #" << context->id();
exitContextInternal(context);
throw;
}
exitContextInternal(context);
return true;
}

View File

@ -131,19 +131,17 @@ class V8DealerFeature final : public application_features::ApplicationFeature {
V8Context* buildContext(size_t id);
V8Context* pickFreeContextForGc();
void shutdownContext(V8Context* context);
void unblockContextsModification();
void unblockDynamicContextCreation();
void loadJavaScriptFileInternal(std::string const& file, V8Context* context,
VPackBuilder* builder);
bool loadJavaScriptFileInContext(TRI_vocbase_t*, std::string const& file, V8Context* context, VPackBuilder* builder);
void enterContextInternal(TRI_vocbase_t* vocbase, V8Context* context, bool allowUseDatabase);
void enterLockedContext(TRI_vocbase_t*, V8Context*, bool allowUseDatabase);
void prepareLockedContext(TRI_vocbase_t*, V8Context*, bool allowUseDatabase);
void exitContextInternal(V8Context*);
void exitLockedContext(V8Context*);
void cleanupLockedContext(V8Context*);
void applyContextUpdate(V8Context* context);
void shutdownContexts();
private:
std::atomic<bool> _ok;
std::atomic<uint64_t> _nextId;
std::unique_ptr<Thread> _gcThread;
@ -152,10 +150,10 @@ class V8DealerFeature final : public application_features::ApplicationFeature {
basics::ConditionVariable _contextCondition;
std::vector<V8Context*> _contexts;
std::vector<V8Context*> _freeContexts;
std::vector<V8Context*> _idleContexts;
std::vector<V8Context*> _dirtyContexts;
std::unordered_set<V8Context*> _busyContexts;
size_t _contextsModificationBlockers;
size_t _dynamicContextCreationBlockers;
JSLoader _startupLoader;

View File

@ -1079,11 +1079,11 @@ static void JS_ExecuteGlobalContextFunction(
// extract the action name
v8::String::Utf8Value utf8def(args[0]);
if (*utf8def == 0) {
if (*utf8def == nullptr) {
TRI_V8_THROW_TYPE_ERROR("<definition> must be a UTF-8 function definition");
}
std::string const def = *utf8def;
std::string const def = std::string(*utf8def, utf8def.length());
// and pass it to the V8 contexts
if (!V8DealerFeature::DEALER->addGlobalContextMethod(def)) {

View File

@ -957,7 +957,7 @@ static void JS_DropVocbaseCol(v8::FunctionCallbackInfo<v8::Value> const& args) {
allowDropSystem = TRI_ObjectToBoolean(args[0]);
}
}
Result res = methods::Collections::drop(vocbase, collection,
allowDropSystem, timeout);
if (res.fail()) {
@ -2396,14 +2396,14 @@ static void JS_StatusVocbaseCol(
std::shared_ptr<LogicalCollection> const ci =
ClusterInfo::instance()->getCollection(databaseName,
collection->cid_as_string());
TRI_V8_RETURN(v8::Number::New(isolate, (int)ci->getStatusLocked()));
TRI_V8_RETURN(v8::Number::New(isolate, (int)ci->status()));
} catch (...) {
TRI_V8_RETURN(v8::Number::New(isolate, (int)TRI_VOC_COL_STATUS_DELETED));
}
}
// intentionally falls through
TRI_vocbase_col_status_e status = collection->getStatusLocked();
TRI_vocbase_col_status_e status = collection->status();
TRI_V8_RETURN(v8::Number::New(isolate, (int)status));
TRI_V8_TRY_CATCH_END

View File

@ -1387,7 +1387,12 @@ static void MapGetVocBase(v8::Local<v8::String> const name,
// check if the collection is from the same database
if (collection != nullptr && collection->vocbase() == vocbase) {
TRI_vocbase_col_status_e status = collection->getStatusLocked();
// we cannot use collection->getStatusLocked() here, because we
// have no idea who is calling us (db[...]). The problem is that
// if we are called from within a JavaScript transaction, the
// caller may have already acquired the collection's status lock
// with that transaction. if we now lock again, we may deadlock!
TRI_vocbase_col_status_e status = collection->status();
TRI_voc_cid_t cid = collection->cid();
uint32_t internalVersion = collection->internalVersion();

View File

@ -36,11 +36,18 @@ struct AccessMode {
WRITE = 2,
EXCLUSIVE = 4
};
// no need to create an object of it
AccessMode() = delete;
static_assert(AccessMode::Type::NONE < AccessMode::Type::READ &&
AccessMode::Type::READ < AccessMode::Type::WRITE &&
AccessMode::Type::READ < AccessMode::Type::EXCLUSIVE,
"AccessMode::Type total order fail");
static inline bool isNone(Type type) {
return (type == Type::NONE);
}
static inline bool isRead(Type type) {
return (type == Type::READ);
}

View File

@ -34,7 +34,7 @@
#include "GeneralServer/AuthenticationFeature.h"
#include "RestServer/DatabaseFeature.h"
#include "StorageEngine/PhysicalCollection.h"
#include "Transaction/StandaloneContext.h"
#include "Transaction/V8Context.h"
#include "Utils/ExecContext.h"
#include "Utils/SingleCollectionTransaction.h"
#include "V8/v8-conv.h"
@ -78,8 +78,8 @@ void Collections::enumerate(
}
Result methods::Collections::lookup(TRI_vocbase_t* vocbase,
std::string const& name,
FuncCallback func) {
std::string const& name,
FuncCallback func) {
if (name.empty()) {
return Result(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
}
@ -240,7 +240,7 @@ Result Collections::load(TRI_vocbase_t* vocbase, LogicalCollection* coll) {
#endif
}
auto ctx = transaction::StandaloneContext::Create(vocbase);
auto ctx = transaction::V8Context::CreateWhenRequired(vocbase, true);
SingleCollectionTransaction trx(ctx, coll->cid(), AccessMode::Type::READ);
Result res = trx.begin();
@ -280,7 +280,7 @@ Result Collections::properties(LogicalCollection* coll, VPackBuilder& builder) {
std::unique_ptr<SingleCollectionTransaction> trx;
if (!ServerState::instance()->isCoordinator()) {
auto ctx = transaction::StandaloneContext::Create(coll->vocbase());
auto ctx = transaction::V8Context::CreateWhenRequired(coll->vocbase(), true);
trx.reset(new SingleCollectionTransaction(ctx, coll->cid(),
AccessMode::Type::READ));
trx->addHint(transaction::Hints::Hint::NO_USAGE_LOCK);
@ -319,7 +319,7 @@ Result Collections::updateProperties(LogicalCollection* coll,
auto info = ci->getCollection(coll->dbName(), coll->cid_as_string());
return info->updateProperties(props, false);
} else {
auto ctx = transaction::StandaloneContext::Create(coll->vocbase());
auto ctx = transaction::V8Context::CreateWhenRequired(coll->vocbase(), false);
SingleCollectionTransaction trx(ctx, coll->cid(),
AccessMode::Type::EXCLUSIVE);
Result res = trx.begin();
@ -484,7 +484,7 @@ Result Collections::warmup(TRI_vocbase_t* vocbase, LogicalCollection* coll) {
return warmupOnCoordinator(vocbase->name(), cid);
}
auto ctx = transaction::StandaloneContext::Create(vocbase);
auto ctx = transaction::V8Context::CreateWhenRequired(vocbase, false);
SingleCollectionTransaction trx(ctx, coll->cid(), AccessMode::Type::READ);
Result res = trx.begin();
if (res.fail()) {
@ -517,16 +517,19 @@ Result Collections::revisionId(TRI_vocbase_t* vocbase,
if (ServerState::instance()->isCoordinator()) {
return revisionOnCoordinator(databaseName, cid, rid);
} else {
auto ctx = transaction::StandaloneContext::Create(vocbase);
SingleCollectionTransaction trx(ctx, coll->cid(),
AccessMode::Type::READ);
Result res = trx.begin();
if (res.fail()) {
THROW_ARANGO_EXCEPTION(res);
}
rid = coll->revision(&trx);
return TRI_ERROR_NO_ERROR;
}
auto ctx = transaction::V8Context::CreateWhenRequired(vocbase, true);
SingleCollectionTransaction trx(ctx, coll->cid(),
AccessMode::Type::READ);
Result res = trx.begin();
if (res.fail()) {
THROW_ARANGO_EXCEPTION(res);
}
rid = coll->revision(&trx);
return TRI_ERROR_NO_ERROR;
}

View File

@ -43,6 +43,7 @@
#include "Transaction/Helpers.h"
#include "Transaction/Hints.h"
#include "Transaction/StandaloneContext.h"
#include "Transaction/V8Context.h"
#include "Utils/Events.h"
#include "Utils/ExecContext.h"
#include "Utils/SingleCollectionTransaction.h"
@ -248,7 +249,7 @@ static Result EnsureIndexLocal(arangodb::LogicalCollection* collection,
READ_LOCKER(readLocker, collection->vocbase()->_inventoryLock);
SingleCollectionTransaction trx(
transaction::StandaloneContext::Create(collection->vocbase()),
transaction::V8Context::CreateWhenRequired(collection->vocbase(), false),
collection->cid(),
create ? AccessMode::Type::EXCLUSIVE : AccessMode::Type::READ);
@ -260,17 +261,12 @@ static Result EnsureIndexLocal(arangodb::LogicalCollection* collection,
bool created = false;
std::shared_ptr<arangodb::Index> idx;
if (create) {
// TODO Encapsulate in try{}catch(){} instead of errno()
try {
idx = collection->createIndex(&trx, definition, created);
} catch (arangodb::basics::Exception const& e) {
return Result(e.code());
}
if (idx == nullptr) {
// something went wrong during creation
int res = TRI_errno();
return Result(res);
return Result(e.code(), e.what());
}
TRI_ASSERT(idx != nullptr);
} else {
idx = collection->lookupIndex(definition);
if (idx == nullptr) {
@ -278,6 +274,8 @@ static Result EnsureIndexLocal(arangodb::LogicalCollection* collection,
return Result(TRI_ERROR_ARANGO_INDEX_NOT_FOUND);
}
}
TRI_ASSERT(idx != nullptr);
VPackBuilder tmp;
try {
@ -541,7 +539,7 @@ arangodb::Result Indexes::drop(LogicalCollection const* collection,
READ_LOCKER(readLocker, collection->vocbase()->_inventoryLock);
SingleCollectionTransaction trx(
transaction::StandaloneContext::Create(collection->vocbase()),
transaction::V8Context::CreateWhenRequired(collection->vocbase(), false),
collection->cid(), AccessMode::Type::EXCLUSIVE);
Result res = trx.begin();

View File

@ -1118,7 +1118,6 @@ int TRI_vocbase_t::dropCollection(arangodb::LogicalCollection* collection,
int res;
{
READ_LOCKER(readLocker, _inventoryLock);
res = dropCollectionWorker(collection, state, timeout);
}

View File

@ -1260,7 +1260,6 @@ function ReplicationIncrementalKeyConflict() {
setUp : function() {
connectToMaster();
db._drop(cn);
},
@ -1391,7 +1390,6 @@ function ReplicationIncrementalKeyConflict() {
jsunity.run(ReplicationSuite);
jsunity.run(ReplicationOtherDBSuite);
// TODO: activate this test once it works
jsunity.run(ReplicationIncrementalKeyConflict);
return jsunity.done();

View File

@ -49,7 +49,10 @@ ApplicationServer* ApplicationServer::server = nullptr;
ApplicationServer::ApplicationServer(std::shared_ptr<ProgramOptions> options,
const char *binaryPath)
: _options(options), _stopping(false), _binaryPath(binaryPath) {
: _state(ServerState::UNINITIALIZED),
_options(options),
_stopping(false),
_binaryPath(binaryPath) {
// register callback function for failures
fail = failCallback;
@ -172,8 +175,8 @@ void ApplicationServer::run(int argc, char* argv[]) {
// collect options from all features
// in this phase, all features are order-independent
_state = ServerState::IN_COLLECT_OPTIONS;
reportServerProgress(_state);
_state.store(ServerState::IN_COLLECT_OPTIONS, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_COLLECT_OPTIONS);
collectOptions();
// setup dependency, but ignore any failure for now
@ -192,8 +195,8 @@ void ApplicationServer::run(int argc, char* argv[]) {
_options->seal();
// validate options of all features
_state = ServerState::IN_VALIDATE_OPTIONS;
reportServerProgress(_state);
_state.store(ServerState::IN_VALIDATE_OPTIONS, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_VALIDATE_OPTIONS);
validateOptions();
// setup and validate all feature dependencies
@ -211,8 +214,8 @@ void ApplicationServer::run(int argc, char* argv[]) {
// furthermore, they must not write any files under elevated privileges
// if they want other features to access them, or if they want to access
// these files with dropped privileges
_state = ServerState::IN_PREPARE;
reportServerProgress(_state);
_state.store(ServerState::IN_PREPARE, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_PREPARE);
prepare();
// turn off all features that depend on other features that have been
@ -224,28 +227,28 @@ void ApplicationServer::run(int argc, char* argv[]) {
dropPrivilegesPermanently();
// start features. now features are allowed to start threads, write files etc.
_state = ServerState::IN_START;
reportServerProgress(_state);
_state.store(ServerState::IN_START, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_START);
start();
// wait until we get signaled the shutdown request
_state = ServerState::IN_WAIT;
reportServerProgress(_state);
_state.store(ServerState::IN_WAIT, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_WAIT);
wait();
// stop all features
_state = ServerState::IN_STOP;
reportServerProgress(_state);
_state.store(ServerState::IN_STOP, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_STOP);
stop();
// unprepare all features
_state = ServerState::IN_UNPREPARE;
reportServerProgress(_state);
_state.store(ServerState::IN_UNPREPARE, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_UNPREPARE);
unprepare();
// stopped
_state = ServerState::STOPPED;
reportServerProgress(_state);
_state.store(ServerState::STOPPED, std::memory_order_relaxed);
reportServerProgress(ServerState::STOPPED);
}
// signal the server to shut down
@ -312,7 +315,7 @@ void ApplicationServer::collectOptions() {
[this](ApplicationFeature* feature) {
LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name() << "::loadOptions";
feature->collectOptions(_options);
reportFeatureProgress(_state, feature->name());
reportFeatureProgress(_state.load(std::memory_order_relaxed), feature->name());
},
true);
}
@ -369,7 +372,7 @@ void ApplicationServer::validateOptions() {
<< "::validateOptions";
feature->validateOptions(_options);
feature->state(FeatureState::VALIDATED);
reportFeatureProgress(_state, feature->name());
reportFeatureProgress(_state.load(std::memory_order_relaxed), feature->name());
}
}
@ -561,7 +564,7 @@ void ApplicationServer::prepare() {
throw;
}
reportFeatureProgress(_state, feature->name());
reportFeatureProgress(_state.load(std::memory_order_relaxed), feature->name());
}
}
}
@ -581,7 +584,7 @@ void ApplicationServer::start() {
try {
feature->start();
feature->state(FeatureState::STARTED);
reportFeatureProgress(_state, feature->name());
reportFeatureProgress(_state.load(std::memory_order_relaxed), feature->name());
} catch (basics::Exception const& ex) {
res.reset(ex.code(), std::string("startup aborted: caught exception during start of feature '") + feature->name() + "': " + ex.what());
} catch (std::bad_alloc const& ex) {
@ -660,7 +663,7 @@ void ApplicationServer::stop() {
LOG_TOPIC(ERR, Logger::STARTUP) << "caught unknown exception during stop of feature '" << feature->name() << "'";
}
feature->state(FeatureState::STOPPED);
reportFeatureProgress(_state, feature->name());
reportFeatureProgress(_state.load(std::memory_order_relaxed), feature->name());
}
}
@ -680,7 +683,7 @@ void ApplicationServer::unprepare() {
LOG_TOPIC(ERR, Logger::STARTUP) << "caught unknown exception during unprepare of feature '" << feature->name() << "'";
}
feature->state(FeatureState::UNPREPARED);
reportFeatureProgress(_state, feature->name());
reportFeatureProgress(_state.load(std::memory_order_relaxed), feature->name());
}
}

View File

@ -134,9 +134,13 @@ class ApplicationServer {
}
static bool isPrepared() {
return server != nullptr && (server->_state == ServerState::IN_START ||
server->_state == ServerState::IN_WAIT ||
server->_state == ServerState::IN_STOP);
if (server != nullptr) {
ServerState tmp = server->_state.load(std::memory_order_relaxed);
return tmp == ServerState::IN_START ||
tmp == ServerState::IN_WAIT ||
tmp == ServerState::IN_STOP;
}
return false;
}
// returns the feature with the given name if known
@ -291,7 +295,7 @@ class ApplicationServer {
private:
// the current state
ServerState _state = ServerState::UNINITIALIZED;
std::atomic<ServerState> _state;
// the shared program options
std::shared_ptr<options::ProgramOptions> _options;

View File

@ -35,7 +35,7 @@ using namespace arangodb;
#if defined(TRI_HAVE_POSIX_THREADS)
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
#ifdef ARANGO_ENABLE_DEADLOCK_DETECTION
// initialize _holder to "maximum" thread id. this will work if the type of
// _holder is numeric, but will not work if its type is more complex.
Mutex::Mutex()
@ -46,13 +46,13 @@ Mutex::Mutex() : _mutex() {
pthread_mutexattr_init(&_attributes);
#ifdef __linux__
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
#ifdef ARANGO_ENABLE_DEADLOCK_DETECTION
// use an error checking mutex if available (only for LinuxThread) and only
// in maintainer mode
pthread_mutexattr_settype(&_attributes, PTHREAD_MUTEX_ERRORCHECK_NP);
#endif
#endif
pthread_mutex_init(&_mutex, &_attributes);
}
@ -62,7 +62,7 @@ Mutex::~Mutex() {
}
void Mutex::lock() {
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
#ifdef ARANGO_ENABLE_DEADLOCK_DETECTION
// we must not hold the lock ourselves here
TRI_ASSERT(_holder != Thread::currentThreadId());
#endif
@ -79,13 +79,13 @@ void Mutex::lock() {
FATAL_ERROR_ABORT();
}
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
#ifdef ARANGO_ENABLE_DEADLOCK_DETECTION
_holder = Thread::currentThreadId();
#endif
}
bool Mutex::tryLock() {
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
#ifdef ARANGO_ENABLE_DEADLOCK_DETECTION
// we must not hold the lock ourselves here
TRI_ASSERT(_holder != Thread::currentThreadId());
#endif
@ -104,7 +104,7 @@ bool Mutex::tryLock() {
FATAL_ERROR_ABORT();
}
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
#ifdef ARANGO_ENABLE_DEADLOCK_DETECTION
_holder = Thread::currentThreadId();
#endif
@ -112,7 +112,7 @@ bool Mutex::tryLock() {
}
void Mutex::unlock() {
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
#ifdef ARANGO_ENABLE_DEADLOCK_DETECTION
TRI_ASSERT(_holder == Thread::currentThreadId());
_holder = 0;
#endif
@ -125,7 +125,7 @@ void Mutex::unlock() {
}
}
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
#ifdef ARANGO_ENABLE_DEADLOCK_DETECTION
void Mutex::assertLockedByCurrentThread() {
TRI_ASSERT(_holder == Thread::currentThreadId());
}
@ -150,7 +150,7 @@ bool Mutex::tryLock() { return TryAcquireSRWLockExclusive(&_mutex) != 0; }
void Mutex::unlock() { ReleaseSRWLockExclusive(&_mutex); }
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
#ifdef ARANGO_ENABLE_DEADLOCK_DETECTION
void Mutex::assertLockedByCurrentThread() {}
void Mutex::assertNotLockedByCurrentThread() {}
#endif

View File

@ -28,6 +28,18 @@
#include "Basics/Common.h"
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
#define ARANGO_ENABLE_DEADLOCK_DETECTION
#if defined(__SANITIZE_THREAD__)
// Avoid fals positives with ThreadSanitizer
# undef ARANGO_ENABLE_DEADLOCK_DETECTION
#elif defined(__has_feature)
# if __has_feature(thread_sanitizer)
# undef ARANGO_ENABLE_DEADLOCK_DETECTION
# endif
#endif
#endif
#ifdef ARANGO_ENABLE_DEADLOCK_DETECTION
#include "Basics/Thread.h"
#endif
@ -49,7 +61,7 @@ class Mutex {
// assert that the mutex is locked by the current thread. will do
// nothing in non-maintainer mode and will do nothing for non-posix locks
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
#ifdef ARANGO_ENABLE_DEADLOCK_DETECTION
void assertLockedByCurrentThread();
void assertNotLockedByCurrentThread();
#else
@ -68,7 +80,7 @@ class Mutex {
SRWLOCK _mutex;
#endif
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
#ifdef ARANGO_ENABLE_DEADLOCK_DETECTION
TRI_tid_t _holder;
#endif
};