stepcode/src/clstepcore/trynext.cc
hoiji09 e9d878edae Simplified Locking in MultList
This was done by moving the mutex from EntList to MultList.
2014-10-05 21:15:24 +05:30

171 lines
6.6 KiB
C++

/*****************************************************************************
* \file trynext.cc *
* *
* Description: Contains the tryNext() functions for the EntList descen- *
* dants. Their purpose is to search for additional branches *
* of OrList's to try the next available untried solution. AND *
* and ANDOR iterate through their children looking for OR's. *
* OR tries the next branch of its children if more remain. *
* *
* Created by: David Rosenfeld *
* Date: 10/24/96 *
*****************************************************************************/
#include "complexSupport.h"
#include "sc_memmgr.h"
// Local function prototypes:
static EntList * firstCandidate( EntList * );
static EntList * nextCandidate( EntList * );
/**
* Loops backwards through the children of this, recursively searching for
* alternate solutions (i.e., OR's which have alternate paths we haven't
* tried yet). We loop through all MultList type children which may have
* an OR with additional choices. (Which children to try is determined in
* a local function. We loop backwards because this is the reverse of the
* order we set the OR's. Later choices must be redone before earlier
* (reasons discussed in notes, 10/17). This function is the tryNext()
* for AND and ANDOR; the OR version is redefined.
*/
MatchType MultList::tryNext( EntNode * ents ) {
MatchType retval;
EntList * child = getLast();
child = firstCandidate( child );
while( child != NULL ) {
if( ( retval = ( dynamic_cast< MultList * >(child) )->tryNext( ents ) ) == MATCHALL ) {
// We're done - a good solution was found.
return MATCHALL;
}
if( retval == NEWCHOICE ) {
// If a new viable choice was found below, we must now reset all
// later OR's to their first choice. (That's what acceptChoice()
// does when choice = LISTEND.) This is necessary so that we can
// exhaust every possibility with this new choice. To do this we
// first reset all our children, and then return NEWCHOICE so that
// our parent (if exists) will also know to reset all its later OR
// children.
while( ( child = nextCandidate( child ) ) != NULL ) {
if( child->acceptChoice( ents ) && ents->allMarked() ) {
return MATCHALL;
}
}
return NEWCHOICE;
}
child = firstCandidate( child->prev );
}
// If we got here, we didn't find any new OR choices:
return NOMORE;
}
/**
* Finds an EntList from child's list which may have an OR with more
* choices below it. The acceptable choices are described in commenting
* below.
*/
static EntList * firstCandidate( EntList * child ) {
EntList * ent = child->lastNot( SIMPLE );
while( ent != NULL ) {
if( ent->viableVal() >= MATCHSOME ) {
// Return any non-SIMPLE ent where viable >= MATCHSOME. We even
// want to check an OR where numChoices = 1, because it may have
// an OR descendant with more choices.
return ent;
}
ent = ent->prevNot( SIMPLE );
}
return ent;
}
/**
* Same as prev function, searches forwards from ent after child.
*/
static EntList * nextCandidate( EntList * child ) {
EntList * ent = child->nextNot( SIMPLE );
while( ent != NULL ) {
if( ent->viableVal() >= MATCHSOME ) {
return ent;
}
ent = ent->nextNot( SIMPLE );
}
return ent;
}
/**
* Tries out the next choice of this. Basic algorithm is to first recurse
* to check for other solutions in the descendants of the current choice,
* and then to try our next choice.
*/
MatchType OrList::tryNext( EntNode * ents ) {
EntList * child;
MatchType retval;
mtxP->lock(); // For choice
if( choice == LISTEND ) {
// if we've already exhausted all the choices in this OR,
mtxP->unlock();
return NOMORE;
}
// First try other choices of descendants of current choice:
child = getChild( choice );
ents->sharedMtxP->lock(); // For EntNode Consistency
if( child->multiple() ) {
// I.e., if there are (or may be) more choices within the current
// choice, try those first. We must be sure to exhaust all choices in
// our descendants before moving on.
retval = ( ( MultList * )child )->tryNext( ents );
if( retval == MATCHALL ) {
mtxP->unlock();
ents->sharedMtxP->unlock();
return MATCHALL;
}
if( retval == NEWCHOICE ) {
// I.e., we found a next choice to go to, return so that the
// EntLists on the higher levels (if there are) can retry all the
// later choices with the new choice we just found. Otherwise,
// we'll continue below looking into our next choice.
mtxP->unlock();
ents->sharedMtxP->unlock();
return NEWCHOICE;
}
}
// No other choices among our descendants. Look for new choice at our
// level:
child->unmarkAll( ents );
if( choiceCount == 1 ) {
// Quick way to determine that there won't be any more choices here.
// (Also, it's nec. to unmark now, as we did above before returning and
// before the calling tryNext() tries earlier OR's - see notes, 11/12.)
choice = LISTEND;
mtxP->unlock();
ents->sharedMtxP->unlock();
return NOMORE;
}
mtxP->unlock();
// Otherwise, try our next:
if( acceptNextChoice( ents ) ) {
if( ents->allMarked() ) {
retval = MATCHALL;
} else {
retval = NEWCHOICE;
}
} else {
// Must have been no next choice (or none which mark anything new).
// acceptNextChoice() has set choice to LISTEND. We leave this OR
// unset and return NOMORE. If our calling function finds another
// choice elsewhere, it will reloop forwards and set all later OR's to
// their first choices, so that we'll be able to test every possibility
// with the new choice. At that time, this OrList will be reset to its
// first choice too.
retval = NOMORE;
}
ents->sharedMtxP->unlock();
return retval;
}