bool ZTSoup::Sync() { ZMutexLocker locker_CallSync(fMutex_CallSync); ZMutexLocker locker_Structure(fMutex_Structure); ZMutexLocker locker_TSWatcher(fMutex_TSWatcher); ZRef<ZTSWatcher> theTSWatcher = fTSWatcher; if (!theTSWatcher) return false; locker_TSWatcher.Release(); if (ZLOG(s, eDebug + 1, "ZTSoup")) s << "Sync start"; fCalled_SyncNeeded = false; vector<uint64> removedIDs, addedIDs; // We put written tuples into a map, so that they can // be extracted in sorted order, required by ZTSWatcher::Sync. map<uint64, ZTuple> writtenTupleMap; if (sNotEmpty(fPCroutons_Sync)) { removedIDs.reserve(fPCroutons_Sync.Size()); addedIDs.reserve(fPCroutons_Sync.Size()); for (DListEraser<PCrouton, DLink_PCrouton_Sync> iter = fPCroutons_Sync; iter; iter.Advance()) { PCrouton* thePCrouton = iter.Current(); if (thePCrouton->fHasValue_ForWatcher) { thePCrouton->fHasValue_ForWatcher = false; writtenTupleMap.insert( pair<uint64, ZTuple>(thePCrouton->fID, thePCrouton->fValue_ForWatcher)); if (!thePCrouton->fWatcherKnown) { thePCrouton->fWatcherKnown = true; addedIDs.push_back(thePCrouton->fID); } // Put it on fPCroutons_Syncing, which will be transcribed // to fCroutons_Pending after we've called our watcher. sQInsertBack(fPCroutons_Syncing, thePCrouton); } else if (thePCrouton->fTCroutons_Using) { // It's in use. if (!thePCrouton->fWatcherKnown) { // But not known to the watcher. thePCrouton->fWatcherKnown = true; addedIDs.push_back(thePCrouton->fID); } } else if (thePCrouton->fWatcherKnown) { // It's not in use, and is known to the watcher. thePCrouton->fWatcherKnown = false; removedIDs.push_back(thePCrouton->fID); thePCrouton->fHasValue = false; sQInsertBack(fPCroutons_Update, thePCrouton); } else { // It's not in use, and not known to the watcher, so it should have // been pulled from the sync list by update and deleted. #if 0 //## if (ZLOG(s, eNotice, "ZTSoup")) { s << "Got a PCrouton on the sync list that maybe shouldn't be there: " << " On update list? " << (static_cast<DLink_PCrouton_Update*>(thePCrouton)->fNext ? "yes " : "no ") << sStringf("%llX: ", thePCrouton->fID) << thePCrouton->fValue; } #endif } } } vector<int64> removedQueries; vector<ZTSWatcher::AddedQueryCombo> addedQueries; for (DListEraser<PSieve, DLink_PSieve_Sync> iter = fPSieves_Sync; iter; iter.Advance()) { PSieve* thePSieve = iter.Current(); if (thePSieve->fTSieves_Using) { // It's in use. if (!thePSieve->fWatcherKnown) { // But not known to the watcher. thePSieve->fWatcherKnown = true; ZTSWatcher::AddedQueryCombo theCombo; theCombo.fRefcon = reinterpret_cast<int64>(thePSieve); theCombo.fTBQuery = thePSieve->fTBQuery; theCombo.fPrefetch = thePSieve->fPrefetch; addedQueries.push_back(theCombo); } } else if (thePSieve->fWatcherKnown) { // It's not in use, and is known to the watcher. thePSieve->fWatcherKnown = false; thePSieve->fHasResults = false; removedQueries.push_back(reinterpret_cast<int64>(thePSieve)); sQInsertBack(fPSieves_Update, thePSieve); } else { // Shouldn't still be on the sync list if it's not in use and not known to the watcher #if 0 //## if (ZLOG(s, eNotice, "ZTSoup")) { s << "Got a PSieve on the sync list that maybe shouldn't be there: " << " On update list? " << (static_cast<DLink_PSieve_Update*>(thePSieve)->fNext ? "yes " : "no ") << sStringf("ID: %llX, value: ", reinterpret_cast<int64>(thePSieve)) << thePSieve->fTBQuery.AsTuple(); } #endif } } if (ZLOG(s, eDebug + 1, "ZTSoup")) s << "Sync releasing lock"; locker_Structure.Release(); vector<uint64> writtenTupleIDs; vector<ZTuple> writtenTuples; writtenTupleIDs.reserve(writtenTupleMap.size()); writtenTuples.reserve(writtenTupleMap.size()); for (map<uint64, ZTuple>::const_iterator i = writtenTupleMap.begin(); i != writtenTupleMap.end(); ++i) { writtenTupleIDs.push_back(i->first); writtenTuples.push_back(i->second); } vector<uint64> watcherAddedIDs; vector<uint64> changedTupleIDs; vector<ZTuple> changedTuples; map<int64, vector<uint64> > changedQueries; if (!theTSWatcher->Sync( sFirstOrNil(removedIDs), removedIDs.size(), sFirstOrNil(addedIDs), addedIDs.size(), sFirstOrNil(removedQueries), removedQueries.size(), sFirstOrNil(addedQueries), addedQueries.size(), watcherAddedIDs, changedTupleIDs, changedTuples, sFirstOrNil(writtenTupleIDs), sFirstOrNil(writtenTuples), writtenTupleIDs.size(), changedQueries)) { // Possibly should move fPCroutons_Syncing stuff onto fPCroutons_Pending. // Might allow destructor to execute without needing explicit Purge. locker_TSWatcher.Acquire(); fTSWatcher.Clear(); return false; } locker_Structure.Acquire(); if (ZLOG(s, eDebug + 1, "ZTSoup")) s << "Sync acquired lock"; for (vector<uint64>::iterator iterWatcherAddedIDs = watcherAddedIDs.begin(), theEnd = watcherAddedIDs.end(); iterWatcherAddedIDs != theEnd; ++iterWatcherAddedIDs) { PCrouton* thePCrouton = this->pGetPCrouton(*iterWatcherAddedIDs); ZAssertStop(ZTSoup::kDebug, !thePCrouton->fWatcherKnown); thePCrouton->fWatcherKnown = true; // Stick it on the pending list, so it will last past // the end of the next update. sQInsertBack(fPCroutons_Pending, thePCrouton); } for (vector<uint64>::iterator iterChangedTuples = changedTupleIDs.begin(), theEnd = changedTupleIDs.end(); iterChangedTuples != theEnd; ++iterChangedTuples) { map<uint64, PCrouton>::iterator iterPCrouton = fID_To_PCrouton.find(*iterChangedTuples); // We never toss a PCrouton that has not positively been unregistered. ZAssertStop(ZTSoup::kDebug, fID_To_PCrouton.end() != iterPCrouton); PCrouton* thePCrouton = &(*iterPCrouton).second; if (!thePCrouton->fWrittenLocally) { thePCrouton->fValue_FromWatcher = *(changedTuples.begin() + (iterChangedTuples - changedTupleIDs.begin())); sQInsertBack(fPCroutons_Changed, thePCrouton); } } for (map<int64, vector<uint64> >::iterator i = changedQueries.begin(), theEnd = changedQueries.end(); i != theEnd; ++i) { PSieve* thePSieve = reinterpret_cast<PSieve*>((*i).first); thePSieve->fResults_Remote.swap((*i).second); sQInsertBack(fPSieves_Changed, thePSieve); } for (DListEraser<PCrouton, DLink_PCrouton_Syncing> iter = fPCroutons_Syncing; iter; iter.Advance()) { sQInsertBack(fPCroutons_Pending, iter.Current()); } locker_CallSync.Release(); if (fPSieves_Update || fPSieves_Changed || fPCroutons_Update || fPCroutons_Changed || fPCroutons_Pending) { this->pTriggerUpdate(); } if (ZLOG(s, eDebug + 1, "ZTSoup")) s << "Sync exit"; return true; }