Announcements Framework

Bring Your Own SubscriptionRegistry

September 14, 2006 2:43:36.623

After a long period of stability, there is a new version of Announcements in the public repository, preview-28.0. The change it contains is unusual in that it removes rather than adds functionality. It can therefore affect people who already use announcements.

Removed is the ability for an arbitrary object to remember subscriptions. There is a new class in System-Announcements, called Announcer (which previously was included as an example in the Systems-Announcements-Extras package). In order to be able to accept subscriptions, an object should either inherit from Announcer or implement in a sufficiently similar way the two or three methods that Announcer implements: #subscriptionRegistry:, #subscriptionRegistryOrNil and (typically) #postCopy.

In the original design, any object, including something as primitive as a SmallInteger, could accept subscription requests, remember the subscriptions, and later deliver announcements to the subscribers. This was possible because Object implemented subscription storage using one external global registry, following the tradition set by change/update (DependentsFields) and triggerEvent (EventTable). Only classes that wanted the lower cost of local subscription storage would care to store subscriptions in an instance variable instead of inheriting the global mechanism from Object.

The problem with that tradition are a number of subtle issues in the interaction between the external registry, #become:, and collection growth logic, as well as the risk of garbage accumulating in the global registry. A detailed review would easily make up a post of its own going some fifteen years of VW evolution back, but for now suffice it to say that the issues were bad enough that we decided to explore the alternative approach, more in the spirit of "worse is better".

The common case is that a relatively small number of specialized announcement sources, such as Pollock Panes, does indeed broadcast events. Such objects often replace the default storage management with local storage as a matter of optimization. In the new Announcements, this is no longer an optimization. Now it's strictly Bring Your Own SubscriptionRegistry. Any object that wants to accept subscriptions must make its own arrangements to hold onto its subscription registry.

I've been careful to say that an object has to make these arrangements "to be able to accept subscriptions" rather than "to be able to broadcast announcements". Conceptually, the framework still considers all objects as capable of broadcasting announcements. It's just that not all objects will accept subscriptions. From the framework's point of view, any newly created object starts off with zero subscriptions. It doesn't matter whether the object is an instance of Point or Pane. It is OK to send #unsubscribe: to both of those, which will be a no-op since there are no subscriptions to remove. It is even OK to send #announce: to both, which again will be a no-op as there are no interested recipients. The only difference is that one can send a #when:... message to Pane to create a new subscription, while sending it to Point will cause an error.

Announcements Framework

Counting the Chickens

December 9, 2005 14:51:02.545

This is the last major post in the announcements series. After seeing how announcements are more powerful, flexible and consistent than events, it's interesting to compare the implementations.

I guess we all are used to the concept that there is never a free lunch, and all those niceties with "real" objects have to cost something. One of the first email responses I got after posting the first message outlining the framework said, "How do announcements compare to the old event system in term of performance? I know CPUs are faster and faster, but...."

I should say that I never made any concessions to performance in Announcements. I didn't ignore the subject entirely by doing stupid things in the code, of course, but nothing in the API is the way it is because "it's faster this way." So, I also was interested to know how announcements with their OO purity would compare to triggerEvent in the end.

The main thing to test is of course the speed of announcement/event delivery, since it is the main overhead events and announcements add to application logic. The tests I used were these:

	ObjectMemory garbageCollect.
	foo := Object new.
	1 to: <N> do: [:i | foo when: #foo send: #yourself to: i].
	Time millisecondsToRun: 
		[100000 timesRepeat: [foo triggerEvent: #foo]]
	
	ObjectMemory garbageCollect.
	foo := Object new.
	1 to: <N> do: [:i | foo when: Announcement send: #yourself to: i].
	Time millisecondsToRun: 
		[100000 timesRepeat: [foo announce: Announcement]]

Where <N> would vary to change the number of subscriptions. The memory is reset to the same state before the test to avoid sudden random spikes due to garbage collection. An interesting point is why I'm using #to:do: instead of a simple #timesRepeat:. As said more than once before, the overall policy of Announcements is that the framework is very subscription-centric. A subscription request always sets up a new subscription. If you do

	10 timesRepeat: [foo when: Announcement do: [Transcript show: 'gotcha']]

you set up ten subscriptions for the same object. Why you want multiple subsriptions is your business, and the framework doesn't care. You ask for it, you get it. With triggerEvent, you only get a subscription if there wasn't another one like it before. So, the ten requests above would create one subscription.

Here are the millisecond times and the speed ratio depending on the number of subscriptions (times are averages of three runs, after one ignored "warm up" run to fill the VM caches).

<N> triggerEvent Announcements Ratio
0 267 314 0.850
1 617 529 1.17
3 734 610 1.20
10 1004 741 1.35
30 1711 1120 1.53
100 4320 2430 1.78

These are rather stunning results, and the natural question is why triggerEvent starts with a 15% speed advantage but then the tables are turned so dramatically.

The answer is that this gap between the ratios for 0 and 1 subscription is the cost of event delivery. With 0 subscriptions there are no deliveries happening, with 1 delivery code starts working. This huge gap shows how expensive event delivery in triggerEvent really is. It is that expensive because of complex argument handling (I wish I could summarize what exactly is so expensive in it but honestly, I never could understand the thing in its entirety). In contrast, all that needs to be done in Announcements is pass the announcements to the subscribers who want it.

Another interesting point is why triggerEvent has that initial advantage at all. This is simply because triggerEvent cuts corners. As I said in Advanced Announcements, part 5, even though in triggerEvent you can declare the events a class presumably triggers, that declaration is not enforced. Even if the class says it triggers only #a and #b it's free to trigger #c as well. In contrast, the #mayAnnounce: validation hook in announcements is used consistently, and announcements are validated when they are announced.

Now let's look at the implementation size. Both frameworks have some classes of their own (though you rarely interact with any of those in triggerEvent) and some methods in Object. triggerEvent also has a number of #evaluate and #evaluateWithArguments: methods scattered around the system as part of argument processing implementation (and I consider that generic #evaluate the worst conceptual sin of triggerEvent).

Here are the counts of all of those, and also the combined byte size of all the involved methods. Byte size is a pretty good indication of how much "real" code is in the system, since it doesn't depend on variable and selector name length, formatting style and other noise simple source code-based metrics suffer from.

triggerEvent Announcements
Classes 6 7
methods in those 41 58
methods in Object 44 12
other methods 13 0
total methods 98 70
byte code size 1130 697

It appears that Announcements code size is only 61% of that of triggerEvent. I hope this calms another concern I heard after adding weakness support, that Announcements should not become a swiss army knife with solutions for all imaginable problems. The framework is so small because its API is deliberately designed to offer a small set of parts that can be composed, rather than a bunch of monolithic solutions. If anything, Announcements is a knife construction kit with a couple of basic useful knives pre-assembled.

So on the subject of cost. Since it appears all the extra power of announcements comes with a smaller and faster implementation to boot, is this a free lunch after all?

In a way yes, because Smalltalk pays for it. triggerEvent could have been implemented in C++. Announcements couldn't, at least not anywhere close in simplicity. I have strong doubts about Java either. As a mini-rant, I think even Smalltalkers often underestimate how much power Smalltalk objects give them at what little cost. The fundamental problem with triggerEvents was that it didn't embrace objects from the start. There was a valid reason for that, when its foundations were laid down in a Smalltalk system running in 640k of memory with a reference counting garbage collector. But it was twenty years ago. Unfortunately, when those constraints stopped being an issue fifteen years ago all the while the framework faced new requirements, it was made more and more complex instead of reviewing the priorities. That is the second main problem. It's a strange fact that simplifying things takes more work and ingenuity than complicating them, and that's another reason why Announcements are so small and simple with all their power. Quite a lot of work went into polishing the basic concepts to make them so.

To paraphrase J. S. Bach, "Simplifying a framework in Smalltalk is easy. You select the right methods, click 'remove' and Smalltalk simplifies it for you".

Announcements Framework

API update: R.I.P. veto

December 8, 2005 13:23:25.957

As these posts were progressing together with new framework versions published, nothing already described was invalidated by later published versions, until the version preview-16.0.

That and later versions have the built-in vetoing as described in "announcing and vetoing" removed. All things considered, with announcement object as a natural two-way communication channel between the announcer and the subscriber, vetoing implementation is so trivial that it's better applied as a pattern where needed rather than implemented by the generic Announcement and carried around by all announcements, when most of them are in fact not vetoable.

All an announcement class needs in order to support vetoing are three simple things:

  1. an instance variable named something like 'vetoed'
  2. a method named something like #veto that sets the variable to a non-nil value, used by subscribers to veto.
  3. a method named somehing like #isVetoed that does "^vetoed notNil"

The announcer would do this to announce and respond to a veto request:

    | announcement |
    announcement := AboutToChange new.
    self announce: announcement.
    announcement isVetoed ifTrue: [^self]
    ...

or even simpler, using the fact that the #announce: message answers the announcement instance that has just been announced:

    (self announce: AboutToChange) isVetoed ifTrue: [^self]
    ...

Having that variable and methods in generic Announcement when most announcements are in fact not vetoable was a waste, and the extra #isVetoable declaration to enable that support added unnecessary complexity.

A valid question is why not place this support in a subclass of announcement called VetoableAnnouncement and have vetoable announcements inherit from it. There is no such class provided because with Announcements, the primary purpose of subsclassing is not to reuse code, but to describe how announcements are related in their meaning. For example, if I have something like this:

    PropertyChange
        PropertyAdded
        PropertyRemoved
        PropertyValueChanged
            PropertyResetToDefault

the framework knows that if I say I want to know about PropertyValueChanged then I also want PropertyResetToDefault, because I consider it a kind of the same thing. Making PropertyResetToDefault vetoable shouldn't change that.

Having said that there is no such class, there is one in System-Announcements-Extras package I mentioned in the previous post--if anything, as an example of how simple it is to make an announcement vetoable.

Terry could rightfully point out again that traits could solve this by making vetoing a trait, and they would indeed. But even within classic Smalltalk, adding a variable and two one-liners by hand where needed isn't the worst problem to face.

Announcements Framework

Advanced Announcements, part 5

December 8, 2005 1:09:15.184

Rich and Travis have already been discussing declarations and reflection. This post is not as much a user's guide as an explanation why the current incarnation of the framework doesn't commit very much to that idea. I can say right away that I am not brimming with excitement regarding declarations. Nor am I rejecting the idea outright.

I'll start with some definitions and clarifications so we don't entangle ourselves in our own misinterpretations.

One thing we talk about is reflection. Loosely speaking, reflection is the ability of our Smalltalk image to know and/or modify its structure. In our neck of the woods, reflection is universally considered to be a Very Good Thing.

The other thing are declarations. One motif that rather bothered me in the discussion was the apparent confusion between declarations and reflection, with an implication that declarations are needed because they provide reflection and reflection is good. Declarations and reflection are entirely different things. You can have one and not the other. Declarations can support reflection, but they are neither required nor automatically enable it. Consider that C++ and similar languages are very declaration-heavy and reflection-light, while Smalltalk is the other way around.

We can fairly accurately find out what messages methods of a particular class send without having the class declare each message it sends. What we find is not guaranteed to be 100% accurate, but for most tasks in tools 90% is good enough. Enough to say that requiring mandatory declarations of all sent messages so the tools could gain the last 10% is not a very attractive proposition, at least not within our laid-back Smalltalk culture.

To avoid confusion, it's better to separate reflection into two kinds: introspective reflection, when you muck around with code and figure out what it does, and declarative reflection when you know what the code does because declaration restricts what it could do. (Yes, I've just made these terms up).

The important difference between these is that introspective reflection follows the code while declarations want the code to follow them. This once again shows why reflection and declarations are not the same. The primary goal of a declaration is limiting what is possible in the code, and reflection is only a byproduct of that. In fact, declarative reflection is not true reflection at all--you know your code won't do what declaration does not allow, but you don't know how much it does of what is allowed. You still need introspective reflection to know that.

Declarations establish a law for code to follow, and there is no law without law enforcement. Unless you ensure the code only does what the declarations say, any "reflection" based on the declarations is only an educated guess. So, reflection might not go hand-in-hand with declarations, but enforcement does.

Enforcement can be done at compile time and runtime. Compile-time enforcement is far superior. It's more user-friendly because it points our errors as soon as they are made. It's also more reliable because it catches all errors, while runtime errors can go unnoticed if the particular piece of code runs rarely. This is why even for message sends, the sent selector is verified to at least exist at compile time, with a warning if it doesn't.

With all the above combined, I think I know what Travis meant when he said about using introspective reflection for finding announcements, "This seemed like a cool idea, because, it's so... I don't know what the right word is." It's so much more in the spirit of Smalltalk to let the code be free and follow it when needed, rather than establish artificial limits at a high level, with whatever good intentions. Consider how the existing declarations (such as variable, import, pragma) restrict the code only in the most basic ground rules, usually enforced at compile time.

This is why I am not too keen on embracing announcement declarations outright. They feel alien. They are too high up the food chain. Consider the parallel with Exceptions. There are no declarations of exceptions a class can signal. Do we need them? I'm sure someone could come up with a reason for that. Are we doing well without them? There's the rub.

If Exceptions are not a good example as far as declarations are concerned, what about #eventsTriggered in triggerEvent? Here is why eventsTriggered are apparently intended to solve an unrelated problem, which Announcements solve much better by different means.

Let's start with this. Assuming a class has a declaration of announced things of some sort, enforced at runtime, when exactly should it be enforced? The #announce: method is one obvious spot, since otherwise we could announce things we claim we don't announce. What about when accepting subscriptions--should it be ok to subscribe to something an object can't announce? In other words, what precisely does an announcement declaration restrict: only announcements, or both announcements and subscriptions?

Regardless of the answer, what triggerEvent provides is off the mark. It has something called event checking, turned off by default in Object. A subclass can turn it on and add #eventsTriggered declarations. However, a closer look reveals that what is checked against those declarations are actually the "when:..." subscription messages, while triggerEvent: won't balk at anything. So, despite the "eventsTriggered" name, instances can trigger anything at all--it's just that they won't accept subscriptions to it!

Obviously, event checking in triggerEvent isn't concerned with any high-level principles like "declarations should limit and describe what the code can do". It must have been implemented ad-hoc to address some immediate practical needs. And I think the main of those was protecting against typos. When events are symbols, it's easy to mistype one, subscribe to an unexistent event and then wonder why the event is apparently not being triggered. So the true purpose of #eventsTriggered, despite the name, is not to list the events a class triggers, but simply list some symbols the class knows are valid event names it presumably triggers.

Now, to Announcements.

If the true purpose of #eventsTriggered appears to be protection against typos, Announcements have it. Only better. If you mistype an announcement name you get an error at compile time, and the error checker can event find the correct name for you.

With this out of the way and the counter-example of Exceptions, I hope it's getting clear why I am not entirely convinced declarations are the way to go. I believe we simply don't have enough collective experience using them to come to any conclusions right now. Now is the time to let a hundred flowers bloom to get that experience, and not expect an answer in a month--and probably not in a year.

This is why all the Announcements proper provides is a hook for runtime enforcement of both subscriptions and announcements. All announcements and all subscription requests need to pass a class check implemented by #mayAnnounce: method of the announcer. The method accepts the class being announced or subscribed to as the argument and should answer false if the class is not supported.

However, there is no "official" way in Object of declaring the supported announcements, and the default implementation of #mayAnnounce: in Object answers true to everything.

Having said this, I published to the public repository System-Announcements-Extras package with Announcer class implementing announcement declarations--as an example or a superclass to subsclass from for those willing to experiment. As I said, I don't reject the idea outright.

This is the lay of the land for the near future.

Announcements Framework

Advanced Announcements, part 4

December 6, 2005 18:22:32.568

This part's subject are Weak Subscriptions.

Ladislav Lenart brought up an interesting point about one feature shared by all depedency frameworks so far--change/update, triggerEvent and first publicly available versions of Announcements. The feature is that, in Announcements language, subscribing creates a strong reference from the announcer to the subscriber. Even if any other references to the subscriber are gone, the subscriber will stay alive as long as the announcer it's subscribed to is alive. This is actually what allows the following to work:

	anObject when: Announcement do: [Transcript cr; show: 'gotcha']

despite the fact that no references to the block closure are saved anywhere.

On the one hand, allowing code like the above to work the way it does seems like a good thing. Even for "regular" objects rather than blocks as subscribers, sometimes it's convenient to be able to just attach some kind of a logger object to the announcer and let it hang there without bothering to explicitly arrange for a separate reference to it to keep it alive.

On the other hand, a reverse scenario is also possible, where we don't want subscribers to stay alive unless they have other reasons to exist. We could have relatively short-lived temporary objects, such as components of a UI, subscribe for events from a long-lived model object. Those temporary objects would need to be explicitly unsubscribed before they could become garbage.

There seem to be two points of view on this subject, one that the Right Thing is to require subscribers to unsubscribe themselves explicitly, as their duty in maintaining the overall solid object structure. The other is that C++ people also say it's good to maintain object structure by hand, while we know that it's nice when things just work. Both sides have their valid convincing arguments, and all together it looks like different scenarios can benefit from different behavior, or at least different people prefer different approaches and are ready to deal with the shortcomings of their preference.

Fortunately, Announcements can now easily support and mix and match both approaches, with strong as the default and weak as an option. Even better, support for weakness comes at no cost to the default strong behavior, and plugs naturally into the rest of the framework API.

As with the rest of the advanced API, subscriptions selection messages take center stage in configuring weakness . To weaken some subscriptions we do something like

    (anObject subscriptionRegistry subscriptionsOf: self) makeWeak

From now on, when there are no strong references to self, the object will get garbage collected and its subscriptions with anObject will disappear on their own.

It is possible to weaken subscriptions right when you create them, using a feature of subscription messages we have not yet talked about. The feature is that all subscription messages (#when:send:to:, #when:do:, #when:do:for:) answer the subscriptions they have just set up. So instead of a separate protocol for setting up weak subscriptions, all we need is send #makeWeak to the result:

    (anObject when: Foo send: #fooHappened: to: self) makeWeak.

This covers one side of announcer-subscriber relationship, where the subscriber wants to create susbcriptions that won't keep it alive. On the other side of the relationship, it is possible to configure the announcer so that all subscriptions created with it are weak by default. The announcer's subscription registry is the factory that actually creates subscriptions, and the class it instantiates to do that is a parameter. The default strong subscriptions are instances of AnnouncementSubscription. Weak subscriptions are instances of its ephemeral subclass WeakAnnouncementSubscription. So, in order to configure an object to always use weak subscriptions for its announcements, all we need to do is this:

    anObject subscriptionRegistry subscriptionClass: WeakAnnouncementSubscription

From this moment on, all new subscriptions set up with that object are created as weak. This does not affect already existing subscriptions.

This preference for weak subscriptions can be turned into the default for a particular announcer class, by hooking into the framework in a different place. A subscription registry for an object is originally created by the method #createSubscriptionRegistry. Instances of a class reimplementing that method as

    createSubscriptionRegistry

        ^SubscriptionRegistry new subscriptionClass: WeakAnnouncementSubscription

will always default to weak subscriptions, without the need to explicitly reset the subscription class in each. And on the flip side, just like you can individually weaken some subscriptions created by classes that default to strong, a subscriber can individually strengthen its subscriptions with classes defaulting to weak:

     (anObject subscriptionRegistry subscriptionsOf: self) makeStrong
   
     (anObject when: Foo send: #fooHappened: to: self) makeStrong

To be continued...

Announcements Framework

Advanced Announcements, part 3

December 5, 2005 20:30:25.243

Back to suspending subscriptions.

Another suspension option is #suspendWhile:ifAnyMissed:. It takes a second argument which should always be a no-argument block. It works just like #suspendWith: in that the subscriptions you send this to are suspended and don't deliver anything to their recipients while the block runs. In addition, they keep track of whether there have been any "missed calls". After the "while" block finishes, the second block is evaluated once if there have been any undelivered announcements while the first block ran.

This serves the use case of "I want to bunch up potential multiple updates". For example, the following code will suppress Foo announcements but will then ensure one of those gets announced as a summary if needed:

	(anObject subscriptionRegistry subscriptionsFor: Foo)
		suspendWhle: [...do stuff...]
		ifAnyMissed: [anObject announce: Foo]

On the recipient side, if we want to suspend response to updates from a certain object but then catch up with a single update, we can also do something like:

	(anObject subscriptionRegistry subscriptionsOf: self)
		suspendWhile: [...]
		ifAnyMissed: [self update]

Again, overlap between subscrptions suspended by nested blocks is handled correctly, in the sense that nested suspend requests don't affect the outer ones and vice versa. If an outer block suspends a subscription, and then an inner block suspends it again with #suspendWhile:ifAnyMissed:, the missed block will run if needed after the inner block ends, while the subscription will stay suspended until the outer block ends. Conversely, if an outer block runs with #suspendWhile:ifAnyMissed: and the inner block suspends the same subscription outright, and an announcement arrives to that subscription inside the inner block, the summary block will run after the outer block exits.

The third suspending option we need to cover arranges it so that a block of code runs in lieu of each delivery that would have happened otherwise. For example, the following code will count how many actual announcement deliveries would have occurred:

	count := 0.
	(anObject subscriptionRegistry subscriptionsFor: Foo)
		interceptWith: [count := count + 1]
		while: [anObject announce: Foo].
	^count

Interceptor block can take arguments, with the same interpretation as in handler blocks established by #when:do:. These open up quite a lot of options of what can be done by the interceptor.

For example, the above code counts deliveries. If there are five subscribers for Foo, and Foo has been announced twice, the count will be 10 for the ten deliveries that would have occurred. If we want to count how many actual announcements were broadcast, regardless of how many objects would have received them, we can do this:

	announcements := IdentitySet new.
	(anObject subscriptionRegistry subscriptionsFor: Foo)
		interceptWith: [:ann | announcements add: ann]
		while: [anObject announce: Foo].
	^announcements size

If the interceptor block has two arguments, it receives the announcement and the announcer, again just like in a regular handler. In the context of an interceptor block this probably isn't as useful. Since in order to get the subscriptions to intercept we start with the announcer and its registry, we typically know who the announcer is anyway.

The interceptor block can also take three arguments. In that case, the third argument is the subscription that has just been intercepted. Given that, the interceptor can find out the subscriber of the intercepted delivery. Coming back to our example, to count how many subscribers would have received the announcements we intercepted, we would do this:

	subscribers := IdentitySet new.
	(anObject subscriptionRegistry subscriptionsFor: Foo)
		interceptWith: [:a :o :s | subscribers add: s subscriber]
		while: [anObject announce: Foo].
	^subscribers size

Another important option the access to subscription gives us is writing transparent interceptors, those that don't prevent announcements from reaching their subscribers. The following code will silently count how many announcements have been announced, but other than that it will be business as usual and all announcements will safely make it to all of their subscribers:

	announcements := IdentitySet new.
	(anObject subscriptionRegistry subscriptionsFor: Foo)
		interceptWith: 
			[:announcement :announcer :subscription | 
			announcements add: announcement.
			subscription deliver: announcement from: announcer]
		while: [anObject announce: Foo].
	^announcements size

The final "subscription deliver: announcement from: announcer" is what you can use in interceptors to pass the announcement on to the intended recipient, conditionally or unconditionally at the end of the interceptor block.

I mentioned that an interceptor block can take the same arguments a handler block or a handler method can. Indeed, ordinary handler blocks and methods can also take the subscription as their third argument. However, in a regular handler, knowing the subscription that delivers the announcement is not quite as useful. Asking it about its subscriber is pointless when you are the subscriber, just as telling it to deliver the announcement when it is already in the process of being delivered.

One final note about interceptors is their behavior in case of nesting. Interceptors are additive. If you set up an interceptor on a subscription and then set one up in a nested block, both will run when the subscription attempts to deliver an announcement. This is in line with the overall philosophy that nested suspend and intercept requests are independent and don't affect each other's behavior. One notable consequence of this is if both interceptor blocks do "subscription deliver: announcement from: announcer", the subscriber will get the same announcement twice, once from each of the interceptors. Such is the nature of the beast.

To be continued...

Announcements Framework

Advanced Announcement Handling, part 2

December 5, 2005 2:18:02.844

Today's subject is suspending subscriptions.

Sometimes it's handy to be able to turn off some subscriptions temporarily, to let a piece of code run without the usual reaction the announcements it broadcasts would trigger. What subscriptions exactly should be turned off depends on the circumstances. We may want to turn off all subscriptions for announcements of an object, or only for a particular kind of announcements. Or a subscriber might want to stop receiving announcements from a particular announcer, or only a subset of those announcements.

Announcements framework takes care of all of these with a single method. The method suspends a group of announcements previously selected using the selection messages we discussed in the previous post. By combining those methods with the single suspending method we have a wide choice of what gets suspended without the need for multiple versions of the suspend method.

If an object wants to temporarily disable all the current subscriptions to its announcements it does:

	self subscriptionRegistry allSubscriptions 
		suspendWhile: [...]

or particular kinds of announcement

	(self subscriptionRegistry subscriptionsFor: Foo, Bar)
		suspendWhile: [...]

If self wants to temporarily stop receiving particular announcements from a particular object:

	(anObject subscriptionRegistry subscriptionsOf: self for: Foo, Bar)
		suspendWhile: [...]

And there is always the catch-all option to get #allSubscriptions, filter the collection with #select: or #reject: using an arbitrary condition based on the subscriber and announcementClass, and then send #suspendWhile: to the filtered result.

Note that #suspendWhile: is sent directly to the result of the selection messages. Obviously, what those answer aren't ordinary collections.

All subscription selection messages of SubscriptionRegistry answer instances of AnnouncementSubscriptionCollection, a subclass of OrderedCollection. This isn't a hack, this is a conceptual feature. What the framework does is elevates the concept of a group of subscriptions to first-class status. Some of the operations we want to do--suspending subscriptions is one of those--are the easiest to think of as operations on groups of subscriptions, and we do just that. We represent groups of subscriptions as collections with extra behavior appropriate for susbcription collections.

Here are some other points worth noting about suspending subscriptions.

What you disable is always a collection of specific subscriptions rather than the ability of an object to broadcast announcements. For example, the following

	anObject subscriptionRegistry allSubscriptions suspendWhile: [...]

disables all currently existing subscriptions of an object, but if new subscriptions are added to the object while the block runs, those new subscriptions will be active and will deliver announcements broadcast inside the block.

The previous feature fits the overall subscription-centric spirit of the framework, already mentioned when we discussed unsubscribing. As you remember, subsclass relationship is considered only when delivering announcements--a subscription for Foo will also deliver any subclass of Foo--but to remove a subscription for Foo you need to specify Foo exactly.

The same principle applies to subscription selection. "subscriptionsFor: Foo" won't select subscriptions for superclasses of Foo, even though those subscriptions would deliver instances of Foo when asked. (It's not surprising that the principle is the same, since unsubscribing relies on the same selection methods). This means that if you have this arrangement of announcement classes

	ValueChangeAnnouncement (abstract)
		AboutToChangeValue
		ChangingValue
		ChangedValue

and you want to suspend all three concrete classes, simply saying

	(self subscriptionRegistry subscriptionsFor: ValueChangeAnnouncement)
		supendWhile: [...]

will not do, as this won't match any of the concrete subclasses. We already considered this feature applied to unsubscribing, and in that context it wasn't quite as bad. After all, a subscription is something you create explicitly when you set up your object structure. It isn't a big stretch to say that when you dismantle that structure you should match what you did to create it.

In the context of suspending, it's different. Even considering that we can list classes using an announcement set as "subscriptionsFor: AboutToChangeValue, ChangingValue, ChangedValue", this code is fragile and will break if we add a new ValueChangeAnnouncement subclass. It's also plain tedious.

This is a good use case for another way of creating announcement sets. The right solution is this:

	(self subscriptionRegistry subscriptionsFor: ValueChangeAnnouncement andSubclasses)
		suspendWhile: [...]

#andSubclasses sent to an announcement class creates an announcement set with that class and all its subclasses, avoiding both the tedium and the need to keep the code in sync with the class structure.

Of course, #andSubclasses can be used in any context where announcement sets are allowed. For example, you can unsubscribe an object from all ValueChangeAnnouncement subclasses it previously subscribed to in one shot by saying

	announcer unsubscribe: self from: ValueChangeAnnouncement andSubclasses

Potentially, you could even use #andSubclasses when subscribing:

	announcer when: Foo andSubsclasses do: [...]

though in this case it's not only pointless but the result is likely unwanted, too. It's pointless because a subscription for Foo alone will deliver any subclass of Foo. It's unwanted because given this class structure:

	Foo
		Bar
			Zork

the code will create three subscriptions, one for each class. When the announcer later announces Zork, that same announcement will be delivered three times, once by each subscription. (Did I mention the framework was very subscription-centric?)

Suspend requests can be nested, and if sets of suspended subscriptions of such nested requests overlap, the framework does the right thing. The right thing being that when you suspend a subscription for the duration of a block, and then inside that block suspend the same subscription again for the duration of an inner block, the subscription will not be reactivated after the inner block ends and will stay suspended until the end of the outer block.

To be continued...

Announcements Framework

Advanced Announcement Handling, part 1

December 4, 2005 1:30:20.936

Announcements-related methods in Object implement only the bread-and-butter stuff--the basic subscribing, unsubscribing and announcing functions we've discussed so far. For anything advanced (read less frequently used) you need to go to the object's subscription registry. One reason for this separation is to avoid (and in the distant future when triggerEvent is retired, reduce) Object's API bloat. triggerEvent has 44 methods in Object, plus about 15 #evaluate and #evaluateWithArguments: sprinkled around various classes. All the current version of Announcements has "out in the open" are 13 methods in Object.

The registry is the gateway to the rest of announcement API. This is another important point--it's a gateway rather than just the container of the rest of the API. In Announcements, the API is not simply split in two with the less frequently used stuff moved out of Object. The overall API design is different from triggerEvent in that it's focused on capturing the key concepts as objects you can work with, rather just adding a bunch of methods to Object for the functions the users are expected to need. This leads to a smaller API that goes much further.

If this still sounds vague at this point, I hope this and the following posts will illustrate what I mean. Also note that if you want to look at the actual code, for this and the following posts you'll need an up-to-date version (at least "preview-13.0").

So, suppose we want to do some advanced stuff and we got a hold of an object's registry.

To start with the simplest thing, you can send messages #isEmpty and #notEmpty to it to find out if it has any subscriptions--in case you want to do something differently when it doesn't compared to when it does. (I don't know why you might want to do that, but Travis does, so ask him if you are curious).

Most important in the grand scheme of things are the four selection messages that select the currently existing subscriptions:

	allSubscriptions
	subscriptionsFor: announcementClassOrSet
	subscriptionsOf: anObject
	subscriptionsOf: anObject for: announcementClassOrSet

These are fairly self-explanatory. "Of:" means we want subscriptions where the object we specify is the subscriber, "for:" means we want subscriptions for the specific announcement class or class set, and both mean we combine both of those conditions.

Once we've found the subscriptions we are interested in, we can do a number of things with them. One is we can unsubscribe from them. As you remember, Object's #unsubscribe: and #unsubscribe:from: handle the two most common cases: you can say

	anObject unsubscribe: self

or

	anObject unsubscribe: self from: Foo

Somewhere down below these actually do

	registry removeSubscriptions:
		(registry subscriptionsOf: self)

and

	registry removeSubscriptions:
		(registry subscriptionsOf: self for: Foo)

Registry API enables less considerate unsubscribing options, those that affect multiple subscribers at once and for that reason are not included in the basic API in Object:

	registry removeSubscriptions: registry allSubscriptions

zaps all subscriptions no matter who subscribed and for what announcements. (Just trashing the whole registry with "anObject subscriptionRegistry: nil" might work but isn't nice, because as we'll see in future posts, the registry might not be an object you can just throw away. Incidentally, #subscriptionRegistry: is a private method).

	registry removeSubscriptions: 
		(registry subscriptionsFor: Foo)

removes all subscriptions for the announcement class Foo, no matter the subscriber.

So far this might not have looked very impressive. After all, one subscription removal method combinable with four selection methods sounds nice in theory but in practice, why not just make the registry understand #removeAllSubscriptions, #removeSubscriptionsFor: and all those other cases?

It's because elevating subscription selection to the level of public API (and making subscriptions real objects in the first place) gives us tremendous flexibility, nearly for free. Even before we get to other advanced things in followup posts, the simple fact that subscriptions are objects and we can work with their collections allows the framework user to "just do" things triggerEvent had to provide for specifically. Or not provide at all.

For example, in triggerEvent there is a method Object>>hasActionForEvent: to test, in Announcements speak, whether an object has any subscriptions for an event. Why, finding this out with Announcements is as simple as

	(registry subscriptionsFor: Foo) isEmpty

Or just as easily we can do

	(registry subscriptionsOf: anObject) isEmpty

to check whether a particular object is a subscriber--something triggerEvent doesn't do.

Or to find out what announcement classes are in demand at the moment:

	(registry allSubscriptions
		collect: [:each | each announcementClass]) asSet

Or, similarly, to get a collection of all the current subscribers:

	(registry allSubscriptions
		collect: [:each | each subscriber]) asSet

Or we can remove all subscriptions whose subscribers we don't like for whatever reason:

	registry removeSubscriptions:
		(registry allSubscriptions select: 
			[:each | self dislikes: each subscriber])

or the same thing, but like this:

	registry allSubscriptions do:
		[:each |
		(self dislikes: each subscriber) ifTrue:
			[registry removeSubscription: each]]

I hope this gives a a taste of "capturing the key concepts as objects you can work with, rather just adding a bunch of methods to Object for the functions the users are expected to need".

To be continued.

Announcements Framework

Announcements: Subscription Management

December 1, 2005 16:51:38.332

How exactly subscriptions are stored is typically not important for the framework end user. However, we'll look at the overall picture because some bits of it are good to know for the more advanced scenarios.

Subscriptions to announcements are managed by instances of two classes, SubscriptionRegistry and AnnouncementSubscription. A SubscriptionRegistry is associated with an announcer object, one registry per announcer. A registry uses instances of AnnouncementSubscription to record individual subscriptions for the announcements of that object.

A registry for an object is accessible by sending the #subscriptionRegistry message to the object. This will always answer a SubscriptionRegistry, creating and associating one with the object if it doesn't exist yet. A variant of that message, #subscriptionRegistryOrNil, answers a registry only if one is already set up, or nil if it isn't.

The primary reason SubscriptionRegistry is publicly accessible like this, even though it works entirely behind the scenes in basic announcements-related tasks, is that it provides second-tier subscription management protocol. We'll look at that protocol in the next post.

Any object can be an announcer, so the implementation in Object uses an external EphemeronDictionary which maps objects (by identity) to their registries, similar to DependentsFields and EventHandlers of change/update and triggerEvent frameworks. One difference is that the dictionary is kept in a shared variable Registries of the SubscriptionRegistry class rather than in a shared variable of Object.

If a class wants to store its registry locally, in an instance variable similar to 'depedents' in Model or 'handlers' in EventManager, it should as a minimum implement two methods: #subscriptionRegistry: to store the argument in that variable and #subscriptionRegistryOrNil to return the value of the variable. Ideally, it should also implement #postCopy to nil out the registry in the copy (with the default mechanism objects are mapped to their registries based on identity, so a copy automatically loses the original's registry).

In future, the framework may provide Announcer class as a ready-made superclass for those classes that need such local subscription storage. There is no such class at the moment because Pollock, the first planned major client, doesn't need it. In Pollock, local handler storage is implemented from scratch in those superclasses that need it. Apparently, there are often more important reasons for choosing a superclass than inheriting two or three trivial methods.

Announcements Framework

Announcements: Announcing and Vetoing

November 28, 2005 18:05:56.999

Announcing is even simpler than subscribing and unsubscribing. The one message that does all the work is #announce:. The message is sent to the announcer-to-be, and the announcement is passed as the argument.

The argument can be either an announcement instance or a class. If it is a class, the actual instance to announce is automatically created behind the scenes, so you can do either

	self announce: StuffHappened

or

	self announce: (StuffHappened new explanation: 'foo')

The way this works is the first thing #announce: does is it sends #asAnnouncement message to the argument. The result of the message is used as the actual announcement to announce. Announcement instances answer self to #asAnnouncement, and Announcement classes answer self new. This also means that in principle any object that properly implements #asAnnouncement can be used as the argument of #announce: (though as an independent feature this doesn't seem to be obviously useful).

As an extended example, and to introduce announcement vetoing, here is a complete implementation of ObservedValue>>value: method updated to use announcements.

value: anObject

	| changedAnnouncement |
	self announce: (AboutToChangeValue from: value to: anObject)
		ifVetoed: [^self].
	self announce: (ChangingValue from: value to: anObject).
	changedAnnouncement := ChangedValue from: value to: anObject.
	value := anObject.
	self announce: changedAnnouncement

One moderately interesting feature here is how we create ChangedValue in advance to preserve the value we are about to overwrite. But the really interesting one is the #announce:ifVetoed: message used to signal AboutToChangeValue.

As we discussed a couple of posts ago, AboutToChangeValue is the first phase of a three-phase event notification. With it, the announcer notifies the interested parties of its intent to do something. If one of the subscribers voices an objection ("vetoes" the announcement), the announcer is expected to abort the intended action.

The #announce:ifVetoed: message is for implementing this vetoing behavior on the announcer side. The first argument is the same as in the #announce: message. The second one is a block evaluated if the announcement has been vetoed by at least one subscriber. In our example, we simply leave the #value: method if that happens.

On the subscriber side, an announcement is vetoed by sending the #veto message to it. For example, here is a subscriber that ensures an ObservedValue rejects anything but a symbol.

	aValue
		when: AboutToChangeValue
		do: [:change |
			change newValue isSymbol ifFalse: [change veto]]

This sending of #veto to change (an instance of AboutToChangeValue) eventually makes the ifVetoed: block in the implementation of #value: to run, aborting the change.

It's interesting to compare vetoing in Announcements and in triggerEvent.

In triggerEvent, since there was nothing to send anything like a #veto message to, handler code vetoes an event by raising a VetoAction exception. Instead of "change veto" above, the code would read "VetoAction raise". The signaler would set up a handler for VetoAction to respond to it, something like

	[self triggerEvent: #aboutToChange]
		on: VetoAction
		do: [:ex | ...]

The difference in Announcements is not only that the guts are hidden inside a clean protocol of #veto and #announce:ifVetoed:. In addition, Announcements don't rely on exceptions for communication. Because an announcement is a tangible object, vetoing simply sets a flag in the announcement itself. The announcer then checks if the flag is set. In fact, #announce:ifVetoed: is only a convenience message and the #value: method above could instead be doing this:

	| intent |
	intent := AboutToChangeValue from: value to: anObject.
	self announce: intent.
	intent isVetoed ifTrue: [^self].
	...

The exact behavior of this code (which is how #announce:ifVetoed: is implemented) is different from VetoAction in that raising VetoAction effectively cancels delivery of the event, while vetoing an announcement doesn't. In other words, if the first subscriber vetoes an announcement, the announcement is still delivered to all the other subscribers. In triggerEvent, when the first event handler raises a VetoAction, none of the other handlers for the same event are invoked. The new behavior is a deliberate choice, to give vetoable announcement handlers as a whole a more deterministic behavior.

This implementation of vetoing illustrates how announcements, by virtue of being objects, make it easier for the announcer and subscribers to communicate. This communication can be even more involved. As an example, here is an implementation of voting alternative to vetoing, so that the announcer aborts an action when the majority of subscribers, rather than just one, object. One way to implement this is an Announcement subclass Vote, with an instance variable "bias" initialized to 0. Vote would have methods

voteFor
	bias := bias + 1

voteAgainst
	bias := bias - 1

isApproved
	^bias > 0

Subscribers would listen to Vote (or perhaps its specific subclass) and send either #voteFor or #voteAgainst to it. The announcer would run the poll as

	| vote |
	vote := KickUserVote new.
	self announce: vote.
	vote isApproved ifTrue: [...]

Next (13 total)