r4684@vps: verhaegs | 2007-05-04 21:04:10 -0400
[AROS.git] / rom / exec / freemem.c
blob79dd532fe318bf76f7511a306b1ac921a3017cd8
1 /*
2 Copyright © 1995-2001, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Free memory allocated by AllocMem()
6 Lang: english
7 */
8 #include <exec/alerts.h>
9 #include <exec/execbase.h>
10 #include <aros/libcall.h>
11 #include <aros/config.h>
12 #include <aros/macros.h>
13 #include "memory.h"
14 #include <aros/rt.h>
15 #include <exec/memory.h>
16 #include <exec/memheaderext.h>
17 #include <proto/exec.h>
19 #include "exec_debug.h"
20 #ifndef DEBUG_FreeMem
21 # define DEBUG_FreeMem 0
22 #endif
23 #undef DEBUG
24 #if DEBUG_FreeMem
25 # define DEBUG 1
26 #endif
27 #define MDEBUG 1
29 #include <aros/debug.h>
31 #include <stdlib.h>
33 /*****************************************************************************
35 NAME */
37 AROS_LH2(void, FreeMem,
39 /* SYNOPSIS */
40 AROS_LHA(APTR, memoryBlock, A1),
41 AROS_LHA(ULONG, byteSize, D0),
43 /* LOCATION */
44 struct ExecBase *, SysBase, 35, Exec)
46 /* FUNCTION
47 Give a block of memory back to the system pool.
49 INPUTS
50 memoryBlock - Pointer to the memory to be freed
51 byteSize - Size of the block
53 RESULT
55 NOTES
57 EXAMPLE
59 BUGS
61 SEE ALSO
62 AllocMem()
64 INTERNALS
66 ******************************************************************************/
68 AROS_LIBFUNC_INIT
70 struct MemHeader *mh;
71 struct MemChunk *p1, *p2, *p3;
72 UBYTE *p4;
73 #if AROS_MUNGWALL_DEBUG
74 ULONG origsize = byteSize;
75 #endif
77 D(bug("Call FreeMem (%08lx, %ld)\n", memoryBlock, byteSize));
79 /* If there is no memory free nothing */
80 if(!byteSize || !memoryBlock)
81 ReturnVoid ("FreeMem");
83 RT_Free (RTT_MEMORY, memoryBlock, byteSize);
85 /* The following two lines are necessary because of AllocAbs(),
86 were memoryBlock might not be aligned to a multiple of
87 MEMCHUNK_TOTAL!!!! */
89 /* Align size to the requirements (needed because of AllocAbs) */
90 byteSize+=(IPTR)memoryBlock&(MEMCHUNK_TOTAL-1);
92 /* Align the block as well (needed because of AllocAbs) */
93 memoryBlock=(APTR)AROS_ROUNDDOWN2((IPTR)memoryBlock,MEMCHUNK_TOTAL);
95 #if AROS_MUNGWALL_DEBUG
96 /* Add the size of mung walls and mungwall header */
97 memoryBlock -= MUNGWALL_SIZE + MUNGWALLHEADER_SIZE;
98 byteSize += MUNGWALL_SIZE * 2 + MUNGWALLHEADER_SIZE;
99 #endif
101 byteSize=AROS_ROUNDUP2(byteSize,MEMCHUNK_TOTAL);
103 #if AROS_MUNGWALL_DEBUG
105 struct MungwallHeader *header;
107 header = (struct MungwallHeader *)memoryBlock;
109 if (header->mwh_magicid != MUNGWALL_HEADER_ID)
111 struct Task *__t = FindTask(NULL); \
112 kprintf("\x07MUNGWALL_HEADER_ID mismatch (%s) mem = %x"
113 "allocsize = %d freesize = %d Task: 0x%x, Name: %s\n", \
114 __FUNCTION__,
115 memoryBlock + MUNGWALL_SIZE + MUNGWALLHEADER_SIZE,
116 *(ULONG *)memoryBlock,
117 origsize,
118 __t,
119 __t->tc_Node.ln_Name);\
122 if (header->mwh_allocsize != origsize)
124 struct Task *__t = FindTask(NULL); \
125 kprintf("\x07FreeMem size mismatches AllocMem size (%s) mem = %x"
126 "allocsize = %d freesize = %d Task: 0x%x, Name: %s\n", \
127 __FUNCTION__,
128 memoryBlock + MUNGWALL_SIZE + MUNGWALLHEADER_SIZE,
129 *(ULONG *)memoryBlock,
130 origsize,
131 __t,
132 __t->tc_Node.ln_Name);\
135 CHECK_WALL((UBYTE *)memoryBlock + MUNGWALLHEADER_SIZE, 0xDB, MUNGWALL_SIZE);
136 CHECK_WALL((UBYTE *)memoryBlock + MUNGWALLHEADER_SIZE + MUNGWALL_SIZE + origsize, 0xDB,
137 MUNGWALL_SIZE + AROS_ROUNDUP2(origsize, MEMCHUNK_TOTAL) - origsize);
139 /* Remove from AROSSupportBase->AllocMemList */
141 if ((header->mwh_node.mln_Pred != (struct MinNode *)0x44332211) ||
142 (header->mwh_node.mln_Succ != (struct MinNode *)0xCCBBAA99))
144 /* Reason for above checks: see allocmem.c */
145 Forbid();
146 Remove((struct Node *)&header->mwh_node);
147 Permit();
150 /* Fill block with weird stuff to esploit bugs in applications
152 * DOH! There's some _BAD_ code around that assumes memory can still be
153 * accessed after freeing by just preventing task switching. In AROS,
154 * RemTask(NULL) suffers of this problem because DOS processes are
155 * created with their TCB placed in the tc_MemEntry list. The workaround
156 * is to avoid munging when FreeMem() is called with task switching disabled.
158 /* DOH! it doesn't work even this way. What's wrong???
160 * if ((SysBase->TDNestCnt < 0) && (SysBase->IDNestCnt < 0))
161 * MUNGE_BLOCK(memoryBlock, MEMFILL_FREE, byteSize);
165 #endif
167 /* Start and end(+1) of the block */
168 p3=(struct MemChunk *)memoryBlock;
169 p4=(UBYTE *)p3+byteSize;
171 /* Protect the memory list from access by other tasks. */
172 Forbid();
174 ForeachNode(&SysBase->MemList, mh)
176 /* Test if the memory belongs to this MemHeader. */
177 if (mh->mh_Lower > memoryBlock || mh->mh_Upper <= memoryBlock)
178 continue;
180 if (mh->mh_Attributes & MEMF_MANAGED)
182 struct MemHeaderExt *mhe = (struct MemHeaderExt *)mh;
184 if (mhe->mhe_Free)
185 mhe->mhe_Free(mhe, memoryBlock, byteSize);
187 Permit();
188 ReturnVoid ("FreeMem");
192 #if !defined(NO_CONSISTENCY_CHECKS)
193 /* Test if it really fits into this MemHeader. */
194 if ((APTR)p4 > mh->mh_Upper)
195 /* Something is completely wrong. */
196 Alert(AN_MemCorrupt|AT_DeadEnd);
197 #endif
199 The free memory list is only single linked, i.e. to insert
200 elements into the list I need the node as well as it's
201 predessor. For the first element I can use freeList->mh_First
202 instead of a real predessor.
204 p1=(struct MemChunk *)&mh->mh_First;
205 p2=p1->mc_Next;
207 /* No chunk in list? Just insert the current one and return. */
208 if (p2 == NULL)
210 p3->mc_Bytes = byteSize;
211 p3->mc_Next = NULL;
212 p1->mc_Next = p3;
213 mh->mh_Free += byteSize;
215 Permit();
216 ReturnVoid ("FreeMem");
219 /* Follow the list to find a place where to insert our memory. */
222 #if !defined(NO_CONSISTENCY_CHECKS)
224 Do some constistency checks:
225 1. All MemChunks must be aligned to
226 MEMCHUNK_TOTAL.
227 2. The end (+1) of the current MemChunk
228 must be lower than the start of the next one.
230 if( ((IPTR)p2|p2->mc_Bytes)&(MEMCHUNK_TOTAL-1)
231 ||( (UBYTE *)p2+p2->mc_Bytes>=(UBYTE *)p2->mc_Next
232 &&p2->mc_Next!=NULL))
233 Alert(AN_MemCorrupt|AT_DeadEnd);
234 #endif
235 /* Found a block with a higher address? */
236 if(p2>=p3)
238 #if !defined(NO_CONSISTENCY_CHECKS)
240 If the memory to be freed overlaps with the current
241 block something must be wrong.
243 if(p4>(UBYTE *)p2)
244 Alert(AN_FreeTwice|AT_DeadEnd);
245 #endif
246 /* End the loop with p2 non-zero */
247 break;
249 /* goto next block */
250 p1=p2;
251 p2=p2->mc_Next;
253 /* If the loop ends with p2 zero add it at the end. */
254 } while (p2 != NULL);
256 /* If there was a previous block merge with it. */
257 if(p1!=(struct MemChunk *)&mh->mh_First)
259 #if !defined(NO_CONSISTENCY_CHECKS)
260 /* Check if they overlap. */
261 if((UBYTE *)p1+p1->mc_Bytes>(UBYTE *)p3)
262 Alert(AN_FreeTwice|AT_DeadEnd);
263 #endif
264 /* Merge if possible */
265 if((UBYTE *)p1+p1->mc_Bytes==(UBYTE *)p3)
266 p3=p1;
267 else
268 /* Not possible to merge */
269 p1->mc_Next=p3;
271 else
274 There was no previous block. Just insert the memory at
275 the start of the list.
277 p1->mc_Next=p3;
280 /* Try to merge with next block (if there is one ;-) ). */
281 if(p4==(UBYTE *)p2&&p2!=NULL)
284 Overlap checking already done. Doing it here after
285 the list potentially changed would be a bad idea.
287 p4+=p2->mc_Bytes;
288 p2=p2->mc_Next;
291 /* relink the list and return. */
292 p3->mc_Next = p2;
293 p3->mc_Bytes = p4-(UBYTE *)p3;
294 mh->mh_Free += byteSize;
295 Permit();
296 ReturnVoid ("FreeMem");
299 #if !defined(NO_CONSISTENCY_CHECKS)
300 /* Some memory that didn't fit into any MemHeader? */
301 Alert(AN_MemCorrupt|AT_DeadEnd);
302 #else
303 Permit();
304 #endif
306 ReturnVoid ("FreeMem");
308 AROS_LIBFUNC_EXIT
309 } /* FreeMem */