BNZTransactionalNotificationCenter



Abstract

The BNZTransactionalNotificationCenter is the base of the whole framework

Discussion

The BNZTransactionalNotificationCenter is the base of the whole framework. It keeps track of all the transactions on a system.

A reference to the notification center can be obtained by the statid defaultCenter method.

The BNZTransactionalNotificationCenter manages exactly zero or one transaction for each thread. A transaction for the current thread can be obtained by calling currentTransaction. This will return the current transaction for the thread of the caller. If no such transaction exists, the BNZTransactionalNotificationCenter creates one (see also currentTransactionCreateIfNotPresent:)

The BNZTransactionalNotificationCenter also offers the possibility to register observers (through BNZObservation objects). Similar to the NSNotificationCenter, such observers are informed when a certain event occurs An event in this framework is: a BNZAtomicChange is contained in a committing transaction.

When a transaction is committed, all observers are collected that have interest in at least one of the changes contained in the transaction. Then, depending on the current state of the transaction (COMMITTING, COMMITTED, ROLLED_BACK), the observers receive the messages they registered through the BNZObservation.

The "synchronization" of the transactions that want to commit is done in the clearToCommit: method that is called by the BNZTransaction. This method blocks the thread of the calling transaction until it is save to commit.

IMPORTANT NOTE FOR FUTURE WORK: see documentation of BNTTransaction for more details about when the commit is halted. However, there may still be issues with dirty reads during a commit. Example:

1 Person firstname="Bob", lastname="Sinclair" 2 Change 1: "Bob" -> "Alice" 3 Change 2: "Sinclair" -> "Johnson" 4 Thread A is committing the transaction with the two changes. 5 While A is in the for-loop that iterates all changes and commits (applies) them individually: a Thread B steps in 6 Thread B may now read something like "Alice Sinclair", which is illegal and therefore a dirty read

I am not sure if this is currently possible and if yes, how to prevent this in an elegant manner. The problem is: Either: How to tell the system that the commit for-loop may not be interrupted by other threads or: How to block getter-methods until the commit is finished.

The goal would be, of course, to do this with the least possible intrusion to the transactional objects (i.e., the implementation of a getter of an object should be as easy as possible) without too much overhead (putting locks everywhere is a bad idea:)



Methods

clearToCommit:

Abstract: called by transactions before they commit
- (void)clearToCommit:(BNZTransaction*)transaction; 

transactions have to call this method immediately before they commit. This method will block the current thread (==the transaction's thread) until the transaction can commit.

Note: A transaction may have to wait for other transactions it depends on. see BNZTransaction documentation for more details

Parameters

NameDescription
transactionthe transaction that wants to get clearance

currentTransaction

Abstract: return the transaction for the current thread. Creates one if no transaction exists yet
- (BNZTransaction*)currentTransaction; 

calls currentTransactionIfNotPresent:YES, so look there

Result: a transaction for the current thread

currentTransactionCreateIfNotPresent:

Abstract: returns the transaction for the current thread.
- (BNZTransaction*)currentTransactionCreateIfNotPresent:(BOOL)create; 

returns the transaction for the current thread. If currently there is no transaction, a new transaction will be created for the current thread, if create == YES. Else, nil will be returned.

Parameters

NameDescription
createcreate a new transaction for the current thread if none exists yet?
Result: the transaction for the current thread, or nil if not present and create was NO

defaultCenter

Abstract: returns the default instance of the BNZTransactionalNotificationCenter
+ (BNZTransactionalNotificationCenter*)defaultCenter; 

There should be only one instance of the BNZTransactionalNotificationCenter per system. Use this static method to obtain this instance

Result: the instance of the BNZTransactionalNotificationCenter for the system

finalizeTransaction:

Abstract: finalize a transaction. has to be called, regardless of outcome of transaction
- (void)finalizeTransaction:(BNZTransaction*)transaction; 

This method must be called by a transaction after it has been either committed or rolled back (in any case!). It will remove the transaction from the schedule and the transactions list (and therefore may cause the transaction to be deallocated).

Also, it will check sceduled transactions if they wait for the finalizing transaction and if yes it will unlock their lock (and therefore may cause the waiting transaction to start, if the finalized transaction was the last one in its list).

Parameters

NameDescription
transactionthe transaction to be finalized

hasCurrentTransaction

- (BOOL)hasCurrentTransaction; 

Result: YES if a transaction for the current thread exists

lastChangeInCurrentthreadOfType:onTarget:

Abstract: a helper for easier retrieval of the last change of a certain type
- (id)lastChangeInCurrentThreadOfType:(id)changeType onTarget:(id)target; 

this is a short helper that makes retrieving the last change of a certain type on a certain target easier. Basically, it forwards the call to lastChangeOfType:onTarget: of the current transaction.

However, it additionally checks if there is a current transaction at all and if not, it returns nil (without accidentially creating a new transaction, as it would be the case when currentTransaction was called). So clients of this method don't need to care about all that

Retrieving only the last change is interesting for changes that overwrite previous changes anyway (as it would be the case with subsequent, let's say, setFirstName: calls)

Parameters

NameDescription
changeTypethe type of change of interest
targetwhich target is affected
Result: the last change of the given type on the given target, nil if no such change is in the current transaction's change set or if no transaction for the current thread exists.

observationsForChange:

Abstract: find all observations that match the change
- (NSArray*)observationsForChange:(id)change; 

find all observations that match the change. see BNZObservation for more details

Parameters

NameDescription
changethe change for which observations are searched
Result: an array containing all BNZObservation objects that match the change

recordForObserver:create:

Abstract: retrieve the record for a specific observer
- (BNZObserverRecord*)recordForObserver:(id)observer create:(BOOL)shallCreate; 

Parameters

NameDescription
observerthe observer for which the record is searched
shallCreateYES: create a new record if such a record does not yet exist, NO: don't create a new record
Result: the BNZObserverRecord for the observer, or nil if no such record exists and shallCreate was NO

registerObservation:

Abstract: register an observer for observing certain changes
-  registerObservation:(BNZObservation*)observation; 

This method allows external objects to be notified about commits that contain certain changes. All relevant information is collected in an BNZObservation object

This method has a similar purpose as the addobserver:selector:name:object in NSNotificationCenter

Important: for each possible combination of observer, changeType, and target object only one observation is stored. If calling this method with the same values for the above objects (but, say, different callbacks) the previous records are overwritten.

Example: imagine: add observation for observer o, changeType "PersonNameChange", onTarget nil (observe all objects) with callbacks c1, c2, c3

then: add observation for observer o, changeType "PersonNameChange", onTarget p (a concrete Person) with callbacks c4, c5, c6 will not overwrite the previous adding, because onTarget is different. (during a commit, a PersonNameChange on p will result in calls of c1, c4 (before commit) and c2, c5 (after commit) or c3, c6 (in case of rollback)


Parameters

NameDescription
observationa BNZObservation object containing all relevant info about whom to call with wich selector at what time for what changes on which objects
Result: returns self

removeObservationOfObserver:forChanges:onTarget:

Abstract: Remove an entry of an observer
-  removeObservationOfObserver:(id)observer forChanges:(id)type onTarget:(id)target; 

Will remove the entry of an observer. Only the entry that exactly matches the triple: observer, type, target (including nil values) will be removed. Other observations remain untouched

Parameters

NameDescription
observerthe observer
typethe type of change
targetthe target
Result: returns self

transactionForThread:

- (BNZTransaction*)transactionForThread:(NSThread*)tread; 

Parameters

NameDescription
threada thread
Result: the transaction that is associated with this thread, nil if no such transaction exists

(Last Updated 8/31/2006)