I don't really think I agree with this. These kinds of operations are by their very nature "multi tasking". I/O *should* be processed behind the scenes IMO. And since the focal point of IPC is very clearly definable, complexity isn't much of an issue. There is the queue out outgoing messages that is shared, and there is the incoming message dispatching. Protecting access to the output queue is trivial. For incoming messages, there's a design issue of wheather to process messages in a separate event process or let the calling code provide the process (or something else entirely).
The incoming messages are precisely the hard part. Those guys will make big changes to the model, and they'll trigger UI updates. How do you manage these changes, if the messages are being processed outside of the UI thread? You either have to put locks everywhere -- meaning you always wonder whether you have the locks set up correctly -- or you have to explicitly forward the updates over a message queue -- meaning you have to work out an object model for the updates.
The single-threaded version doesn't have these concerns.
But it's not a major block in the road IMO. For my Java IRC implementation I opted to use an event dispatching thread to handle events in a listener pattern fashion because I thought it was the obvious choice. It never even occurred to me to use some kind of polling API :)
In the code in question, there is just one polling loop; the rest of the code is driven via direct method calls and on event broadcasts. The vast simplification is that none of the code worries about synchronization; the message handlers can just update the model and call #changed: directly.
Maybe I'm in the minority. But I've never really had a problem with multithreading; as far as I can recall I've never ran into a deadlock bug for example. IMO the advantages by far outweight the disadvantages.
Well, I don't have much personal experience with this, either. Then again, I tend to write sequential code! I can give three examples of other peoples' code with threading problems, though, in increasing order of vagueness:
1. The Windows Squeak VM and its handling of sockets. Each socket gets at least one thread, and I've been told that socket shutdown, for example, is extremely complicated.
2. The sophomore class I'm an assistant for is using Squeak, and the newsgroup has several posts from people whose images are locked up.
From the descriptions, it sounds like threading errors to me -- one busy
loop at a high priority, and bye bye image.
3. The lore of both CS departments I've spent time in is that it's risky to log into department machines when students have threading assignments -- the machines tend to fill up with processes and lock up.
I can also argue this from principle, too, but I won't bother unless requested. Surely most people agree that threads are error prone, and that the errors they generate tend to be hard to track down.
Second, there are cases where polling doesn't have the ills that are usually ascribed to it. In particular, polling works well when failed polls are cheap, and where polls can happen infrequently. If a problem meets both of these criterea, then polling will work fine. CPU usage will not be abnormally high, and responsiveness will be good. IRC meets both criteria, while HTTP serving probably does not.
This might be acceptable in practice. I'm a purist though; I don't like polling as a matter of principle unless there's a *really* good reason for it (such as implementing a progress bar when you don't want to trigger an update after each I/O operation).
At the moment I have Squeak running an IRC bot on a server. It constantly hogs about 0.5-2% of the CPU - probably because I'm invoking processIO four times per second.
So you are claiming that the CPU usage is actually significant? I disagree, and provide two arguments. First, the CPU load in Squeak is typically a couple of percentage points anyway, even *without* IRC running. Second, the following code is a direct timing of the polling overhead:
c := IRCConnection new. c openView. "connect to a server and wait for initial messages to go by" [ 1000 timesRepeat: [ c processIO. ] ] timeToRun "40"
40 milliseconds for 1000 polls is not a CPU drain. And note, this code hasn't had any time devoted to optimization -- there is probably room for improvement, should it ever become a concern! Furthermore, note that when the IRC connection gets busy, the overhead goes *down*; in the extreme case where every poll finds work to do, the overhead is *less* than that of threading plus synchronization.
Overall, if you argue from purity, then there is little I can say. If we talk about concrete measures, however. it seems that avoiding threads will make this system simpler, it will avoid some hard bugs, and it won't significantly affect performance.
-Lex
squeak-dev@lists.squeakfoundation.org