[Modeling-cvs] ProjectModeling/Modeling CHANGES,1.98,1.99 DatabaseChannel.py,1.11,1.12
Status: Abandoned
Brought to you by:
sbigaret
From: <sbi...@us...> - 2003-05-06 12:20:35
|
Update of /cvsroot/modeling/ProjectModeling/Modeling In directory sc8-pr-cvs1:/tmp/cvs-serv3082 Modified Files: CHANGES DatabaseChannel.py Log Message: Fixed DatabaseChannel.fetchObject(): it was not MT-safe wrt Database snapshots' caching when an EC in an other thread is garbage-collected in the middle of fetchObject() (see comments in the code for further details) Index: CHANGES =================================================================== RCS file: /cvsroot/modeling/ProjectModeling/Modeling/CHANGES,v retrieving revision 1.98 retrieving revision 1.99 diff -C2 -d -r1.98 -r1.99 *** CHANGES 25 Apr 2003 18:13:33 -0000 1.98 --- CHANGES 6 May 2003 12:20:31 -0000 1.99 *************** *** 8,11 **** --- 8,15 ---- -------------------------------------------------------- + * Fixed DatabaseChannel.fetchObject(): it was not MT-safe wrt Database + snapshots' caching in some circumstances (see comments in the code for + further details) + * Added scripts/mdl_compile_model.py and update the __init__.py generated by mdl_generate_DB_schema.py so that it automatically tries to load the Index: DatabaseChannel.py =================================================================== RCS file: /cvsroot/modeling/ProjectModeling/Modeling/DatabaseChannel.py,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** DatabaseChannel.py 14 Mar 2003 11:40:06 -0000 1.11 --- DatabaseChannel.py 6 May 2003 12:20:32 -0000 1.12 *************** *** 150,229 **** self.databaseContext().registerLockedObjectWithGlobalID(globalID) ! # Handle snapshot ! # NB: ask delegate __TBD ! if self.isRefreshingObjects() or self.isLocking(): ! # Unconditional refresh ! database.recordSnapshotForGlobalID(snapshot, globalID) ! if self.isRefreshingObjects(): ! # NB: isLocking-> no need to invalidate objects since two ECs cannot ! # hold the same object at the same time ! NC.postNotification(ObjectsChangedInStoreNotification, self, ! {UpdatedKey: (globalID,)}) ! else: ! # Should the snapshot be updated? if there is already one, no update. ! # __TBD ask the DatabaseContext delegate whether we should refresh ! # __TBD and, if yes, post ObjectsChangedInStoreNotification ! registeredSnapshot=database.snapshotForGlobalID(globalID) ! if not registeredSnapshot: database.recordSnapshotForGlobalID(snapshot, globalID) else: ! snapshot=registeredSnapshot ! ec=self._editingContext ! object=ec.objectForGlobalID(globalID) ! ! if object is not None and not object.isFault(): ! return object ! ! if object is not None and object.isFault(): ! #NC.removeObserver(object.faultHandler(), GlobalIDChangedNotification, ! # globalID) ! NC.removeObserver(ec, GlobalIDChangedNotification, globalID) ! #object.clearFault() ! #else: ! cd=ClassDescription.classDescriptionForName(self._currentEntity.name()) ! # first, check that we do not have a fault registered under the ``root'' ! # GlobalID ! root_cd_name=cd.rootClassDescription().entityName() ! ! if root_cd_name!=cd.entityName(): ! root_globalID=KeyGlobalID(root_cd_name, globalID.keyValues()) ! trace('Searching for possible fault w/ rootGlobalID=%s'%root_globalID) ! possible_faulted_object=ec.objectForGlobalID(root_globalID) ! if possible_faulted_object: ! if possible_faulted_object.isFault(): ! trace('Found it: posting GlobalIDChangedNotification') ! NC.postNotification(GlobalIDChangedNotification, root_globalID, ! {root_globalID: globalID}) ! object=possible_faulted_object ! # useless: it did receive the notification, and it has unregistered ! # itself from the list of the observers ! #NC.removeObserver(object.faultHandler(), ! # GlobalIDChangedNotification, globalID) ! object.clearFault() ! new_class=cd.classForInstances() ! object.__class__=new_class ! object.__class__.__init__(object) ! else: ! raise RuntimeError, "Serious: shouldn't happen, please report" else: ! # create object ! object=cd.createInstanceWithEditingContext(ec) ! ec.recordObject(object, globalID) ! else: ! if not object: ! # create object ! object=cd.createInstanceWithEditingContext(ec) ! ec.recordObject(object, globalID) ! ! # Initialize the new object or the cleared fault ! try: ! object.clearFault() ! except: ! pass ! ec.initializeObject(object, globalID, ec) ! return object --- 150,256 ---- self.databaseContext().registerLockedObjectWithGlobalID(globalID) ! ! # Now we need to lock the corresponding database object. Why? ! # ! # One could argue that e.g. since an EC forwards objectsWithFetchSpec() ! # ultimately to its root object store --an ObjectStoreCoordinator--, and ! # since OSC locks/unlocks itself before/after each operation, an other EC ! # used in a concurrent thread can access the Database object (and its ! # snapshots) in the meantime. ! # ! # This is true w.r.t. ECs standard operations, but there remains a case ! # where a concurrent thread can change the Database's snapshots: when an ! # EC is finalized and garbage-collected, it forgets its objects and ! # decrements the corresponding snapshot reference count --at this time, if ! # the ref.count falls down to zero, the snapshot is removed from the ! # Database's snapshots cache. ! # ! # Now this can happen in another thread while in this thread, the snapshot ! # has been retrieved but it has not been used yet by ec.initializeObject() ! # (forwarded to and handled by DBContext.initializeObject()). When this ! # happens, Database.incrementSnapshotCountForGlobalID() called by ! # DBContext.initializeObject() raises KeyError on the corresp. GlobalID ! # since the corresponding snapshot has been thrown away. ! # ! database.lock() ! try: ! # Handle snapshot ! # NB: ask delegate __TBD ! if self.isRefreshingObjects() or self.isLocking(): ! # Unconditional refresh database.recordSnapshotForGlobalID(snapshot, globalID) + if self.isRefreshingObjects(): + # NB: isLocking-> no need to invalidate objects since two ECs cannot + # hold the same object at the same time + NC.postNotification(ObjectsChangedInStoreNotification, self, + {UpdatedKey: (globalID,)}) else: ! # Should the snapshot be updated? if there is already one, no update. ! # __TBD ask the DatabaseContext delegate whether we should refresh ! # __TBD and, if yes, post ObjectsChangedInStoreNotification ! registeredSnapshot=database.snapshotForGlobalID(globalID) ! if not registeredSnapshot: ! database.recordSnapshotForGlobalID(snapshot, globalID) ! else: ! snapshot=registeredSnapshot ! ! ec=self._editingContext ! object=ec.objectForGlobalID(globalID) ! ! if object is not None and not object.isFault(): ! return object ! ! if object is not None and object.isFault(): ! #NC.removeObserver(object.faultHandler(), GlobalIDChangedNotification, ! # globalID) ! NC.removeObserver(ec, GlobalIDChangedNotification, globalID) ! #object.clearFault() ! #else: ! cd=ClassDescription.classDescriptionForName(self._currentEntity.name()) ! # first, check that we do not have a fault registered under the ``root'' ! # GlobalID ! root_cd_name=cd.rootClassDescription().entityName() ! if root_cd_name!=cd.entityName(): ! root_globalID=KeyGlobalID(root_cd_name, globalID.keyValues()) ! trace('Searching for possible fault w/ rootGlobalID=%s'%root_globalID) ! possible_faulted_object=ec.objectForGlobalID(root_globalID) ! if possible_faulted_object: ! if possible_faulted_object.isFault(): ! trace('Found it: posting GlobalIDChangedNotification') ! NC.postNotification(GlobalIDChangedNotification, root_globalID, ! {root_globalID: globalID}) ! object=possible_faulted_object ! # useless: it did receive the notification, and it has unregistered ! # itself from the list of the observers ! #NC.removeObserver(object.faultHandler(), ! # GlobalIDChangedNotification, globalID) ! object.clearFault() ! new_class=cd.classForInstances() ! object.__class__=new_class ! object.__class__.__init__(object) ! ! else: ! raise RuntimeError, "Serious: shouldn't happen, please report" else: ! # create object ! object=cd.createInstanceWithEditingContext(ec) ! ec.recordObject(object, globalID) else: ! if not object: ! # create object ! object=cd.createInstanceWithEditingContext(ec) ! ec.recordObject(object, globalID) ! ! # Initialize the new object or the cleared fault ! try: ! object.clearFault() ! except: ! pass ! ec.initializeObject(object, globalID, ec) ! finally: ! database.unlock() ! return object *************** *** 332,336 **** if not self._adaptorChannel.isOpen(): self._adaptorChannel.openChannel() ! #self._adaptorChannel.executeExpression(sqlExpr) self._adaptorChannel.selectAttributes(entity.attributesToFetch(), --- 359,363 ---- if not self._adaptorChannel.isOpen(): self._adaptorChannel.openChannel() ! #self._adaptorChannel.executeExpression(sqlExpr) self._adaptorChannel.selectAttributes(entity.attributesToFetch(), *************** *** 338,342 **** self._isLocking, entity) - def setCurrentEditingContext(self, anEditingContext): --- 365,368 ---- |