From 06a56a7d36a953d9cae4c7fa303b7abf14ca7c1b Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 21 May 2014 18:11:59 +0200 Subject: [PATCH] removed MRuby for arangod --- arangod/CMakeLists.txt | 14 - arangod/MRServer/ApplicationMR.cpp | 507 ---------------------------- arangod/MRServer/ApplicationMR.h | 408 ---------------------- arangod/MRServer/mr-actions.cpp | 451 ------------------------- arangod/MRServer/mr-actions.h | 64 ---- arangod/Makefile.files | 11 - arangod/RestServer/ArangoServer.cpp | 39 +-- arangod/RestServer/ArangoServer.h | 8 - 8 files changed, 1 insertion(+), 1501 deletions(-) delete mode 100644 arangod/MRServer/ApplicationMR.cpp delete mode 100644 arangod/MRServer/ApplicationMR.h delete mode 100644 arangod/MRServer/mr-actions.cpp delete mode 100644 arangod/MRServer/mr-actions.h diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index a0156ab272..35dae29e2a 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -30,18 +30,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") ### @brief arangod ################################################################################ -if (USE_MRUBY) - set(ARANGOD_MRUBY_SOURCE - MRServer/ApplicationMR.cpp - MRServer/mr-actions.cpp - ) - - set(ARANGOD_MRUBY_LIBS - ${LIB_ARANGO_MRUBY} - ${MRUBY_LIBS} - ) -endif () - add_executable( ${BIN_ARANGOD} Actions/actions.cpp @@ -155,7 +143,6 @@ add_executable( Wal/Slots.cpp Wal/SynchroniserThread.cpp Wal/TestThread.cpp - ${ARANGOD_MRUBY_SOURCE} ) target_link_libraries( @@ -163,7 +150,6 @@ target_link_libraries( ${LIB_ARANGO_FE} ${LIB_ARANGO_V8} ${LIB_ARANGO} - ${ARANGOD_MRUBY_LIBS} ${LIBEV_LIBS} ${V8_LIBS} # need this for rest::Version ${ICU_LIBS} diff --git a/arangod/MRServer/ApplicationMR.cpp b/arangod/MRServer/ApplicationMR.cpp deleted file mode 100644 index 37d8467523..0000000000 --- a/arangod/MRServer/ApplicationMR.cpp +++ /dev/null @@ -1,507 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// @brief MR engine configuration -/// -/// @file -/// -/// DISCLAIMER -/// -/// Copyright 2004-2013 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 triAGENS GmbH, Cologne, Germany -/// -/// @author Dr. Frank Celler -/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany -//////////////////////////////////////////////////////////////////////////////// - -#include "ApplicationMR.h" - -#include "Basics/ConditionLocker.h" -#include "Basics/ReadLocker.h" -#include "Basics/Thread.h" -#include "Basics/WriteLocker.h" -#include "BasicsC/logging.h" -#include "MRServer/mr-actions.h" -#include "VocBase/server.h" -#include "VocBase/vocbase.h" - -using namespace triagens::basics; -using namespace triagens::arango; -using namespace std; - -#include "mr/common/bootstrap/mr-error.h" -#include "mr/server/mr-server.h" - -// ----------------------------------------------------------------------------- -// --SECTION-- class MRGcThread -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoDB -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -namespace { - -//////////////////////////////////////////////////////////////////////////////// -/// @brief garbage collector -//////////////////////////////////////////////////////////////////////////////// - - class MRGcThread : public Thread { - public: - MRGcThread (ApplicationMR* applicationMR) - : Thread("mr-gc"), - _applicationMR(applicationMR), - _lock(), - _lastGcStamp(TRI_microtime()) { - } - - public: - -//////////////////////////////////////////////////////////////////////////////// -/// @brief collect garbage in an endless loop (main functon of GC thread) -//////////////////////////////////////////////////////////////////////////////// - - void run () { - _applicationMR->collectGarbage(); - } - -//////////////////////////////////////////////////////////////////////////////// -/// @brief get the timestamp of the last GC -//////////////////////////////////////////////////////////////////////////////// - - double getLastGcStamp () { - READ_LOCKER(_lock); - return _lastGcStamp; - } - -//////////////////////////////////////////////////////////////////////////////// -/// @brief set the global GC timestamp -//////////////////////////////////////////////////////////////////////////////// - - void updateGcStamp (double value) { - WRITE_LOCKER(_lock); - } - - private: - ApplicationMR* _applicationMR; - ReadWriteLock _lock; - double _lastGcStamp; - }; - -} - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- class ApplicationMR -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -// --SECTION-- constructors and destructors -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoDB -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief constructor -//////////////////////////////////////////////////////////////////////////////// - -ApplicationMR::ApplicationMR (TRI_server_t* server) - : ApplicationFeature("MRuby"), - _server(server), - _startupPath(), - _startupModules(), - _actionPath(), - _gcInterval(1000), - _gcFrequency(10.0), - _startupLoader(), - _actionLoader(), - _vocbase(0), - _nrInstances(0), - _contexts(0), - _contextCondition(), - _freeContexts(), - _dirtyContexts(), - _stopping(0) { - - assert(_server != 0); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief destructor -//////////////////////////////////////////////////////////////////////////////// - -ApplicationMR::~ApplicationMR () { -} - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- public methods -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoDB -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief sets the concurrency -//////////////////////////////////////////////////////////////////////////////// - -void ApplicationMR::setConcurrency (size_t n) { - _nrInstances = n; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief sets the database -//////////////////////////////////////////////////////////////////////////////// - -void ApplicationMR::setVocbase (TRI_vocbase_t* vocbase) { - _vocbase = vocbase; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief enters an context -//////////////////////////////////////////////////////////////////////////////// - -ApplicationMR::MRContext* ApplicationMR::enterContext () { - CONDITION_LOCKER(guard, _contextCondition); - - while (_freeContexts.empty()) { - LOG_DEBUG("waiting for unused MRuby context"); - guard.wait(); - } - - LOG_TRACE("found unused MRuby context"); - - MRContext* context = _freeContexts.back(); - _freeContexts.pop_back(); - - return context; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief exists an context -//////////////////////////////////////////////////////////////////////////////// - -void ApplicationMR::exitContext (MRContext* context) { - MRGcThread* gc = dynamic_cast(_gcThread); - assert(gc != 0); - double lastGc = gc->getLastGcStamp(); - - ++context->_dirt; - - { - CONDITION_LOCKER(guard, _contextCondition); - - if (context->_lastGcStamp + _gcFrequency < lastGc) { - LOG_TRACE("periodic gc interval reached"); - _dirtyContexts.push_back(context); - } - else if (context->_dirt >= _gcInterval) { - LOG_TRACE("maximum number of requests reached"); - _dirtyContexts.push_back(context); - } - else { - _freeContexts.push_back(context); - } - - guard.broadcast(); - } - - LOG_TRACE("returned dirty MR context"); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief runs the garbage collection -//////////////////////////////////////////////////////////////////////////////// - -void ApplicationMR::collectGarbage () { - MRGcThread* gc = dynamic_cast(_gcThread); - assert(gc != 0); - uint64_t waitTime = (uint64_t) (_gcFrequency * 1000.0 * 1000.0); - - while (_stopping == 0) { - MRContext* context = 0; - - { - bool gotSignal = false; - CONDITION_LOCKER(guard, _contextCondition); - - if (_dirtyContexts.empty()) { - // check whether we got a wait timeout or a signal - gotSignal = guard.wait(waitTime); - } - - if (! _dirtyContexts.empty()) { - context = _dirtyContexts.back(); - _dirtyContexts.pop_back(); - } - else if (! gotSignal && ! _freeContexts.empty()) { - // we timed out waiting for a signal - - // do nothing for now - // TODO: fix this if MRuby needs some proactive GC - context = 0; - - // TODO: pick one of the free contexts to clean up, based on its last GC stamp - // this is already implemented in ApplicationV8::pickContextForFc() - // if necessary for MRuby, the code in pickContextForGc() can be used as a prototype - } - } - - // update last gc time - double lastGc = TRI_microtime(); - gc->updateGcStamp(lastGc); - - if (context != 0) { - LOG_TRACE("collecting MR garbage"); - - mrb_garbage_collect(context->_mrb); - - context->_dirt = 0; - context->_lastGcStamp = lastGc; - - { - CONDITION_LOCKER(guard, _contextCondition); - - _freeContexts.push_back(context); - guard.broadcast(); - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief disables actions -//////////////////////////////////////////////////////////////////////////////// - -void ApplicationMR::disableActions () { - _actionPath.clear(); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- ApplicationFeature methods -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ApplicationServer -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// {@inheritDoc} -//////////////////////////////////////////////////////////////////////////////// - -void ApplicationMR::setupOptions (map& options) { - options["RUBY Options:help-admin"] - ("ruby.gc-interval", &_gcInterval, "Ruby request-based garbage collection interval (each x requests)") - ("ruby.gc-frequency", &_gcFrequency, "Ruby time-based garbage collection frequency (each x seconds)") - ; - - options["RUBY Options:help-admin"] - ("ruby.action-directory", &_actionPath, "path to the Ruby action directory") - ("ruby.modules-path", &_startupModules, "one or more directories separated by (semi-) colons") - ("ruby.startup-directory", &_startupPath, "path to the directory containing alternate Ruby startup scripts") - ; -} - -//////////////////////////////////////////////////////////////////////////////// -/// {@inheritDoc} -//////////////////////////////////////////////////////////////////////////////// - -bool ApplicationMR::prepare () { - // check the startup modules - if (_startupModules.empty()) { - LOG_FATAL_AND_EXIT("no 'ruby.modules-path' has been supplied, giving up"); - } - else { - LOG_INFO("using Ruby modules path '%s'", _startupModules.c_str()); - } - - // set up the startup loader - if (_startupPath.empty()) { - LOG_INFO("using built-in Ruby startup files"); - - _startupLoader.defineScript("common/bootstrap/error.rb", MR_common_bootstrap_error); - _startupLoader.defineScript("server/server.rb", MR_server_server); - } - else { - LOG_INFO("using Ruby startup files at '%s'", _startupPath.c_str()); - - _startupLoader.setDirectory(_startupPath); - } - - // set up action loader - if (_actionPath.empty()) { - LOG_FATAL_AND_EXIT("no 'ruby.action-directory' has been supplied, giving up"); - } - else { - LOG_INFO("using Ruby action files at '%s'", _actionPath.c_str()); - - _actionLoader.setDirectory(_actionPath); - } - - // setup instances - _contexts = new MRContext*[_nrInstances]; - - for (size_t i = 0; i < _nrInstances; ++i) { - bool ok = prepareMRInstance(i); - - if (! ok) { - return false; - } - } - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// {@inheritDoc} -//////////////////////////////////////////////////////////////////////////////// - -bool ApplicationMR::start () { - _gcThread = new MRGcThread(this); - _gcThread->start(); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// {@inheritDoc} -//////////////////////////////////////////////////////////////////////////////// - -void ApplicationMR::close () { - _stopping = 1; - _contextCondition.broadcast(); -} - -//////////////////////////////////////////////////////////////////////////////// -/// {@inheritDoc} -//////////////////////////////////////////////////////////////////////////////// - -void ApplicationMR::stop () { - _gcThread->shutdown(); - delete _gcThread; - - for (size_t i = 0; i < _nrInstances; ++i) { - shutdownMRInstance(i); - } - - delete[] _contexts; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- private methods -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoDB -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief prepares a MR instance -//////////////////////////////////////////////////////////////////////////////// - -bool ApplicationMR::prepareMRInstance (size_t i) { - static char const* files[] = { "common/bootstrap/error.rb", - "server/server.rb" - }; - - LOG_TRACE("initialising MR context #%d", (int) i); - - MRContext* context = _contexts[i] = new MRContext(); - - // create a new shell - context->_mrb = MR_OpenShell(); - - TRI_InitMRUtils(context->_mrb); - - if (! _actionPath.empty()) { - TRI_InitMRActions(context->_mrb, this); - } - - // load all init files - for (i = 0; i < sizeof(files) / sizeof(files[0]); ++i) { - bool ok = _startupLoader.loadScript(context->_mrb, files[i]); - - if (! ok) { - LOG_FATAL_AND_EXIT("cannot load Ruby utilities from file '%s'", files[i]); - } - } - - // load all actions - if (! _actionPath.empty()) { - bool ok = _actionLoader.executeAllScripts(context->_mrb); - - if (! ok) { - LOG_FATAL_AND_EXIT("cannot load Ruby actions from directory '%s'", _actionLoader.getDirectory().c_str()); - } - } - - context->_lastGcStamp = TRI_microtime(); - - // and return from the context - LOG_TRACE("initialised MR context #%d", (int) i); - - _freeContexts.push_back(context); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief shut downs a MR instances -//////////////////////////////////////////////////////////////////////////////// - -void ApplicationMR::shutdownMRInstance (size_t i) { - LOG_TRACE("shutting down MR context #%d", (int) i); - - MRContext* context = _contexts[i]; - mrb_state* mrb = context->_mrb; - - mrb_garbage_collect(mrb); - - MR_CloseShell(mrb); - - LOG_TRACE("closed MR context #%d", (int) i); - - delete context; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// Local Variables: -// mode: outline-minor -// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" -// End: diff --git a/arangod/MRServer/ApplicationMR.h b/arangod/MRServer/ApplicationMR.h deleted file mode 100644 index ef5f871078..0000000000 --- a/arangod/MRServer/ApplicationMR.h +++ /dev/null @@ -1,408 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// @brief MR enigne configuration -/// -/// @file -/// -/// DISCLAIMER -/// -/// Copyright 2004-2013 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 triAGENS GmbH, Cologne, Germany -/// -/// @author Dr. Frank Celler -/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany -//////////////////////////////////////////////////////////////////////////////// - -#ifndef TRIAGENS_MRSERVER_APPLICATION_MR_H -#define TRIAGENS_MRSERVER_APPLICATION_MR_H 1 - -#include "Basics/Common.h" - -#include "ApplicationServer/ApplicationFeature.h" - -#include "MRuby/mr-utils.h" - -#include "Basics/ConditionVariable.h" -#include "MRuby/MRLoader.h" - -// ----------------------------------------------------------------------------- -// --SECTION-- forward declarations -// ----------------------------------------------------------------------------- - -extern "C" { - struct TRI_server_s; - struct TRI_vocbase_s; -} - -namespace triagens { - namespace basics { - class Thread; - } - -// ----------------------------------------------------------------------------- -// --SECTION-- class ApplicationMR -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoDB -/// @{ -//////////////////////////////////////////////////////////////////////////////// - - namespace arango { - -//////////////////////////////////////////////////////////////////////////////// -/// @brief application simple user and session management feature -//////////////////////////////////////////////////////////////////////////////// - - class ApplicationMR : public rest::ApplicationFeature { - private: - ApplicationMR (ApplicationMR const&); - ApplicationMR& operator= (ApplicationMR const&); - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- public types -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoDB -/// @{ -//////////////////////////////////////////////////////////////////////////////// - - public: - -//////////////////////////////////////////////////////////////////////////////// -/// @brief MR isolate and context -//////////////////////////////////////////////////////////////////////////////// - - struct MRContext { - -//////////////////////////////////////////////////////////////////////////////// -/// @brief ruby state -//////////////////////////////////////////////////////////////////////////////// - - mrb_state* _mrb; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief number of requests since last GC of the context -//////////////////////////////////////////////////////////////////////////////// - - size_t _dirt; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief timestamp of last GC for the context -//////////////////////////////////////////////////////////////////////////////// - - double _lastGcStamp; - - }; - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- constructors and destructors -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoDB -/// @{ -//////////////////////////////////////////////////////////////////////////////// - - public: - -//////////////////////////////////////////////////////////////////////////////// -/// @brief constructor -//////////////////////////////////////////////////////////////////////////////// - - ApplicationMR (struct TRI_server_s*); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief destructor -//////////////////////////////////////////////////////////////////////////////// - - ~ApplicationMR (); - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- public methods -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoDB -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief sets the concurrency -//////////////////////////////////////////////////////////////////////////////// - - void setConcurrency (size_t); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief sets the database -//////////////////////////////////////////////////////////////////////////////// - - void setVocbase (struct TRI_vocbase_s*); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief enters an context -//////////////////////////////////////////////////////////////////////////////// - - MRContext* enterContext (); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief exists an context -//////////////////////////////////////////////////////////////////////////////// - - void exitContext (MRContext*); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief runs the garbage collection -//////////////////////////////////////////////////////////////////////////////// - - void collectGarbage (); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief disables actions -//////////////////////////////////////////////////////////////////////////////// - - void disableActions (); - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- ApplicationFeature methods -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ApplicationServer -/// @{ -//////////////////////////////////////////////////////////////////////////////// - - public: - -//////////////////////////////////////////////////////////////////////////////// -/// {@inheritDoc} -//////////////////////////////////////////////////////////////////////////////// - - void setupOptions (map&); - -//////////////////////////////////////////////////////////////////////////////// -/// {@inheritDoc} -//////////////////////////////////////////////////////////////////////////////// - - bool prepare (); - -//////////////////////////////////////////////////////////////////////////////// -/// {@inheritDoc} -//////////////////////////////////////////////////////////////////////////////// - - bool start (); - -//////////////////////////////////////////////////////////////////////////////// -/// {@inheritDoc} -//////////////////////////////////////////////////////////////////////////////// - - void close (); - -//////////////////////////////////////////////////////////////////////////////// -/// {@inheritDoc} -//////////////////////////////////////////////////////////////////////////////// - - void stop (); - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- private methods -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoDB -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief prepares a MR instance -//////////////////////////////////////////////////////////////////////////////// - - bool prepareMRInstance (size_t i); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief shut downs a MR instances -//////////////////////////////////////////////////////////////////////////////// - - void shutdownMRInstance (size_t i); - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- private variables -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoDB -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief server object -//////////////////////////////////////////////////////////////////////////////// - - struct TRI_server_s* _server; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief path to the directory containing alternate startup scripts -/// -/// @CMDOPT{\--ruby.directory @CA{directory}} -/// -/// Specifies the @CA{directory} path to the MRuby files used for bootstraping. -/// Multiple paths can be specified separated with commas. -//////////////////////////////////////////////////////////////////////////////// - - string _startupPath; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief semicolon separated list of module directories -/// -/// @CMDOPT{\--ruby.modules-path @CA{directory}} -/// -/// Specifies the @CA{directory} paths where the MRuby modules are located. -/// Multiple paths can be specified separated with commas. -//////////////////////////////////////////////////////////////////////////////// - - string _startupModules; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief path to the system action directory -/// -/// @CMDOPT{\--ruby.action-directory @CA{directory}} -/// -/// Specifies the @CA{directory} containg the MRuby files describing the system -/// actions. Multiple paths can be specified separated with commas. -//////////////////////////////////////////////////////////////////////////////// - - string _actionPath; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief MRuby garbage collection interval (each x requests) -/// -/// @CMDOPT{\--ruby.gc-interval @CA{interval}} -/// -/// Specifies the interval (approximately in number of requests) that the -/// garbage collection for MRuby objects will be run in each thread. -//////////////////////////////////////////////////////////////////////////////// - - uint64_t _gcInterval; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief MRuby garbage collection frequency (each x seconds) -/// -/// @CMDOPT{\--ruby.gc-frequency @CA{frequency}} -/// -/// Specifies the frequency in seconds for the automatic garbage collection of -/// MRuby objects. This setting is useful to have the garbage collection -/// still work in periods with no or little numbers of requests. -//////////////////////////////////////////////////////////////////////////////// - - double _gcFrequency; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief MR startup loader -//////////////////////////////////////////////////////////////////////////////// - - MRLoader _startupLoader; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief MR action loader -//////////////////////////////////////////////////////////////////////////////// - - MRLoader _actionLoader; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief database -//////////////////////////////////////////////////////////////////////////////// - - struct TRI_vocbase_s* _vocbase; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief number of instances to create -//////////////////////////////////////////////////////////////////////////////// - - size_t _nrInstances; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief MR contexts -//////////////////////////////////////////////////////////////////////////////// - - MRContext** _contexts; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief MR contexts queue lock -//////////////////////////////////////////////////////////////////////////////// - - basics::ConditionVariable _contextCondition; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief MR free contexts -//////////////////////////////////////////////////////////////////////////////// - - std::vector _freeContexts; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief MR free contexts -//////////////////////////////////////////////////////////////////////////////// - - std::vector _dirtyContexts; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief shutdown in progress -//////////////////////////////////////////////////////////////////////////////// - - volatile sig_atomic_t _stopping; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief garbage collection thread -//////////////////////////////////////////////////////////////////////////////// - - basics::Thread* _gcThread; - }; - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -#endif - -// Local Variables: -// mode: outline-minor -// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" -// End: diff --git a/arangod/MRServer/mr-actions.cpp b/arangod/MRServer/mr-actions.cpp deleted file mode 100644 index b011465c5a..0000000000 --- a/arangod/MRServer/mr-actions.cpp +++ /dev/null @@ -1,451 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// @brief mruby actions -/// -/// @file -/// -/// DISCLAIMER -/// -/// 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 triAGENS GmbH, Cologne, Germany -/// -/// @author Dr. Frank Celler -/// @author Copyright 2012-2014, triAGENS GmbH, Cologne, Germany -//////////////////////////////////////////////////////////////////////////////// - -#include "mr-actions.h" - -#include "Actions/actions.h" -#include "Basics/ReadLocker.h" -#include "Basics/WriteLocker.h" -#include "BasicsC/conversions.h" -#include "BasicsC/logging.h" -#include "BasicsC/tri-strings.h" -#include "MRServer/ApplicationMR.h" -#include "Rest/HttpRequest.h" -#include "Rest/HttpResponse.h" -#include "VocBase/vocbase.h" - -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/hash.h" -#include "mruby/string.h" -#include "mruby/variable.h" - -using namespace std; -using namespace triagens::basics; -using namespace triagens::rest; -using namespace triagens::arango; - -// ----------------------------------------------------------------------------- -// --SECTION-- forward declarations -// ----------------------------------------------------------------------------- - -static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, - mrb_state* mrb, - TRI_action_t const* action, - mrb_value callback, - HttpRequest* request); - -// ----------------------------------------------------------------------------- -// --SECTION-- private variables -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @brief global MRuby dealer -//////////////////////////////////////////////////////////////////////////////// - -ApplicationMR* GlobalMRDealer = 0; - -// ----------------------------------------------------------------------------- -// --SECTION-- private types -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -// --SECTION-- class mr_action_t -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @brief action description for MRuby -//////////////////////////////////////////////////////////////////////////////// - -class mr_action_t : public TRI_action_t { - public: - -//////////////////////////////////////////////////////////////////////////////// -/// @brief constructor -//////////////////////////////////////////////////////////////////////////////// - - mr_action_t (set const& contexts) - : TRI_action_t(contexts) { - _type = "RUBY"; - } - -//////////////////////////////////////////////////////////////////////////////// -/// @brief destructor -//////////////////////////////////////////////////////////////////////////////// - - ~mr_action_t () { - // TODO cleanup - } - -//////////////////////////////////////////////////////////////////////////////// -/// @brief creates callback for a context -//////////////////////////////////////////////////////////////////////////////// - - void createCallback (mrb_state* mrb, mrb_value callback) { - WRITE_LOCKER(_callbacksLock); - - _callbacks[mrb] = callback; - } - -//////////////////////////////////////////////////////////////////////////////// -/// @brief creates callback for a context -//////////////////////////////////////////////////////////////////////////////// - - TRI_action_result_t execute (TRI_vocbase_t* vocbase, HttpRequest* request, Mutex*, void**) { - TRI_action_result_t result; - ApplicationMR::MRContext* context = GlobalMRDealer->enterContext(); - mrb_state* mrb = context->_mrb; - - READ_LOCKER(_callbacksLock); - - map< mrb_state*, mrb_value >::iterator i = _callbacks.find(mrb); - - if (i == _callbacks.end()) { - LOG_WARNING("no callback function for Ruby action '%s'", _url.c_str()); - - result.isValid = true; - result.response = new HttpResponse(HttpResponse::NOT_FOUND, request->compatibility()); - - return result; - } - - mrb_value callback = i->second; - - HttpResponse* response = ExecuteActionVocbase(vocbase, mrb, this, callback, request); - - GlobalMRDealer->exitContext(context); - - result.isValid = true; - result.response = response; - - return result; - } - -//////////////////////////////////////////////////////////////////////////////// -/// {@inheritDoc} -//////////////////////////////////////////////////////////////////////////////// - - bool cancel (Mutex* dataLock, void** data) { - return false; - } - - private: - -//////////////////////////////////////////////////////////////////////////////// -/// @brief callback dictionary -//////////////////////////////////////////////////////////////////////////////// - - map< mrb_state*, mrb_value > _callbacks; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief lock for the callback dictionary -//////////////////////////////////////////////////////////////////////////////// - - ReadWriteLock _callbacksLock; -}; - -// ----------------------------------------------------------------------------- -// --SECTION-- private functions -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @brief float value -//////////////////////////////////////////////////////////////////////////////// - -double MR_float (mrb_state* mrb, mrb_value val) { - switch (mrb_type(val)) { - case MRB_TT_FIXNUM: - return (double) mrb_fixnum(val); - - case MRB_TT_FLOAT: - return mrb_float(val); - break; - - default: - return 0; - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief string value -//////////////////////////////////////////////////////////////////////////////// - -char const* MR_string (mrb_state* mrb, mrb_value val) { - return RSTRING_PTR(val); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief executes an action -//////////////////////////////////////////////////////////////////////////////// - -static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, - mrb_state* mrb, - TRI_action_t const* action, - mrb_value callback, - HttpRequest* request) { - MR_state_t* mrs; - mrb_sym id; - mrb_sym bodyId; - mrb_value key; - mrb_value val; - - mrs = (MR_state_t*) mrb->ud; - - // setup the request - mrb_value req = mrb_class_new_instance(mrb, 0, 0, mrs->_arangoRequest); - - // setup the response - mrb_value res = mrb_class_new_instance(mrb, 0, 0, mrs->_arangoResponse); - - // copy suffixes - vector const& suffix = request->suffix(); - mrb_value suffixArray = mrb_ary_new_capa(mrb, suffix.size()); - - uint32_t index = 0; - - for (size_t s = action->_urlParts; s < suffix.size(); ++s, ++index) { - string const& str = suffix[s]; - val = mrb_str_new(mrb, str.c_str(), str.size()); - - mrb_ary_set(mrb, suffixArray, index, val); - } - - id = mrb_intern(mrb, "@suffix"); - mrb_iv_set(mrb, req, id, suffixArray); - - // copy header fields - map const& headers = request->headers(); - map::const_iterator iter = headers.begin(); - - mrb_value headerFields = mrb_hash_new_capa(mrb, headers.size()); - - for (; iter != headers.end(); ++iter) { - string const& f = iter->first; - string const& s = iter->second; - - key = mrb_str_new(mrb, f.c_str(), f.size()); - val = mrb_str_new(mrb, s.c_str(), s.size()); - - - mrb_hash_set(mrb, headerFields, key, val); - } - - id = mrb_intern(mrb, "@headers"); - mrb_iv_set(mrb, req, id, headerFields); - - // copy request type - id = mrb_intern(mrb, "@request_type"); - bodyId = mrb_intern(mrb, "@body"); - - switch (request->requestType()) { - case HttpRequest::HTTP_REQUEST_POST: - mrb_iv_set(mrb, req, id, mrb_str_new_cstr(mrb, "POST")); - mrb_iv_set(mrb, req, bodyId, mrb_str_new(mrb, request->body(), request->bodySize())); - break; - - case HttpRequest::HTTP_REQUEST_PUT: - mrb_iv_set(mrb, req, id, mrb_str_new_cstr(mrb, "PUT")); - mrb_iv_set(mrb, req, bodyId, mrb_str_new(mrb, request->body(), request->bodySize())); - break; - - case HttpRequest::HTTP_REQUEST_DELETE: - mrb_iv_set(mrb, req, id, mrb_str_new_cstr(mrb, "DELETE")); - break; - - case HttpRequest::HTTP_REQUEST_HEAD: - mrb_iv_set(mrb, req, id, mrb_str_new_cstr(mrb, "DELETE")); - break; - - default: - mrb_iv_set(mrb, req, id, mrb_str_new_cstr(mrb, "GET")); - break; - } - - // copy request parameter - map values = request->values(); - mrb_value parametersArray = mrb_hash_new_capa(mrb, values.size()); - - for (map::iterator i = values.begin(); i != values.end(); ++i) { - string const& k = i->first; - string const& v = i->second; - - key = mrb_str_new(mrb, k.c_str(), k.size()); - val = mrb_str_new(mrb, v.c_str(), v.size()); - - mrb_hash_set(mrb, parametersArray, key, val); - } - - id = mrb_intern(mrb, "@parameters"); - mrb_iv_set(mrb, req, id, parametersArray); - - // execute the callback - mrb_value args[2]; - args[0] = req; - args[1] = res; - - id = mrb_intern(mrb, "service"); - mrb_funcall_argv(mrb, callback, id, 2, args); - - if (mrb->exc) { - TRI_LogRubyException(mrb, mrb->exc); - mrb->exc = 0; - return new HttpResponse(HttpResponse::SERVER_ERROR, request->compatibility()); - } - - // set status code - id = mrb_intern(mrb, "@status"); - val = mrb_iv_get(mrb, res, id); - HttpResponse::HttpResponseCode code = HttpResponse::OK; - - if (! mrb_nil_p(val)) { - code = (HttpResponse::HttpResponseCode) MR_float(mrb, val); - } - - // generate response - HttpResponse* response = new HttpResponse(code, request->compatibility()); - - // set content type - id = mrb_intern(mrb, "@content_type"); - val = mrb_iv_get(mrb, res, id); - - if (! mrb_nil_p(val)) { - response->setContentType(MR_string(mrb, val)); - } - - id = mrb_intern(mrb, "@body"); - val = mrb_iv_get(mrb, res, id); - - if (! mrb_nil_p(val)) { - response->body().appendText(MR_string(mrb, val)); - } - - return response; -} - -// ----------------------------------------------------------------------------- -// --SECTION-- ruby functions -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @brief defines an action -//////////////////////////////////////////////////////////////////////////////// - -static mrb_value MR_Mount (mrb_state* mrb, mrb_value self) { - char* s; - size_t l; - mrb_value cl; - struct RClass* rcl; - - mrb_get_args(mrb, "so", &s, &l, &cl); - - // extract the mount point - if (s == NULL) { - return mrb_false_value(); - } - - // extract the class template - rcl = mrb_class_ptr(cl); - - if (rcl == 0) { - return mrb_false_value(); - } - - // create an action with the given options - set contexts; - contexts.insert("api"); - contexts.insert("admin"); - - mr_action_t* action = new mr_action_t(contexts); - - // store an action with the given name - TRI_action_t* result = TRI_DefineActionVocBase(s, action); - - // and define the callback - if (result != 0) { - action = dynamic_cast(result); - - if (action != 0) { - mrb_value callback = mrb_class_new_instance(mrb, 0, 0, rcl); - - action->createCallback(mrb, callback); - return mrb_false_value(); - } - else { - LOG_ERROR("cannot create callback for MRuby action"); - return mrb_true_value(); - } - } - else { - LOG_ERROR("cannot define MRuby action"); - return mrb_false_value(); - } -} - -// ----------------------------------------------------------------------------- -// --SECTION-- module functions -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @brief init mruby utilities -//////////////////////////////////////////////////////////////////////////////// - -void TRI_InitMRActions (mrb_state* mrb, triagens::arango::ApplicationMR* applicationMR) { - MR_state_t* mrs; - struct RClass *rcl; - struct RClass *arango; - - mrs = (MR_state_t*) mrb->ud; - arango = mrb_define_module(mrb, "Arango"); - - GlobalMRDealer = applicationMR; - - // ............................................................................. - // HttpServer - // ............................................................................. - - rcl = mrb_define_class_under(mrb, arango, "HttpServer", mrb->object_class); - - mrb_define_class_method(mrb, rcl, "mount", MR_Mount, ARGS_REQ(2)); - - // ............................................................................. - // HttpRequest - // ............................................................................. - - // TODO: rcl is assigned and then re-assigned directly. Is this intentional? - rcl = mrs->_arangoRequest = mrb_define_class_under(mrb, arango, "HttpRequest", mrb->object_class); - - // ............................................................................. - // HttpResponse - // ............................................................................. - - rcl = mrs->_arangoResponse = mrb_define_class_under(mrb, arango, "HttpResponse", mrb->object_class); -} - -// Local Variables: -// mode: outline-minor -// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" -// End: diff --git a/arangod/MRServer/mr-actions.h b/arangod/MRServer/mr-actions.h deleted file mode 100644 index 3fee32dff1..0000000000 --- a/arangod/MRServer/mr-actions.h +++ /dev/null @@ -1,64 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// @brief mruby actions -/// -/// @file -/// -/// DISCLAIMER -/// -/// 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 triAGENS GmbH, Cologne, Germany -/// -/// @author Dr. Frank Celler -/// @author Copyright 2011-2014, triAGENS GmbH, Cologne, Germany -//////////////////////////////////////////////////////////////////////////////// - -#ifndef TRIAGENS_MRSERVER_MR_ACTIONS_H -#define TRIAGENS_MRSERVER_MR_ACTIONS_H 1 - -#include "Basics/Common.h" - -#include "MRuby/mr-utils.h" - -// ----------------------------------------------------------------------------- -// --SECTION-- forward declarations -// ----------------------------------------------------------------------------- - -namespace triagens { - namespace arango { - class ApplicationMR; - } -} - -// ----------------------------------------------------------------------------- -// --SECTION-- ACTIONS -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -// --SECTION-- module functions -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @brief init utilities -//////////////////////////////////////////////////////////////////////////////// - -void TRI_InitMRActions (mrb_state* mrb, triagens::arango::ApplicationMR*); - -#endif - -// Local Variables: -// mode: outline-minor -// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" -// End: diff --git a/arangod/Makefile.files b/arangod/Makefile.files index b9bb34cac4..8675c26ab9 100644 --- a/arangod/Makefile.files +++ b/arangod/Makefile.files @@ -133,17 +133,6 @@ bin_arangod_SOURCES = \ arangod/Cluster/ClusterMethods.cpp -if ENABLE_MRUBY - -bin_arangod_LDADD += \ - lib/libarango_mruby.a \ - @MRUBY_LIBS@ - -bin_arangod_SOURCES += \ - arangod/MRServer/ApplicationMR.cpp \ - arangod/MRServer/mr-actions.cpp -endif - ################################################################################ ## --SECTION-- SCANNER & PARSER ################################################################################ diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 53db506e08..8f93369c1b 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -29,14 +29,6 @@ #include -#ifdef TRI_ENABLE_MRUBY -#include "mruby.h" -#include "mruby/compile.h" -#include "mruby/data.h" -#include "mruby/proc.h" -#include "mruby/variable.h" -#endif - #include "Actions/RestActionHandler.h" #include "Actions/actions.h" #include "Admin/ApplicationAdminServer.h" @@ -83,13 +75,6 @@ #include "Cluster/RestShardHandler.h" #include "Cluster/ClusterComm.h" -#ifdef TRI_ENABLE_MRUBY -#include "MRServer/ApplicationMR.h" -#include "MRServer/mr-actions.h" -#include "MRuby/MRLineEditor.h" -#include "MRuby/MRLoader.h" -#endif - using namespace std; using namespace triagens::basics; using namespace triagens::rest; @@ -287,9 +272,6 @@ ArangoServer::ArangoServer (int argc, char** argv) _applicationAdminServer(0), _applicationCluster(0), _jobManager(0), -#ifdef TRI_ENABLE_MRUBY - _applicationMR(0), -#endif _applicationV8(0), _authenticateSystemOnly(false), _disableAuthentication(false), @@ -421,21 +403,9 @@ void ArangoServer::buildApplicationServer () { _applicationServer->addFeature(_applicationV8); // ............................................................................. - // MRuby engine + // MRuby engine (this has been removed from arangod in version 2.2) // ............................................................................. -#ifdef TRI_ENABLE_MRUBY - - _applicationMR = new ApplicationMR(_server); - - if (_applicationMR == 0) { - LOG_FATAL_AND_EXIT("out of memory"); - } - - _applicationServer->addFeature(_applicationMR); - -#else - string ignoreOpt; additional[ApplicationServer::OPTIONS_HIDDEN] @@ -445,8 +415,6 @@ void ArangoServer::buildApplicationServer () { ("ruby.startup-directory", &ignoreOpt, "path to the directory containing alternate Ruby startup scripts") ; -#endif - // ............................................................................. // and start a simple admin server // ............................................................................. @@ -782,11 +750,6 @@ int ArangoServer::startupServer () { _applicationV8->skipUpgrade(); } -#ifdef TRI_ENABLE_MRUBY - _applicationMR->setVocbase(vocbase); - _applicationMR->setConcurrency(_dispatcherThreads); -#endif - _applicationServer->prepare(); // ............................................................................. diff --git a/arangod/RestServer/ArangoServer.h b/arangod/RestServer/ArangoServer.h index 731e1e87c5..765962fcb5 100644 --- a/arangod/RestServer/ArangoServer.h +++ b/arangod/RestServer/ArangoServer.h @@ -224,14 +224,6 @@ namespace triagens { rest::AsyncJobManager* _jobManager; -//////////////////////////////////////////////////////////////////////////////// -/// @brief application MR -//////////////////////////////////////////////////////////////////////////////// - -#ifdef TRI_ENABLE_MRUBY - ApplicationMR* _applicationMR; -#endif - //////////////////////////////////////////////////////////////////////////////// /// @brief application V8 ////////////////////////////////////////////////////////////////////////////////