Newsgroups: comp.lang.smalltalk,comp.programming.threads
Path: cantaloupe.srv.cs.cmu.edu!nntp.club.cc.cmu.edu!goldenapple.srv.cs.cmu.edu!das-news2.harvard.edu!fas-news.harvard.edu!newspump.wustl.edu!data.ramona.vix.com!sonysjc!sonybc!newsjunkie.ans.net!newsfeeds.ans.net!news.aloha.net!svr1.gstis.net!news-chi-8.sprintlink.net!news-pull.sprintlink.net!news.sprintlink.net!news-peer.sprintlink.net!howland.erols.net!blackbush.xlink.net!ins.net!heeg.de!uucp
From: Hans-Martin Mosner <hmm@heeg.de>
Subject: Re: Multi-threading in smalltalk
Content-Type: text/plain; charset=us-ascii
Message-ID: <332D0470.3D06@heeg.de>
Sender: uucp@heeg.de
Content-Transfer-Encoding: 7bit
Cc: Rajanish Calisa <rcalisa@emails.com>
Organization: Georg Heeg Objektorientierte Systeme
References: <858145254.8295@dejanews.com>
Mime-Version: 1.0
Date: Mon, 17 Mar 1997 08:44:32 GMT
X-Mailer: Mozilla 2.01I [de] (WinNT; I)
Lines: 63
Xref: glinda.oz.cs.cmu.edu comp.lang.smalltalk:52956 comp.programming.threads:4148

Rajanish Calisa wrote:
> 
> Hi,
> 
> I am a smalltalk beginner and started using VisualWorks couple of
> weeks back. The application that I am developing requires certain
> tasks to be fired up simultaneously. I figured out that something
> like this can be done using '#fork' message sent to BlockClosure.
> 
> I also noticed that the LWPs created by '#fork' seem to be
> non-preemptive in the sense that if I have more than one process
> at the same priority, then only one LWP remains active until it
> relinquishes control by an '#yield'.
> What I ideally want is to schedule the processes in a round-robin
> fashion so that a computationally intensive task does not starve
> other tasks.
> You're right, the Smalltalk threads are non-preemptive in this sense.
It is possible, however, to implement a kind of round-robin scheduler
as a thread that runs at a higher priority and re-schedules the
threads running on a lower priority regularly. You seem to be using
VisualWorks which has a large number of priority levels to work with,
making the task a little bit simpler.

First, you probably don't want to re-schedule the user interface
thread. It is not sufficiently well protected against concurrent
resource usage. And you probably want it to have priority relative
to your long-running tasks. Therefore, you would best start your
background tasks at Processor userBackgroundPriority or so.
Any priority level less than Processor userSchedulingPriority
will do.

Then you need to fire up your round-robin scheduler. Here's a sketch
of what it should do:

priority := Processor userBackgroundPriority.
[[true] whileTrue: ["forever"
    (Processor processesAt: priority) > 1
	ifFalse: [(Processor suspendFirstAt: priority) resume].
    (Delay forMilliseconds: 100) wait]] fork

This wakes up every 100 msecs if the user has nothing else to do,
and checks whether more than 1 process at userBackgroundPriority
is runnable. If so, it suspends the first one and immediately
resumes it, which puts it at the end of its queue.

In effect, this gives you round-robin scheduling, but without any
fairness consideration. It is not easily possible to measure the
CPU time a process gets. One option would be to make the delay
shorter and only re-schedule the running background process when
it has been running for a number of consecutive checks.

Remember to start only one of these round-robin schedulers, unless
you're willing to create unusual and surprising effects. You might
either start it in your user interface when you start up the
long-runners, and terminate it when they're finished, or you might
start it in the application image as a process that runs forever,
persistenly modifying the semantics of background processes,
which is probably easier and less problematic in the case of multiple
instances of your user interface.

Hope this helps...

Hans-Martin
