Could we use multiple stacks?

Sean 'Captain Napalm' Conner
Mon, 27 May 1996 19:11:50 -0400 (EDT)

On some network somewhere in cyberspace, Jecel Assumpcao Jr transmitted:
> Sean 'Captain Napalm' Conner wrote:
> >   Example:  My Real-Time requirements require that the handler for event A
> > is executed within 10uSecs (10 microseconds).  Event sequence as follows:
> > 
> >         Thread B just passed a "check point".  It won't get to the next
> >         "check point" until another 50uSecs.
> > 
> >         Event A happens.  I have 10uSecs (or less) to handle this situation,
> >         or something bad happens.
> > 
> >   I can't just stop Thread B, because that would be ... oh ...
> > PREEMPTION, now wouldn't it?  Or do you get around that by calling some
> > special code in Thread B that causes it to PREEMPT itself?  Oh, real
> > COOPERATIVE there.
> You are just being dogmatic, here. 

  I suppose I am.

> Look at the Macintosh OS,
> for example. There is a lot of preemption there - when you
> move a mouse, when a disk block comes in, etc. 

  Okay, although I tend to call those interrupts.

> Why is it
> called a cooperative OS, then? Because:
>    1) the preempting task *must* always return to the
>       preempted task, never to another one
  Interrupted task.  Okay.

>    2) the preempting task can only use a very restricted
>       subset of system services
> A device driver is divided into two parts: a time critical
> one that is scheduled preemptively and the rest, which is
> scheduled cooperatively. So a responsive cooperative OS
> only needs to yield every 5 ms or so, not every 10 us!
  But (playing Devil's Advocate here) my hypothetical Real Time requirements
of 10uSecs is more than just an interrupt.  A whole new thread of control
has to be switched in to deal with the situation, not wait for the current
thread to give up control, as it were.

> A popular method of dealing with GC in a cooperative system
> is to push a word on the stack with each call that has a
> 1 bit for each register that contains a live pointer at that
> point. Since a GC can only occur as a result of a call
> (with an implicit yield), the GC has all the information
> it needs about all the stacks in the system. In a preemptive
> system, the GC may be invoked between *any* two instructions.
> This makes scanning the stack much more challanging.
  I address this in another post.  A "system" call can do an implicit
"yeild" in a preemptive system as well.

> >   To even hope for a response time of XuSecs in a cooperative system, you'll
> > have to insert checks every X/2usecs (assuming you can), which doubles the
> > overhead.
> A cooperative system has many downsides, but one advantage is
> that every program segment between system calls (yield points)
> is a critical region. 

  This fails in a multiprocessor system, which are becoming more popular (in
fact, there's at least one "PC" now out that contains two processors right
in the box (

  How well does cooperative multitasking do in a multiprocessor box?

> In a preemptive OS you will have to
> create the critical region yourself by surrounding the code
> with DisableInterrupt() and EnableInterrupt() or a higher
> level construct. 

  Come tomarrow, even DisableInterrupt()/EnableInterrup() won't even cut it
and you (and I) will have to use higher level constructs (like semaphores or
mutexes (which is the same as a semaphore, only with a worse sounding

> It is not at all hard to delay interrupts
> by more than 10 us on such a system. The only way to get
> as good a response time as we have in a cooperative system
> is on a CPU with multiple interrupt levels, where we can
> mask the timer interrupt while letting others preempt
> even a critical region.
  I don't quite follow.  The 80x86 gets around this by using an external
interrupt controller.  But even on a system WITH one interrupt level, the
handler can leave interrupts disabled until the cause of the interrupt is
determined, then re-enable them.  It's only the poor hardware design of the
PC that disallows sharing of interrupts (although the newer busses (PCI) fix
this, it'll be a few years before this becomes used)).

> >   And garbage collection is going through memory looking for allocated
> > memory that no longer is referenced, and freeing it.  A secondary objective
> > may be to compact the memory so that free space is coalessed into one block,
> > but that doesn't seem to be mandatory.
> Great definition. The problem is finding out what "no longer is
> referenced" if you look at a bit pattern and don't know
> if it is a pointer or an integer!
  Which is a problem, isn't it?

  I know that Emacs got around this by using the upper 8 bits of a pointer
for GC.  At least, until computers started using more than 16M of memory
(way to go, rms!).

> Most copying GCs don't need indirect pointers at all. Since
> they sweep through the whole memory, they can fix every
> reference to some object that moved. 
  The trick then, is in keeping tabs on all references.

  -spc (Oh these damn computers with their finite speed and memory!  If
	only they ran faster than the speed of light with infinite