bool DecisionPointIterator::nextContainer(int contID, DynamicContext *context) { DbXmlConfiguration *conf = GET_CONFIGURATION(context); { // Find the correct QueryPlan for the container // // Hold the compile time mutex whilst searching the linked list. // This stops the list from being modified by just-in-time // optimisation while we traverse it. // // (The mutex in the runtime configuration is the same as the // one from the compile time configuration.) MutexLock lock(conf->getMutex()); while(list_ != 0 && list_->container->getContainerID() < contID) { list_ = list_->next; } } if(list_ == 0 || list_->container->getContainerID() != contID) { list_ = const_cast<DecisionPointQP*>(dp_)->justInTimeOptimize(contID, context); } AutoDecisionPointIteratorReset reset(conf, this); result_ = list_->qp->createNodeIterator(context); return true; }
DecisionPointQP::ListItem *DecisionPointQP::justInTimeOptimize(int contID, DynamicContext *context) { // **** IMPORTANT - This algorithm is very carefully arranged to avoid // **** deadlocks and race-conditions. Don't rearrange things unless you // **** know what you are doing! // Get the runtime configuration DbXmlConfiguration *conf = GET_CONFIGURATION(context); // Lookup the container ScopedContainer scont((Manager&)conf->getManager(), contID, /*mustExist*/true); // Just-in-time optimise the QueryPlan, using a temporary memory manager for thread safety XPath2MemoryManagerImpl tmpMM; QueryPlan *qp; { AutoMemoryManagerReset resetMM(context, &tmpMM); qp = arg_->copy(&tmpMM); try { AutoDecisionPointReset reset(conf, this); justInTimeOptimize(qp, scont.get(), context); } catch(XmlException &e) { if(e.getQueryLine() == 0) e.setLocationInfo(this); throw; } } // Hold the compile time mutex whilst altering the query plan. // This protects the compile time XPath2MemoryManager as well // as the query plan itself. // // (The mutex in the runtime configuration is the same as the // one from the compile time configuration.) MutexLock lock(conf->getMutex()); // Now we hold the lock, re-search qpList_ for the container, // in case someone beat us to creating it. DecisionPointQP::ListItem **li = &qpList_; while(*li != 0 && (*li)->container->getContainerID() < contID) { li = &(*li)->next; } if(*li == 0 || (*li)->container->getContainerID() != contID) { // Add the container to the compile time ReferenceMinder, in case it has been auto-opened if (contID > 0) compileTimeMinder_->addContainer(scont.getContainer()); // Create a new ListItem and copy the optimised QueryPlan using the // compile time memory manager - so that they can both become a // permanent part of the query's AST XPath2MemoryManager *compile_mm = compileTimeContext_->getMemoryManager(); DecisionPointQP::ListItem *newListItem = new (compile_mm) DecisionPointQP::ListItem(scont.get(), *li); newListItem->qp = qp->copy(compile_mm); newListItem->qp->staticTypingLite(compileTimeContext_); // Only add the new ListItem to the linked list once it is fully optimised // and ready to execute - otherwise another thread could come along and try // to use it. *li = newListItem; } else { // All our work was in vain! Someone beat us to creating a QueryPlan // for this container. Oh well, we'll just use the existing one then... } qp->release(); return *li; }