diff --git a/LICENSES-OTHER-COMPONENTS.md b/LICENSES-OTHER-COMPONENTS.md index 08f82d06f6..0afeccd958 100644 --- a/LICENSES-OTHER-COMPONENTS.md +++ b/LICENSES-OTHER-COMPONENTS.md @@ -2,195 +2,201 @@ ## C/C++ Libraries -### Google V8 +### Boost 1.58.0 -* https://code.google.com/p/v8/ -* [BSD 3-Clause License](http://opensource.org/licenses/BSD-3-Clause) +* Project Home: http://www.boost.org/ +* License: [boost software license](http://www.boost.org/LICENSE_1_0.txt), [free as-is license](https://raw.githubusercontent.com/arangodb/arangodb/devel/3rdParty/boost/1.58.0/boost/test/utils/runtime/cla/detail/argument_value_usage.hpp) -### icu +### Google V8 4.3.61 -* http://site.icu-project.org/ -* [ICU License](http://source.icu-project.org/repos/icu/icu/trunk/license.html) +* Project Home: https://code.google.com/p/v8/ +* License: [v8-overview](https://raw.githubusercontent.com/v8/v8/4.3.61/LICENSE), [V8](https://raw.githubusercontent.com/v8/v8/4.3.61/LICENSE.v8), [strongtalk](https://raw.githubusercontent.com/v8/v8/4.3.61/LICENSE.strongtalk), [valgrind](https://raw.githubusercontent.com/v8/v8/4.3.61/LICENSE.valgrind), [vtune](https://raw.githubusercontent.com/v8/v8/4.3.61/src/third_party/vtune/v8-vtune.h), [gmock](https://raw.githubusercontent.com/arangodb/arangodb/devel/3rdParty/V8-4.3.61/testing/gmock/LICENSE), [gtest](https://raw.githubusercontent.com/arangodb/arangodb/devel/3rdParty/V8-4.3.61/testing/gtest/LICENSE), [fdlibm](https://raw.githubusercontent.com/v8/v8/4.3.61/src/third_party/fdlibm/LICENSE), [PCRE](https://raw.githubusercontent.com/v8/v8/master/test/mjsunit/third_party/regexp-pcre/LICENSE), [object-keys](https://raw.githubusercontent.com/v8/v8/master/test/mjsunit/third_party/object-keys/LICENSE) -### libev +### ICU 54.1 -* http://software.schmorp.de/pkg/libev.html -* [free as-is license](http://cvs.schmorp.de/libev/LICENSE?revision=1.11&view=markup) - -### zlib - -* [free as-is license](https://github.com/arangodb/arangodb/blob/devel/3rdParty/zlib-1.2.7/README#L85) - -### Valgrind - -* valgrind.h header file only -* [BSD-style license](https://github.com/arangodb/arangodb/blob/devel/3rdParty/valgrind/valgrind.h) - -### boost - -* http://www.boost.org/ -* [boost software license](http://www.boost.org/LICENSE_1_0.txt) - -### linenoise - -* https://github.com/antirez/linenoise -* [free as-is license](https://github.com/antirez/linenoise/blob/master/LICENSE) +* Project Home: http://site.icu-project.org/ +* License: [ICU License](http://source.icu-project.org/repos/icu/icu/trunk/license.html) ### fpconv_dtoa -* https://github.com/night-shift/fpconv/ -* [MIT License](https://raw.githubusercontent.com/night-shift/fpconv/master/license) +* Project Home: https://github.com/night-shift/fpconv/ +* License: [MIT License](https://raw.githubusercontent.com/night-shift/fpconv/master/license) + +### libev 4.11 + +* Project Home: http://software.schmorp.de/pkg/libev.html +* License: Dual-License, including [free as-is license](http://cvs.schmorp.de/libev/LICENSE?revision=1.11&view=markup) + +### linenoise + +* Project Home: https://github.com/antirez/linenoise +* License: [free as-is license](https://raw.githubusercontent.com/antirez/linenoise/master/LICENSE) + +### Valgrind + +* Project Home: http://valgrind.org/ +* valgrind.h header file only +* License: [BSD-style license](https://raw.githubusercontent.com/arangodb/arangodb/devel/3rdParty/valgrind/valgrind.h) + +### zlib 1.2.7 + +* License: [free as-is license](https://github.com/arangodb/arangodb/blob/devel/3rdParty/zlib-1.2.7/README#L85) ## Programs +### autoconf + +* Project Home: http://www.gnu.org/software/autoconf/autoconf.html +* only used to generate code, not part of the distribution +* License: [configure](https://github.com/arangodb/arangodb/blob/master/configure#L11) [ax_cxx_compile_stdcxx_11.m4](https://github.com/arangodb/arangodb/blob/master/m4/ax_cxx_compile_stdcxx_11.m4#L25) + +### automake + +* Project https://www.gnu.org/software/automake/ +* only used to generate code, not part of the distribution +* License: [Makefile.in](https://raw.githubusercontent.com/arangodb/arangodb/master/Makefile.in) + +### Bison 3.0 + +* Project Home: https://www.gnu.org/software/bison/ +* only used to generate code, not part of the distribution; for details about using Bison in this way see http://www.gnu.org/software/bison/manual/bison.html#Conditions +* License: [grammar.cpp](https://github.com/arangodb/arangodb/blob/devel/arangod/Aql/grammar.cpp#L20) + ### CoreOS etcd -* https://coreos.com/using-coreos/etcd/ -* [Apache 2 License](https://github.com/coreos/etcd/blob/master/LICENSE) +* Project Home: https://coreos.com/using-coreos/etcd/ +* License: [Apache 2 License](https://github.com/coreos/etcd/blob/master/LICENSE) -### autotools +### Flex 2.5 -* http://www.gnu.org/software/autoconf/autoconf.html -* https://www.gnu.org/software/automake/ +* Project Home: http://flex.sourceforge.net/ * only used to generate code, not part of the distribution -* parts generated are free as-is license +* License: [free as-is license](http://flex.sourceforge.net/manual/Copyright.html#Copyright) -### Bison - -* https://www.gnu.org/software/bison/ -* only used to generate code, not part of the distribution -* parts generated use see https://github.com/arangodb/arangodb/blob/devel/arangod/Aql/grammar.cpp#L20 - -### Flex - -* http://flex.sourceforge.net/ -* only used to generate code, not part of the distribution -* [free as-is license](http://flex.sourceforge.net/manual/Copyright.html#Copyright) - -## Javascript, Node and NPM +## Javascript ### Node core modules -* http://nodejs.org -* [MIT License](https://raw.githubusercontent.com/joyent/node/v0.10.33/LICENSE) +* Project Home: http://nodejs.org +* License: Node MIT License [_stream_duplex.js,_stream_passthrough.js,_stream_readable.js,_stream_transform.js,_stream_writable.js,events.js,querystring.js,stream.js,string_decoder.js,url.js](https://raw.githubusercontent.com/joyent/node/v0.10.33/LICENSE), MIT [punycode.js](https://raw.githubusercontent.com/joyent/node/v0.10.33/LICENSE) ### Bundled NPM modules -#### Jasmine +#### Ace -* https://jasmine.github.io -* [MIT License](https://raw.githubusercontent.com/jasmine/jasmine/master/MIT.LICENSE) - -#### JSUnity - -* https://github.com/atesgoral/jsunity -* [MIT License](http://www.opensource.org/licenses/mit-license.php) +* Project Home: https://github.com/ajaxorg/ace +* License: [BSD 3-Clause License](https://raw.githubusercontent.com/ajaxorg/ace/master/LICENSE) #### ArangoDB Query Builder -* https://github.com/arangodb/aqbjs -* [Apache 2](https://raw.githubusercontent.com/arangodb/aqbjs/master/LICENSE) +* Project Home: https://github.com/arangodb/aqbjs +* License: [Apache 2](https://raw.githubusercontent.com/arangodb/aqbjs/master/LICENSE) #### Chai -* http://chaijs.com -* [MIT License](https://github.com/chaijs/chai/blob/master/README.md) +* Project Home: http://chaijs.com +* License: [MIT License](https://github.com/chaijs/chai/blob/master/README.md) #### CoffeeScript -* http://coffeescript.org -* [MIT License](https://www.npmjs.com/package/coffee-script) - -#### stacktrace.js - -* http://www.stacktracejs.com/ -* [The Unlicense](http://unlicense.org) +* Project Home: http://coffeescript.org +* License: [MIT License](https://www.npmjs.com/package/coffee-script) #### expect.js -* https://github.com/Automattic/expect.js -* [MIT License](https://github.com/Automattic/expect.js/blob/master/README.md) +* Project Home: https://github.com/Automattic/expect.js +* License: [MIT License](https://github.com/Automattic/expect.js/blob/master/README.md) #### extendible -* https://github.com/3rd-Eden/extendible -* [MIT License](https://github.com/bigpipe/extendible/blob/master/README.md) +* Project Home: https://github.com/3rd-Eden/extendible +* License: [MIT License](https://github.com/bigpipe/extendible/blob/master/README.md) #### Foxx Generator -* https://github.com/moonglum/foxx_generator -* [Apache 2 License](https://raw.githubusercontent.com/moonglum/foxx_generator/master/LICENSE) +* Project Home: https://github.com/moonglum/foxx_generator +* License: [Apache 2 License](https://raw.githubusercontent.com/moonglum/foxx_generator/master/LICENSE) #### highlight.js -* https://highlightjs.org -* [BSD 3-Clause License](https://raw.githubusercontent.com/isagalaev/highlight.js/master/LICENSE) +* Project Home: https://highlightjs.org +* License: [BSD 3-Clause License](https://raw.githubusercontent.com/isagalaev/highlight.js/master/LICENSE) #### http-errors -* https://github.com/jshttp/http-errors -* [MIT License](https://raw.githubusercontent.com/jshttp/http-errors/master/LICENSE) +* Project Home: https://github.com/jshttp/http-errors +* License: [MIT License](https://raw.githubusercontent.com/jshttp/http-errors/master/LICENSE) -#### i +#### inflect -* https://github.com/pksunkara/inflect -* [MIT License](https://raw.githubusercontent.com/pksunkara/inflect/master/LICENSE) +* Project Home: https://github.com/pksunkara/inflect +* License: [MIT License](https://raw.githubusercontent.com/pksunkara/inflect/master/LICENSE) + +#### Jasmine + +* Project Home: https://jasmine.github.io +* License: [MIT License](https://raw.githubusercontent.com/jasmine/jasmine/master/MIT.LICENSE) #### Joi -* https://github.com/hapijs/joi -* [BSD 3-Clause License](https://raw.githubusercontent.com/hapijs/joi/master/LICENSE) +* Project Home: https://github.com/hapijs/joi +* License: [BSD 3-Clause License](https://raw.githubusercontent.com/hapijs/joi/master/LICENSE) -#### Ace +#### JSHint -* https://github.com/ajaxorg/ace -* [BSD 3-Clause License](https://raw.githubusercontent.com/ajaxorg/ace/master/LICENSE) +* Project Home: http://jshint.com +* License: [MIT License](https://raw.githubusercontent.com/jshint/jshint/master/LICENSE) + +#### JSUnity + +* Project Home: https://github.com/atesgoral/jsunity +* License: [MIT License](http://www.opensource.org/licenses/mit-license.php) + +#### minimatch + +* Project Home: https://github.com/isaacs/minimatch +* License: [MIT License](https://raw.githubusercontent.com/isaacs/minimatch/master/LICENSE) + +#### mocha + +* Project Home: http://mochajs.org +* License: [MIT License](https://raw.githubusercontent.com/mochajs/mocha/master/LICENSE) + +#### qs + +* Project Home: https://github.com/hapijs/qs +* License: [BSD 3-Clause License](https://raw.githubusercontent.com/hapijs/hapi/master/LICENSE) + +#### Ramda + +* Project Home: http://ramdajs.com +* License: [MIT License](https://raw.githubusercontent.com/ramda/ramda/master/LICENSE.txt) + +#### semver + +* Project Home: https://github.com/npm/node-semver +* License: [BSD 2-Clause License](http://opensource.org/licenses/BSD-2-Clause) + +#### Sinon.JS + +* Project Home: http://sinonjs.org +* License: [BSD 3-Clause License](https://www.npmjs.com/package/sinon) + +#### stacktrace.js + +* Project Home: http://www.stacktracejs.com/ +* License: [The Unlicense](http://unlicense.org) + +#### underscore + +* Project Home: http://underscorejs.org +* License: [MIT License](https://github.com/jashkenas/underscore/blob/master/LICENSE) #### YAML * https://github.com/nodeca/js-yaml * [MIT License](https://raw.githubusercontent.com/nodeca/js-yaml/master/LICENSE) -#### JSHint - -* http://jshint.com -* [MIT License](https://raw.githubusercontent.com/jshint/jshint/master/LICENSE) - -#### minimatch - -* https://github.com/isaacs/minimatch -* [MIT License](https://raw.githubusercontent.com/isaacs/minimatch/master/LICENSE) - -#### mocha - -* http://mochajs.org -* [MIT License](https://raw.githubusercontent.com/mochajs/mocha/master/LICENSE) - -#### qs - -* https://github.com/hapijs/qs -* [BSD 3-Clause License](https://raw.githubusercontent.com/hapijs/hapi/master/LICENSE) - -#### Ramda - -* http://ramdajs.com -* [MIT License](https://raw.githubusercontent.com/ramda/ramda/master/LICENSE.txt) - -#### semver - -* https://github.com/npm/node-semver -* [BSD 2-Clause License](http://opensource.org/licenses/BSD-2-Clause) - -#### Sinon.JS - -* http://sinonjs.org -* [BSD 3-Clause License](https://www.npmjs.com/package/sinon) - -#### underscore - -* http://underscorejs.org -* [MIT License](https://github.com/jashkenas/underscore/blob/master/LICENSE) - ### Frontend libraries #### swagger diff --git a/UnitTests/Basics/json-test.cpp b/UnitTests/Basics/json-test.cpp index 20ba89a902..0f1ad0ed9a 100644 --- a/UnitTests/Basics/json-test.cpp +++ b/UnitTests/Basics/json-test.cpp @@ -436,18 +436,88 @@ BOOST_AUTO_TEST_CASE (tst_json_string_utf8_3) { //////////////////////////////////////////////////////////////////////////////// /// @brief test empty json list //////////////////////////////////////////////////////////////////////////////// - + BOOST_AUTO_TEST_CASE (tst_json_list_empty) { INIT_BUFFER - + TRI_json_t* json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); - + STRINGIFY BOOST_CHECK_EQUAL("[]", STRING_VALUE); FREE_JSON FREE_BUFFER } +//////////////////////////////////////////////////////////////////////////////// +/// @brief test remove from array +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_remove_from_array_empty) { + TRI_json_t* json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + + BOOST_CHECK_EQUAL(0ULL, TRI_LengthArrayJson(json)); + BOOST_CHECK_EQUAL(false, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 0)); + BOOST_CHECK_EQUAL(false, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 10)); + BOOST_CHECK_EQUAL(false, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 5)); + BOOST_CHECK_EQUAL(false, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 2)); + BOOST_CHECK_EQUAL(false, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 1)); + BOOST_CHECK_EQUAL(false, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 0)); + + FREE_JSON +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test remove from array +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_remove_from_array_nonempty1) { + TRI_json_t* json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + TRI_PushBack3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, TRI_CreateNullJson(TRI_UNKNOWN_MEM_ZONE)); + TRI_PushBack3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 3.5)); + + BOOST_CHECK_EQUAL(2ULL, TRI_LengthArrayJson(json)); + BOOST_CHECK_EQUAL(false, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 10)); + BOOST_CHECK_EQUAL(false, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 5)); + BOOST_CHECK_EQUAL(false, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 2)); + + BOOST_CHECK_EQUAL(true, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 1)); + BOOST_CHECK_EQUAL(static_cast(0), TRI_LookupArrayJson(json, 1)); + BOOST_CHECK_EQUAL(1ULL, TRI_LengthArrayJson(json)); + BOOST_CHECK_EQUAL(true, TRI_IsNullJson(TRI_LookupArrayJson(json, 0))); + + BOOST_CHECK_EQUAL(true, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 0)); + BOOST_CHECK_EQUAL(0ULL, TRI_LengthArrayJson(json)); + BOOST_CHECK_EQUAL(static_cast(0), TRI_LookupArrayJson(json, 0)); + + FREE_JSON +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test remove from array +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_remove_from_array_nonempty2) { + TRI_json_t* json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + TRI_PushBack3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, TRI_CreateNullJson(TRI_UNKNOWN_MEM_ZONE)); + TRI_PushBack3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 3.5)); + + BOOST_CHECK_EQUAL(2ULL, TRI_LengthArrayJson(json)); + BOOST_CHECK_EQUAL(false, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 10)); + BOOST_CHECK_EQUAL(false, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 5)); + BOOST_CHECK_EQUAL(false, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 2)); + + BOOST_CHECK_EQUAL(true, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 0)); + BOOST_CHECK_EQUAL(1ULL, TRI_LengthArrayJson(json)); + BOOST_CHECK_EQUAL(static_cast(0), TRI_LookupArrayJson(json, 1)); + BOOST_CHECK_EQUAL(true, TRI_IsNumberJson(TRI_LookupArrayJson(json, 0))); + + BOOST_CHECK_EQUAL(true, TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, json, 0)); + BOOST_CHECK_EQUAL(0ULL, TRI_LengthArrayJson(json)); + BOOST_CHECK_EQUAL(static_cast(0), TRI_LookupArrayJson(json, 0)); + + FREE_JSON +} + //////////////////////////////////////////////////////////////////////////////// /// @brief test json list mixed //////////////////////////////////////////////////////////////////////////////// diff --git a/UnitTests/Basics/priorityqueue-test.cpp b/UnitTests/Basics/priorityqueue-test.cpp index 860a9c5b73..b057a41b3c 100644 --- a/UnitTests/Basics/priorityqueue-test.cpp +++ b/UnitTests/Basics/priorityqueue-test.cpp @@ -83,6 +83,7 @@ BOOST_AUTO_TEST_CASE (tst_deque_case) { BOOST_CHECK_EQUAL(true, pq.empty()); bool b; + MyValue* v; b = pq.insert("a", new MyValue("a", 1)); BOOST_CHECK_EQUAL(b, true); @@ -92,8 +93,10 @@ BOOST_AUTO_TEST_CASE (tst_deque_case) { BOOST_CHECK_EQUAL(b, true); b = pq.insert("d", new MyValue("d", 4)); BOOST_CHECK_EQUAL(b, true); - b = pq.insert("c", new MyValue("c", 5)); + v = new MyValue("c", 5); + b = pq.insert("c", v); BOOST_CHECK_EQUAL(b, false); + delete v; BOOST_CHECK_EQUAL(4, (int) pq.size()); BOOST_CHECK_EQUAL(false, pq.empty()); @@ -112,7 +115,6 @@ BOOST_AUTO_TEST_CASE (tst_deque_case) { BOOST_CHECK(p == nullptr); std::string k; - MyValue* v; p = pq.getMinimal(); BOOST_CHECK_EQUAL(p->_key, "a"); @@ -173,6 +175,7 @@ BOOST_AUTO_TEST_CASE (tst_heap_case) { BOOST_CHECK_EQUAL(true, pq.empty()); bool b; + MyValue* v; b = pq.insert("a", new MyValue("a", 4)); BOOST_CHECK_EQUAL(b, true); @@ -182,8 +185,10 @@ BOOST_AUTO_TEST_CASE (tst_heap_case) { BOOST_CHECK_EQUAL(b, true); b = pq.insert("d", new MyValue("d", 2)); BOOST_CHECK_EQUAL(b, true); - b = pq.insert("c", new MyValue("c", 5)); + v = new MyValue("c", 5); + b = pq.insert("c", v); BOOST_CHECK_EQUAL(b, false); + delete v; BOOST_CHECK_EQUAL(4, (int) pq.size()); BOOST_CHECK_EQUAL(false, pq.empty()); @@ -202,7 +207,6 @@ BOOST_AUTO_TEST_CASE (tst_heap_case) { BOOST_CHECK(p == nullptr); std::string k; - MyValue* v; p = pq.getMinimal(); BOOST_CHECK_EQUAL(p->_key, "b"); @@ -263,6 +267,7 @@ BOOST_AUTO_TEST_CASE (tst_deque_case_with_lowering) { BOOST_CHECK_EQUAL(true, pq.empty()); bool b; + MyValue* v; b = pq.insert("a", new MyValue("a", 1)); BOOST_CHECK_EQUAL(b, true); @@ -272,8 +277,10 @@ BOOST_AUTO_TEST_CASE (tst_deque_case_with_lowering) { BOOST_CHECK_EQUAL(b, true); b = pq.insert("d", new MyValue("d", 4)); BOOST_CHECK_EQUAL(b, true); - b = pq.insert("c", new MyValue("c", 5)); + v = new MyValue("c", 5); + b = pq.insert("c", v); BOOST_CHECK_EQUAL(b, false); + delete v; BOOST_CHECK_EQUAL(4, (int) pq.size()); BOOST_CHECK_EQUAL(false, pq.empty()); @@ -294,7 +301,6 @@ BOOST_AUTO_TEST_CASE (tst_deque_case_with_lowering) { BOOST_CHECK(p == nullptr); std::string k; - MyValue* v; p = pq.getMinimal(); BOOST_CHECK_EQUAL(p->_key, "a"); @@ -355,6 +361,7 @@ BOOST_AUTO_TEST_CASE (tst_heap_case_with_lowering) { BOOST_CHECK_EQUAL(true, pq.empty()); bool b; + MyValue* v; b = pq.insert("a", new MyValue("a", 4)); BOOST_CHECK_EQUAL(b, true); @@ -364,8 +371,10 @@ BOOST_AUTO_TEST_CASE (tst_heap_case_with_lowering) { BOOST_CHECK_EQUAL(b, true); b = pq.insert("d", new MyValue("d", 3)); BOOST_CHECK_EQUAL(b, true); - b = pq.insert("c", new MyValue("c", 5)); + v = new MyValue("c", 5); + b = pq.insert("c", v); BOOST_CHECK_EQUAL(b, false); + delete v; BOOST_CHECK_EQUAL(4, (int) pq.size()); BOOST_CHECK_EQUAL(false, pq.empty()); @@ -386,7 +395,6 @@ BOOST_AUTO_TEST_CASE (tst_heap_case_with_lowering) { BOOST_CHECK(p == nullptr); std::string k; - MyValue* v; p = pq.getMinimal(); BOOST_CHECK_EQUAL(p->_key, "a"); diff --git a/UnitTests/Basics/string-buffer-test.cpp b/UnitTests/Basics/string-buffer-test.cpp index b36bf9b338..f7a1a0e1d5 100644 --- a/UnitTests/Basics/string-buffer-test.cpp +++ b/UnitTests/Basics/string-buffer-test.cpp @@ -480,8 +480,6 @@ BOOST_AUTO_TEST_CASE (tst_clear) { const char* ptr = TRI_BeginStringBuffer(&sb); TRI_ClearStringBuffer(&sb); BOOST_CHECK_EQUAL(0, (int) TRI_LengthStringBuffer(&sb)); - // clang 5.1 failes without the cast - BOOST_CHECK_EQUAL((unsigned int) '\0', (unsigned int) TRI_LastCharStringBuffer(&sb)); // buffer should still point to ptr BOOST_CHECK_EQUAL((void*) ptr, (void*) TRI_BeginStringBuffer(&sb)); @@ -531,9 +529,6 @@ BOOST_AUTO_TEST_CASE (tst_last_char) { TRI_InitStringBuffer(&sb, TRI_CORE_MEM_ZONE); - // clang 5.1 failes without the cast - BOOST_CHECK_EQUAL((unsigned int) '\0', (unsigned int) TRI_LastCharStringBuffer(&sb)); - TRI_AppendStringStringBuffer(&sb, "f"); BOOST_CHECK_EQUAL((unsigned int) 'f', (unsigned int) TRI_LastCharStringBuffer(&sb)); @@ -544,7 +539,7 @@ BOOST_AUTO_TEST_CASE (tst_last_char) { BOOST_CHECK_EQUAL((unsigned int) '\n', (unsigned int) TRI_LastCharStringBuffer(&sb)); TRI_ClearStringBuffer(&sb); - BOOST_CHECK_EQUAL((unsigned int) '\0', (unsigned int) TRI_LastCharStringBuffer(&sb)); + BOOST_CHECK_EQUAL(0, (int) TRI_LengthStringBuffer(&sb)); for (size_t i = 0; i < 100; ++i) { TRI_AppendStringStringBuffer(&sb, "the quick brown fox jumped over the lazy dog"); @@ -554,7 +549,6 @@ BOOST_AUTO_TEST_CASE (tst_last_char) { BOOST_CHECK_EQUAL((unsigned int) '.', (unsigned int) TRI_LastCharStringBuffer(&sb)); TRI_AnnihilateStringBuffer(&sb); - BOOST_CHECK_EQUAL((unsigned int) '\0', (unsigned int) TRI_LastCharStringBuffer(&sb)); TRI_DestroyStringBuffer(&sb); } diff --git a/arangod/V8Server/v8-collection.cpp b/arangod/V8Server/v8-collection.cpp index 096e1f57e2..d84d591abf 100644 --- a/arangod/V8Server/v8-collection.cpp +++ b/arangod/V8Server/v8-collection.cpp @@ -3734,6 +3734,10 @@ static void JS_CollectionVocbase (const v8::FunctionCallbackInfo& arg TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } + if (TRI_IsDeletedVocBase(vocbase)) { + TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); + } + // expecting one argument if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("_collection(|)"); diff --git a/arangod/V8Server/v8-util.cpp b/arangod/V8Server/v8-util.cpp index c65d4ae511..7366f0b7e2 100644 --- a/arangod/V8Server/v8-util.cpp +++ b/arangod/V8Server/v8-util.cpp @@ -54,9 +54,9 @@ TRI_vocbase_t* GetContextVocBase (v8::Isolate* isolate) { v8::Handle V8TickId (v8::Isolate* isolate, TRI_voc_tick_t tick) { char buffer[21]; - size_t len = TRI_StringUInt64InPlace((uint64_t) tick, (char*) &buffer); + size_t len = TRI_StringUInt64InPlace(static_cast(tick), &buffer[0]); - return TRI_V8_PAIR_STRING((char const*) buffer, (int) len); + return TRI_V8_PAIR_STRING(&buffer[0], static_cast(len)); } //////////////////////////////////////////////////////////////////////////////// @@ -65,9 +65,9 @@ v8::Handle V8TickId (v8::Isolate* isolate, TRI_voc_tick_t tick) { v8::Handle V8RevisionId (v8::Isolate* isolate, TRI_voc_rid_t rid) { char buffer[21]; - size_t len = TRI_StringUInt64InPlace((uint64_t) rid, (char*) &buffer); + size_t len = TRI_StringUInt64InPlace(static_cast(rid), &buffer[0]); - return TRI_V8_PAIR_STRING((char const*) buffer, (int) len); + return TRI_V8_PAIR_STRING(&buffer[0], static_cast(len)); } //////////////////////////////////////////////////////////////////////////////// @@ -75,9 +75,9 @@ v8::Handle V8RevisionId (v8::Isolate* isolate, TRI_voc_rid_t rid) { //////////////////////////////////////////////////////////////////////////////// v8::Handle V8DocumentId (v8::Isolate* isolate, - string const& collectionName, - string const& key) { - string const&& id = DocumentHelper::assembleDocumentId(collectionName, key); + std::string const& collectionName, + std::string const& key) { + std::string const&& id = DocumentHelper::assembleDocumentId(collectionName, key); return TRI_V8_STD_STRING(id); } @@ -87,7 +87,7 @@ v8::Handle V8DocumentId (v8::Isolate* isolate, //////////////////////////////////////////////////////////////////////////////// static bool ParseDocumentHandle (v8::Handle const arg, - string& collectionName, + std::string& collectionName, std::unique_ptr& key) { TRI_ASSERT(collectionName.empty()); diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 81b12ace69..102e5f66d3 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -3140,6 +3140,10 @@ static void JS_UseDatabase (const v8::FunctionCallbackInfo& args) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_INTERNAL); } + if (TRI_IsDeletedVocBase(vocbase)) { + TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); + } + if (TRI_EqualString(name.c_str(), vocbase->_name)) { // same database. nothing to do TRI_V8_RETURN(WrapVocBase(isolate, vocbase)); diff --git a/arangod/VocBase/document-collection.cpp b/arangod/VocBase/document-collection.cpp index 3309faa77d..066f1d6155 100644 --- a/arangod/VocBase/document-collection.cpp +++ b/arangod/VocBase/document-collection.cpp @@ -63,8 +63,6 @@ #include "Wal/Marker.h" #include "Wal/Slots.h" -#include - using namespace triagens::arango; //////////////////////////////////////////////////////////////////////////////// @@ -117,7 +115,9 @@ void TRI_doc_mptr_copy_t::setDataPtr (void const* d) { //////////////////////////////////////////////////////////////////////////////// TRI_document_collection_t::TRI_document_collection_t () - : _useSecondaryIndexes(true), + : _lock(), + _shaper(nullptr), + _useSecondaryIndexes(true), _capConstraint(nullptr), _ditches(this), _headersPtr(nullptr), @@ -199,6 +199,14 @@ int TRI_document_collection_t::beginWrite () { // std::cout << "BeginWrite: " << document->_info._name << std::endl; TRI_WRITE_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(this); + try { + _vocbase->_deadlockDetector.setWriterStarted(this); + } + catch (...) { + TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(this); + return TRI_ERROR_OUT_OF_MEMORY; + } + return TRI_ERROR_NO_ERROR; } @@ -220,6 +228,12 @@ int TRI_document_collection_t::endWrite () { // LOCKING-DEBUG // std::cout << "EndWrite: " << document->_info._name << std::endl; TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(this); + + try { + _vocbase->_deadlockDetector.setWriterFinished(this); + } + catch (...) { + } return TRI_ERROR_NO_ERROR; } @@ -241,10 +255,45 @@ int TRI_document_collection_t::beginReadTimed (uint64_t timeout, } } uint64_t waited = 0; + if (timeout == 0) { + // we don't allow looping forever. limit waiting to 15 minutes max. + timeout = 15 * 60 * 1000 * 1000; + } // LOCKING-DEBUG // std::cout << "BeginReadTimed: " << document->_info._name << std::endl; + int iterations = 0; + bool wasBlocked = false; + while (! TRI_TRY_READ_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(this)) { + try { + if (! wasBlocked) { + // insert reader + if (_vocbase->_deadlockDetector.setReaderBlocked(this)) { + // deadlock + return TRI_ERROR_DEADLOCK; + } + wasBlocked = true; + } + else if (++iterations >= 5) { + // periodically check for deadlocks + TRI_ASSERT(wasBlocked); + iterations = 0; + if (_vocbase->_deadlockDetector.isDeadlocked(this)) { + // deadlock + _vocbase->_deadlockDetector.setReaderUnblocked(this); + return TRI_ERROR_DEADLOCK; + } + } + } + catch (...) { + // clean up! + if (wasBlocked) { + _vocbase->_deadlockDetector.setReaderUnblocked(this); + return TRI_ERROR_OUT_OF_MEMORY; + } + } + #ifdef _WIN32 usleep((unsigned long) sleepPeriod); #else @@ -254,10 +303,16 @@ int TRI_document_collection_t::beginReadTimed (uint64_t timeout, waited += sleepPeriod; if (waited > timeout) { + _vocbase->_deadlockDetector.setReaderUnblocked(this); return TRI_ERROR_LOCK_TIMEOUT; } } + // when we are here, we've got the read lock + if (wasBlocked) { + _vocbase->_deadlockDetector.setReaderUnblocked(this); + } + return TRI_ERROR_NO_ERROR; } @@ -278,10 +333,45 @@ int TRI_document_collection_t::beginWriteTimed (uint64_t timeout, } } uint64_t waited = 0; + if (timeout == 0) { + // we don't allow looping forever. limit waiting to 15 minutes max. + timeout = 15 * 60 * 1000 * 1000; + } // LOCKING-DEBUG // std::cout << "BeginWriteTimed: " << document->_info._name << std::endl; + int iterations = 0; + bool wasBlocked = false; + while (! TRI_TRY_WRITE_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(this)) { + try { + if (! wasBlocked) { + // insert writer (with method named "setReaderBlocked"..., but it works) + if (_vocbase->_deadlockDetector.setReaderBlocked(this)) { + // deadlock + return TRI_ERROR_DEADLOCK; + } + wasBlocked = true; + } + else if (++iterations >= 5) { + // periodically check for deadlocks + TRI_ASSERT(wasBlocked); + iterations = 0; + if (_vocbase->_deadlockDetector.isDeadlocked(this)) { + // deadlock + _vocbase->_deadlockDetector.setReaderUnblocked(this); + return TRI_ERROR_DEADLOCK; + } + } + } + catch (...) { + // clean up! + if (wasBlocked) { + _vocbase->_deadlockDetector.setReaderUnblocked(this); + return TRI_ERROR_OUT_OF_MEMORY; + } + } + #ifdef _WIN32 usleep((unsigned long) sleepPeriod); #else @@ -291,9 +381,23 @@ int TRI_document_collection_t::beginWriteTimed (uint64_t timeout, waited += sleepPeriod; if (waited > timeout) { + _vocbase->_deadlockDetector.setReaderUnblocked(this); return TRI_ERROR_LOCK_TIMEOUT; } } + + // when we are here, we've got the write lock + if (wasBlocked) { + _vocbase->_deadlockDetector.setReaderUnblocked(this); + } + + try { + _vocbase->_deadlockDetector.setWriterStarted(this); + } + catch (...) { + TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(this); + return TRI_ERROR_OUT_OF_MEMORY; + } return TRI_ERROR_NO_ERROR; } diff --git a/arangod/VocBase/document-collection.h b/arangod/VocBase/document-collection.h index 5a5050396a..28e5209453 100644 --- a/arangod/VocBase/document-collection.h +++ b/arangod/VocBase/document-collection.h @@ -364,7 +364,6 @@ struct TRI_document_collection_t : public TRI_collection_t { // TRI_read_write_lock_t _lock; triagens::basics::ReadWriteLockCPP11 _lock; - private: VocShaper* _shaper; diff --git a/arangod/VocBase/transaction.cpp b/arangod/VocBase/transaction.cpp index 3193423023..d4233ce8a5 100644 --- a/arangod/VocBase/transaction.cpp +++ b/arangod/VocBase/transaction.cpp @@ -365,8 +365,6 @@ static void FreeDitch (TRI_transaction_collection_t* trxCollection) { static int LockCollection (TRI_transaction_collection_t* trxCollection, TRI_transaction_type_e type, int nestingLevel) { - int res; - TRI_ASSERT(trxCollection != nullptr); TRI_transaction_t* trx = trxCollection->_transaction; @@ -394,29 +392,20 @@ static int LockCollection (TRI_transaction_collection_t* trxCollection, TRI_document_collection_t* document = trxCollection->_collection->_collection; + int res; if (type == TRI_TRANSACTION_READ) { LOG_TRX(trx, nestingLevel, "read-locking collection %llu", (unsigned long long) trxCollection->_cid); - if (trx->_timeout == 0) { - res = document->beginRead(); - } - else { - res = document->beginReadTimed(trx->_timeout, TRI_TRANSACTION_DEFAULT_SLEEP_DURATION); - } + res = document->beginReadTimed(trx->_timeout, TRI_TRANSACTION_DEFAULT_SLEEP_DURATION); } else { LOG_TRX(trx, nestingLevel, "write-locking collection %llu", (unsigned long long) trxCollection->_cid); - if (trx->_timeout == 0) { - res = document->beginWrite(); - } - else { - res = document->beginWriteTimed(trx->_timeout, TRI_TRANSACTION_DEFAULT_SLEEP_DURATION); - } + res = document->beginWriteTimed(trx->_timeout, TRI_TRANSACTION_DEFAULT_SLEEP_DURATION * 50); } if (res == TRI_ERROR_NO_ERROR) { @@ -816,7 +805,7 @@ TRI_transaction_t* TRI_CreateTransaction (TRI_vocbase_t* vocbase, trx->_timeout = (uint64_t) (timeout * 1000000.0); } else if (timeout == 0.0) { - trx->_timeout = (uint64_t) 0; + trx->_timeout = static_cast(0); } TRI_InitVectorPointer(&trx->_collections, TRI_UNKNOWN_MEM_ZONE, 2); diff --git a/arangod/VocBase/vocbase.h b/arangod/VocBase/vocbase.h index 36e059517d..644aaaff67 100644 --- a/arangod/VocBase/vocbase.h +++ b/arangod/VocBase/vocbase.h @@ -32,6 +32,7 @@ #include "Basics/Common.h" #include "Basics/associative.h" +#include "Basics/DeadlockDetector.h" #include "Basics/locks.h" #include "Basics/ReadWriteLock.h" #include "Basics/threads.h" @@ -274,6 +275,8 @@ struct TRI_vocbase_t { TRI_server_t* _server; TRI_vocbase_defaults_t _settings; + triagens::basics::DeadlockDetector _deadlockDetector; + triagens::basics::ReadWriteLock _collectionsLock; // collection iterator lock std::vector _collections; // pointers to ALL collections std::vector _deadCollections; // pointers to collections dropped that can be removed later diff --git a/arangosh/Benchmark/test-cases.h b/arangosh/Benchmark/test-cases.h index 41125bd006..e2ed7ba4af 100644 --- a/arangosh/Benchmark/test-cases.h +++ b/arangosh/Benchmark/test-cases.h @@ -1290,6 +1290,79 @@ struct TransactionCountTest : public BenchmarkOperation { }; +// ----------------------------------------------------------------------------- +// --SECTION-- transaction deadlock test +// ----------------------------------------------------------------------------- + +struct TransactionDeadlockTest : public BenchmarkOperation { + TransactionDeadlockTest () + : BenchmarkOperation () { + } + + ~TransactionDeadlockTest () { + } + + bool setUp (SimpleHttpClient* client) { + _c1 = std::string(Collection + "1"); + _c2 = std::string(Collection + "2"); + + return DeleteCollection(client, _c1) && + DeleteCollection(client, _c2) && + CreateCollection(client, _c1, 2) && + CreateCollection(client, _c2, 2) && + CreateDocument(client, _c2, "{ \"_key\": \"sum\", \"count\": 0 }"); + } + + void tearDown () { + } + + std::string url (const int threadNumber, const size_t threadCounter, const size_t globalCounter) { + return std::string("/_api/transaction"); + } + + HttpRequest::HttpRequestType type (const int threadNumber, const size_t threadCounter, const size_t globalCounter) { + return HttpRequest::HTTP_REQUEST_POST; + } + + const char* payload (size_t* length, const int threadNumber, const size_t threadCounter, const size_t globalCounter, bool* mustFree) { + const size_t mod = globalCounter % 2; + TRI_string_buffer_t* buffer; + buffer = TRI_CreateSizedStringBuffer(TRI_UNKNOWN_MEM_ZONE, 256); + + TRI_AppendStringStringBuffer(buffer, "{ \"collections\": { "); + TRI_AppendStringStringBuffer(buffer, "\"write\": [ \""); + + if (mod == 0) { + TRI_AppendStringStringBuffer(buffer, _c1.c_str()); + } + else { + TRI_AppendStringStringBuffer(buffer, _c2.c_str()); + } + + TRI_AppendStringStringBuffer(buffer, "\" ] }, \"action\": \"function () { "); + TRI_AppendStringStringBuffer(buffer, "var c = require(\\\"internal\\\").db[\\\""); + if (mod == 0) { + TRI_AppendStringStringBuffer(buffer, _c2.c_str()); + } + else { + TRI_AppendStringStringBuffer(buffer, _c1.c_str()); + } + TRI_AppendStringStringBuffer(buffer, "\\\"]; c.any();"); + + TRI_AppendStringStringBuffer(buffer, " }\" }"); + + *length = TRI_LengthStringBuffer(buffer); + *mustFree = true; + char* ptr = TRI_StealStringBuffer(buffer); + TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); + + return (const char*) ptr; + } + + std::string _c1; + std::string _c2; +}; + // ----------------------------------------------------------------------------- // --SECTION-- transaction test // ----------------------------------------------------------------------------- @@ -1738,6 +1811,9 @@ static BenchmarkOperation* GetTestCase (const std::string& name) { if (name == "multitrx") { return new TransactionMultiTest(); } + if (name == "deadlocktrx") { + return new TransactionDeadlockTest(); + } if (name == "multi-collection") { return new TransactionMultiCollectionTest(); } diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/bootstrap/errors.js b/js/apps/system/_admin/aardvark/APP/frontend/js/bootstrap/errors.js index 6d4f390bdb..ed7e62ff9e 100644 --- a/js/apps/system/_admin/aardvark/APP/frontend/js/bootstrap/errors.js +++ b/js/apps/system/_admin/aardvark/APP/frontend/js/bootstrap/errors.js @@ -39,6 +39,7 @@ "ERROR_LEGEND_NOT_IN_WAL_FILE" : { "code" : 26, "message" : "internal error if a legend for a marker does not yet exist in the same WAL file" }, "ERROR_FILE_EXISTS" : { "code" : 27, "message" : "file exists" }, "ERROR_LOCKED" : { "code" : 28, "message" : "locked" }, + "ERROR_DEADLOCK" : { "code" : 29, "message" : "deadlock detected" }, "ERROR_HTTP_BAD_PARAMETER" : { "code" : 400, "message" : "bad parameter" }, "ERROR_HTTP_UNAUTHORIZED" : { "code" : 401, "message" : "unauthorized" }, "ERROR_HTTP_FORBIDDEN" : { "code" : 403, "message" : "forbidden" }, diff --git a/js/common/bootstrap/errors.js b/js/common/bootstrap/errors.js index 6d4f390bdb..ed7e62ff9e 100644 --- a/js/common/bootstrap/errors.js +++ b/js/common/bootstrap/errors.js @@ -39,6 +39,7 @@ "ERROR_LEGEND_NOT_IN_WAL_FILE" : { "code" : 26, "message" : "internal error if a legend for a marker does not yet exist in the same WAL file" }, "ERROR_FILE_EXISTS" : { "code" : 27, "message" : "file exists" }, "ERROR_LOCKED" : { "code" : 28, "message" : "locked" }, + "ERROR_DEADLOCK" : { "code" : 29, "message" : "deadlock detected" }, "ERROR_HTTP_BAD_PARAMETER" : { "code" : 400, "message" : "bad parameter" }, "ERROR_HTTP_UNAUTHORIZED" : { "code" : 401, "message" : "unauthorized" }, "ERROR_HTTP_FORBIDDEN" : { "code" : 403, "message" : "forbidden" }, diff --git a/lib/Basics/DeadlockDetector.h b/lib/Basics/DeadlockDetector.h new file mode 100644 index 0000000000..4d2dc3bee6 --- /dev/null +++ b/lib/Basics/DeadlockDetector.h @@ -0,0 +1,257 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief deadlock detector +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2014 ArangoDB GmbH, Cologne, Germany +/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany +/// +/// 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. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany +/// @author Copyright 2013-2013, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#ifndef ARANGODB_BASICS_DEADLOCK_DETECTOR_H +#define ARANGODB_BASICS_DEADLOCK_DETECTOR_H 1 + +#include "Basics/Common.h" +#include "Basics/Mutex.h" +#include "Basics/MutexLocker.h" +#include "Basics/threads.h" + +namespace triagens { + namespace basics { + +// ----------------------------------------------------------------------------- +// --SECTION-- DeadlockDetector +// ----------------------------------------------------------------------------- + + template + class DeadlockDetector { + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors / destructors +// ----------------------------------------------------------------------------- + + public: + + DeadlockDetector () = default; + ~DeadlockDetector () = default; + + DeadlockDetector (DeadlockDetector const&) = delete; + DeadlockDetector& operator= (DeadlockDetector const&) = delete; + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + + public: + + bool isDeadlocked (T const* value) { + auto tid = TRI_CurrentThreadId(); + std::unordered_set watchFor({ tid }); + + std::vector stack; + + TRI_tid_t writerTid; + + { + MUTEX_LOCKER(_writersLock); + // find responsible writer + auto it = _writers.find(value); + + if (it == _writers.end()) { + return false; + } + + writerTid = (*it).second; + } + + stack.push_back(writerTid); + + MUTEX_LOCKER(_readersLock); + + while (! stack.empty()) { + TRI_tid_t current = stack.back(); + stack.pop_back(); + + watchFor.emplace(current); + auto it2 = _readersBlocked.find(current); + + if (it2 == _readersBlocked.end()) { + return false; + } + + if (watchFor.find((*it2).second) != watchFor.end()) { + // deadlock! + return true; + } + + stack.push_back((*it2).second); + } + + // no deadlock found + return false; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief insert a reader into the list of blocked readers +/// returns true if a deadlock was detected and false otherwise +//////////////////////////////////////////////////////////////////////////////// + + bool setReaderBlocked (T const* value) { + auto tid = TRI_CurrentThreadId(); + std::unordered_set watchFor({ tid }); + + std::vector stack; + + TRI_tid_t writerTid; + + { + MUTEX_LOCKER(_writersLock); + // find responsible writer + auto it = _writers.find(value); + + if (it == _writers.end()) { + return false; + } + + writerTid = (*it).second; + } + + stack.push_back(writerTid); + + MUTEX_LOCKER(_readersLock); + _readersBlocked.emplace(tid, writerTid); + + try { + while (! stack.empty()) { + TRI_tid_t current = stack.back(); + stack.pop_back(); + + watchFor.emplace(current); + auto it2 = _readersBlocked.find(current); + + if (it2 == _readersBlocked.end()) { + return false; + } + + if (watchFor.find((*it2).second) != watchFor.end()) { + // deadlock! + _readersBlocked.erase(tid); + return true; + } + + stack.push_back((*it2).second); + } + + // no deadlock found + return false; + } + catch (...) { + // clean up and re-throw + _readersBlocked.erase(tid); + throw; + } + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief remove a reader from the list of blocked readers +//////////////////////////////////////////////////////////////////////////////// + + void setReaderUnblocked (T const* value) noexcept { + auto tid = TRI_CurrentThreadId(); + + try { + MUTEX_LOCKER(_readersLock); + _readersBlocked.erase(tid); + } + catch (...) { + } + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief inserts a writer into the list of writers +//////////////////////////////////////////////////////////////////////////////// + + void setWriterStarted (T const* value) { + auto tid = TRI_CurrentThreadId(); + + MUTEX_LOCKER(_writersLock); + _writers.emplace(value, tid); + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes a writers from the list of writers +//////////////////////////////////////////////////////////////////////////////// + + void setWriterFinished (T const* value) noexcept { + try { + MUTEX_LOCKER(_writersLock); + _writers.erase(value); + } + catch (...) { + } + } + +// ----------------------------------------------------------------------------- +// --SECTION-- private variables +// ----------------------------------------------------------------------------- + + private: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief lock for managing the writers +//////////////////////////////////////////////////////////////////////////////// + + triagens::basics::Mutex _writersLock; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief all operating writers +//////////////////////////////////////////////////////////////////////////////// + + std::unordered_map _writers; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief lock for managing the readers +//////////////////////////////////////////////////////////////////////////////// + + triagens::basics::Mutex _readersLock; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief readers that are blocked on writers +//////////////////////////////////////////////////////////////////////////////// + + std::unordered_map _readersBlocked; + + }; + + } // namespace triagens::basics +} // namespace triagens + +#endif + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + +// Local Variables: +// mode: outline-minor +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" +// End: diff --git a/lib/Basics/errors.dat b/lib/Basics/errors.dat index 6e4106a46d..920c9255fd 100755 --- a/lib/Basics/errors.dat +++ b/lib/Basics/errors.dat @@ -31,6 +31,7 @@ ERROR_IP_ADDRESS_INVALID,25,"IP address is invalid","Will be raised when the str ERROR_LEGEND_NOT_IN_WAL_FILE,26,"internal error if a legend for a marker does not yet exist in the same WAL file","Will be raised internally, then fixed internally, and never come out to the user." ERROR_FILE_EXISTS,27,"file exists","Will be raised when a file already exists." ERROR_LOCKED,28,"locked","Will be raised when a resource or an operation is locked." +ERROR_DEADLOCK,29,"deadlock detected","Will be raised when a deadlock is detected when accessing collections." ################################################################################ ## HTTP standard errors diff --git a/lib/Basics/json.cpp b/lib/Basics/json.cpp index 564d928b38..afc6320aca 100644 --- a/lib/Basics/json.cpp +++ b/lib/Basics/json.cpp @@ -669,6 +669,27 @@ TRI_json_t* TRI_LookupArrayJson (TRI_json_t const* array, size_t pos) { return static_cast(TRI_AtVector(&array->_value._objects, pos)); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief deletes an element from a json array +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_DeleteArrayJson (TRI_memory_zone_t* zone, TRI_json_t* array, size_t index) { + TRI_ASSERT(TRI_IsArrayJson(array)); + + size_t const n = TRI_LengthArrayJson(array); + + if (index >= n) { + return false; + } + + TRI_json_t* element = static_cast(TRI_AtVector(&array->_value._objects, index)); + TRI_ASSERT(element != nullptr); + TRI_DestroyJson(zone, element); + TRI_RemoveVector(&array->_value._objects, index); + + return true; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief adds a new attribute to an object, using copy //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/Basics/json.h b/lib/Basics/json.h index 5e8e607273..39f8e08c9a 100644 --- a/lib/Basics/json.h +++ b/lib/Basics/json.h @@ -292,6 +292,14 @@ int TRI_PushBack3ArrayJson (TRI_memory_zone_t*, TRI_json_t* TRI_LookupArrayJson (TRI_json_t const*, size_t); +//////////////////////////////////////////////////////////////////////////////// +/// @brief deletes an element from a json array +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_DeleteArrayJson (TRI_memory_zone_t* zone, + TRI_json_t* object, + size_t index); + //////////////////////////////////////////////////////////////////////////////// /// @brief adds a new attribute to an object, using copy //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/Basics/voc-errors.cpp b/lib/Basics/voc-errors.cpp index eb180e0af6..49b1bb8d54 100644 --- a/lib/Basics/voc-errors.cpp +++ b/lib/Basics/voc-errors.cpp @@ -35,6 +35,7 @@ void TRI_InitializeErrorMessages () { REG_ERROR(ERROR_LEGEND_NOT_IN_WAL_FILE, "internal error if a legend for a marker does not yet exist in the same WAL file"); REG_ERROR(ERROR_FILE_EXISTS, "file exists"); REG_ERROR(ERROR_LOCKED, "locked"); + REG_ERROR(ERROR_DEADLOCK, "deadlock detected"); REG_ERROR(ERROR_HTTP_BAD_PARAMETER, "bad parameter"); REG_ERROR(ERROR_HTTP_UNAUTHORIZED, "unauthorized"); REG_ERROR(ERROR_HTTP_FORBIDDEN, "forbidden"); diff --git a/lib/Basics/voc-errors.h b/lib/Basics/voc-errors.h index 21b7436e52..23c35fe070 100644 --- a/lib/Basics/voc-errors.h +++ b/lib/Basics/voc-errors.h @@ -68,6 +68,8 @@ /// Will be raised when a file already exists. /// - 28: @LIT{locked} /// Will be raised when a resource or an operation is locked. +/// - 29: @LIT{deadlock detected} +/// Will be raised when a deadlock is detected when accessing collections. /// - 400: @LIT{bad parameter} /// Will be raised when the HTTP request does not fulfill the requirements. /// - 401: @LIT{unauthorized} @@ -966,6 +968,16 @@ void TRI_InitializeErrorMessages (); #define TRI_ERROR_LOCKED (28) +//////////////////////////////////////////////////////////////////////////////// +/// @brief 29: ERROR_DEADLOCK +/// +/// deadlock detected +/// +/// Will be raised when a deadlock is detected when accessing collections. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_DEADLOCK (29) + //////////////////////////////////////////////////////////////////////////////// /// @brief 400: ERROR_HTTP_BAD_PARAMETER ///