To LUGNET HomepageTo LUGNET News HomepageTo LUGNET Guide Homepage
 Help on Searching
 
Post new message to lugnet.robotics.handyboardOpen lugnet.robotics.handyboard in your NNTP NewsreaderTo LUGNET News Traffic PageSign In (Members)
 Robotics / Handy Board / 8173
8172  |  8174
Subject: 
An analysis of Rich Drushel's lock_process() errors
Newsgroups: 
lugnet.robotics.handyboard
Date: 
Sat, 11 Nov 2000 17:40:30 GMT
Viewed: 
898 times
  
An analysis of lock_process() errors.

My lock_process(float sec) library code has some bugs in it.  After some
digging around in the pcode assembly source, and some test programs in IC,
I have determined the cause of the bugs.  What to do about them, however,
is still to be determined.  First, some history.

What is lock_process()?

I wrote lock_process(float sec) to be hog_processor() with a user-defined
locking time (instead of a fixed 256 msec).  Sometimes you want to lock in
the current process for longer than 256 msec, and with only hog_processor()
available, you must kill_process() everything that you don't want to be
running during the "lock", do your uninterruptible stuff, then start_process()
everything that you killed.  This method may require lots of process states
to be saved and restored, and that can get tricky.  Why not, I thought, just
be able to lock in the current process indefinitely?  Then there would be no
other processes to kill, no state to worry about restoring.

How does it work?

The IC process scheduler has a data area for the current process, one byte of
which is a counter of how many 1 kHz timer ticks this process has left to
"live" before the scheduler swaps it out for another process.  The timer tick
interrupt service routine decrements this counter; when it reaches zero, some
self-modifying code (ick!) guarantees that the current process will get
swapped out at the completion of the current pcode instruction.

lock_process() and hog_processor() prevent the IC scheduler from swapping in
a new process by simply poking a zero into the counter of ticks remaining for
the current process.  Since the timer tick interrupt service routine
decrements this counter before checking for zero, and the counter is only one
byte, 0 - 1 = 255, so effectively the counter rolls over to another 256
ticks.

hog_processor() is just an IC library function which pokes a zero into the
ticks-remaining counter.  Thus, to keep a process locked in, you must
repeatedly call hog_processor().  There is no foolproof way, however, to
guarantee that the counter won't tick down to zero before you get a chance
to call hog_processor() again, and thus let the IC scheduler swap in the next
process.  So, to insure uninterruptibility, you're forced to kill_process()
any competing processes.

lock_process() installs its own daemon into the 1 kHz timer tick interrupt
chain.  It maintains its own counter (4 bytes long) and a flag byte to
indicate whether lock_process() is on or off.  The user call to
lock_process() loads the requested locking time (converted to msec) into the
4-byte counter and sets the flag byte to on.  With every timer tick, the
lock_process() daemon decrements its counter:  if the counter is not yet
zero, it pokes a zero into the current process's tick remaining counter
(just like hog_processor()).  If the counter is zero, it pokes a one into
the current process counter (1 tick remaining) and clears the lock_process()
flag.  This allows a normal process switch at the next timer tick.

unlock_process() is user IC code which clears the lock_process() flag and
counters, and causes an immediate defer() to the next process.

What are the bugs?

(1) defer()

The biggest bug is that defer() causes escape from lock_process().  If you
lock_process() and then at some point call defer(), the scheduler will swap
in the next process at the next timer tick, regardless of the number of
ticks in the ticks-remaining counter for the current process.  Worse, the
lock_process() daemon will still be running, decrementing its own counter,
repeatedly poking zeroes into the ticks-remaining counter until its own
counter expires.  This has the effect of locking in the *next* process
instead of the one you wanted to lock in.

The defer() bug can be avoided, with some effort, by simply creating a new
defer2() function, which knows about lock_process().  If the current
process is locked with lock_process(), defer2() does *not* call defer(),
but rather does nothing.  (unlock_process() should be the only way to
force escape from a lock_process() lock before the 4-byte counter runs
out.)  If the current process is not lock_process() locked, then go ahead
and defer().  The only disadvantage to a defer2() solution is that some
existing IC library code uses defer(), and would have to be changed.

defer() itself is not IC library code, but is instead part of the pcode
interpreter, written in assembler.  defer() could be changed at the
pcode level, but then the pcode interpreter would have to be rewritten to
include at least the lock_process() flag as a global variable.  The
disadvantage to this solution is that everyone would need to switch to the
new version of the pcode interpreter, and that new versions would have to
be created for Handy Boards, 6.270 boards, and any other IC-capable boards
in circulation.

(2) printf()

A second serious bug, associated with printf() is also internal to the pcode
interpreter, and would require patching and reassembly to make compatible
with lock_process().

printf() is implemented in a way intended to be reentrant, i.e., multiple
processes can concurrently call printf() without busywaits.  Printing to
the LCD screen utilizes a ring buffer into which processes can put characters
to be printed, and a timer-tick print daemon to take characters out of this
buffer, in whatever order they were added, and actually display them.

The print buffer is 80 characters long.  If the print buffer becomes full,
and a single instance of printf() tries to print another character, there
is an internal defer() to the next process, rather than a foreground
busywait for the buffer to clear.

If the user IC program is organized so that there is only one process doing
printf() and other processes are doing I/O or analysis, this is "polite"
behavior, improving throughput of the other processes.  If a process locked
by lock_process() calls printf() and fills up the print buffer, however, the
"polite" defer() has the same bad effect as described above:  the "locked"
process is swapped out, and the next process is swapped in, *and* locked in
until another defer(), unlock_process(), or the lock_process() counter runs
down.

The "polite" defer() for full print buffer can be changed in the pcode
interpreter to a busywait, but this requires reassembly and a new version
for all platforms.  One might try to simply patch out the offending code at
runtime with some NOPs (by poking zeroes into the appropriate memory
locations), but the location of this code is version-dependent, and there is
no guaranteed universal way to dynamically find it.

Final bugfixing.

The most elegant solution is to make lock_process() a standard part of
IC, and thus make the necessary rewrites to the pcode interpreter to make
defer() aware of the lock_process() on/off flag, put the lock_process()
daemon into the stock timer tick interrupt service routine, and change the
"polite" defer() from an in-line forced swap to a call to the new defer().

I will do this for my Autonomous Robotics course here at CWRU, since I can
control all the code.  I am open to suggestions as to how to proceed if
others want lock_process() to be an extra option (as it currently is, with
its own .c and .icb), rather than an integral part of IC.

Fred Martin, any comments?

*Rich*

--
Richard F. Drushel, Ph.D.            | "Aplysia californica" is your taxonomic
Department of Biology, Slug Division | nomenclature.  /  A slug, by any other
Case Western Reserve University      | name, is still a slug by nature.
Cleveland, Ohio  44106-7080  U.S.A.  |     -- apologies to Data, "Ode to Spot"



1 Message in This Thread:

Entire Thread on One Page:
Nested:  All | Brief | Compact | Dots
Linear:  All | Brief | Compact
    

Custom Search

©2005 LUGNET. All rights reserved. - hosted by steinbruch.info GbR