Appendix B. COM+ Synchronization Through Activities

Writing a thread-safe component—that is, one that can safely be called by multiple threads at any time—has always been a challenge. Such development demands knowledge of semaphores, mutexes, critical sections, and other forms of OS thread locking primitives. These must be set like sentinels at the gates of every resource in a process that requires serialized access.

COM offers protection for objects that might be called concurrently by multiple threads, but cannot or do not employ any form of mutual exclusion primitive (mutex, semaphores, and so on). This protection comes in the form of the Single-Threaded Apartment (STA), which, as discussed in Chapter 4, "Threading and Apartment Models," serializes all method calls made by any thread through a protective message queue. COM then serially pops the messages from the queue one at a time and invokes them on the object.

STA serialization guarantees that only one method is invoked on the object at a time. Thus, if three threads call method Foo() on an object in an STA at the same time, the method calls are serialized in an FIFO fashion. There is no danger of the first method invocation, M1, being interrupted by another thread's method invocation, M2, before M1 has completed and returns. The thread protection, then, is at the method level. That is certainly useful, but it cannot guard against more subtle forms of race conditions.

Imagine the scenario where object A creates B which creates C which creates D in the context of a transaction. Object A might call a method on B which, in turn, calls a method on C. If A, B, and C are apartment model (they are protected by their STAs), there is no danger of their individual method calls being interrupted—A's method call to B is guaranteed to complete as is object B's call to object C and C's to D. However, there is the possibility that some other alien thread could butt in between A's call to B and B's call to C and make another method invocation that could interrupt the chain of related method calls. By interrupt, I mean to say that the foreign thread could make a serialized method call that is perfectly legitimate as far as the STA is concerned, but might result in the recipient object being in a state inappropriate for what A, B, and C are trying to accomplish together. Perhaps an example is helpful to visualize this. Examine the pseudo VB code in Listing B.1.

Example B.1. Simple VB Objects

 'The Client
A.DoSomethingOnBFromA
 'Object A:
Sub DoSomethingOnBFromA()
 'Do something, then:
B.DoSomethingOnCFromB
end sub
 'Object B:
Sub DoSomethingOnCFromB()
 'Do something, then:
    C.DoSomethingOnDFromC
end sub

You can see from Listing B.1 that a call to object A's DoSomethingOnBFromA method initiates a chain of related method invocations involving four objects (A through D). Now, here's the problem: From the moment A's DoSomethingOnBFromA method is called, objects A, B, C, and D become conceptually dependent on one another; they are grouped in a common action. It is as if there is an electric impulse moving along a nerve, jumping synapses from A to B to C to D with a singular purpose. After the current flows from A to B, you want to be certain that D is still there and in the appropriate state to cooperate with the rest of the objects. However, when A first calls B, it is still possible for some alien thread to make a method call on object D before D is reached. All upstream objects are counting on D's contribution, but the danger exists that a foreign thread could manipulate D at the last moment. Thus, D might be put in a state now inappropriate for the method invocation it is about to receive and, therefore, the group action it is about to participate in. Fortunately, there is a way to protect against this scenario—COM+ activities.

I find it helpful to visualize a COM+ activity as a large, transparent dome covering a group of related objects. Any time a method call is made by an outside thread to any one object in the activity, the dome snaps opaque and no outside method invocations is, methods invoked by a thread not participating in the activity—are not allowed in to any object under the dome. When the method call completes, the dome turns transparent, and external method calls are again allowed in. This dome can spread to encompass objects on different machines in different processes and, as always, turn opaque the moment any one outside thread calls a method on any object, in any process on any machine within the dome until the call returns. Interestingly, even in the unlikely event that one object independently, spontaneously calls a method on another object within the activity not at the behest of an outside thread, the dome snaps opaque until the call completes.

If you are comfortable with the concept of the STA, it can be helpful to think of an activity as one big STA, serializing access to all objects within itself.

Configuration

Configuration of activities work in a fashion identical to COM+ transactions. TheConcurrency tab of the properties for a configured component displays the same options as the Transactions tab. Specifically, it displays the following options:

  • Disabled. Object does not require synchronization.

  • Not Supported. Object cannot participate in an activity.

  • Supported. Inherits an activity if its creator's context holds an activity. Otherwise, no new activity is created.

  • Required. Inherits an activity if its creator's context holds an activity. If not, a new activity is created, and this object becomes the root.

  • Requires New. A new activity is always created.

Note that, just as with transactions, an activity can span multiple processes and machines.

Activities and Transactions

All transactions must occur in an activity to make sure that isolation (the I in ACID, introduced in Chapter 8 "Transactions") is properly enforced. The same is true for Just-In-Time Activation(JITA), introduced and discussed in Chapter 8. If JITA is enabled for an object, that object must participate in an activity. Remember that JITA objects tto be deactivated and pooled (or destroyed altogether in the case of VB6 objects) whenever a method call returns. Thus, JITA objects require activities to protect against a case where a method call made by one thread T1 is interrupted by a method call made by thread T2. If T2 completes its method call while T1 is blocked, JITA deactivates/destroys the object, thus pulling the rug out from T1. Activities prevent this from happening.

Transactions require both JITA support and activities, and Component Services automatically configure your component to use activities if transactions or JITA (or both) settings are enabled.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.42.158