1
0
Fork 0
arangodb/arangod/VocBase/index-garbage-collector.c

519 lines
19 KiB
C

////////////////////////////////////////////////////////////////////////////////
/// @brief index garbage collector
///
/// @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 Anonymous
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
#include "BasicsC/win-utils.h"
#endif
#include "VocBase/index-garbage-collector.h"
#include "BasicsC/locks.h"
#include "BasicsC/logging.h"
#include "BasicsC/skip-list.h"
#include "VocBase/document-collection.h"
#include "VocBase/index.h"
#include "VocBase/transaction.h"
// @@@@@@@@ TODO: TRI_addToIOndexGC & ExciseNode
// -----------------------------------------------------------------------------
// --SECTION-- private constants
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup VocBase
/// @{
////////////////////////////////////////////////////////////////////////////////
#define MAX_INDEX_GC_CAS_RETRIES 100
////////////////////////////////////////////////////////////////////////////////
/// @brief the period between garbage collection tries in microseconds
////////////////////////////////////////////////////////////////////////////////
static int const INDEX_GC_INTERVAL = (1 * 1000 * 1000);
// .............................................................................
// The rubbish collection operates as a simple linked list. Whenever an index
// requests an item to be added to the collector, we insert a node at the end
// of this linked list. Each item may require 1,..,n passes before we can
// say that the item has been destroyed. Once all passes have been completed
// for an item, the item is excised from the linked list. (The excision
// occurs using CAS operations so that the amount of 'blocking' is minimised.
// Note that there is no ordering to the list, first come first served.
// .............................................................................
typedef struct linked_list_node_s {
TRI_index_gc_t* _indexData;
void* volatile _next; // (struct linked_list_node_s* _next) the value _next is required to be volatile
void* volatile _prev; // (struct linked_list_node_s* _prev) the value _prev is required to be volatile
volatile uint32_t _nodeFlag;
} linked_list_node_t;
typedef struct linked_list_s {
linked_list_node_t _startNode;
linked_list_node_t _endNode;
volatile uint32_t _listFlag;
} linked_list_t;
enum {
INDEX_GC_LIST_NORMAL_FLAG,
INDEX_GC_LIST_FORBIDDEN_FLAG,
INDEX_GC_NODE_NORMAL_FLAG,
INDEX_GC_NODE_DELETED_FLAG,
};
// .............................................................................
// The linked list has to be stored somewhere. Store it here.
// .............................................................................
static linked_list_t* INDEX_GC_LINKED_LIST = NULL;
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup VocBase
/// @{
////////////////////////////////////////////////////////////////////////////////
static int ExciseNode (linked_list_node_t*);
static void InitialiseStaticLinkedList (void);
static void InnerThreadLoop (bool*);
static void RemoveLinkedList (void);
static void SetForbiddenFlag (void);
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup VocBase
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief index garbage collection event loop
////////////////////////////////////////////////////////////////////////////////
void TRI_IndexGCVocBase (void* data) {
bool goToSleep;
TRI_vocbase_t* vocbase = (TRI_vocbase_t*)(data);
LOG_TRACE("attempting to start the index garbage collector ...");
// ..........................................................................
// Check that the database is in 'normal' operational mode before starting
// this thread
// ..........................................................................
if (vocbase->_state != 1) {
LOG_FATAL_AND_EXIT("Index garbage collector can not start with when server is in state %d.",vocbase->_state);
}
// ..........................................................................
// Initialise the static linked list: INDEX_GC_LINKED_LIST
// ..........................................................................
// InitialiseStaticLinkedList();
// ..........................................................................
// The main 'event loop' for this thread
// ..........................................................................
LOG_TRACE("the index garbage collector event loop has started");
while (true) {
// ........................................................................
// keep initial _state value as vocbase->_state might change during
// execution within the loop
// ........................................................................
int oldState = vocbase->_state;
/*
// ........................................................................
// The loop goes to sleep whenever we are at the end of the linked list.
// ........................................................................
goToSleep = TRI_ComparePointer(
&((INDEX_GC_LINKED_LIST->_startNode)._next),
&(INDEX_GC_LINKED_LIST->_endNode));
InnerThreadLoop (&goToSleep);
*/
goToSleep = true;
if (vocbase->_state == 1 && goToSleep) { // only sleep while server is still running
// ........................................................................
// Sleep for the specified interval
// ........................................................................
usleep(INDEX_GC_INTERVAL);
}
if (oldState == 2) { // server shutdown, terminate this thread
break;
}
}
// ..........................................................................
// Change the flag of the static linked list so that no more insertions
// can be made.
// ..........................................................................
//SetForbiddenFlag();
// ..........................................................................
// Remove all memory we assigned to any structures
// ..........................................................................
//RemoveLinkedList();
LOG_TRACE("the index garbage collector event loop has stopped");
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup VocBase
/// @{
////////////////////////////////////////////////////////////////////////////////
int TRI_AddToIndexGC(TRI_index_gc_t* indexData) {
int result = TRI_ERROR_NO_ERROR;
bool ok;
// ...........................................................................
// Check that we have something to add
// ...........................................................................
if (indexData == NULL) {
return TRI_ERROR_INTERNAL;
}
// ...........................................................................
// Check that the rubbish collector is accepting rubbish.
// Generally this means that the server has been shut down. In this case we
// will not accept anymore rubbish.
// ...........................................................................
// ...........................................................................
// The indexData structure whose memory has been allocated by the INDEX
// (and not this function) will also be removed by the INDEX which called
// this function. When indexData._lastPass = 254, then the collectGarbage
// callback will be alerted to the fact that the excision of the item from the
// rubbish collector will be imminent. When indexData._lastPass = 255, then the
// collectGarbage callback will be alerted that the excision has occured and
// that any memory allocated must be deallocated.
// ...........................................................................
/*
INDEX_GC_LIST_NORMAL_FLAG
ok = TRI_CompareAndSwapIntegerUInt32 (&(leftNN->_nbFlag), INDEX_GC_LIST_NORMAL_FLAG, INDEX_GC_LIST_NORMAL_FLAG);
*/
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup VocBase
/// @{
////////////////////////////////////////////////////////////////////////////////
int ExciseNode(linked_list_node_t* nodeToExcise) {
int result = TRI_ERROR_NO_ERROR;
return result;
}
void InitialiseStaticLinkedList(void) {
// ..........................................................................
// Assign memory to the static linked list structure
// ..........................................................................
INDEX_GC_LINKED_LIST = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(linked_list_t), true);
if (INDEX_GC_LINKED_LIST == NULL) { // out of memory?
LOG_FATAL_AND_EXIT("Index garbage collector can not start - out of memory");
}
// ..........................................................................
// 'Lock' the list so that no other process can interfere with it
// ..........................................................................
INDEX_GC_LINKED_LIST->_listFlag = INDEX_GC_LIST_FORBIDDEN_FLAG;
(INDEX_GC_LINKED_LIST->_startNode)._indexData = NULL;
(INDEX_GC_LINKED_LIST->_startNode)._next = &(INDEX_GC_LINKED_LIST->_endNode);
(INDEX_GC_LINKED_LIST->_startNode)._prev = NULL;
(INDEX_GC_LINKED_LIST->_startNode)._nodeFlag = INDEX_GC_NODE_NORMAL_FLAG;
(INDEX_GC_LINKED_LIST->_endNode)._indexData = NULL;
(INDEX_GC_LINKED_LIST->_endNode)._next = NULL;
(INDEX_GC_LINKED_LIST->_endNode)._prev = &(INDEX_GC_LINKED_LIST->_startNode);
(INDEX_GC_LINKED_LIST->_endNode)._nodeFlag = INDEX_GC_NODE_NORMAL_FLAG;
// ..........................................................................
// 'Unlock' the list so that other process can use it
// ..........................................................................
if (TRI_CompareAndSwapIntegerUInt32 (&(INDEX_GC_LINKED_LIST->_listFlag),
INDEX_GC_LIST_FORBIDDEN_FLAG,
INDEX_GC_LIST_NORMAL_FLAG) ) {
LOG_FATAL_AND_EXIT("Index garbage collector can not start - CAS failure");
}
}
void InnerThreadLoop (bool* goToSleep) {
TRI_index_gc_t* indexData;
linked_list_node_t* currentNode = &(INDEX_GC_LINKED_LIST->_startNode);
linked_list_node_t* tempNode = NULL;
uint64_t lastCompleteGlobalTransID = 0;
TRI_transaction_global_stats_t* stats = NULL;
int result;
stats = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_transaction_global_stats_t), true);
if (stats == NULL) {
LOG_TRACE("the index garbage collector inner loop failed due to lack of memory");
*goToSleep = true;
return;
}
result = TRI_GetGlobalTransactionFigures(stats);
if (result != TRI_ERROR_NO_ERROR) {
LOG_TRACE("the index garbage collector inner loop failed due transactions figures being unavailable");
*goToSleep = true;
return;
}
// todo: fill in the lastCompletedGlobalTransID from the stats returned above
currentNode = currentNode->_next;
while (! *goToSleep) {
// ........................................................................
// The currentNode->_next may be null because it is the end node,
// or it may be null because we have stepped onto a node which is being
// excised from the list.
// ........................................................................
if (currentNode == NULL) {
*goToSleep = true;
return;
}
// ........................................................................
// Get the next node and check that we can operate on it
// ........................................................................
if (!TRI_CompareIntegerUInt32 (&(currentNode->_nodeFlag),
INDEX_GC_NODE_NORMAL_FLAG) ) {
*goToSleep = true;
return;
}
// ........................................................................
// Have we reached the end of the list, if so sleep a little while
// ........................................................................
if (currentNode == &(INDEX_GC_LINKED_LIST->_endNode)) {
*goToSleep = true;
break;
}
// ........................................................................
// Operate on this node. Observe that ONLY this thread can actually
// destroy the memory associated with this node.
// ........................................................................
indexData = currentNode->_indexData;
// ........................................................................
// First lets check whether we have actually finished with this node.
// ........................................................................
if (indexData->_passes == indexData->_lastPass) {
indexData->_lastPass = 254;
result = indexData->_collectGarbage(indexData);
if (result != TRI_ERROR_NO_ERROR) {
LOG_TRACE("the index garbage collector called the callback which returend error %d", result);
}
result = ExciseNode(currentNode);
if (result != TRI_ERROR_NO_ERROR) {
LOG_TRACE("the index garbage collector function ExcisENode returned with error %d", result);
}
indexData->_lastPass = 255;
result = indexData->_collectGarbage(indexData);
if (result != TRI_ERROR_NO_ERROR) {
LOG_TRACE("the index garbage collector called the callback which returend error %d", result);
}
tempNode = currentNode->_next;
TRI_Free(TRI_UNKNOWN_MEM_ZONE, currentNode);
currentNode = tempNode;
continue;
}
// ........................................................................
// Check whether or not we can actually execute the call back for that
// particular pass.
// ........................................................................
if (lastCompleteGlobalTransID <= indexData->_transID) {
currentNode = currentNode->_next;
continue;
}
}
}
void RemoveLinkedList(void) {
linked_list_node_t* currentNode;
LOG_TRACE("the index garbage collector has commenced removing all allocated memory");
currentNode = &(INDEX_GC_LINKED_LIST->_startNode);
if ( currentNode->_next != NULL ) {
currentNode = currentNode->_next;
}
else {
currentNode = NULL;
}
while (currentNode != NULL) {
int result;
if (currentNode->_indexData == NULL) {
continue;
}
currentNode->_indexData->_lastPass = 255;
result = currentNode->_indexData->_collectGarbage(currentNode->_indexData);
if (result != TRI_ERROR_NO_ERROR) {
LOG_TRACE("the index garbage collector executed the callback and has returned error code %d",result);
}
if ( currentNode->_next != NULL ) {
linked_list_node_t* tempNode = currentNode->_next;
TRI_Free(TRI_UNKNOWN_MEM_ZONE, currentNode);
currentNode = tempNode;
}
else {
currentNode = NULL;
}
}
TRI_Free(TRI_UNKNOWN_MEM_ZONE, INDEX_GC_LINKED_LIST);
LOG_TRACE("the index garbage collector has completed removing all allocated memory");
}
void SetForbiddenFlag(void) {
int counter = 0;
LOG_TRACE("the index garbage collector is attempting to block insertions");
while (counter < MAX_INDEX_GC_CAS_RETRIES) {
if (TRI_CompareAndSwapIntegerUInt32 (&(INDEX_GC_LINKED_LIST->_listFlag),
INDEX_GC_LIST_NORMAL_FLAG,
INDEX_GC_LIST_FORBIDDEN_FLAG) ) {
counter = -1;
break;
}
usleep(1000);
}
if (counter == -1) {
LOG_TRACE("the index garbage collector has succeeded in blocking insertions");
}
else {
LOG_TRACE("the index garbage collector has failed in blocking insertions");
}
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End: