[comp.sys.amiga] A proposed solution to the resource tracking problem

karl@sugar.UUCP (Karl Lehenbauer) (09/05/87)

The question often comes up on the net, "How can I kill my Amiga tasks?" or
more often, "Why can't I kill my Amiga tasks?".  The problem, of course,
is resource tracking, or more specifically the lack of support for it under
AmigaDOS.

We've been told that there would be too much overhead to implement this.
I think most of us tend to discount that as we end up having to write 
equivalent (and tricky) code in each of our programs anyway.  There is 
a case to be made for the former position, though, as the existing OS 
routines (memory alloc, etc.) are quite speedy and it would undeniably 
increase overhead to add resource tracking.

Anyway, I think any solution should be compatible with existing code
(as much as possible possible), easy to use and would be most likely 
to succeed if it didn't require changing the operating system.

Peter da Silva (sugar!peter) and I were quite surprised recently when
we found out that Mac windows all behave in a manner that is analogous 
to the Amiga's simple refresh windows.  What that means is that every
time your program's window gets resized or has something moved from
in front of it, it has to redraw part or all of the window.  This 
makes even "Hello, world" type programs hard to write.  The Maccians 
get around this by using libraries as a front-end to the toobox calls 
to provide some enhanced capabilities that aren't present in the toolbox 
itself.

We should do that, too.  The memory allocation problem can be solved
by using AllocRemember (Thanks, Peter, for showing me this).
Library routines would front-end calls to create message ports and 
such.  The library routines would keep track of what things need to
be cleaned up on termination and how to do the cleanup in addition 
to making the system calls to perform the specified operations.  The 
_main (for C people) startup code would set up to receive a software 
interrupt on the various control keys.  The interrupt would call the 
code that cleans everything up.  

All a programmer would have to do to use this is to link from the 
new resource-tracking library.  It is even possible for the routines 
in this new library to have the same names as their non-resource-
tracking counterparts.  The existing names, after all, are just the 
names of assembly routines that call an offset to A6 after playing 
with the stack and registers.  You give those routines new names and 
call them, via their new names, from the new resource tracking routines 
that bear the original names.  If one were to do the same for RemTask,
one wouldn't need to explicitly call any special resource tracking code
at all!  (Peter came up with this part.  I think it'll work.)

Yes, it is stinky that the resource tracking code will have to be 
linked into every program that makes use of it rather than just being
part of the OS.  It mightn't neccessarily increase the size of 
programs a whole lot, though, as it could be implemented as a shared, 
reentrant, dynamically-loaded library like the ones in LIBS:.

---

Now I'd like to end my posting, which I hope has provided a positive 
and technical contribution to this newsgroup, with a remark that 
postings about the inappropriateness of other postings, please-don't-
post-about-this postings, postings about the danger to the net posed
by all the garbage postings and so on consume net bandwidth too, 
and often create the destructive meta discussions warned about in 
the netnews new user docs.  

Flaming, also, increases net bandwidth, as few people are going to be 
willing to undergo a thorough, public charcoaling without making some 
sort of public reply.  For most people, to not do so feels like tacit 
surrender to the flamer, with additional scorching caused by the 
provocative language that characterizes such flames.
-- 
...!soma!uhnix1!sugar!karl    "Life is wasted on the living." - Z.B. IV

dillon@CORY.BERKELEY.EDU (Matt Dillon) (09/09/87)

	Interesting proposal. 

	Let's put aside the memory allocation tracking for the moment and
first concentrate on other resources.  All other system resources are 
either device or library oriented, or special EXEC calls.  For instance,
to use DOS file handles you must open the DOS library; to use the 
SERIAL.DEVICE, you must open the serial device.  To use Intutition you must 
open the intuition library.  To allocate resources you must make the proper 
EXEC calls.

	Assuming the libraries and devices did their own tracking of which
OpenLibrary/OpenDevice is associated with an IO request, all EXEC would have
to do is keep a list of all currently open libraries and devices for a
specific task.  Even if a task forgets to close a library, EXEC would have
the list and would close the library for the task (the library/device then
closing associated IOrequests, windows, etc...). This takes care of Libraries 
and Devices.

	Other items EXEC would have to track would be those EXEC calls which
transform the machine in some way... things intuitively obvious like 
SetIntVector().

	The above is relatively simple and incurs almost no overhead in terms
of memory and time, though it will mean an extensive rewrite of the OS.  BUT 
now we come to memory.  The problem with tracking memory is that in most cases
programs allocate small chunks at a time and to stick a linked list (or some
other structure) tag onto each chunk can, in the worst case, double the 
memory requirements of a task.  Other problems occur when we are bandying
messages back and forth between tasks... we must allow for multiple owners
of a memory segment.  Anybody have any bright ideas?

COMPATIBILITY:
	It is impossible to make resource tracking compatible with current
software.  That is, you *can* resource track current software, but cannot
free those resources when the task is killed even though you know what they
are.  REASON:  Simply put, many programs rely on resources they haven't 
closed (memory, interrupts) NOT to be released when they exit.  Another
reason of equal import is the fact that under the current OS, you can
RemTask() a task which is in the middle of a library call.  Clearly, the
libraries were not set up to handle something like that happenning!!!!

	Clearly we must add EXEC calls to allow resources to be kept resident
on exit.  Current software does not know about these calls so the ABSOLUTE BEST
we can hope for is to make a standard for NEW software that is written.

CONCLUSION:
	The end result would be that we would then be able to kill new
programs which understand the new EXEC calls and still not be able to kill
old programs which do not.  New programs would make an EXEC call 
TrackResources() at the very beginning to turn on resource tracking and 
automatic deallocation, etc....  Since old programs do not make this call,
EXEC knows they are old and will not attempt to track them.  Under the 
new order, programs would be able to selectively turn on and off tracking 
for certain operations.  Under the new system, Libraries and Devices would
be constrained to set a flag (or something) ensuring they are not killed
in the middle of execution when called from a task.  If any task opens a
library without this capability, resource tracking would automatically be
turned off and kills would not be allowed on that task (Basically, you would
not be able to kill any task which did not have resource tracking turned on).


					-Matt

jesup@steinmetz.steinmetz.UUCP (Randell Jesup) (09/09/87)

In article <636@sugar.UUCP> karl@sugar.UUCP (Karl Lehenbauer) writes:
>If one were to do the same for RemTask,
>one wouldn't need to explicitly call any special resource tracking code
>at all!  (Peter came up with this part.  I think it'll work.)

Sorry to disappoint you, but RemTask may not do you any good.  Most
Amiga programs return via rts; or Forbid()ing, ReplyMsg()ing, and then
rts; or (EVIL!) Exit().  What you really need to do is what Lattice does,
have cleanup code in the startup/exit module.
-- 
	Randell Jesup  (Please use one of these paths for mail)
	jesup@steinmetz.UUCP (uunet!steinmetz!jesup)
	jesup@ge-crd.ARPA

bryce@hoser.berkeley.edu (Bryce Nesbitt) (09/09/87)

In article <> dillon@CORY.BERKELEY.EDU (Matt Dillon) writes:
>
>COMPATIBILITY:
>	It is impossible to make resource tracking compatible with current
>software...  REASON:  Simply put, many programs rely on resources they haven't 
>closed (memory, interrupts) NOT to be released when they exit.

Good point.  I have written several programs that deliberatly leave
memory allocated after a sucessful exit.  Read on...


>Clearly we must add EXEC calls to allow resources to be kept resident
>on exit.  Current software does not know about these calls so the ABSOLUTE BEST
>we can hope for is to make a standard for NEW software that is written.

Um... Read on...


>CONCLUSION:
>	The end result would be that we would then be able to kill new
>programs which understand the new EXEC calls and still not be able to kill
>old programs which do not.

Argh!!! I think this is a _very_ wrong approach.  You see, it says "Every
last person who _ever_ writes _any_ software must know about this and
use it.  *All* of you, every one.  And, you must do it correctly.". 
Chances of getting everybody to do this are so near zero as to be
uncountable.  Think of all the software now that uses busy waits,
or MOVE SR,<ea> or any other forbidden fruit.
If it's done like that, 5 years from now there will still be untrackable
software that will force people to suffer from reboots, lost resources,
whatever.

A conversion of this magnitude is never easy... but there is a better way.
There is a _much_ smaller base of programmers to target:  People who write
software that must leave resources.

What you do is _well_ before the resource tracking goes in publish a tech
note (to use an Apple-ism) explaining the situation.  Explain that if
you must leave a resource dangling *YOUR PROGRAM WILL BREAK WHEN OS
VX.X IS RELEASED*.  Then describe a check or work arround that will
let your software work now and *AND* in the resource tracked future.

Apple is a company that has done very well at doing the "impossible"
upgrade.  They even switched from the brain-dead "flat" MFS to a
filesystem that actually knows about subdirectories!  I talked with
<name deleted>, a leading Mac software publisher.  They had a software package
in final Q&A.  A tech note came from Apple warning about some future
updates.  They listened and delayed the shipping for one week.  Nearly
a year later Apple released a new system file.  The thing that was
supposed to change did... and because of the last moment change, the
program still worked.  I guess what I'm saying is that this is a tried and
true tactic.

The Intuition LockIBase() call seems like a good example.  It was
in V1.1 but was just a stub.  In V1.2 jimm added some code to go
with it.  The ultimate in structured programming... not only was
it documented before it was written, but people were using it! :-)
:-) :-)


> libraries and devices.

Libraries and devices may need to help in the resource tracking.  What
can be done to let old ones run may become a problem.


|\ /|  . Ack! (NAK, EOT, SOH)
{O o} . 
 (")	bryce@hoser.berkeley.EDU -or- ucbvax!hoser!bryce
  U	

dillon@CORY.BERKELEY.EDU (Matt Dillon) (09/09/87)

>>CONCLUSION:
>>	The end result would be that we would then be able to kill new
>>programs which understand the new EXEC calls and still not be able to kill
>>old programs which do not.
>
>Argh!!! I think this is a _very_ wrong approach.  You see, it says "Every
>last person who _ever_ writes _any_ software must know about this and
>use it.  *All* of you, every one.  And, you must do it correctly.". 
>Chances of getting everybody to do this are so near zero as to be
>uncountable.  Think of all the software now that uses busy waits,
>or MOVE SR,<ea> or any other forbidden fruit.
>If it's done like that, 5 years from now there will still be untrackable
>software that will force people to suffer from reboots, lost resources,
>whatever.

	No, you simply have one call to EXEC 'TrackResource(TRUE)'. 
Presumably the major compilers (Manx/Lattice) would incorporate this into
their assembly code.

	Commodore isn't stable enough (yet) to be able to release a new OS
which makes half the software in existance incompatible.

	I have an idea:  Make the resource tracking contingent on a new DOS.
That is, if C-A releases a new DOS *everybody* will use it, and we can make
the resource tracking 'forced' automatically upon any program using the new
DOS (as well as have the EXEC calls mentioned above).  Reason:  Anybody who
knows about the new DOS (Assuming C-A releases one) would also know about
the automatic resource tracking and write their programs accordingly.

		Is that convoluted or What?

			-Matt

mwm@eris.BERKELEY.EDU (Mike (My watch has windows) Meyer) (09/10/87)

In article <8709091634.AA19588@cory.Berkeley.EDU> dillon@CORY.BERKELEY.EDU (Matt Dillon) writes:
<	I have an idea:  Make the resource tracking contingent on a new DOS.

This appears to be the only solution. The problem is (as stated
before) that the Amiga probably can't survive a non-compatable (or at
least mostly non-compatable) upgrade at this point. Maybe a two-phase
upgrade, over the course of a year or more:

Phase 1) A new DOS that is backwards compatable, but supports resource
tracking if you want it. This should include notes on how to make a
program track resources, and warnings that the resource tracking will
be mandatory in the future.

Phase 2) The mandatory resource tracking version, that is *not*
backwards compatable.

The reason for this is that there are more nasties than you want to
think about with adding the ability to kill a task. For instance,
let's take a trivial example:

CLIENT opens port "server.port" and sends it a message containing some
text.

SERVER recieves the message from CLIENT, and goes away to do things
based on the text of the message.

Later, the SERVER is through with the text, so it does a ReplyMsg() on
the saved message.

CLIENT gets the reply, frees the port & etc, and then goes about it's
merry business.

Now, add resource tracking and the ability to kill a task/process to
this picture. Suddenly, CLIENT has to be able to deal with "He's dead,
Jim" messages on the reply port, and SERVER has to be able to figure
out which of the ports it's hanging onto should be thrown out when it
gets a "He's dead" message on "server.port".

Likewise for *any* resource that might be shared between two
processes. Especially if the protocol is such that task1 allocates the
resource, fills it and passes it to task2, who will free it.

Basically, you need a new set of mechanisms. One for indicating which
resources you do/don't want freed, whether it's shared or not (and
with who), and what you want done if one of the sharers dies. Plus, of
course, ways of indicating that you're picking up a shared resource,
and what to do etc.

Of course, I'd love to be wrong, and see a transparent way of doing
resource tracking including shared resource & death of shareholders
without breaking existing code. Anyone wanna try?

	<mike
--
The handbrake penetrates your thigh.			Mike Meyer
A tear of petrol is in your eye.			mwm@berkeley.edu
Quick, let's make love before we die.			ucbvax!mwm
On warm leatherette.					mwm@ucbjade.BITNET

fgd3@jc3b21.UUCP (Fabbian G. Dufoe) (09/11/87)

in article <636@sugar.UUCP#, karl@sugar.UUCP (Karl Lehenbauer) says:
# The question often comes up on the net, "How can I kill my Amiga tasks?" or
# more often, "Why can't I kill my Amiga tasks?".  The problem, of course,
# is resource tracking, or more specifically the lack of support for it under
# AmigaDOS...
# 
# We should do that, too.  The memory allocation problem can be solved
# by using AllocRemember (Thanks, Peter, for showing me this).
# Library routines would front-end calls to create message ports and 
# such.  The library routines would keep track of what things need to
# be cleaned up on termination and how to do the cleanup in addition 
# to making the system calls to perform the specified operations.  The 
# _main (for C people) startup code would set up to receive a software 
# interrupt on the various control keys.  The interrupt would call the 
# code that cleans everything up.  
# 
     As I understand it, that is exactly how Lattice handles the problem
with version 3.10 of their C compiler.  I believe you have the option of
disabling that code, but the default is to trap BREAKs (^C) and put up a
requester asking if you wish to continue or abort.  Doesn't that pretty
well solve the problem for Lattice users?

--Fabbian Dufoe
  350 Ling-A-Mor Terrace South
  St. Petersburg, Florida  33705
  813-823-2350

UUCP: ...gatech!codas!usfvax2!jc3b21!fgd3 

peter@sugar.UUCP (Peter da Silva) (09/12/87)

In article <8709082136.AA19121@cory.Berkeley.EDU>, dillon@CORY.BERKELEY.EDU (Matt Dillon) writes:
> now we come to memory.  The problem with tracking memory is that in most cases
> programs allocate small chunks at a time and to stick a linked list (or some
> other structure) tag onto each chunk can, in the worst case, double the 
> memory requirements of a task.  Other problems occur when we are bandying
> messages back and forth between tasks... we must allow for multiple owners
> of a memory segment.  Anybody have any bright ideas?

Put up with doubling the memory requirements of the task, or else allocate a
bigger chunk to each task (say: 128 bytes minimum), and give it bits of this
chunk as it requires. Make AllocMem act more like Malloc. The free list within
the 128 byte chunk can be manipulated as normal.

Messages are tougher. If SendMsg transfers the ownership of the segment
containing the message, or better yet marks it as having shared ownership,
that should help most cases... shared chunks can't be freed automatically.

It's still not perfect, but the only place this will cause problems is if you
kill the task. All messages should be back at the originator by the time the
task exits normally. Killing a task is an emergency measure anyway.

> COMPATIBILITY:
> 	It is impossible to make resource tracking compatible with current
> software.  That is, you *can* resource track current software, but cannot
> free those resources when the task is killed even though you know what they
> are.  REASON:  Simply put, many programs rely on resources they haven't 
> closed (memory, interrupts) NOT to be released when they exit.

I don't think they should do this.

The only resource a program should expect to hang around is a process it
has LoadSegment()ed and CreateProcess()ed. And which has its own resources.

Cna you name any programs that expect anything else to hang around after they
go away? The only one I can think of is something that chews up RAM so your
512K-only programs can run. Oh yes, the Manx SET command, but that's a little
joker that can easily be rewritten to give up its memory nicely.

> Another
> reason of equal import is the fact that under the current OS, you can
> RemTask() a task which is in the middle of a library call.  Clearly, the
> libraries were not set up to handle something like that happenning!!!!

This is a real problem. But since we're modifying the libraries anyway and
not using RemTask to Kill tasks, it's no real bigee.

> 	Clearly we must add EXEC calls to allow resources to be kept resident
> on exit.  Current software does not know about these calls so the ABSOLUTE BEST
> we can hope for is to make a standard for NEW software that is written.

I don't see why. One mark of a good program is that when you exit the Free
Memory list in the workbench title bar hasn't changed. Most software should
in fact work well with resource tracking.

> CONCLUSION:
> 	The end result would be that we would then be able to kill new
> programs which understand the new EXEC calls and still not be able to kill
> old programs which do not.

We would also be able to kill old programs that were well-behaved.

Also, this will have to be done eventually to allow for protected mode
operation. Why not start now. Look how well burying heads in the sand
has worked for IBM and Microsoft. If the Amiga doesn't go to protected mode
it will be dead within 5 years.
-- 
-- Peter da Silva `-_-' ...!seismo!soma!uhnix1!sugar!peter
--                 'U`  <-- Public domain wolf.

peter@sugar.UUCP (09/14/87)

>      As I understand it, that is exactly how Lattice handles the problem
> with version 3.10 of their C compiler.  I believe you have the option of
> disabling that code, but the default is to trap BREAKs (^C) and put up a
> requester asking if you wish to continue or abort.  Doesn't that pretty
> well solve the problem for Lattice users?

Lattice and Aztec both check the value of SIGB_CTRL[CDEF] when you call
their runtime libraries. Programs that do not use standard I/O won't be
helped by this, because SIGB_CTRL[CDEF] will never be checked. Anyway,
when these signal bits are detected, Aztec just calls _abort and exits.
Lattice puts up this requestor then calls its equivalent of _abort and
exits.

To get a SIGB_CTRL[CDEF], you have to be run from the CLI. Programs that run
from the workbench won't be helped by this.

Also, all this cleans up is the stdio stuff (files opened with
fopen, memory allocated with malloc)... you have to write your own _abort
routine to catch your windows and AllocMemmed memory (which you need some
of in any case: you shouldn't be malloccing a FileInfoBlock, for example,
or anything that needs to be in CHIP memory). With Lattice this may or
may not be possible.

Also, I'm a little doubtful of the value of popping up a requestor. What if
you're running remotely? I know there are some requestors you can't avoid,
but is it really such a good idea to add another? CLI-based programs should
try their damndest to avoid needing the screen.
-- 
-- Peter da Silva `-_-' ...!hoptoad!academ!uhnix1!sugar!peter
--                 'U`      ^^^^^^^^^^^^^^ Not seismo!soma (blush)

dpvc@ur-tut.UUCP (Davide P. Cervone) (09/15/87)

[RoboCop wasn't thuroughly debugged yet...]

I have a concern that doesn't seem to have been addressed yet in the "kill
any process" suggestions.  While I beleive that there have been excellent 
methods proposed for making it possible to free up memory that's been 
allocated by a process, I'm not sure that that's all there is to it.  One 
must look at how that memory is being used.  For instance, if the memory
is part of a message that has been sent to another process, and that process
is still using that memory when you kill the original process, then you will
be freeing memory that is in use (not a good idea).  Does this mean that when
we kill a process, we should Wait() for all outstanding messages to be replied
to?  But what if the messages are never returned (like there is still a bug
in the program we're debugging)?  Do we not remove the process?  Or do we 
remove it anyway, and possibly risk a crash later?

How about other process that have pointers into the memory that I am using.
Suppose for instance, that I call OpenWindow() to get a new winodw.  That 
allocates memory.  It also links my window into the screen's window list,
creates a Layer structure and links that into the screen's LayerInfo structure.
If I deallocate that memory without properly closing the window, the screen's
window and LayerInfo lists will point to invalid data.  This could cause 
serious problems when I start to do aything with windows (like refresh them).

How about if a fopen() a CON: window?  That causes a new console task to be 
born (this takes up memory, but it belongs to another task).  If someone kills
my process without closing that CON: window, the console process will never
be removed, and will stick around in memory, doing I-don't-know-what.

Similarly, if I open the serial port, but don't close it properly (even if I
free the memory I used), no one else will be able to access the serial port.

While it is possible to encapsulate all the DOS and exec routines that 
allocate memory or other resources within new routines that track them, I am
not so sure it's a practical thing to add in after the fact.  There may just
be too many to change.  I hope I'm wrong, but it's not a project to be taken 
lightly.  And you need to realize that just freeing memory isn't enough
(and sometimes it's too much, like when that memory is in use by another
process).

In an environment where process share their resources in so many ways, I 
really worry about cleaning up after a process without some knowledge of what
that process was doing.  I hope someone finds a way to do it, though.

Sorry for the length of this, but I hope it helps some people think about
these issues.

Davide P. Cervone
dpvc@tut.cc.rochester.EDU
dpvc@ur-tut.UUCP
DPVC@UORDBV.BITNET

brianr@tekig4.UUCP (09/17/87)

This naive suggestion is only just barely making it past
my foot-in-mouth-avoidance inhibitions, so I'll keep it
short:

Is there any merit in the notion of maintaining a linked
list of pointers to subroutines which do "cleanup" type
activities such as device/window/message port closing,
as well as memory deallocation?  Sort of an extension to
the Memlist idea.

"Killing" a task would then involve scanning its "cleanup"
list in reverse order of creation.

The cleanup list is built by substitutes for the normal CreateXXXX
functions; the substitutes call their forebears, and then add the
appropriate information to the task's current cleanup list.  The
cleanup vector in some (maybe most) cases will be implicit (e.g.,
NewOpenWindow automatically links a pointer to a CloseWindow call),
but some might have to be supplied explicitly.

I suppose the list would have to be more than mere pointers to
existing subroutines;  it needs to include the appropriate
parameters for those subroutines; probably faked-up stack frame images.
This could tie up a fair ammount of memory in its own right, but would
save a lot of code that's currently being generated in each of a zillion
applications.

The list-unwinding program would be included in the code currently
pointed to by a Task's exit vector (I haven't my manuals handy,
but I remember there being one).

I haven't figured out how to deal with unreturned messages in this scheme,
but there might be a way, mightn't there?


Staring wistfully in through real programmers' windows,

Brian Rhodefer