[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 ----
|