[Search for users] [Overall Top Noters] [List of all Conferences] [Download this site]

Conference hydra::amiga_v1

Title:AMIGA NOTES
Notice:Join us in the *NEW* conference - HYDRA::AMIGA_V2
Moderator:HYDRA::MOORE
Created:Sat Apr 26 1986
Last Modified:Wed Feb 05 1992
Last Successful Update:Fri Jun 06 1997
Number of topics:5378
Total number of notes:38326

3042.0. "(Lattice ?) C question: disappearing memory" by MSBIS2::LANDINGHAM (Guy M.,BXB1-1/F11,293-5297) Tue Oct 24 1989 16:21

Hello,

I'm not sure if my problem is with coding or with Lattice C, so please bear
with me.  I am using 5.02 of Lattice C.

I have a program which uses linked lists to dynamically allocated structures
as needed.  Relevant code sections for the allocation are:

typedef struct vnode {
	double Element[4];
	struct vnode *next;
} VertexNode;
.
.
.
VertexNode *VPtr;

Vptr = (VertexNode *)malloc(sizeof(VertexNode));


This seems to work ok.  Periodically, the program walks down a linked list of
these structure to free up space:

error = free((VertexNode *)VPtr);

Now, after this call error == 0, which according to Lattice means that free()
was successful.

However, if I start this program on a rather large dataset and watch the
available memory in the Workbench title bar, it constantly decreases until the
Guru appears.  I've double checked my pointer manipulation and it seems ok.
The free() call returns indicating success.  Yet memory steadily decreases.

Any help is much appreciated...
T.RTitleUserPersonal
Name
DateLines
3042.1WJG::GUINEAUTue Oct 24 1989 18:276
I just looked up free() in the ANSI version of K&R and it says
free is of return type void. So it doesn't return anything.

Not sure about Lattice free()...

John
3042.2Some more suggestionsBANKS1::MIANOI see the N end of a S bound horseTue Oct 24 1989 20:1236
If would add something like:

enum block_type { Vnode=3482 } ;

and modify your structure 

>typedef struct vnode {
>	double Element[4];
>	struct vnode *next;
	enum block_type block_type ;
>} VertexNode;
>
>.
>.
>.
>VertexNode *VPtr;
>
And add
>Vptr = (VertexNode *)malloc(sizeof(VertexNode));
Vptr->block_type = Vnode ;

When you walk through the structure, after every 
a = a-> next.

add 

if ( a->block_type != Vnode ) { REPORT AN ERROR } ;

ALSO NOTE THAT 
free(0) in the Lattice library Royally F*&Ks up your system.

How about creating routines allocate_vnode and deallocate_vnode
that allocate and free nodes.  That way you can add debugging code for 
memory allocation/freeing in one place.

John
3042.3Suggest you use AllocMem & FreeMemDECSIM::GILLETTWhat does soft wear? DTN 225-7172Tue Oct 24 1989 23:4820
    Re: .0
    
    Your original version looks fine to me, free() should set that memory
    free, sort of.  There is an issue of free block collection & compaction
    (the C standard library memory management routines: malloc, calloc, and
    free use managed space), but your nodes don't seem to be of varying
    size, so this shouldn't be a problem.
    
    Try this, until someone can verify that free() might be broken:
    
    User the Amiga system service calls from Exec: AllocMem & FreeMem.
    These are documented in both Exec and in Appendix A of your orange ROM
    kernel manual.  I'd look 'em both up for you, but my Amiga and
    attendant docs are in a truck somewhere between Detroit &
    Mass. (i'm relocating).
    
                     Lots 'O Luck,
    
                            chris
    
3042.4castMILKWY::JANZENcf. ANT::CIRCUITS,ANT::UWAVESWed Oct 25 1989 10:113
    aren't you supposed to just cast the result into unsigned long int or
    somethign?
    Tom
3042.5More info (correction)MSBIS1::LANDINGHAMGuy M.,BXB1-1/F11,293-5297Wed Oct 25 1989 11:3625
    re: .4
    
    Well, I've tried this two ways so far:
    
    free(VPtr);
    
    ...causes free memory reported in WB title bar to slowly shrink
    down to almost 0 then GURU.
    
    free((VertexNode *)VPtr);
    
    ...again causes free memory to shrink, but more slowly than the
    first example.  This time the reported free memory gets down to
    about 33K (from 600+ K), and the program terminates successfully.
    (sorry, I mixed these two cases up in the first note.)
    
    I don't understand this: for the dataset I'm testing with, I don't
    allocate more than 144 nodes between free() sweeps.  Shouldn't I see
    free memory shrink to no less than 144 * sizeof(VertexNode) (roughly)?
    
    Appreciate all the suggestions and help...
    
     
    
    
3042.6Not the castTLE::RMEYERSRandy MeyersWed Oct 25 1989 18:4045
Re: .5

The argument to free(), for a remotely ANSI conforming compiler, is void *.
Void * is a pointer type that is compatible with any other pointer type.
It exists so that the argument of free need not be cast to anything
(and the result of malloc need not be cast).

Pre-ANSI compilers defined free as taking a char * argument.  Tasteful
programmers would cast the argument of free to char * in the call.

In (one version of) your program, you have the following:

    free((VertexNode *)VPtr);

This the cast is not needed.  First, free does not take a VertexNode *
argument.  Second, VPtr is (supposedly) already declared to be a
VertexNode *, and casting a value to its present type is never
needed.

I recommend that you remove the cast.  It'll show that yer a cool dude.

The cast isn't your problem, anyway.  On a 680x0, all pointers have
the same representation, and free doesn't really care what the type
the pointer is.

I suspect that the error in your program exists elsewhere.  You
might have a pointer that goes wild and smashes the next field
of the structure or smashes internal memory management headers that
malloc and free put on the start of your node.  You might have
bad or uninitialized next values in your nodes.  You might be
calling free on 0 (not terminating your loop properly).  You
might be accessing a node after you free it.

Make sure you don't have a loop like this:


	while (vptr != NULL) {
		free(vptr);
		vptr = vptr->next;	/* Very bad error here */
	}

because after you free a piece of memory, you aren't allowed
to look at it again.  And on the Amiga, there is a great danger
that someone may grab the memory you just deallocated and use
it for something else.
3042.7I'll look again, thanksMSBIS1::LANDINGHAMGuy M.,BXB1-1/F11,293-5297Wed Oct 25 1989 20:5421
    re: .6
    
    Thanks, Randy.  Yup, I'll admit the first cast was a pitiful shot
    in the dark.  I was wondering how free() knew how many bytes to
    return to the free list, and I can see from your reply that malloc
    and free are tacking on some more info onto my chunk of memory.
    
    I was fairly careful in writing the free-up sweep and used two pointers
    to avoid the error you mention in your example loop.  If I use
    CodePRobe, I can keep track of the addresses of each mallocated
    node and, stepping through the free-up sweep code, it appears that
    everything is pointing in the right place when I call free().  I
    will try the (char *) cast and check the code yet again.
    
    I'm using Lattice 5.02 and it's interesting that the manual states
    that their level 3 memory allocation functions are "fully compatible
    with the ANSI standard" and yet they state that free() expects a
    pointer to a character.   Guess you can't believe everything you
    read in a manual...
    
    Thanks again.
3042.8Argument to freeTLE::RMEYERSRandy MeyersWed Oct 25 1989 21:2812
Re: .7

>[Lattice states] their level 3 memory allocation functions are "fully
>compatible with the ANSI standard" and yet they state that free() expects
>a pointer to a character.

I suspect a documentation error (I thought they had this right).  Type
out stdlib.h to see what type the argument really is.  

>Guess you can't believe everything you read in a manual...

Guess that's true!
3042.9Casting, free(), & offer to helpDECSIM::GILLETTWhat does soft wear? DTN 225-7172Wed Oct 25 1989 23:3232
    re: .6
    
    "...it'll show yer a cool dude..."
    
    Cool dudes always, always, ALWAYS cast everything when the target does
    not agree with the source type.  The only way to guarentee portability
    across platforms is to do type casting correctly (along with a bunch 'o
    other stuff besides).  Note that on most Digital projects, C code that
    does not cast correctly will not make it through code review.
    
    Re:  Original topic
    
    Got myself to an Amiga and ran a few simple tests.  free() works "as
    you'd expect it to" meaning that I got back that which I released,
    regardless casting (or lack thereof).  So, it looks like somthing else
    is broke in your code.  Probably .6 is correct...you're probably
    walking all over something else.
    
    If you're absolutely certain that your code is correct (if it's not
    Amiga specific, maybe you should try running it on a different machine
    just for grins), then it's time to examine your environment.  Specific
    interests would include your launch code, stack size, and the state of
    the memory system prior to launch.
    
    If you're truly ready to scream, email it to me, and I'll read it on my
    lunch hour.  I've done a lot on the Amiga (used to work for an Amiga
    software house), and maybe I can spot something.
    
                              Later,
    
                                  chris
    
3042.10About the argument to free()TLE::RMEYERSRandy MeyersFri Oct 27 1989 01:1670
Re: .9

>    Cool dudes always, always, ALWAYS cast everything when the target does
>    not agree with the source type.  The only way to guarentee portability
>    across platforms is to do type casting correctly (along with a bunch 'o
>    other stuff besides).

Its very good advice to avoid representational dependencies, but it does
not quite fit the situation here.

To insure compatibility and transportability, the programmer must #include
proper declarations of all functions called.  Once that is done, the
programmer should insert needed casts.

But, once the prototype of free() is included, free() takes any pointer
type as an argument.  No cast is necessary.

If you desire to port the code to a non-ANSI C system, there are two
cases:  the system uses a uniform representation for all pointers, or the
system uses different pointer representations for pointers to different
data types.

If the system uses a uniform pointer representation, then you don't
need the cast for the program to work.  The modern code works for
the wrong reason.  Over time, non-ANSI C systems are going to become
ANSI C.  Then the modern code will still work, but for the right
reason.

If the system uses different representations for different types of
pointers, then there is a problem.  The programmer would be forced to
insert the cast.  Of course, if the programmer can hold off porting
his code until the system gets an ANSI C compiler, he or she can
avoid making the change. :-)

Systems without ANSI C compilers and that have non-uniform pointers
are rare, and getting rarer.  DEC now writes code for bunches of
systems: clones, Macs, MIPS machines, etc.  I don't know of any
of those platforms that have the double problem of no ANSI C and
non-uniform pointers.

It comes down to a judgment call:

    1.  Cast the argument of free() to char *.  Works with everything.
	Rarely needed.  Weird from the future point of view.  You cast
	the argument to a type that isn't really the type of parameter
	so the program will work on rare machines.

    2.  Don't cast the argument of free().  Works with everything except
	pre-ANSI C compilers running on machines that do not have uniform
	pointers.  The code will never cause anyone to scratch their
	head.

    3.  Define a typedef for the right type of the parameter of free on
	your system and cast the argument to free to that type.  It
	works everywhere, but has the potential to really screw the
	readability of the code.

    4.  Do #3, sort of: Go ahead and cast the argument of free to void *,
	and take the strategy of typedef'ing void on the non-ANSI C compiler.
	This only get you in trouble when you are working with a compiler
	that has partially added support for void (I've seen such a compiler:
	it supported void as a return type but did not allow pointer to
	void).

My personal preference: #2, #4, #1, and as a distant last, #3.

Making calls to free() portable to systems with non-uniform pointers
and without ANSI C is a problem with small payback.  The effort would
be much better spent removing dependencies on the sizes of types,
byte ordering, sign-ness preserving, and what have you.
3042.11Could be the stackTLE::RMEYERSRandy MeyersFri Oct 27 1989 01:239
Re: .9

>... examine your environment.  Specific interests would include your
>launch code, stack size, and the state of the memory system prior to
>launch.

Try using a huge stack.  I've noticed that Lattice C programs running
under the debugger require large stacks.  Weird stuff happens when
the stack overflows.
3042.12BANKS1::MIANOI see the N end of a S bound horseFri Oct 27 1989 12:0812
RE: < Note 3042.11 by TLE::RMEYERS "Randy Meyers" >

>Try using a huge stack.  I've noticed that Lattice C programs running
>under the debugger require large stacks.  Weird stuff happens when
>the stack overflows.

This is absolutely correct.  A bug in the debugger makes
THREE-FOURTHS of the stack unusable so you need to make the
stack FOUR times larger than you would need if you were not
using the debugger.

John
3042.13Or use current software...FRAMBO::BALZERChristian Balzer DTN:785-1029Fri Oct 27 1989 12:347
    Re: .12
    
    Or just use the 5.04 version of CodeProbe, which fixes that problem...
    
    Regards,
    
    <CB>
3042.14...what cool dudes do...DECSIM::GILLETTWhat does soft wear? DTN 225-7172Sun Oct 29 1989 15:4813
    re: .10
    
    Gues I've been told :-) !  Your points are well taken when looking at C
    from the ANSI point of view.  Our group is developing for VAX and
    MIPSco platforms, and we've found a lot of stuff in the MIPSco C
    compiler that hardly qualifies as ANSI (well, same goes for VAX C,
    except that VAX C is much more mature than MIPSco C).
    
    Anyways, the problem here doesn't have anything to do with casting, so
    this thread is getting worn.
    
                               chris
    
3042.15don't use free()AIAG::WISNERyou may ask yourself &#039;How do I work this?&#039;.Tue Nov 07 1989 12:3026
    
    
    
    Here are some alternate approaches to memory allocation that you may
    find useful. 
    
    
    I suggest that .0 should not use free().  Are you malloc'ing and
    free'ing mostly VertexNode's?  If so, then I suggest a different 
    approach.  Instead of free'ing VertexNodes, push them onto a
    "SpareVertexNodes" list.  Write a GetVertexNode function which first 
    tries to pop a node off the spare's list; if no spare's exist, then
    allocate a new node.  
    
    Also, you can save storage by guessing the average number of nodes that 
    your program will use, allocate one big block
    size=(sizeof(node)*number) of nodes and 
    push all the nodes onto the spare's list.  This way, each node does not
    have the header that malloc and free would create for each node.  
    
    If you use the intuition AllocRemember() function, then you can easily
    free all the memory you allocated when you exist your program using the
    FreeRemember() function; these two functions automatically maintain a 
    list of all your allocations.
    
    
3042.16memory management tipsTLE::RMEYERSRandy MeyersTue Nov 07 1989 14:0119
RE: .15

>    If you use the intuition AllocRemember() function, then you can easily
>    free all the memory you allocated when you exist your program using the
>    FreeRemember() function; these two functions automatically maintain a 
>    list of all your allocations.

If you use the C library memory management routines (malloc/calloc/free)
then memory will be freed when you program exits by calling exit(), abort(),
or returning from the main().

Note that it doesn't return the memory if you call the AmigaDOS routine
Exit(), but then Exit() is evil for a number of reasons (Workbench
programs are never unloaded if they call Exit()!).

The advice in .15 about maintaining your own free list and doing
zoned memory management is good, and will improve your performance.
I don't think it will fix the bug that is in your program, although
it may make it go into hiding by shifting the memory map around.