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