[Squeak-e] Comments on Anthony's "...Shared Smalltalk"

Andreas Raab andreas.raab at gmx.de
Wed Feb 12 15:49:45 CET 2003


> Andreas, I'll just cc the list in response, since this may be 
> interesting and related to our writing a squeake processor.

Fine with me.

> I am not sure, but I am thinking that this interruptability 
> breaks the event-loop model.

Yes, most likely so. This is a (continuous) time-driven framework not a
(discrete) event-driven one.

> What are the entry points you use for suspending a script until an 
> event occurs (#waitUntil:), creating and running a new script 
> (#runScript:), triggering an event (#signalEvent:)?  I tried 
> following the senders until it got to something that looked like
> public protocol, but I keep running out of senders. :)

You're right - this is slightly confusing since I am still experimenting
with stuff. The essential entry points are:

* #startScript:[when:[withArguments:]] - this says that we want to start
(asynchronously) after some event occured. For example:
	button startScript: #onMouseDown when:{button. #mouseDown}.
The first argument is the "script descriptor" which can be either a symbol
or a block. The second one is an event specification consisting of
receiver/eventname pairs. Optionally, you can provide additional arguments.
Here are some samples:
	button startScript:[button color: Color red] when:{button.
	button startScript: #color: when:{button. #mouseUp}
withArguments:{Color white}.

* waitUntil: - this says we want to wait until the given event is triggered.
the argument is the name of the event to trigger, its return value is the
event that triggered the wait to complete.

* stopScript: - this says we want to *gracefully* stop a script (which is
different from #terminateScript:). The argument is a script descriptor which
can be matched against the existing scripts.

That's the essence of it. I have been playing around with more complex
control structures at various levels but I am still somewhat unhappy with
them. There are also a few support mechanisms of which the most important
one is:

* catchAnyOf:during: - which catches the occurance of any events in the spec
(specified by the first argument) during the evaluation of some block
(second arg). For example:
	self catchAnyOf:{button. #mouseDown} during:[
		self wait: 10.
will tell you if the button was pressed while you were waiting.

Note that there are many subtleties in this regime. The most important ones
to keep in mind are:

* event masking: Any "inner handler" for an event will mask any "outer
handler". This is a non-issue for strict blocking waits but a biggie when
you make control structures. For example, a handler like:
	outer := self catchAnyOf: {foo. #bar} during:[
		foo waitUntil: #bar.
would return nil to the outer handler since the inner one (established by
using #waitUntil:) will catch the event.

* multiple script start: A script cannot be easily "started twice", e.g., if
a script is already running it will not be triggered by the occurence of the
same event again. For example:
	foo startScript:[foo waitUntil: #mumble] when: {foo. #bar}.
	foo signal: #bar.
	self wait: 1.
	foo signal: #bar.
	self wait: 1.
	foo signal: #mumble.
will not run upon the second signal. This is to a large extend for allowing
error handling to be more user friendly. For example, something like:
	self startScript:[self halt] when:{World hand. #mouseMove}
will work perfectly fine. Since there is a need for allowing the same script
to be triggered multiple times even if running I have made a fallback (which
is really a workaround until a proper solution is found) for "special system
scripts" (such as Hand>>processEvents ;). See #isSpecialSystemScript:.

* event dropping: If events are triggered multiple times, you will only see
the last event by default. So for example,
	event := self catchAnyOf: {foo. #bar} during:[
		foo signal: #bar with: 42.
		foo signal: #bar.
will answer the *last* event triggered. You can obtain all of the events
that have been triggered in the mean time by using #withDroppedEventsDo: on
the event but the queue size for remembering dropped events is (by default)
limited to 100 (I think...).

> Certainly the pattern of creating a new ProcessScheduler and defining 
> the Processes that use it as non-preemptable and uninterruptable, *at 
> least among each other*, may allow this minimal Squeake engine to be 
> built.  My DispatchSecretary is slow and needs replacing.  Could we 
> just use the ScriptProcessor for our squeake event-loop or should we 
> roll our own?

I'd be delighted if you'd use it! That is as long as you keep me in the loop
about any "system level changes" to it ;-) 

  - Andreas

More information about the Squeak-e mailing list