• Updated 2023-07-12: Hello, Guest! Welcome back, and be sure to check out this follow-up post about our outage a week or so ago.

Why didn't the Original Mac OS have pre-emptive multitasking

The original vision for the Macintosh project, when it was led by Jef Raskin, was for a low cost $500 computer. It would have featured the 8 bit 6809 processor (with a 64K memory limit) running at perhaps 2 MHz, so multi-tasking was not viable for memory and performance reasons.

Leaving aside the matter of whether or not the Lisa had true pre-emptive multitasking (I'd argue that it was more akin to co-operative multitasking), implementing pre-emptive multitasking was non-trivial in the early 80s. It requires memory protection hardware (to avoid software programmes trampling all over each other), which Motorola didn't support for the 68000 - companies had to design their own hardware to do this. Furthermore the operating system needs to be written carefully to support multitasking and provide the illusion to a software programme that it is the only thing running. Pre-emptive task scheduling is complex to implement and must be written very efficiently.

To answer the final part of your question: Multitasking, like security, is hard to retrofit. No unitasking operating system was successfully converted to be multitasking, as far as I know. Windows 9x provided some degree of multitasking but proper pre-emptive multitasking didn't arrive until Windows NT, which was a written from scratch, and designed from Day 1 to be multi-tasking.
 

cheesestraws

Well-known member
requires memory protection hardware

edit: (apparently there are two threads on this topic and I thought I was replying to the other one: OP, this is very much not aimed at you)

Agree with @stepleton. Multitasking and memory protection are orthogonal. Another good example is the wonderfully clever Wimp2 hack for RISC OS which essentially third retrofitted pre-emptive multitasking back onto the thoroughly co-operatively multitasked OS. It worked far better than it had any right to.

This thread is in serious danger of becoming nothing but a pedant-off where everyone vigorously yells "But The Internet Agrees With Me", and at that point all meaningful conversation is impossible. But I'm going to make one last attempt to strike out for sanity: to me, from the perspective of practical application software engineering, the most useful dichotomy is "multitasking systems where the process voluntarily and explicitly gives up the CPU" (i.e., MacOS, RISC OS with "Wimp_Poll") and "systems where the process generally does not". The Lisa is definitely in the latter camp.

From the perspective of having written software for the Lisa, the strategies one adopts (or at least that I adopt) are more like those I would adopt in a fully pre-emptively multitasked OS with a fair scheduler than those I would adopt for, say, RISC OS.

Now, there are a lot of opinions about Lisa programming and architecture on the Internet from people who have never written Lisa software, and these I have to admit I view with a great deal of suspicion. I don't know how many people on this thread have written Lisa software, besides @stepleton and I, but those of you out there who have, I want to know what your strategies for dealing with Lisa OS multitasking look like.

Otherwise - people who want to know how the Lisa multitasks, no need to read speculation online, it's all documented in the OS reference manual. People who want to reify terminology and pretend that software strategies sit in nice boxes instead of being messy spectra everywhere, you are doing bad engineering and bad history.
 
Last edited:

Gorgonops

Moderator
Staff member
The Lisa could run XENIX or UniPlus with full UNIX-style multitasking.

In all fairness, so could machines like the TRS-80 Model 16/Tandy Model 6000, which had less sophisticated MMUs. Also, FWIW, the 8088 can run Xenix (and Minix/Coherent/QNX/etc); segmentation(*), for all the bad rap it gets, actually works pretty well for creating task memory spaces with something *like* relative addressing. (What the 8088 doesn't have is memory protection, of course. The 286 does.)

What neither the Lisa nor those Tandy machines with 68000 could do is properly handle a page fault, so while, again, both of them were capable of "virtual memory" through swapping they needed to wrap bubble wrap around it, neither can handle a true page fault. (I don't think even the "true UNIX" SUN-1 could handle one either, it wasn't until the 68010 SUN-2 that you could actually describe its memory management as "modern".) Not that this is a problem unique to the Lisa, just worth noting that it was dealing with the same issues as everyone else trying to make a "Supermicro" with 1980-vintage technology.

(* Random factoid: the TRS-80 Model 16's MMU was essentially a segmentation-based design, with just "system" and "user" contexts implemented with "offset" and "limit" registers; it's crude and makes inter-process shared memory a little awkward but it uses *much* less memory than an MMU using page tables. With hindsight being 20/20 I'm kind of curious why Apple "overbuilt" the Lisa as much as they did. It's genuinely sad that *all* that hardware is basically wasted when you convert a Lisa into a Macintosh XL.)
 
Last edited:

Gorgonops

Moderator
Staff member
Clearly it's confusing that two copies of this same thread exist; perhaps one needs to be locked and a reference stuck pointing to the other one, or maybe I should see if there's some sensible way to merge them.

Off (on?) topic, re: this:

Otherwise - people who want to know how the Lisa multitasks, no need to read speculation online, it's all documented in the OS reference manual. People who want to reify terminology and pretend that software strategies sit in nice boxes instead of being messy spectra everywhere, you are doing bad engineering and bad history.

I fully agree that, yes, all these things are on a spectrum and don't fall into neat little boxes. But it's also pointless to get huffy about it. (FWIW, your own post in the other thread implied linkage between the fact that the Lisa has "an MMU with memory protection" and "preemptive multitasking". So either we all need to be *highly specific* with our language or cut everyone a little slack.)

FWIW, Section 3.5 of the Lisa operating system manual referenced in the other thread, "Process Scheduling", specifically says this:

The Scheduler generally executes the highest-priority ready process. Once a
process is executing, it loses the CPU only under certain circumstances. The
CPU ls lost when there ls some specific request for the process to walt (for
an event, for example^ when there is an I/O request, or when there is a
reference to a code segment that is not in memory. A process that makes
any Operating System call may lose the CPU. The process gets the CPU back
when the Operating System is finished, except under the following conditions:

(bullet point list of reasons why the OS might decide to switch to a different process)

Because the Operating System cannot seize the CPU from an executing
process except in the cases noted above, background processes should be
liberally sprinkled with YIELD_CPU calls.


Processes are allowed to control other processes, and the OS takes the liberty of evaluating the scheduler whenever a process makes a system call, but my reading of this is if a process elected to go hog wild running its own code without making a system call then it's free to do so. That kind of reads like the OS cannot "Preempt" a process, unless I'm missing something. I haven't sat down and read the whole thing but a quick scan doesn't turn up anything about "timeslicing" or other active measures to enforce fair queuing.

(But on the flip side, if the OS can evaluate the scheduler on every system call, or even a subset of them, that certainly gives it a lot more *chances* to do scheduling without explicitly inserting "shares" into code than a lot of systems, so, sure, if we're grading on a curve it gets a lot closer to being preemptive than systems that explicitly *only* schedule if a foreground process specifically offers to surrender the ball.)
 
Last edited:

Crutch

Well-known member
This is very interesting.

That feels to me like cooperative multitasking by definition, but a model of such where that cooperation is kind of mandatory. ;) (Again never programmed a Lisa but I would gather than in practice there isn’t much one can do, besides say a bunch of math in a tight loop, that doesn’t require making a system call pretty regularly.)
 
I don't think this is so. All you need for "true preemptive multitasking" is preemption. AmigaOS famously had preemptive multitasking and no memory protection. I don't think you'll find memory protection hardware on this A1000 mainboard.
Fair point - "requires" is too strong a word here.

Let me rephrase as "it is sensible to implement memory protection alongside pre-emptive multitasking". Without memory protection, the stability of the overall system is limited to the stability of the least reliable piece of software running. In other words, one application crashing is liable to bring the whole system down. This would be enormously frustrating to the user.

Just as I wouldn't want to drive in a car that wasn't equipped with safety belts, I wouldn't want to use a multitasking computer without memory protection.
 

Gorgonops

Moderator
Staff member
I’ve never programmed for either but honestly the Lisa scheduler doesn’t sound like it’s *that* much different in practice than 16 bit Microsoft Windows. My vague and possibly flawed recollection is that Windows programs are basically designed as ”event handlers” that are supposed to regularly respond to event queues, and Windows has the option of grabbing the ball and giving it to a different process when those queue events happen. (In addition to explicit yield calls that well behaved background/cpu intensive processes are supposed to make.)
 

Gorgonops

Moderator
Staff member
... arguments about MMUs and what the word "Preemptive" means aside, back to the OP's original question:

Here's an article on Folklore.org that briefly mentions some of the basic design flaws with the original MacOS that made it difficult to bolt on "real" multitasking, among other things. The long and short of it boils down to with the original MacOS there basically wasn't any distinction enforced between the "OS" and a "Task"; they all run at the same privilege level, the OS code isn't re-entrant, and the Toolbox was designed to use fixed globals anchored in a single location in memory instead of giving every task its own "context" that could live at an arbitrary place in memory. All of these are basically deal-killers for cleanly migrating to true preemptive multitasking short of "virtualizing" the whole mess and preemptively multitasking the virtual containers. (Which would have come with pretty significant performance and memory footprint costs for the time and not an option for any Mac without an MMU, which were still things into the 1990's.) Piecemeal workarounds did make multitasking of a sort possible, first with 'Switcher', then MultiFinder and its descendants, but the ways they did it actually exacerbated some of the fundamental memory allocation problems and made it even more difficult to migrate to some other scheme without breaking existing software. (Wikipedia article with some good info on this subject)

In short, the decisions made by the original Mac team were probably defensible at the time (they were under huge pressure to somehow fit 50 pounds of UI into a five pound sack, but the end result was some uniquely difficult to undo design constraints that somehow lasted almost 20 years, right up until the death of "Classic". These issues were so severe that even "Copland", the much lamented attempt at trying to "fix" MacOS without converting completely to something new, ala OS X, actually ran existing software in a (para)virtualized container. (The "Blue Box".) In some alternate universe Apple might have been able to fix some of these basic architectural issues earlier so there'd be "less to break", but the Macintosh was actually in a sort of precarious position marketwise for its first few years. Splitting the software base in a way that massively broke existing software on new machines or locked out users with lower end machines would have been a pretty dangerous thing to do.
 

stepleton

Well-known member
What neither the Lisa nor those Tandy machines with 68000 could do is properly handle a page fault, so while, again, both of them were capable of "virtual memory" through swapping they needed to wrap bubble wrap around it, neither can handle a true page fault.
I can't say for the Tandy, but just like the discussion around whether Lisa shipped with preemptive multitasking (a battle I'll watch from the sidelines), I think you'll have to be pretty precise about what you mean here.

With the Lisa, your program could try to access memory that was in a proper virtual address space, with address translation and everything. If the logical address wasn't mapped to physical memory, then the MMU would raise an error condition that interrupts the processor (Hardware manual (1983), PDF pp. 35-39). I think this is a page fault in every real sense of the word, and Lisa software could and did handle these errors successfully.

The "but" part of the story here is that this error would interrupt whatever 68000 instruction was in the midst of trying to access the memory, and the 68000 didn't always save enough information during these faults to restart the instruction (and the rest of the interrupted program) after the interrupt handler in the OS had sorted out the problem (e.g. by paging in memory from the disk). Critically, for some instructions, the 68000 saved enough information, but not all of them (See also Rood et al. Byte 9/1986 PDF p. 10).

So the solution is simple if maybe a bit kludgy: if Lisa software wants to access a memory page that might not be mapped, it just needs to touch it with one of the restartable instructions first. TST (retrieve data and set flags) seems to have been the instruction of choice for this, not just for Apple (Workshop 3.0 User's Guide, PDF p. 110) but evidently also for various UNIX OSs running on the 68k (Rood et al. PDF p. 11).

So, could the Lisa and its 68000 properly handle a page fault? Definitely and it did it all the time, neither the OS nor the application software would work if it couldn't. Could the Lisa properly handle every page fault? No, if the fault wasn't caused by one of the restartable instructions, all bets are off.
 
Last edited:

Gorgonops

Moderator
Staff member
So, could the Lisa and its 68000 properly handle a page fault? Definitely and it did it all the time, neither the OS nor the application software would work if it couldn't. Could the Lisa properly handle every page fault? No, if the fault wasn't caused by one of the restartable instructions, all bets are off.
Thanks for the PDF links; I think I vaguely recall reading the one about UNIX on the 68000 many moons ago, but it's fun to page through again.

So, yeah, again, this is a case of the slippery slope you run into trying to compare 40 year old systems and declare if they qualify as "modern". I did specifically say "these systems were capable of virtual memory ... they needed to wrap bubble wrap around it". Needing to compile into the code a constant sprinkling of disposable probe instructions would qualify as "bubble wrap"? The 68010 specifically fixed this problem and it's a basic expectation for a modern MMU, so... eh. It is what it is. To be clear I'm not *slagging* the Lisa here, just pointing out that it's doing about the same things hardware-wise as other "workstation" class machines of the time also built around the 68000.

(And let's not forget the Lisa cost five figures when it was initially released; this was seriously expensive stuff at the time.)

I kind of halfheartedly searched for a XENIX reference that might talk more about its memory management implementation on the Model 16, but I didn't immediately find anything that went into that level of detail. I get the vague impression that its implementation was somewhat short of true virtual memory, IE, you can't have a single process larger than physical memory and you had to page at least the entirety of the code portion of a process in to execute. It does have memory *protection*, but since it does so in a simple linear way I guess it's kind of a head scratcher how you'd do a virtual space larger than physical. Lisa definitely gets credit for trying harder!

(The TRS-80 hardware will throw an error for an out-of-bounds access, my guess without trying to find another obscure manual is this would just be handled as a "segmentation fault" and the process would be axed.)

just like the discussion around whether Lisa shipped with preemptive multitasking (a battle I'll watch from the sidelines)

Re: your mention of the Amiga in that other thread, I guess at one point when Linus Torvalds was a snot-nosed kid he went out on a limb and made the claim that since the Amiga OS A: had a couple OS calls that allowed a process to explicitly disable preemption (meant to be used sparingly) and B: didn't have memory protection to prevent a process from just stomping on the OS'es data structure and take over without even bothering to be nice about it it didn't qualify as "Preemptive Multitasking". (By a definition he made up in which any OS which relied on processes not being intentionally evil, IE, being "cooperative", was disqualified.) So, sure, anyone's free to pick their definition and go with it.

Personally I think "Pre-emption", strictly speaking, means exactly what the word implies, the OS can butt in at any time and take the ball away. (Based on a timer tick or whatever you're using to create timeslices.) Which means the Amiga is preemptively scheduled and the Lisa isn't. But automatically assuming that "cooperative" is a pejorative is a mistake. The technique has value today in terms of utilizing resources efficiently compared to fixed timeslicing, and if your OS is designed to guarantee (to a reasonable degree) that the scheduler is going to get regular bites at the apple during system calls then there's nothing to be ashamed of.
 

stepleton

Well-known member
It is what it is. To be clear I'm not *slagging* the Lisa here, just pointing out that it's doing about the same things hardware-wise as other "workstation" class machines of the time also built around the 68000.
It's possible that I'm sniffy about the poor Lisa MMU owing to this bizarre quote from the Wikipedia page on the Apple Lisa:
The Lisa operating system features protected memory,[27] enabled by a crude hardware circuit compared to the Sun-1 workstation (c. 1982), which features a full memory management unit.
I'm not sure what makes the Lisa's MMU a "crude hardware circuit"; what doesn't it do that would be a necessary part of the basic requirements of a "full memory management unit"? What's crude about it? I suspect the hand of a Sun fanboy :).

I'm also not aware of the "cost cutting measures" that the text goes on to mention (*shrug*); nor is it the case that the Lisa had a hierarchical organisation to files on disk, at least not at first: you didn't get actual filesystem directories until the 3.0 series of the Office System or the Workshop. (The directories in the Office System were a presentation gimmick, similar to the original Macintosh File System.) Ah, Wikipedia...
 

Crutch

Well-known member
Piecemeal workarounds did make multitasking of a sort possible, first with 'Switcher', then MultiFinder and its descendants, but the ways they did it actually exacerbated some of the fundamental memory allocation problems and made it even more difficult to migrate to some other scheme without breaking existing software. (Wikipedia article with some good info on this subject)
That article is pretty good! I had never seen it before, thanks for linking.
 

al kossow

Active member
I'm not sure what makes the Lisa's MMU a "crude hardware circuit"; what doesn't it do that would be a necessary part of the basic requirements of a "full memory management unit"?

--

Lisa has a base-bounds segmented MMU, like the 68451, The SUN used a two-level segment-page mmu that allowed for demand paging, though it did require the Sun2 brain-transplant before BSD paging would work. So while you could run a unix port that was based on segmentation, like Xenix or Uniplus, you couldn't run BSD, not that it would be a sane thing to do..
 

Gorgonops

Moderator
Staff member
Yeah, I find that “crude” description pretty much out of left field after reading Apple’s tech documentation on it. The Model 16’s, that’s “crude”. (Literally has like two orders of magnitude less register space, the registers it did have need to be completely reloaded on every context switch.)
 

Gorgonops

Moderator
Staff member
Moderation note: I moved the dupe of this thread that was sitting in the "Compact Mac" section over to here and merged it with this one. The posts in the two threads seem to mostly interleave "okay", but apologies for any confusion the remaining references to "the other thread" mightt cause.
 

NJRoadfan

Well-known member
Some of the confusion regarding pre-emptive multitasking and MMUs likely is due to the general development of operating systems. Most of the major OSes introduced memory protection (which requires a MMU) at the same time as pre-emptive multitasking. OSes with one but not the other, like AmigaOS were fairly uncommon. Another feature that commonly showed up at the same time was multi-threading.

As for why classic MacOS didn't support multitasking? Well, coming with 128k of RAM didn't help. The Lisa came with 1MB standard and actually had the room to run multiple programs at the same time.
 
Moderation note: I moved the dupe of this thread that was sitting in the "Compact Mac" section over to here and merged it with this one. The posts in the two threads seem to mostly interleave "okay", but apologies for any confusion the remaining references to "the other thread" mightt cause.
Thanks for doing this! I've really enjoyed reading these discussions and so it's very helpful to have the two threads combined into one.
 
Top