[comp.sys.amiga.tech] Manx help

jdp@killer.DALLAS.TX.US (Jim Pritchett) (10/28/88)

Hello,
       I am making my first attempt to use the DOS functions from a C program.
I have all the manuals in one form or another, but I haven't been able to find
the problem with the code below.  Could one of the C experts out there help me
out?  Basically the problem is that although the test program below runs fine,
the system becomes severely unstable and crashes within a couple of commands
after the program terminates.  I'm sure that there is a problem with my
(mis)understanding of locks, but I don't know what it is.  Here is the source
code to my program (Aztec C v3.6a, SDB):



#include <stdio.h>

main()

{
  char cd[108];

  getcwd(cd, 108);
}

# include <functions.h>
# include <exec/types.h>
# include <libraries/dosextens.h>

getcwd(cd, length)
  char *cd;
  int length;

{
  char name[108];
  struct FileLock *lockcd, *lockram;
  struct FileInfoBlock *fib;
  short success;
 
  lockram = Lock("ram:t", ACCESS_READ);
  lockcd = CurrentDir(lockram);
  fib = AllocMem((long)sizeof(fib), 1L);
  success = Examine(lockcd, fib);
  strcpy(name, fib->fib_FileName);

printf("  %s\n", name);

  UnLock(lockram);
  lockram = CurrentDir(lockcd);

  UnLock(lockcd);
  UnLock(lockram);
  
}


                                    Thanks,

                                            Jim Pritchett

                                    UUCP: killer!gtmvax!dms3b1!caleb!jdp



P.S.  I noticed after the fact that I had not returned the memory allocated
      to the FileInfoBlock.  However, this should not destabilize my system.

}

rap@ardent.UUCP (Rob Peck) (10/29/88)

In article <5918@killer.DALLAS.TX.US>, jdp@killer.DALLAS.TX.US (Jim Pritchett) writes:
> out?  Basically the problem is that although the test program below runs fine,
> the system becomes severely unstable and crashes within a couple of commands
> after the program terminates.  I'm sure that there is a problem with my
> (mis)understanding of locks, but I don't know what it is.  Here is the source
> code to my program (Aztec C v3.6a, SDB):
>  
>   lockram = Lock("ram:t", ACCESS_READ);
>   lockcd = CurrentDir(lockram);
>
	(material deleted for brevity)

>   UnLock(lockram);
>   lockram = CurrentDir(lockcd);
> 
	(material deleted for brevity)
> 
>   UnLock(lockcd);
>   UnLock(lockram);
>   

I experimented and found out (and documented it in the Programmers'
Guide) that you should NOT unlock a lock that you get from CurrentDir.

=======================================================================
AmigaLINE:  Care and Feeding of AmigaDOS 'locks'.   Rob Peck 


ONLY unlock locks that you get from Lock() or DupLock().  It seems that
when using CurrentDir(), the system simply hands you a pointer to a lock
that it already was using for something else... I believe it is the lock
associated with the current CLI's current directory.  If you unlock this,
you return the resource to the system which can then reuse that memory
For any other purpose.  The instability you see I believe is that the
CLI from which you started the program still has a pointer to that
memory and when it tries to use it for something, GURU (maybe).

The simplest experiment you can do to confirm this is the following
(done from memory cause my book is not here at this moment):

#include "exec/types.h"
#include "functions.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"

main()  
{
	/* INCORRECT METHOD #1 */
	struct FileLock *testlock, *aarrgghh;
	testlock = Lock("RAM:",ACCESS_READ);
	aarrgghh = CurrentDir(testlock);
	UnLock(aarrgghh);
	UnLock(testlock);
}

#include "exec/types.h"
#include "functions.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"

main()  
{
	/* INCORRECT METHOD #2 */
	struct FileLock *testlock, *aarrgghh;
	testlock = Lock("RAM:",ACCESS_READ);
	aarrgghh = CurrentDir(testlock);
	UnLock(aarrgghh);  /* see, did not even unlock testlock */
}

On exiting either version, try the command 'DIR', or 'LIST'.  
You may be surprised at the results.

This code WILL destabilize a system because CurrentDir returns a pointer
to the lock on the PREVIOUS directory, but puts the program into the
directory associated with the new lock you have obtained.  The purpose
for CurrentDir (and ParentDir) for that matter, returning the PREVIOUS
lock is so you can freely move around in the directory tree.  When
ParentDir returns a NULL BPTR, it means you are at the root of that
filing system and cannot parent-up any further, but you do get the
pointer to the lock on the previous directory so that you can move
back down to wherever you wish to go.  Likewise with CurrentDir, you
get the previous one so that, for example, your program can go out
of the current directory to retrieve music or other files to which
you have been given a lock (for example in a Workbench argument),
then come back to your own current directory to retrieve other
files you may need - that directory from whence you started:

	newlock = Lock("new.directory", ACCESS_READ);

	oldlock = CurrentDir(newlock);
	/* do stuff in new directory */
	ignoredlock = CurrentDir(oldlock);
	/* returns you to where you started. */

Notice that I called this one "ignored lock" because once I finished
with the new directory, I was given a pointer to the lock that I
had handed to CurrentDir earlier.  So I can safely ignore it if
I simply remember to UnLock(newlock) later, because in this case,

	ignoredlock == newlock

which simply means that if you UnLock something given to you by
CurrentDir AND unlock something given to you by Lock, you are
unlocking the same thing TWICE, ... same effect as freeing the
same memory twice and the system fails eventually.

So the RULE is:

	NEVER UnLock any lock given to you by either
	CurrentDir() or ParentDir().  ONLY UnLock a
	lock provided to you by Lock() or DupLock().
	Those BELONG to you.  The others just might
	have been created by the system and it will
	not realize you have killed them -- when it
	tries to reuse them, the GURU may visit.


#include "exec/types.h"
#include "functions.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"

main()  
{
	/* CORRECT METHOD */

	struct FileLock *testlock, *aarrgghh;
	testlock = Lock("RAM:",ACCESS_READ);
	aarrgghh = CurrentDir(testlock);

	/* aarrgghh is a pointer to the lock on the
	 * directory that the CLI started from...
	 * it may still need that lock sometime later.
	 * If we free it by UnLock, we mess things up.
	 */

	UnLock(testlock);
	/* Did nothing more than move current CLI's
	 * current directory to ram:
	 */
}

ditto@cbmvax.UUCP (Michael "Ford" Ditto) (10/29/88)

In article <5918@killer.DALLAS.TX.US> jdp@killer.DALLAS.TX.US (Jim Pritchett) writes:
>  Basically the problem is that although the test program below runs fine,
>the system becomes severely unstable and crashes within a couple of commands
 ...
>  struct FileLock *lockcd, *lockram;
 ...
>  lockram = Lock("ram:t", ACCESS_READ);
>  lockcd = CurrentDir(lockram);
 ...
>  UnLock(lockram);
>  lockram = CurrentDir(lockcd);
>
>  UnLock(lockcd);
>  UnLock(lockram);

Hmm... one call to Lock() and *three* calls to UnLock() .. a sure sign
that Something Bad will happen.

The problem is that you are unlocking your current directory.  Get rid
of the first two UnLock()'s and the function should work.

The basic idea behind CurrentDir() is that it "trades" locks with you --
you give it one and it gives you the one it had.  It completely forgets
about the one it had, and it expects you to "forget" about the one you
gave it; it is no longer your responsibility to unlock the new current
directory, but it is your responsibility to unlock the old one.  Because
it is a "trade", you can "trade it back" when you're done, and the net
effect is nil, so you only need to UnLock whatever you Locked.

Also note that in this particular case, you don't really care what you
temporarily set your CurrentDir to (I assume "ram:t" was an arbitrary
dummy object to pass to CurrentDir).  So, you can use the magic zero
lock; a lock of zero is always valid and need never be UnLocked.  So,
your code can be simplified to:

>  ULONG lockcd;

>  lockcd = CurrentDir((ULONG)0);	/* Steal the cd for a bit */
 [ ... do stuff with lockcd ... ]
>  (void)CurrentDir(lockcd);		/* Put it back */

Note that a lock is NOT the same as a (struct FileLock *); the return
value type of Lock() and CurrentDir() is ULONG.

Hope this helps.
-- 
					-=] Ford [=-

"The number of Unix installations	(In Real Life:  Mike Ditto)
has grown to 10, with more expected."	ford@kenobi.cts.com
- The Unix Programmer's Manual,		...!sdcsvax!crash!elgar!ford
  2nd Edition, June, 1972.		ditto@cbmvax.commodore.com

cmcmanis%pepper@Sun.COM (Chuck McManis) (10/29/88)

In article <5918@killer.DALLAS.TX.US> (Jim Pritchett) writes:
>                          ...  I'm sure that there is a problem with my
>(mis)understanding of locks, but I don't know what it is.  

Locks are "funny" but once you get to know them they can be your friends.
Point #1 Lock() returns a BPTR to a FileLock structure, *not* a pointer.
Point #2 Only unlock what you lock.

Check this out ...
->  lockram = Lock("ram:t", ACCESS_READ);	/* cool you get a lock */
->  lockcd = CurrentDir(lockram);
->  UnLock(lockram);				/* Once */
->  lockram = CurrentDir(lockcd);		
->  UnLock(lockcd);				/* Twice */
->  UnLock(lockram);				/* Three times your out. */
->}

The only lock you can legally unlock is the one in lockram originally.
When you entered you had a lock in CurrentDir (which you don't "own")
and you didn't leave one there when you left. Understand?

--Chuck McManis
uucp: {anywhere}!sun!cmcmanis   BIX: cmcmanis  ARPAnet: cmcmanis@sun.com
These opinions are my own and no one elses, but you knew that didn't you.

jbwaters@bsu-cs.UUCP (J. Brian Waters) (11/01/88)

> ONLY unlock locks that you get from Lock() or DupLock().  It seems that
> when using CurrentDir(), the system simply hands you a pointer to a lock
--
Here you have forgotten that you must UnLock() locks from CreateDir()
also.

> So the RULE is:
> 
> 	NEVER UnLock any lock given to you by either
> 	CurrentDir() or ParentDir().  ONLY UnLock a
> 	lock provided to you by Lock() or DupLock().
> 	Those BELONG to you.  The others just might
> 	have been created by the system and it will
> 	not realize you have killed them -- when it
> 	tries to reuse them, the GURU may visit.

Are you sure about this?  My experience has been otherwise.  When coding the
Amiga routines for Zoo,  I looked at a C-A example of using the new SET_DATE
packet in 1.2.  It did not free the lock from ParentDir() and in the AmigaDOS
manual's write up on UnLock,  it says that UnLock() is to free locks
obtained from Lock, DupLock, or CreateDir.  However I kept getting reports
that the Amiga version of Zoo would leave locks on directories when the 'x//'
option was used.  At first I thought that somehow the lock from CreateDir
was not being freed,  but then John Hiday pointed out to me that the same
problem occured when 'x/' was used... this was the clue I need to find the
problem... it was the lock from ParentDir that was being left... adding the
call to unlock it fixed the problem and has not seemed to cause any new ones.

So I think the UnLock function docs should have ParentDir added to the list
of locks to free.  If am wrong about this please let me know so I can correct
the code in the Amiga version of Zoo.
-- 
Brian Waters              <backbone>!{iuvax|pur-ee}!bsu-cs!jbwaters
                                          uunet!---/

dillon@CORY.BERKELEY.EDU (Matt Dillon) (11/01/88)

>First, what evidence do you have that ParentDir returns a lock that can't
>be freed? There really isn't a close relationship between ParentDir and
>CurrentDir.

	ParentDir() returns a NEW LOCK, right?  Therefore, you *MUST*
UnLock the lock returned by ParentDir() eventually before exiting.  ParentDir()
does NOT touch the lock you give it, it just uses it to find the parent and
create a new lock for the parent and return that.

>Secondly, I believe that if you don't CurrentDir back to the original dir,
>you should free the original lock... otherwise it'll just hang around taking
>up space and keeping you from deleting the directory. You should only do this
>if you're writing a replacement to CD.

	No you can't!   If Program A has some notion of a current directory
and calls program B, which then CHANGES the current directory and returns
without restoring the old lock (or worse, actually UnLock()s it), Program A
comes back and *poof* the nice lock pointer it cached now causes a system
crash.

	Now, UnLock(CurrentDir(newlock)) is perfectly acceptable, but only
if you save/duplicate the original lock in the beginning of your program,
and restore it at the end:

	Forgive the lack of error checking in line 4 of this example:

main()
{
    struct Process *proc = FindTask(NULL);
    long originallock = CurrentDir(DupLock(proc->pr_CurrentDir));

    dostuff(): (  in which you can do lots of:  
	UnLock(CurrentDir(yet-another-lock-you-just-created))
    )

    UnLock(CurrentDir(originallock));
}

					-Matt

guy@b11.UUCP (Guy Streeter) (11/01/88)

In article <10632@cup.portal.com> thad@cup.portal.com (Thad P Floryan) writes:
>Extracted fragment:
>          struct FileInfoBlock *fib;
>
>          fib = AllocMem((long)sizeof(fib), 1L);
>
>Should be:
>   fib = (struct FileInfoBlock *)
>              AllocMem((long)sizeof(struct FileInfoBlock), MEMF_CLEAR);
>                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Actually,

extern void *AllocMem();

prevents your having to cast its return value, but, I think, it is a MANXism.
And,

	fib = AllocMem ((long)sizeof(*fib), MEMF_CLEAR);
                                     ^<--- sizeof what fib points to

would have been perfectly good C code, and a handy typing and thinking
shortcut I use all the time.
But I agree that MEMF_whatever should be used instead of the constant.


-- 
Guy Streeter
...uunet!ingr!b11!guy		(UUCP)
ingr!b11!guy@uunet.uu.net	(ARPANET)

rap@ardent.UUCP (Rob Peck) (11/01/88)

In my AmigaLINE on AmigaDOS locks, I stated a rule which has
since been proved to be incorrect:

Bad version with the erroneous text noted:

        NEVER UnLock any lock given to you by either
                                              xxxxxx
        CurrentDir() or ParentDir().  ONLY UnLock a
                     xxxxxxxxxxxxxx
        lock provided to you by Lock() or DupLock() 
	or ParentDir() or CreateDir() or any other
	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
	function that actually CREATES a lock.
	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        Those BELONG to you.  The others just might
        have been created by the system and it will
        not realize you have killed them -- when it
        tries to reuse them, the GURU may visit.


DELETE the "either" and the "or ParentDir()" where shown ("x"), 
and then add the sentence fragment that reference ParentDir() 
and CreateDir() where shown by ("^"), and the statements are correct.

In other words, only unlock any lock that YOU caused to be CREATED.

Matt Dillon noticed this and via priority EMAIL he reminded me
that ParentDir() returns a BPTR to a NEW LOCK.  Therefore you own
it and are responsible for seeing to it that you get rid of it
when you exit.   THANKS MATT!!!!

Sorry if there was any confusion.  I had incorrectly remembered
things as though ParentDir() moved you up the directory tree.  Aarrgghh,
what a bad memory.  Well, that is (so far) the only problem that has surfaced
in reply to that AmigaLINE.

Oh, yes, before I forget, there is a program on an early FISH disk
Named "LockMon" that I recall seeing that is supposed to tell you
if you exit with more locks (or fewer too I guess) than what you
started with.  I figure that would be a good thing to experiment
with for new DOS'ers.

And the question about BPTR's from AmigaDOS, yes, AmigaDOS handles
BPTR's and BSTR's (BPTR's to BCPL strings which have the length
in the first byte, followed by the string, non-null-terminated).
Declaring something as a ULONG when it is really a BPTR or BSTR
is ok, but to be technically accurate (and perhaps for public-distributed
code, helpful to the new user) one might consider using the appropriate
declaration.  In the Programmers' Guide, I occasionally specified
LONG GfxBase, and sometimes declared it as struct GfxBase *GfxBase.
The compiler does not care, but to make it easier to trace trouble
later on, or to modify the code if one of the variables in these
"base" areas are required, it buys a lot if the right declaration
is used in the first place.

Rob Peck

ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) (11/01/88)

In article <679@ardent.UUCP> rap@ardent.UUCP (Rob Peck) writes:
>main()  
>{
>	/* CORRECT METHOD */
>
>	struct FileLock *testlock, *aarrgghh;
>	testlock = Lock("RAM:",ACCESS_READ);
>	aarrgghh = CurrentDir(testlock);
>
>	/* aarrgghh is a pointer to the lock on the
>	 * directory that the CLI started from...
>	 * it may still need that lock sometime later.
>	 * If we free it by UnLock, we mess things up.
>	 */
>
>	UnLock(testlock);
>	/* Did nothing more than move current CLI's
>	 * current directory to ram:
>	 */
>}

	Are you *sure* about this, Rob?

	It strikes me that, if you UnLock() the lock you handed to
CurrentDir() (and therefore the system), you'll pull the rug out from under
the CLI's feet when it tries to access that lock.

	I thought, as another Net person said (name forgotten; sorry), that
CurrentDir() trades locks with you.  You hand CurrentDir() a lock on the
directory you want to go to.  CurrentDir() takes it away from you, and
gives you posession of a lock on the directory you used to be in.  This is
how I do it in Onion, and I haven't had any problems yet.

	What is the final story?  Naturally, the DOS docs obfuscate this
point.

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
Leo L. Schwab -- The Guy in The Cape	INET: well!ewhac@ucbvax.Berkeley.EDU
 \_ -_		Recumbent Bikes:	UUCP: pacbell > !{well,unicom}!ewhac
O----^o	      The Only Way To Fly.	      hplabs / (pronounced "AE-wack")
"Work FOR?  I don't work FOR anybody!  I'm just having fun."  -- The Doctor

peter@sugar.uu.net (Peter da Silva) (11/01/88)

In article <8810311911.AA06094@cory.Berkeley.EDU>, dillon@CORY.BERKELEY.EDU (Matt Dillon) writes:
> 	ParentDir() returns a NEW LOCK, right?  Therefore, you *MUST*
> UnLock the lock returned by ParentDir() eventually before exiting.

Yeh, that's what I said.

> >[Unlocking your original lock] You should only do this
> >if you're writing a replacement to CD.

> 	No you can't!

If you're writing a replacement for "CD", you certainly can. This is the only
situation in which you should do something like this, of course. If your
program calls "CD" it's got to expect that its current directory is going
to be changed.

> 	Forgive the lack of error checking in line 4 of this example:

[ example that's basically identical to the example I gave, except it has
  less error checking than mine, deleted ]
-- 
		Peter da Silva  `-_-'  peter@sugar.uu.net
		 Have you hugged  U  your wolf today?

	Disclaimer: I accept full responsibility for my own typos.

jesup@cbmvax.UUCP (Randell Jesup) (11/02/88)

In article <7517@well.UUCP> ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) writes:
>In article <679@ardent.UUCP> rap@ardent.UUCP (Rob Peck) writes:
>>main()  
>>{
>>	/* CORRECT METHOD */
>>
>>	struct FileLock *testlock, *aarrgghh;
>>	testlock = Lock("RAM:",ACCESS_READ);
>>	aarrgghh = CurrentDir(testlock);
>>
>>	/* aarrgghh is a pointer to the lock on the
>>	 * directory that the CLI started from...
>>	 * it may still need that lock sometime later.
>>	 * If we free it by UnLock, we mess things up.
>>	 */
>>
>>	UnLock(testlock);
>>	/* Did nothing more than move current CLI's
>>	 * current directory to ram:
>>	 */
>>}
>	It strikes me that, if you UnLock() the lock you handed to
>CurrentDir() (and therefore the system), you'll pull the rug out from under
>the CLI's feet when it tries to access that lock.

	You're right, Leo: the above code is wrong.  If that last UnLock
line read: UnLock(CurrentDir(aarrgghh)), it would be correct.  Note that
all programs run from WB currently must restore the original lock via
CurrentDir before exiting (I think some compiler startups may do this for
you.)

>	I thought, as another Net person said (name forgotten; sorry), that
>CurrentDir() trades locks with you.  You hand CurrentDir() a lock on the
>directory you want to go to.  CurrentDir() takes it away from you, and
>gives you posession of a lock on the directory you used to be in.  This is
>how I do it in Onion, and I haven't had any problems yet.

	You have it exactly right (though PLEASE use CurrentDir(), don't
just trade them yourselves, for future compatiblity.

>	What is the final story?  Naturally, the DOS docs obfuscate this
>point.

	The DOS docs (amigados users, tech ref, and programmers manuals)
should be read 15 times apiece, left under your pillow so you can absorb
them by osmosis, read 5 more times, and consantly referred to.  :-)

>Leo L. Schwab -- The Guy in The Cape	INET: well!ewhac@ucbvax.Berkeley.EDU

-- 
You've heard of CATS? Well, I'm a member of DOGS: Developers Of Great Software.
Randell Jesup, Commodore Engineering {uunet|rutgers|allegra}!cbmvax!jesup