After part 7 of this series came out, I got some interesting feedback and a question in particular stood out. Milan Kovac asked how did MiNT handle things differently regarding applications waiting for evnt_multi() to return?

To clarify, he’s referring to MultiTOS, of which MiNT was the core, and how GEM AES behaved differently in that environment.

That question was sort of out of the scope of the original topic, but it got me thinking and I realized it sort of touched on a few other issues with AES we hadn’t talked about yet.  So here we go, and Milan, if you read through the whole thing your question gets answered eventually.

On a side note… when I write these articles, I often have the GEM source code open in a window in the background so that I can make sure I’m not remembering something incorrectly. Once again I’ve noticed how the original GEM source code is very terse and poorly commented. Function names are generally no more than 6 or 7 characters long, even with an underscore taking up a spot some where. Names of variables or structure elements are about the same. For example:

EVSPEC mwait(mask)	 	 
EVSPEC mask;	 	 
{	 	 
 rlr->p_evwait = mask;	 	 
 if ( !(mask & rlr->p_evflg) )	 	 
 {	 	 
 rlr->p_stat |= WAITIN;	 	 
 dsptch();	 	 
 }	 	 
 return(rlr->p_evflg);	 	 
}	 	 

Of course, that’s not really that unusual for code written back in those days.  But gosh, it often seems like the GEM source code takes things to extremes. Someone really ought to dump this stuff into a modern IDE and refactor the source code to give things meaningful names.

What is MiNT and MultiTOS?

Just in case anybody doesn’t know, MiNT is a multitasking kernel created by Eric Smith while he was a university student. He was trying to port over some GNU libraries and utility software to the Atari ST computers, and the problem was that the TOS operating system on the Atari was lacking certain functionality required by the code.  At first, he modified the individual GNU programs and libraries as needed, but eventually decided that instead of changing the libraries and programs, it’d be easier overall to create an extension to TOS to add the required functions. MiNT was the result.

Originally the name stood for MiNT Is Not TOS.  It basically hooked into the BIOS and GEMDOS and provided the ability to do preemptive multitasking, among other things.

MiNT caught the attention of the programmers in the TOS development group at Atari Corp., including Allan Pratt, the programmer who maintained the GEMDOS portion of TOS. He was impressed with MiNT and eventually started talking with Smith about incorporating it into a new, preemptive multitasking version of TOS. Smith was later hired by Atari in 1992, and in 1993, after a lot more work on everything, MultiTOS was released.

Now the name stood for MiNT Is Now TOS.

Unfortunately, the release of MultiTOS came only shortly before Atari decided to focus all of its development efforts on the new Jaguar game console, effectively ending the active life cycle of the ST computer series.  But that’s a story for another day.

Multitasking 101

Let’s cover a couple of basic concepts regarding multitasking that we haven’t talked about before, or to refresh your memory briefly.

There are two main types of multitasking, cooperative and preemptive.  Task switching is what we call it when the system stops one program’s execution and starts another one. Task switching back and forth quickly enough makes it look like all the programs are actually running at the same time. And in fact, on a modern computer with multiple processor cores, your programs probably are actually running at the same time.

Vanilla GEM features cooperative multitasking. “Cooperative” means that the system doesn’t automatically switch from one program to the next. Programs have to cooperate by doing some specific operation before task switching occurs. In vanilla GEM, that specific operation is making a call to the AES event library. Every time a GEM application calls an event library function, it may end up waiting for the event to occur, and it may end up waiting for other applications as well.

MiNT features the other flavor, preemptive multitasking.  The main thing that’s different about preemptive multitasking is that the program doesn’t have to do anything special to facilitate task switching.  It can happen at any time, regardless of what the program is doing at the moment. (There are exceptions to that which we’ll ignore for now.)

Under a preemptive multitasking system, each program, also known as a process, has at least one thread, which is what we call a distinct piece of code being executed.  A process may own more than one thread, but it always has at least one.

Preemptive multitasking systems typically operate using a timer-based interrupt.  Each thread is given a certain maximum amount of time to execute before the system stops it and gives control to another thread. Each chance that a thread gets to run is called a “time slice“.

There are a couple of things that can happen to make a thread end its time slice early, or to make the system skip a turn for a particular thread.  For example, a threads can voluntarily end its time slice early.  There are a variety of reasons it might do this, but waiting for an asynchronous task to finish is a common example. Threads can also choose to sleep and wait for a certain amount of time to pass.  This is a bit different from simply ending a time slice, as it also means that the system will skip past that thread for future time slices, until the requested duration has passed.

It’s also possible for a thread to be blocked waiting for a semaphore or MUTEX (mutually exclusive) object.  These are software mechanisms that are used to allow a thread to wait for a certain condition, or to control access to something in the system that can only be safely accessed by one thread at a time.  A good example would be something trying to send data to the printer port.  If you had several programs trying to send output to the printer at the same time, the result would be a lot of wasted time and paper.

The idea of a MUTEX is that a process has to ask for exclusive access to such items before it can use them.  Upon receiving such a request, the system will then do one of the following:

  • Grant access if the requested item is currently available. The item now belongs to the requesting process until it releases it or until that process ends.
  • If the item is already in use by another process, then the system will block the thread until the item is released.  In some cases, you can optionally have the system return an error indicating that the item is not currently available.

All this means that threads don’t always execute in the same order and frequency. It’s not always A-B-C-A-B-C, etc. It actually gets even more complicated when you consider things like thread priority settings, but that’s another left turn down this tangental road so let’s not.

There are many other important aspects to multitasking, but they are pretty much beyond the scope of this article.

GEM Applications Under Vanilla GEM

Under vanilla GEM, the core of the cooperative multitasking system was contained in the AES event library.  Whenever program “A” calls an event library function like evnt_multi, and there’s no event of the requested type in the queue waiting to be processed, the event library calls a dispatcher function that checks to see if events are waiting for any other GEM applications, and if so, performs a task switch.

That is, incidentally, the purpose of the mwait function shown above as an example of the GEM source code. As simple as it is, that function is where GEM makes the decision to pass control back to the same program, or task switch to another.  It’s called by each of the various public functions of the AES event library, like evnt_multi or evnt_mouse and so forth.

The mask parameter indicates the types of events the application is requesting, and this function compares that against the events that are available.  If nothing is available, it calls the dsptch function, which is responsible for vanilla GEM AES’s cooperative task switching.

If the dspatch function found events waiting for program “B”, which by definition in vanilla GEM would currently be waiting for an event library function to return, then it would perform a task switch to that application so the events could be processed. Eventually, program “B” would make another call to an event library function, and maybe this time program “A” gets control back, or perhaps program “C” is called instead, depending on what’s waiting in the queue of unprocessed events. In this way, all of the applications currently loaded into the system would get a chance to process their events and interact with the user.

This sort of task switching is essentially the same general process that’s used by preemptive multitasking systems, except that it relies on programs making calls to the AES event library. Note that non-GEM applications couldn’t be included in this setup, since they don’t make calls to the event library. Whenever you ran a non-GEM application, it essentially blocked GEM applications until it exited.

GEM Applications Under MultiTOS (MiNT)

A well-designed GEM application that handles events properly and doesn’t try to draw to parts of the screen that it doesn’t “own” should work fine under MultiTOS.  In fact, programs which occasionally need to suspend event processing while doing something else will arguably work better under MultiTOS, since they will no longer freeze up the whole system.  The program’s own UI will be blocked until it starts making event library calls again, but other programs will continue to operate normally.

But as to how it works…

Quite a lot about GEM AES was changed for MultiTOS, but we’re only going to talk about certain things here.

Under MultiTOS, the MiNT kernel is now responsible for handling task switching between applications, rather than the AES event library.  Each application has at least one thread, including non-GEM applications.  Additionally, the AES maintains its own process that corresponds to the “original” single process in vanilla GEM, which is responsible for managing the user’s interaction with UI elements like the menu bar, window frames, etc.

So, if the event library is no longer doing its own task switching, what happens if program “A” calls the event library to request an event, and the desired event is not available?

Instead of doing its own task switch, AES will tell MiNT “I’m done for now” for the current thread’s time slice, prompting MiNT to perform a task switch.  The AES code is actually shorter and more simple than under vanilla GEM.

On the next time slice for program “A”, the first thing it will do is check again to see if the desired event is available. If not, then it will once again release the time slice. This will repeat until the event becomes available. Thus, programs which are waiting for events use very little CPU time; just enough to see if there are events pending.

AES MUTEX Items

We talked about MUTEX items earlier. While it doesn’t use that terminology, GEM AES has always had something that acts as a MUTEX, and it’s something all GEM programmers should know about.  When an application does a window update, the process is wrapped with calls to wind_update. This is intended to block any other application from starting a window update while another one is already happening.  It’s intended to provide exclusive access to the screen to a single application at a time.

To accomplish this, the original vanilla GEM code for wind_update ties into the event library.  It adds a special “mutex released” item to the list of requested events so that ending the update has to occur before another application can be called.

Under vanilla GEM, the wind_update function didn’t actually check to see if an application had locked down the screen.  It relied solely on the mutex event to block other applications from being able to do anything, since they wouldn’t be running until AES had events for them to process. However, under MultiTOS, another application might not have been waiting for an event to occur.  In that case, the application will keep on doing whatever it was doing. Unfortunately, this could eventually include a window refresh, so under MultiTOS, the wind_update call gets significantly more complicated than it was under vanilla.

Don’t Cross The Beams!  Whoops!

Finally, we’ve come around to another flaw in vanilla GEM AES.  From day one, GEM was supposed to be a multitasking system, but other than using the wind_update function to manage, somewhat imperfectly, screen output, it didn’t include any sort of a general purpose MUTEX or semaphore library so that applications could avoid stepping on each other when they all wanted to use the same resources at the same time.

It always amazed me that this was never revealed to be the big problem it had the potential to be.  I guess users were really just so used to interacting with just program at a time that it rarely came up. But consider how many things in the system could fail if more than one application wanted access.  Just to name a few:

  • Serial ports.  What if you had a FAX program and a telecommunications terminal program going at the same time?  One as the main application, the other as a desk accessory?  Until the Mega STE and TT030 machines, there was only one serial port so this would have definitely resulted in a conflict as both programs tried to access the same port and modem at the same time.
  • Printer port.  Two programs trying to print something at the same time could step on each other unless both were doing it through GDOS.  Under GDOS,  once the printer workstation is opened by one application, any attempts by other applications to open it will fail.  Unfortunately, the application won’t have any idea why it failed because VDI doesn’t return error codes.
  • MIDI ports.  Basically the same problem as the serial ports, except with different kinds of program.
  • Sound.  Sound on the ST computers was mainly done by banging on the sound chip, either directly on the hardware registers, or via the XBIOS DoSound call. Either way, two programs trying to this at the same time would result in some interesting results that would hurt your ears.

Basically, when it came to these items and other similar system resources, AES basically relied on the idea that a program would start using the item and finish it between one event library call and the next, when no other programs could be called and start trying to use the same resource.

That sounds pretty risky, but actually it more or less worked most of the time.

MultiTOS and Mutex

When MultiTOS came out, MiNT added the basic capability needed to create mutex objects, but except for defining a couple of specific hardware resources like the SCSI or ASCI ports, which were used by the system itself, there were no preset definitions for anything that applications could rely on.

Now that it had the low-level functionality to do the job, you would think that someone would have added some functions to GEM AES to do basic application-level resource management.

You’d think that, but you’d be wrong.  AES continued to ignore the problem under MultiTOS.

Sigh.

Don’t get me wrong… I loved MultiTOS when it finally got to be more or less stable, and I used it on a daily basis long before it even got to that point.

Of course,  at the time, my machine at the office was a TT030 with maxed-out RAM, and a big 320mb hard drive.  And it was reasonably useable on the Falcon030 too.  What about on the older machines running at 8 mHz?  Even with the max 4mb of RAM, I avoided ever really using that setup. So I really couldn’t tell you how badly it sucked.  I was just pretty sure it did.

And by the way, 320mb was big for a hard drive back then.  Honest.  But even so, even with a relatively nice system like what I was using, we all knew how easy it really was to do something that just plain wouldn’t work.

Maybe if Atari had kept going with development on the ST series, some of those issues would have gotten fixed.  We weren’t unaware of them, in many cases, but there was only so much we could do with the manpower and time available.  And then, of course, the Jaguar came along and we all shifted gears to focus on it.

It’s really kind of ironic, because the last two or three years worth of TOS development had seen far more improvements and new functionality added to the system than the previous six years had.

Sigh.