Added a new notifyer, NC_SPACE_CHANGED, to signal an editor that
[blender.git] / intern / guardedalloc / intern / mallocn.c
1 /**
2  * $Id$
3  * ***** BEGIN GPL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  *
19  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s): none yet.
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /**
30
31  * $Id$
32  * Copyright (C) 2001 NaN Technologies B.V.
33  * Guarded memory allocation, and boundary-write detection.
34  */
35
36 #include <stdlib.h>
37 #include <string.h>     /* memcpy */
38 #include <stdarg.h>
39
40 #include "../../../source/blender/blenlib/BLI_mempool.h"
41
42 /* mmap exception */
43 #if defined(WIN32)
44 #include <sys/types.h>
45 #include "mmap_win.h"
46 #else
47 #include <sys/types.h>
48 #include <sys/mman.h>
49 #endif
50
51 #include "MEM_guardedalloc.h"
52
53 /* --------------------------------------------------------------------- */
54 /* Data definition                                                       */
55 /* --------------------------------------------------------------------- */
56 /* all memory chunks are put in linked lists */
57 typedef struct localLink
58 {
59         struct localLink *next,*prev;
60 } localLink;
61
62 typedef struct localListBase 
63 {
64         void *first, *last;
65 } localListBase;
66
67         /* note: keep this struct aligned (e.g., irix/gcc) - Hos */
68 typedef struct MemHead {
69         int tag1;
70         int len;
71         struct MemHead *next,*prev;
72         const char * name;
73         const char * nextname;
74         int tag2;
75         int mmap;       /* if true, memory was mmapped */
76 } MemHead;
77
78 typedef struct MemTail {
79         int tag3, pad;
80 } MemTail;
81
82
83 /* --------------------------------------------------------------------- */
84 /* local functions                                                       */
85 /* --------------------------------------------------------------------- */
86
87 static void addtail(volatile localListBase *listbase, void *vlink);
88 static void remlink(volatile localListBase *listbase, void *vlink);
89 static void rem_memblock(MemHead *memh);
90 static void MemorY_ErroR(const char *block, const char *error);
91 static const char *check_memlist(MemHead *memh);
92
93 /* --------------------------------------------------------------------- */
94 /* locally used defines                                                  */
95 /* --------------------------------------------------------------------- */
96
97 #if defined( __sgi) || defined (__sun) || defined (__sun__) || defined (__sparc) || defined (__sparc__) || defined (__PPC__) || (defined (__APPLE__) && !defined(__LITTLE_ENDIAN__))
98 #define MAKE_ID(a,b,c,d) ( (int)(a)<<24 | (int)(b)<<16 | (c)<<8 | (d) )
99 #else
100 #define MAKE_ID(a,b,c,d) ( (int)(d)<<24 | (int)(c)<<16 | (b)<<8 | (a) )
101 #endif
102
103 #define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
104 #define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
105 #define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
106 #define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
107
108 #define MEMNEXT(x) ((MemHead *)(((char *) x) - ((char *) & (((MemHead *)0)->next))))
109         
110 /* --------------------------------------------------------------------- */
111 /* vars                                                                  */
112 /* --------------------------------------------------------------------- */
113         
114
115 static volatile int totblock= 0;
116 static volatile uintptr_t mem_in_use= 0, mmap_in_use= 0;
117
118 static volatile struct localListBase _membase;
119 static volatile struct localListBase *membase = &_membase;
120 static void (*error_callback)(char *) = NULL;
121 static void (*thread_lock_callback)(void) = NULL;
122 static void (*thread_unlock_callback)(void) = NULL;
123
124 static int malloc_debug_memset= 0;
125
126 #ifdef malloc
127 #undef malloc
128 #endif
129
130 #ifdef calloc
131 #undef calloc
132 #endif
133
134 #ifdef free
135 #undef free
136 #endif
137
138
139 /* --------------------------------------------------------------------- */
140 /* implementation                                                        */
141 /* --------------------------------------------------------------------- */
142
143 #ifdef OPTIMIZE_SMALL_BLOCKS
144
145 /*allocator for small objects
146
147   basic strategy: most slowdowns from overuse of the system allocator tends
148   to be from relatively small allocations.
149 */
150
151 /*this is a little bit bigger then it perhaps needs to be, to accomodate vgroup
152   allocations (which are one system alloc per vertex of a mesh, and is a major
153   source of performance loss)*/
154 #define SMALL_BLOCK_LIMIT       1024
155 #define POOL_CHUNK_SIZE         512 /*size in number of elements, not bytes*/
156
157 static BLI_mempool *alloc_pools[SMALL_BLOCK_LIMIT+sizeof(MemHead)+sizeof(MemTail)] = {NULL,};
158
159 static void *mempool_alloc_mem(int size) {
160         size += sizeof(MemHead)+sizeof(MemTail);
161
162         if (!alloc_pools[size]) {
163                 alloc_pools[size] = BLI_mempool_create(size, 1, POOL_CHUNK_SIZE, 1);
164         }
165         
166         return BLI_mempool_alloc(alloc_pools[size]);
167 }
168
169 static void mempool_free(void *mem, int size) {
170         size += sizeof(MemHead)+sizeof(MemTail);
171
172         BLI_mempool_free(alloc_pools[size], mem);
173 }
174
175 #endif
176
177 static void print_error(const char *str, ...)
178 {
179         char buf[1024];
180         va_list ap;
181
182         va_start(ap, str);
183         vsprintf(buf, str, ap);
184         va_end(ap);
185
186         if (error_callback) error_callback(buf);
187 }
188
189 static void mem_lock_thread()
190 {
191         if (thread_lock_callback)
192                 thread_lock_callback();
193 }
194
195 static void mem_unlock_thread()
196 {
197         if (thread_unlock_callback)
198                 thread_unlock_callback();
199 }
200
201 int MEM_check_memory_integrity()
202 {
203         const char* err_val = NULL;
204         MemHead* listend;
205         /* check_memlist starts from the front, and runs until it finds
206          * the requested chunk. For this test, that's the last one. */
207         listend = membase->last;
208         
209         err_val = check_memlist(listend);
210
211         if (err_val == 0) return 0;
212         return 1;
213 }
214
215
216 void MEM_set_error_callback(void (*func)(char *))
217 {
218         error_callback = func;
219 }
220
221 void MEM_set_lock_callback(void (*lock)(void), void (*unlock)(void))
222 {
223         thread_lock_callback = lock;
224         thread_unlock_callback = unlock;
225 }
226
227 void MEM_set_memory_debug(void)
228 {
229         malloc_debug_memset= 1;
230 }
231
232 int MEM_allocN_len(void *vmemh)
233 {
234         if (vmemh) {
235                 MemHead *memh= vmemh;
236         
237                 memh--;
238                 return memh->len;
239         } else
240                 return 0;
241 }
242
243 void *MEM_dupallocN(void *vmemh)
244 {
245         void *newp= NULL;
246         
247         if (vmemh) {
248                 MemHead *memh= vmemh;
249                 memh--;
250                 
251                 if(memh->mmap)
252                         newp= MEM_mapallocN(memh->len, "dupli_mapalloc");
253                 else
254                         newp= MEM_mallocN(memh->len, "dupli_alloc");
255
256                 if (newp == NULL) return NULL;
257
258                 memcpy(newp, vmemh, memh->len);
259         }
260
261         return newp;
262 }
263
264 static void make_memhead_header(MemHead *memh, unsigned int len, const char *str)
265 {
266         MemTail *memt;
267         
268         memh->tag1 = MEMTAG1;
269         memh->name = str;
270         memh->nextname = 0;
271         memh->len = len;
272         memh->mmap = 0;
273         memh->tag2 = MEMTAG2;
274         
275         memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
276         memt->tag3 = MEMTAG3;
277         
278         addtail(membase,&memh->next);
279         if (memh->next) memh->nextname = MEMNEXT(memh->next)->name;
280         
281         totblock++;
282         mem_in_use += len;
283 }
284
285 void *MEM_mallocN(unsigned int len, const char *str)
286 {
287         MemHead *memh;
288
289         mem_lock_thread();
290
291         len = (len + 3 ) & ~3;  /* allocate in units of 4 */
292         
293 #ifdef OPTIMIZE_SMALL_BLOCKS
294         if (len < SMALL_BLOCK_LIMIT)
295                 memh= mempool_alloc_mem(len);
296         else
297                 memh= (MemHead *)malloc(len+sizeof(MemHead)+sizeof(MemTail));
298 #else
299         memh= (MemHead *)malloc(len+sizeof(MemHead)+sizeof(MemTail));
300 #endif
301
302         if(memh) {
303                 make_memhead_header(memh, len, str);
304                 mem_unlock_thread();
305                 if(malloc_debug_memset && len)
306                         memset(memh+1, 255, len);
307                 return (++memh);
308         }
309         mem_unlock_thread();
310         print_error("Malloc returns nill: len=%d in %s, total %u\n",len, str, mem_in_use);
311         return NULL;
312 }
313
314 void *MEM_callocN(unsigned int len, const char *str)
315 {
316         MemHead *memh;
317
318         mem_lock_thread();
319
320         len = (len + 3 ) & ~3;  /* allocate in units of 4 */
321
322 #ifdef OPTIMIZE_SMALL_BLOCKS
323         if (len < SMALL_BLOCK_LIMIT) {
324                 memh= mempool_alloc_mem(len);
325                 memset(memh, 0, len+sizeof(MemHead)+sizeof(MemTail));
326         } else {
327                 memh= (MemHead *)calloc(len+sizeof(MemHead)+sizeof(MemTail),1);
328         }
329 #else
330         memh= (MemHead *)calloc(len+sizeof(MemHead)+sizeof(MemTail),1);
331 #endif
332
333         if(memh) {
334                 make_memhead_header(memh, len, str);
335                 mem_unlock_thread();
336                 return (++memh);
337         }
338         mem_unlock_thread();
339         print_error("Calloc returns nill: len=%d in %s, total %u\n",len, str, mem_in_use);
340         return 0;
341 }
342
343 /* note; mmap returns zero'd memory */
344 void *MEM_mapallocN(unsigned int len, const char *str)
345 {
346         MemHead *memh;
347
348         mem_lock_thread();
349         
350         len = (len + 3 ) & ~3;  /* allocate in units of 4 */
351         
352 #ifdef __sgi
353         {
354 #include <fcntl.h>
355
356           int fd;
357           fd = open("/dev/zero", O_RDWR);
358
359           memh= mmap(0, len+sizeof(MemHead)+sizeof(MemTail),
360                      PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
361           close(fd);
362         }
363 #else
364         memh= mmap(0, len+sizeof(MemHead)+sizeof(MemTail),
365                    PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
366 #endif
367
368         if(memh!=(MemHead *)-1) {
369                 make_memhead_header(memh, len, str);
370                 memh->mmap= 1;
371                 mmap_in_use += len;
372                 mem_unlock_thread();
373                 return (++memh);
374         }
375         else {
376                 mem_unlock_thread();
377                 print_error("Mapalloc returns nill, fallback to regular malloc: len=%d in %s, total %u\n",len, str, mmap_in_use);
378                 return MEM_callocN(len, str);
379         }
380 }
381
382 /* Memory statistics print */
383 typedef struct MemPrintBlock {
384         const char *name;
385         uintptr_t len;
386         int items;
387 } MemPrintBlock;
388
389 static int compare_name(const void *p1, const void *p2)
390 {
391         const MemPrintBlock *pb1= (const MemPrintBlock*)p1;
392         const MemPrintBlock *pb2= (const MemPrintBlock*)p2;
393
394         return strcmp(pb1->name, pb2->name);
395 }
396
397 static int compare_len(const void *p1, const void *p2)
398 {
399         const MemPrintBlock *pb1= (const MemPrintBlock*)p1;
400         const MemPrintBlock *pb2= (const MemPrintBlock*)p2;
401
402         if(pb1->len < pb2->len)
403                 return 1;
404         else if(pb1->len == pb2->len)
405                 return 0;
406         else
407                 return -1;
408 }
409
410 void MEM_printmemlist_stats()
411 {
412         MemHead *membl;
413         MemPrintBlock *pb, *printblock;
414         int totpb, a, b;
415
416         mem_lock_thread();
417
418         /* put memory blocks into array */
419         printblock= malloc(sizeof(MemPrintBlock)*totblock);
420
421         pb= printblock;
422         totpb= 0;
423
424         membl = membase->first;
425         if (membl) membl = MEMNEXT(membl);
426
427         while(membl) {
428                 pb->name= membl->name;
429                 pb->len= membl->len;
430                 pb->items= 1;
431
432                 totpb++;
433                 pb++;
434
435                 if(membl->next)
436                         membl= MEMNEXT(membl->next);
437                 else break;
438         }
439
440         /* sort by name and add together blocks with the same name */
441         qsort(printblock, totpb, sizeof(MemPrintBlock), compare_name);
442         for(a=0, b=0; a<totpb; a++) {
443                 if(a == b) {
444                         continue;
445                 }
446                 else if(strcmp(printblock[a].name, printblock[b].name) == 0) {
447                         printblock[b].len += printblock[a].len;
448                         printblock[b].items++;
449                 }
450                 else {
451                         b++;
452                         memcpy(&printblock[b], &printblock[a], sizeof(MemPrintBlock));
453                 }
454         }
455         totpb= b+1;
456
457         /* sort by length and print */
458         qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
459         printf("\ntotal memory len: %.3f MB\n", (double)mem_in_use/(double)(1024*1024));
460         for(a=0, pb=printblock; a<totpb; a++, pb++)
461                 printf("%s items: %d, len: %.3f MB\n", pb->name, pb->items, (double)pb->len/(double)(1024*1024));
462
463         free(printblock);
464         
465         mem_unlock_thread();
466 }
467
468 /* Prints in python syntax for easy */
469 static void MEM_printmemlist_internal( int pydict )
470 {
471         MemHead *membl;
472
473         mem_lock_thread();
474
475         membl = membase->first;
476         if (membl) membl = MEMNEXT(membl);
477         
478         if (pydict) {
479                 print_error("# membase_debug.py\n");
480                 print_error("membase = [\\\n");
481         }
482         while(membl) {
483                 if (pydict) {
484                         fprintf(stderr, "{'len':%i, 'name':'''%s''', 'pointer':'%p'},\\\n", membl->len, membl->name, membl+1);
485                 } else {
486                         print_error("%s len: %d %p\n",membl->name,membl->len, membl+1);
487                 }
488                 if(membl->next)
489                         membl= MEMNEXT(membl->next);
490                 else break;
491         }
492         if (pydict) {
493                 fprintf(stderr, "]\n\n");
494                 fprintf(stderr,
495 "mb_userinfo = {}\n"
496 "totmem = 0\n"
497 "for mb_item in membase:\n"
498 "\tmb_item_user_size = mb_userinfo.setdefault(mb_item['name'], [0,0])\n"
499 "\tmb_item_user_size[0] += 1 # Add a user\n"
500 "\tmb_item_user_size[1] += mb_item['len'] # Increment the size\n"
501 "\ttotmem += mb_item['len']\n"
502 "print '(membase) items:', len(membase), '| unique-names:', len(mb_userinfo), '| total-mem:', totmem\n"
503 "mb_userinfo_sort = mb_userinfo.items()\n"
504 "for sort_name, sort_func in (('size', lambda a: -a[1][1]), ('users', lambda a: -a[1][0]), ('name', lambda a: a[0])):\n"
505 "\tprint '\\nSorting by:', sort_name\n"
506 "\tmb_userinfo_sort.sort(key = sort_func)\n"
507 "\tfor item in mb_userinfo_sort:\n"
508 "\t\tprint 'name:%%s, users:%%i, len:%%i' %% (item[0], item[1][0], item[1][1])\n"
509                 );
510         }
511         
512         mem_unlock_thread();
513 }
514
515 void MEM_callbackmemlist(void (*func)(void*)) {
516         MemHead *membl;
517
518         mem_lock_thread();
519
520         membl = membase->first;
521         if (membl) membl = MEMNEXT(membl);
522
523         while(membl) {
524                 func(membl+1);
525                 if(membl->next)
526                         membl= MEMNEXT(membl->next);
527                 else break;
528         }
529
530         mem_unlock_thread();
531 }
532
533 short MEM_testN(void *vmemh) {
534         MemHead *membl;
535
536         mem_lock_thread();
537
538         membl = membase->first;
539         if (membl) membl = MEMNEXT(membl);
540
541         while(membl) {
542                 if (vmemh == membl+1)
543                         return 1;
544
545                 if(membl->next)
546                         membl= MEMNEXT(membl->next);
547                 else break;
548         }
549
550         mem_unlock_thread();
551
552         print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
553         return 0;
554 }
555
556 void MEM_printmemlist( void ) {
557         MEM_printmemlist_internal(0);
558 }
559 void MEM_printmemlist_pydict( void ) {
560         MEM_printmemlist_internal(1);
561 }
562
563 short MEM_freeN(void *vmemh)            /* anders compileertie niet meer */
564 {
565         short error = 0;
566         MemTail *memt;
567         MemHead *memh= vmemh;
568         const char *name;
569
570         if (memh == NULL){
571                 MemorY_ErroR("free","attempt to free NULL pointer");
572                 /* print_error(err_stream, "%d\n", (memh+4000)->tag1); */
573                 return(-1);
574         }
575
576         if(sizeof(intptr_t)==8) {
577                 if (((intptr_t) memh) & 0x7) {
578                         MemorY_ErroR("free","attempt to free illegal pointer");
579                         return(-1);
580                 }
581         }
582         else {
583                 if (((intptr_t) memh) & 0x3) {
584                         MemorY_ErroR("free","attempt to free illegal pointer");
585                         return(-1);
586                 }
587         }
588         
589         memh--;
590         if(memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
591                 MemorY_ErroR(memh->name,"double free");
592                 return(-1);
593         }
594
595         mem_lock_thread();
596         if ((memh->tag1 == MEMTAG1) && (memh->tag2 == MEMTAG2) && ((memh->len & 0x3) == 0)) {
597                 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + memh->len);
598                 if (memt->tag3 == MEMTAG3){
599                         
600                         memh->tag1 = MEMFREE;
601                         memh->tag2 = MEMFREE;
602                         memt->tag3 = MEMFREE;
603                         /* after tags !!! */
604                         rem_memblock(memh);
605
606                         mem_unlock_thread();
607                         
608                         return(0);
609                 }
610                 error = 2;
611                 MemorY_ErroR(memh->name,"end corrupt");
612                 name = check_memlist(memh);
613                 if (name != 0){
614                         if (name != memh->name) MemorY_ErroR(name,"is also corrupt");
615                 }
616         } else{
617                 error = -1;
618                 name = check_memlist(memh);
619                 if (name == 0)
620                         MemorY_ErroR("free","pointer not in memlist");
621                 else
622                         MemorY_ErroR(name,"error in header");
623         }
624
625         totblock--;
626         /* here a DUMP should happen */
627
628         mem_unlock_thread();
629
630         return(error);
631 }
632
633 /* --------------------------------------------------------------------- */
634 /* local functions                                                       */
635 /* --------------------------------------------------------------------- */
636
637 static void addtail(volatile localListBase *listbase, void *vlink)
638 {
639         struct localLink *link= vlink;
640
641         if (link == 0) return;
642         if (listbase == 0) return;
643
644         link->next = 0;
645         link->prev = listbase->last;
646
647         if (listbase->last) ((struct localLink *)listbase->last)->next = link;
648         if (listbase->first == 0) listbase->first = link;
649         listbase->last = link;
650 }
651
652 static void remlink(volatile localListBase *listbase, void *vlink)
653 {
654         struct localLink *link= vlink;
655
656         if (link == 0) return;
657         if (listbase == 0) return;
658
659         if (link->next) link->next->prev = link->prev;
660         if (link->prev) link->prev->next = link->next;
661
662         if (listbase->last == link) listbase->last = link->prev;
663         if (listbase->first == link) listbase->first = link->next;
664 }
665
666 static void rem_memblock(MemHead *memh)
667 {
668     remlink(membase,&memh->next);
669     if (memh->prev) {
670         if (memh->next) 
671                         MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
672         else 
673                         MEMNEXT(memh->prev)->nextname = NULL;
674     }
675
676     totblock--;
677     mem_in_use -= memh->len;
678    
679     if(memh->mmap) {
680         mmap_in_use -= memh->len;
681         if (munmap(memh, memh->len + sizeof(MemHead) + sizeof(MemTail)))
682             printf("Couldn't unmap memory %s\n", memh->name);
683     }
684 #ifdef OPTIMIZE_SMALL_BLOCKS
685         else if (memh->len < SMALL_BLOCK_LIMIT) {
686                 mempool_free(memh, memh->len);
687         }
688 #endif
689     else {
690                 if(malloc_debug_memset && memh->len)
691                         memset(memh+1, 255, memh->len);
692         free(memh);
693         }
694 }
695
696 static void MemorY_ErroR(const char *block, const char *error)
697 {
698         print_error("Memoryblock %s: %s\n",block, error);
699 }
700
701 static const char *check_memlist(MemHead *memh)
702 {
703         MemHead *forw,*back,*forwok,*backok;
704         const char *name;
705
706         forw = membase->first;
707         if (forw) forw = MEMNEXT(forw);
708         forwok = 0;
709         while(forw){
710                 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
711                 forwok = forw;
712                 if (forw->next) forw = MEMNEXT(forw->next);
713                 else forw = 0;
714         }
715
716         back = (MemHead *) membase->last;
717         if (back) back = MEMNEXT(back);
718         backok = 0;
719         while(back){
720                 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
721                 backok = back;
722                 if (back->prev) back = MEMNEXT(back->prev);
723                 else back = 0;
724         }
725
726         if (forw != back) return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
727
728         if (forw == 0 && back == 0){
729                 /* geen foute headers gevonden dan maar op zoek naar memblock*/
730
731                 forw = membase->first;
732                 if (forw) forw = MEMNEXT(forw);
733                 forwok = 0;
734                 while(forw){
735                         if (forw == memh) break;
736                         if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
737                         forwok = forw;
738                         if (forw->next) forw = MEMNEXT(forw->next);
739                         else forw = 0;
740                 }
741                 if (forw == 0) return (0);
742
743                 back = (MemHead *) membase->last;
744                 if (back) back = MEMNEXT(back);
745                 backok = 0;
746                 while(back){
747                         if (back == memh) break;
748                         if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
749                         backok = back;
750                         if (back->prev) back = MEMNEXT(back->prev);
751                         else back = 0;
752                 }
753         }
754
755         if (forwok) name = forwok->nextname;
756         else name = "No name found";
757
758         if (forw == memh){
759                 /* voor alle zekerheid wordt dit block maar uit de lijst gehaald */
760                 if (forwok){
761                         if (backok){
762                                 forwok->next = (MemHead *)&backok->next;
763                                 backok->prev = (MemHead *)&forwok->next;
764                                 forwok->nextname = backok->name;
765                         } else{
766                                 forwok->next = 0;
767                                 membase->last = (struct localLink *) &forwok->next; 
768 /*                              membase->last = (struct Link *) &forwok->next; */
769                         }
770                 } else{
771                         if (backok){
772                                 backok->prev = 0;
773                                 membase->first = &backok->next;
774                         } else{
775                                 membase->first = membase->last = 0;
776                         }
777                 }
778         } else{
779                 MemorY_ErroR(name,"Additional error in header");
780                 return("Additional error in header");
781         }
782
783         return(name);
784 }
785
786 uintptr_t MEM_get_memory_in_use(void)
787 {
788         uintptr_t _mem_in_use;
789
790         mem_lock_thread();
791         _mem_in_use= mem_in_use;
792         mem_unlock_thread();
793
794         return _mem_in_use;
795 }
796
797 uintptr_t MEM_get_mapped_memory_in_use(void)
798 {
799         uintptr_t _mmap_in_use;
800
801         mem_lock_thread();
802         _mmap_in_use= mmap_in_use;
803         mem_unlock_thread();
804
805         return _mmap_in_use;
806 }
807
808 int MEM_get_memory_blocks_in_use(void)
809 {
810         int _totblock;
811
812         mem_lock_thread();
813         _totblock= totblock;
814         mem_unlock_thread();
815
816         return _totblock;
817 }
818
819 /* eof */