bool MoveResolver::resolve() { resetState(); orderedMoves_.clear(); InlineList<PendingMove> stack; // This is a depth-first-search without recursion, which tries to find // cycles in a list of moves. // // Algorithm. // // S = Traversal stack. // P = Pending move list. // O = Ordered list of moves. // // As long as there are pending moves in P: // Let |root| be any pending move removed from P // Add |root| to the traversal stack. // As long as S is not empty: // Let |L| be the most recent move added to S. // // Find any pending move M whose source is L's destination, thus // preventing L's move until M has completed. // // If a move M was found, // Remove M from the pending list. // If M's destination is |root|, // Annotate M and |root| as cycles. // Add M to S. // do not Add M to O, since M may have other conflictors in P that have not yet been processed. // Otherwise, // Add M to S. // Otherwise, // Remove L from S. // Add L to O. // while (!pending_.empty()) { PendingMove* pm = pending_.popBack(); // Add this pending move to the cycle detection stack. stack.pushBack(pm); while (!stack.empty()) { PendingMove* blocking = findBlockingMove(stack.peekBack()); if (blocking) { PendingMoveIterator stackiter = stack.begin(); PendingMove* cycled = findCycledMove(&stackiter, stack.end(), blocking); if (cycled) { // Find the cycle's start. // We annotate cycles at each move in the cycle, and // assert that we do not find two cycles in one move chain // traversal (which would indicate two moves to the same // destination). // Since there can be more than one cycle, find them all. do { cycled->setCycleEnd(curCycles_); cycled = findCycledMove(&stackiter, stack.end(), blocking); } while (cycled); blocking->setCycleBegin(pm->type(), curCycles_); curCycles_++; pending_.remove(blocking); stack.pushBack(blocking); } else { // This is a new link in the move chain, so keep // searching for a cycle. pending_.remove(blocking); stack.pushBack(blocking); } } else { // Otherwise, pop the last move on the search stack because it's // complete and not participating in a cycle. The resulting // move can safely be added to the ordered move list. PendingMove* done = stack.popBack(); if (!addOrderedMove(*done)) return false; movePool_.free(done); } } // If the current queue is empty, it is certain that there are // all previous cycles cannot conflict with future cycles, // so re-set the counter of pending cycles, while keeping a high-water mark. if (numCycles_ < curCycles_) numCycles_ = curCycles_; curCycles_ = 0; } return true; }
bool testBasic() { IntThing thing1(1); IntThing thing2(2); IntThing thing3(3); IntThing thing4(4); IntThing thing5(5); // Do these test twice. InlineList does not take ownership of pointers, // so we should be guaranteed we can keep moving them in between lists. for (size_t i = 0; i <= 1; i++) { InlineList<IntThing> list; InlineList<IntThing>::iterator iter = list.begin(); if (!check(iter == list.end(), "list should be empty")) return false; list.append(&thing1); list.append(&thing2); list.append(&thing3); list.append(&thing4); list.append(&thing5); iter = list.begin(); for (int n = 1; n <= 5; n++) { if (!check(iter->value() == n, "value %d should be %d", iter->value(), n)) return false; iter++; } if (!check(iter == list.end(), "iterator should have ended")) return false; list.remove(&thing1); iter = list.begin(); if (!check(iter->value() == 2, "list should start at 2 after removal")) return false; list.remove(&thing5); iter = list.begin(); iter++; iter++; if (!check(iter->value() == 4, "list should end at 4 after removal")) return false; iter++; if (!check(iter == list.end(), "iterator should have ended")) return false; list.remove(&thing3); iter = list.begin(); if (!check(iter->value() == 2, "first value should be 2 after removal")) return false; iter++; if (!check(iter->value() == 4, "second value should be 4 after removal")) return false; iter++; if (!check(iter == list.end(), "iterator should have ended")) return false; iter = list.begin(); while (iter != list.end()) iter = list.erase(iter); if (!check(list.begin() == list.end(), "list should be empty")) return false; } return true; }