@@ -115,9 +115,8 @@ namespace CacheFlag
115115 static constexpr ObjectBase::Flag NOCOMMIT = 0x010 ; // do not commit created version
116116 static constexpr ObjectBase::Flag NOERASED = 0x020 ; // never return erased version, skip till older object
117117 static constexpr ObjectBase::Flag RETIRED = 0x040 ; // object is in a process of GC
118- static constexpr ObjectBase::Flag UPGRADE = 0x080 ; // create new versions for already existing in a cache objects
119- static constexpr ObjectBase::Flag MINISCAN = 0x100 ; // perform minimum scan and set cache entry to reload state
120- static constexpr ObjectBase::Flag DB_VERSION = 0x200 ; // execute version upgrade in database
118+ static constexpr ObjectBase::Flag MINISCAN = 0x080 ; // perform minimum scan and set cache entry to reload state
119+ static constexpr ObjectBase::Flag DB_VERSION = 0x100 ; // execute version upgrade in database
121120
122121 // Useful combinations
123122 static constexpr ObjectBase::Flag TAG_FOR_UPDATE = NOCOMMIT | MINISCAN | DB_VERSION ;
@@ -163,11 +162,13 @@ class ListEntry : public HazardObject
163162public:
164163 enum State { INITIAL , RELOAD , MISSING , SCANNING , READY };
165164
166- ListEntry (Versioned* object, TraNumber traNumber, ObjectBase::Flag fl)
165+ ListEntry (Versioned* object, TraNumber traNumber, ObjectBase::Flag fl, ListEntry* link = nullptr )
167166 : object(object), traNumber(traNumber), cacheFlags(fl), state(INITIAL )
168167 {
169168 if (fl & CacheFlag::ERASED )
170169 fb_assert (!object);
170+ if (link)
171+ next.store (link);
171172 }
172173
173174 ~ListEntry ()
@@ -548,6 +549,21 @@ class ListEntry : public HazardObject
548549 return state == READY ? false : (thd == Thread::getCurrentThreadId ()) && (state == SCANNING );
549550 }
550551
552+ static bool upgradable (HazardPtr<ListEntry>& listEntry, const Versioned* from)
553+ {
554+ for (; listEntry; listEntry.set (listEntry->next ))
555+ {
556+ if (listEntry->object == from)
557+ return false ; // not found upgrade version
558+
559+ if (listEntry->getFlags () & CacheFlag::COMMITTED )
560+ return true ; // already upgraded by someone else
561+ }
562+
563+ fb_assert (false );
564+ return false ; // miss from what to upgrade
565+ }
566+
551567private:
552568
553569 // object (nill/not nill) & ERASED bit in cacheFlags together control state of cache element
@@ -951,6 +967,36 @@ class CacheElement : public ElementBase, public P
951967 }
952968 }
953969
970+ bool upgrade (thread_db* tdbb, const Versioned* from)
971+ {
972+ HazardPtr<ListEntry<Versioned>> l (list);
973+
974+ // list of versions should be present
975+ fb_assert (l);
976+ if (!l)
977+ return false ;
978+
979+ // if there is another version at the top nothing to be added
980+ if (l->getVersioned () != from)
981+ return ListEntry<Versioned>::upgradable (l, from);
982+
983+ // we have candidate for upgrade - make sure it's not half-done
984+ fb_assert (l->getFlags () & CacheFlag::COMMITTED );
985+ if (!(l->getFlags () & CacheFlag::COMMITTED ))
986+ return false ;
987+
988+ // Try to upgrade
989+ ListEntry<Versioned>* newEntry = FB_NEW ListEntry<Versioned>(nullptr , TransactionNumber::current (tdbb),
990+ CacheFlag::COMMITTED | CacheFlag::MINISCAN | CacheFlag::DB_VERSION , l.getPointer ());
991+ if (l.replace (list, newEntry))
992+ return true ;
993+ delete newEntry;
994+
995+ // Someone already added entry - see is it OK for us
996+ l.set (list);
997+ return ListEntry<Versioned>::upgradable (l, from);
998+ }
999+
9541000private:
9551001 void setNewResetAt (TraNumber oldVal, TraNumber newVal)
9561002 {
@@ -1063,7 +1109,6 @@ class CacheVector : public Firebird::PermanentStorage
10631109
10641110 Versioned* getVersioned (thread_db* tdbb, MetaId id, ObjectBase::Flag fl)
10651111 {
1066-
10671112// In theory that should be endless cycle - object may arrive/disappear again and again.
10681113// But in order to faster find devel problems we run it very limited number of times.
10691114#ifdef DEV_BUILD
@@ -1077,17 +1122,7 @@ class CacheVector : public Firebird::PermanentStorage
10771122 {
10781123 StoredElement* data = ptr->load (atomics::memory_order_acquire);
10791124 if (data)
1080- {
1081- if (fl & CacheFlag::UPGRADE )
1082- {
1083- auto val = makeObject (tdbb, id, fl);
1084- if (val)
1085- return val;
1086- continue ;
1087- }
1088-
10891125 return data->getVersioned (tdbb, fl);
1090- }
10911126 }
10921127
10931128 if (!(fl & CacheFlag::AUTOCREATE ))
@@ -1170,6 +1205,28 @@ class CacheVector : public Firebird::PermanentStorage
11701205 return data;
11711206 }
11721207
1208+ bool upgrade (thread_db* tdbb, MetaId id, const Versioned* from)
1209+ {
1210+ fb_assert (id < getCount ());
1211+
1212+ if (id < getCount ())
1213+ {
1214+ auto ptr = getDataPointer (id);
1215+ fb_assert (ptr);
1216+
1217+ if (ptr)
1218+ {
1219+ StoredElement* data = ptr->load (atomics::memory_order_acquire);
1220+ fb_assert (data);
1221+
1222+ if (data)
1223+ return data->upgrade (tdbb, from);
1224+ }
1225+ }
1226+
1227+ return false ;
1228+ }
1229+
11731230 template <typename F>
11741231 StoredElement* lookup (thread_db* tdbb, F&& cmp, ObjectBase::Flag fl) const
11751232 {
@@ -1188,6 +1245,7 @@ class CacheVector : public Firebird::PermanentStorage
11881245 auto listEntry = ptr->getEntry (tdbb, TransactionNumber::current (tdbb), fl | CacheFlag::MINISCAN );
11891246 if (listEntry && cmp (ptr))
11901247 {
1248+ // if (!(fl & (CacheFlag::ERASED | CacheFlag::MINISCAN)))
11911249 if (!(fl & CacheFlag::ERASED ))
11921250 ptr->reload (tdbb, fl); // found object to be reloaded w/o MINISCAN flag
11931251 return ptr;
0 commit comments