//////////////////////////////////////////////////////////////////////////////// /// @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" #ifdef TRI_SKIPLIST_EX // ----------------------------------------------------------------------------- // --SECTION-- private constants // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief the number of times the Garbage Collector will retry when a CAS statement fails //////////////////////////////////////////////////////////////////////////////// static int const MAX_INDEX_GC_CAS_RETRIES = 100; //////////////////////////////////////////////////////////////////////////////// /// @brief the period between garbage collection tries in microseconds //////////////////////////////////////////////////////////////////////////////// static int const INDEX_GC_INTERVAL = (1 * 1000 * 1000); //////////////////////////////////////////////////////////////////////////////// /// @brief the amount of time to sleep when a CAS statement fails (in microseconds) //////////////////////////////////////////////////////////////////////////////// static unsigned int CAS_FAILURE_SLEEP_TIME = 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; uint64_t _size; } linked_list_t; enum { INDEX_GC_LIST_NORMAL_FLAG, INDEX_GC_LIST_FORBIDDEN_FLAG, INDEX_GC_NODE_NORMAL_FLAG, INDEX_GC_NODE_BRICKED_FLAG, INDEX_GC_NODE_DELETED_FLAG, INDEX_GC_NODE_INSERTED_FLAG }; // ............................................................................. // The linked list has to be stored somewhere. Store it here. // ............................................................................. static linked_list_t* INDEX_GC_LINKED_LIST = NULL; static void* INDEX_GC_DATA = NULL; //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// static int ExciseNode (linked_list_node_t*); static int ExciseNodeBrick (linked_list_node_t* nodeToExcise, linked_list_node_t* prevNode, linked_list_node_t* nextNode); static int ExciseNodeBrickUndo (linked_list_node_t* nodeToExcise, linked_list_node_t* prevNode, linked_list_node_t* nextNode, int bricked); static int ExciseNodeSwapPointers (linked_list_node_t* nodeToExcise, linked_list_node_t* prevNode, linked_list_node_t* nextNode); static int ExciseNodeSwapPointersUndo(linked_list_node_t* nodeToExcise, linked_list_node_t* prevNode, linked_list_node_t* nextNode, int swaped); static void InitialiseStaticLinkedList (void); static void InnerThreadLoop (bool*); static int InsertNode (linked_list_node_t*); static int InsertNodeBrick (linked_list_node_t* prevNode, linked_list_node_t* nextNode); static int InsertNodeBrickUndo (linked_list_node_t* prevNode, linked_list_node_t* nextNode, int bricked); static int InsertNodeSwapPointers (linked_list_node_t* nodeToInsert, linked_list_node_t* prevNode, linked_list_node_t* nextNode); static int InsertNodeSwapPointersUndo (linked_list_node_t* nodeToInsert, linked_list_node_t* prevNode, linked_list_node_t* nextNode, int swaped); static void RemoveLinkedList (void); static void SetForbiddenFlag (void); static void UnsetForbiddenFlag (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(); INDEX_GC_DATA = data; // .......................................................................... // 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; //printf("oreste:%s:%d:gotosleep=%d:state=%d\n",__FILE__,__LINE__,goToSleep,vocbase->_state); 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(); // .......................................................................... // We need to wait a little while in case there are any other threads which // are busy adding things to the collector // .......................................................................... usleep(INDEX_GC_INTERVAL); // .......................................................................... // Remove all memory we assigned to any structures // .......................................................................... RemoveLinkedList(); LOG_TRACE("the index garbage collector event loop has stopped"); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Adds a node to the linked list, so that eventually the GC will remove an // item from the given index. //////////////////////////////////////////////////////////////////////////////// int TRI_AddToIndexGC(TRI_index_gc_t* indexData) { int result = TRI_ERROR_NO_ERROR; linked_list_node_t* insertNode; // node to be inserted into our linked list TRI_vocbase_t* vocbase = (TRI_vocbase_t*)(INDEX_GC_DATA); // ........................................................................... // Check if the gc has actually started // ........................................................................... if (vocbase == NULL) { return TRI_ERROR_INTERNAL; } // ........................................................................... // Check if the server has shut down? // ........................................................................... if (vocbase->_state == -1) { return TRI_WARNING_ARANGO_INDEX_GARBAGE_COLLECTOR_SHUTDOWN; } // ........................................................................... // Check that we have something to add // ........................................................................... // ........................................................................... // Check that we have something to add // ........................................................................... if (indexData == NULL) { return TRI_ERROR_INTERNAL; } insertNode = (linked_list_node_t*)(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(linked_list_node_t), true)); if (insertNode == NULL) { return TRI_ERROR_OUT_OF_MEMORY; } insertNode->_indexData = (TRI_index_gc_t*)(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_index_gc_t), true)); if (insertNode->_indexData == NULL) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, insertNode); return TRI_ERROR_OUT_OF_MEMORY; } insertNode->_indexData->_index = indexData->_index; insertNode->_indexData->_passes = indexData->_passes; insertNode->_indexData->_lastPass = 0; insertNode->_indexData->_data = indexData->_data; insertNode->_indexData->_collectGarbage = indexData->_collectGarbage; // TODO: get the current transaction id //insertNode->_indexData->_transID = vocbase->_transactionStuff->_GetGlobalTransactionFigures(0); ++(insertNode->_indexData->_transID); // ........................................................................... // the assignment of the _next and _prev pointers must be done in a CAS loop // within the IndexNode(...) function. // ........................................................................... insertNode->_next = NULL; insertNode->_prev = NULL; insertNode->_nodeFlag = INDEX_GC_NODE_NORMAL_FLAG; result = InsertNode(insertNode); if (result != TRI_ERROR_NO_ERROR) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, insertNode->_indexData); TRI_Free(TRI_UNKNOWN_MEM_ZONE, insertNode); } return result; } //////////////////////////////////////////////////////////////////////////////// // For the given index, all nodes which match the index will be excised from // the linked list. //////////////////////////////////////////////////////////////////////////////// int TRI_ExpungeIndexGC (TRI_index_gc_t* indexData) { int result = TRI_ERROR_NO_ERROR; linked_list_node_t* currentNode; bool finished = true; int casCounter = 0; LOG_TRACE("the index garbage collector has commenced expunging all nodes for a given index"); CAS_LOOP: { result = TRI_ERROR_NO_ERROR; currentNode = &(INDEX_GC_LINKED_LIST->_startNode); if (casCounter > MAX_INDEX_GC_CAS_RETRIES) { LOG_ERROR("max cas loop exceeded"); return TRI_ERROR_INTERNAL; } ++casCounter; while (currentNode != NULL) { linked_list_node_t* tempNode = currentNode->_next; if (currentNode->_indexData == NULL) { currentNode = tempNode; continue; } if (indexData->_index != currentNode->_indexData->_index) { currentNode = tempNode; continue; } // ....................................................................... // Just before we remove the data and associated data, go to the index // and indicate that we are about to remove the node from the linked list // ....................................................................... 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); } // ....................................................................... // Actually remove the node from the linked list here // ....................................................................... result = ExciseNode(currentNode); if (result != TRI_ERROR_NO_ERROR) { LOG_TRACE("the index garbage collector function ExcisENode returned with error %d", result); finished = false; currentNode = tempNode; continue; } // ....................................................................... // Inform the index that the node has been removed from the linked list // ....................................................................... 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); } TRI_Free(TRI_UNKNOWN_MEM_ZONE, currentNode); currentNode = tempNode; } // end of while loop if (!finished) { goto CAS_LOOP; } } // end of CAS_LOOP LOG_TRACE("the index garbage collector has completed expunging nodes for a given index"); return result; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Creates and initialises the linked list used by the garbage collector //////////////////////////////////////////////////////////////////////////////// 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->_size = 0; (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; // this variable is not used. suppres compiler warning // uint64_t lastCompleteGlobalTransID = 0; TRI_transaction_global_stats_t* stats = NULL; int result; // this variable is not used. suppres compiler warning // TRI_vocbase_t* vocbase = (TRI_vocbase_t*)(INDEX_GC_DATA); 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) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, stats); 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) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, stats); *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) ) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, stats); *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; // ........................................................................ // Check whether or not we can actually execute the call back for that // particular pass. // ........................................................................ /* TODO: this needs to be fixed with the transaction handling stuff if (stats->oldestGlobalTransID <= indexData->_transID) { currentNode = currentNode->_next; continue; } */ // ........................................................................ // First lets check whether we have actually finished with this node. // ........................................................................ if (indexData->_lastPass < indexData->_passes) { ++(indexData->_lastPass); result = indexData->_collectGarbage(indexData); if (result != TRI_ERROR_NO_ERROR) { LOG_TRACE("the index garbage collector called the callback which returend error %d", result); if (result == TRI_WARNING_ARANGO_INDEX_SKIPLIST_REMOVE_CAS_FAILURE) { // no harm done we simply try again later --(indexData->_lastPass); } } currentNode = currentNode->_next; } else if (indexData->_passes == indexData->_lastPass) { // ....................................................................... // We have finished essentially finished with the node and are about it // to remove the node from the linked list here. // ....................................................................... // ....................................................................... // Just before we remove the data and associated data, go to the index // and indicate that we are about to remove the node from the linked list // ....................................................................... 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); } // ....................................................................... // Actually remove the node from the linked list here // ....................................................................... result = ExciseNode(currentNode); if (result != TRI_ERROR_NO_ERROR) { LOG_TRACE("the index garbage collector function ExcisENode returned with error %d", result); } // ....................................................................... // Inform the index that the node has been removed from the linked list // ....................................................................... 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; } } // end of while loop TRI_Free(TRI_UNKNOWN_MEM_ZONE, stats); } 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); while (currentNode != NULL) { int result = TRI_ERROR_NO_ERROR; linked_list_node_t* tempNode = currentNode->_next; if (currentNode->_indexData != NULL) { 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); } TRI_Free(TRI_UNKNOWN_MEM_ZONE, currentNode); } currentNode = tempNode; } 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(CAS_FAILURE_SLEEP_TIME); } 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"); } } void UnsetForbiddenFlag(void) { int counter = 0; //LOG_TRACE("the index garbage collector is attempting to unblock insertions"); while (counter < MAX_INDEX_GC_CAS_RETRIES) { if (TRI_CompareAndSwapIntegerUInt32 (&(INDEX_GC_LINKED_LIST->_listFlag), INDEX_GC_LIST_FORBIDDEN_FLAG, INDEX_GC_LIST_NORMAL_FLAG) ) { counter = -1; break; } usleep(CAS_FAILURE_SLEEP_TIME); } if (counter == -1) { //LOG_TRACE("the index garbage collector has succeeded in unblocking insertions"); } else { LOG_TRACE("the index garbage collector has failed in unblocking insertions\n"); } } //////////////////////////////////////////////////////////////////////////////////// // Implementation of static functions for insertion of a node //////////////////////////////////////////////////////////////////////////////////// static int InsertNode(linked_list_node_t* insertNode) { int casCounter = 0; int bricked = 0; int swaped = 0; int result; linked_list_node_t* nextNode; linked_list_node_t* prevNode; CAS_LOOP: { // .......................................................................... // We can not assign these pointers outside this loop, since these may change // any time with threads busy inserting entries into the list. // .......................................................................... insertNode->_next = &(INDEX_GC_LINKED_LIST->_endNode); nextNode = (linked_list_node_t*)(insertNode->_next); insertNode->_prev = (linked_list_node_t*)(nextNode->_prev); prevNode = (linked_list_node_t*)(insertNode->_prev); if (casCounter > 1) { usleep(CAS_FAILURE_SLEEP_TIME); } if (casCounter > MAX_INDEX_GC_CAS_RETRIES) { LOG_ERROR("max cas loop exceeded"); return TRI_ERROR_INTERNAL; } bricked = InsertNodeBrick(prevNode, nextNode); if (bricked != 2) { int tempResult = InsertNodeBrickUndo(prevNode, nextNode, bricked); if (tempResult != TRI_ERROR_NO_ERROR) { return TRI_ERROR_INTERNAL; } ++casCounter; goto CAS_LOOP; } swaped = InsertNodeSwapPointers(insertNode, prevNode, nextNode); if (swaped != 2) { int tempResult1 = InsertNodeBrickUndo(prevNode, nextNode, bricked); int tempResult2 = InsertNodeSwapPointersUndo(insertNode, prevNode, nextNode, swaped); if ((tempResult1 != TRI_ERROR_NO_ERROR) || (tempResult2 != TRI_ERROR_NO_ERROR)) { return TRI_ERROR_INTERNAL; } ++casCounter; goto CAS_LOOP; } result = InsertNodeBrickUndo(prevNode, nextNode, bricked); if (result != TRI_ERROR_NO_ERROR) { return TRI_ERROR_INTERNAL; } ++INDEX_GC_LINKED_LIST->_size; } // end of CAS_LOOP return TRI_ERROR_NO_ERROR; } static int InsertNodeBrick(linked_list_node_t* prevNode, linked_list_node_t* nextNode) { bool ok; ok = TRI_CompareAndSwapIntegerUInt32 (&(prevNode->_nodeFlag), INDEX_GC_NODE_NORMAL_FLAG, INDEX_GC_NODE_BRICKED_FLAG); if (!ok) { return 0; } ok = TRI_CompareAndSwapIntegerUInt32 (&(nextNode->_nodeFlag), INDEX_GC_NODE_NORMAL_FLAG, INDEX_GC_NODE_BRICKED_FLAG); if (!ok) { return 1; } return 2; } static int InsertNodeBrickUndo(linked_list_node_t* prevNode, linked_list_node_t* nextNode, int bricked) { bool ok; if (bricked > 0) { ok = TRI_CompareAndSwapIntegerUInt32 (&(prevNode->_nodeFlag), INDEX_GC_NODE_BRICKED_FLAG, INDEX_GC_NODE_NORMAL_FLAG); if (bricked > 1) { ok = (TRI_CompareAndSwapIntegerUInt32 (&(nextNode->_nodeFlag), INDEX_GC_NODE_BRICKED_FLAG, INDEX_GC_NODE_NORMAL_FLAG)) && (ok); } if (!ok) { LOG_ERROR("InsertNodeBrickUndo failed here"); return TRI_ERROR_INTERNAL; } } return TRI_ERROR_NO_ERROR; } static int InsertNodeSwapPointers(linked_list_node_t* nodeToInsert, linked_list_node_t* prevNode, linked_list_node_t* nextNode) { bool ok; ok = TRI_CompareAndSwapPointer(&(prevNode->_next), nextNode, nodeToInsert); if (!ok) { return 0; } ok = TRI_CompareAndSwapPointer(&(nextNode->_prev), prevNode, nodeToInsert); if (!ok) { return 1; } return 2; } static int InsertNodeSwapPointersUndo(linked_list_node_t* nodeToInsert, linked_list_node_t* prevNode, linked_list_node_t* nextNode, int swaped) { bool ok; if (swaped > 0) { ok = TRI_CompareAndSwapPointer(&(prevNode->_next), nodeToInsert, nextNode); if (swaped > 1) { ok = ok && TRI_CompareAndSwapPointer(&(nextNode->_prev), nodeToInsert, prevNode); } if (!ok) { LOG_ERROR("InsertNodeSwapPointersUndo failed here"); return TRI_ERROR_INTERNAL; } } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////////// // Implementation of static functions for removal of a node //////////////////////////////////////////////////////////////////////////////////// static int ExciseNode(linked_list_node_t* nodeToExcise) { int result = TRI_ERROR_NO_ERROR; int casCounter = 0; int bricked = 0; int swaped = 0; linked_list_node_t* nextNode; linked_list_node_t* prevNode; SetForbiddenFlag(); CAS_LOOP: { result = TRI_ERROR_NO_ERROR; nextNode = nodeToExcise->_next; prevNode = nodeToExcise->_prev; if (casCounter > 1) { usleep(CAS_FAILURE_SLEEP_TIME); } if (casCounter > MAX_INDEX_GC_CAS_RETRIES) { LOG_ERROR("max cas loop exceeded"); return TRI_ERROR_INTERNAL; } bricked = ExciseNodeBrick(nodeToExcise, prevNode, nextNode); if (bricked != 3) { result = ExciseNodeBrickUndo(nodeToExcise, prevNode, nextNode, bricked); if (result != TRI_ERROR_NO_ERROR) { return result; } ++casCounter; goto CAS_LOOP; } swaped = ExciseNodeSwapPointers(nodeToExcise, prevNode, nextNode); if (swaped != 2) { ExciseNodeBrickUndo(nodeToExcise, prevNode, nextNode, bricked); ExciseNodeSwapPointersUndo(nodeToExcise, prevNode, nextNode, swaped); ++casCounter; goto CAS_LOOP; } --INDEX_GC_LINKED_LIST->_size; ExciseNodeBrickUndo(nodeToExcise, prevNode, nextNode, bricked); } // end of CAS_LOOP UnsetForbiddenFlag(); return result; } static int ExciseNodeBrick(linked_list_node_t* nodeToExcise, linked_list_node_t* prevNode, linked_list_node_t* nextNode) { bool ok; ok = TRI_CompareAndSwapIntegerUInt32 (&(nodeToExcise->_nodeFlag), INDEX_GC_NODE_NORMAL_FLAG, INDEX_GC_NODE_BRICKED_FLAG); if (!ok) { return 0; } ok = TRI_CompareAndSwapIntegerUInt32 (&(prevNode->_nodeFlag), INDEX_GC_NODE_NORMAL_FLAG, INDEX_GC_NODE_BRICKED_FLAG); if (!ok) { return 1; } ok = TRI_CompareAndSwapIntegerUInt32 (&(nextNode->_nodeFlag), INDEX_GC_NODE_NORMAL_FLAG, INDEX_GC_NODE_BRICKED_FLAG); if (!ok) { return 2; } return 3; } static int ExciseNodeBrickUndo(linked_list_node_t* nodeToExcise, linked_list_node_t* prevNode, linked_list_node_t* nextNode, int bricked) { bool ok; if (bricked > 0) { ok = TRI_CompareAndSwapIntegerUInt32 (&(nodeToExcise->_nodeFlag), INDEX_GC_NODE_BRICKED_FLAG, INDEX_GC_NODE_NORMAL_FLAG); if (bricked > 1) { ok = ok && TRI_CompareAndSwapIntegerUInt32 (&(prevNode->_nodeFlag), INDEX_GC_NODE_BRICKED_FLAG, INDEX_GC_NODE_NORMAL_FLAG); if (bricked > 2) { ok = TRI_CompareAndSwapIntegerUInt32 (&(nextNode->_nodeFlag), INDEX_GC_NODE_BRICKED_FLAG, INDEX_GC_NODE_NORMAL_FLAG); } } if (!ok) { LOG_ERROR("ExciseNodeBrickUndo failed here"); return TRI_ERROR_INTERNAL; } } return TRI_ERROR_NO_ERROR; } static int ExciseNodeSwapPointers(linked_list_node_t* nodeToExcise, linked_list_node_t* prevNode, linked_list_node_t* nextNode) { bool ok; ok = TRI_CompareAndSwapPointer(&(prevNode->_next), nodeToExcise, nextNode); if (!ok) { return 0; } ok = TRI_CompareAndSwapPointer(&(nextNode->_prev), nodeToExcise, prevNode); if (!ok) { return 1; } return 2; } static int ExciseNodeSwapPointersUndo(linked_list_node_t* nodeToExcise, linked_list_node_t* prevNode, linked_list_node_t* nextNode, int swaped) { bool ok; if (swaped > 0) { ok = TRI_CompareAndSwapPointer(&(prevNode->_next), nextNode, nodeToExcise); if (swaped > 1) { ok = ok && TRI_CompareAndSwapPointer(&(nextNode->_prev), prevNode, nodeToExcise); } if (!ok) { LOG_ERROR("ExciseNodeSwapPointersUndo failed here"); return TRI_ERROR_INTERNAL; } } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// #endif // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: