comparison nspr/pr/src/malloc/prmem.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1e5118fa0cb1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /*
7 ** Thread safe versions of malloc, free, realloc, calloc and cfree.
8 */
9
10 #include "primpl.h"
11
12 #ifdef _PR_ZONE_ALLOCATOR
13
14 /*
15 ** The zone allocator code must use native mutexes and cannot
16 ** use PRLocks because PR_NewLock calls PR_Calloc, resulting
17 ** in cyclic dependency of initialization.
18 */
19
20 #include <string.h>
21
22 union memBlkHdrUn;
23
24 typedef struct MemoryZoneStr {
25 union memBlkHdrUn *head; /* free list */
26 pthread_mutex_t lock;
27 size_t blockSize; /* size of blocks on this free list */
28 PRUint32 locked; /* current state of lock */
29 PRUint32 contention; /* counter: had to wait for lock */
30 PRUint32 hits; /* allocated from free list */
31 PRUint32 misses; /* had to call malloc */
32 PRUint32 elements; /* on free list */
33 } MemoryZone;
34
35 typedef union memBlkHdrUn {
36 unsigned char filler[48]; /* fix the size of this beast */
37 struct memBlkHdrStr {
38 union memBlkHdrUn *next;
39 MemoryZone *zone;
40 size_t blockSize;
41 size_t requestedSize;
42 PRUint32 magic;
43 } s;
44 } MemBlockHdr;
45
46 #define MEM_ZONES 7
47 #define THREAD_POOLS 11 /* prime number for modulus */
48 #define ZONE_MAGIC 0x0BADC0DE
49
50 static MemoryZone zones[MEM_ZONES][THREAD_POOLS];
51
52 static PRBool use_zone_allocator = PR_FALSE;
53
54 static void pr_ZoneFree(void *ptr);
55
56 void
57 _PR_DestroyZones(void)
58 {
59 int i, j;
60
61 if (!use_zone_allocator)
62 return;
63
64 for (j = 0; j < THREAD_POOLS; j++) {
65 for (i = 0; i < MEM_ZONES; i++) {
66 MemoryZone *mz = &zones[i][j];
67 pthread_mutex_destroy(&mz->lock);
68 while (mz->head) {
69 MemBlockHdr *hdr = mz->head;
70 mz->head = hdr->s.next; /* unlink it */
71 free(hdr);
72 mz->elements--;
73 }
74 }
75 }
76 use_zone_allocator = PR_FALSE;
77 }
78
79 /*
80 ** pr_FindSymbolInProg
81 **
82 ** Find the specified data symbol in the program and return
83 ** its address.
84 */
85
86 #ifdef HAVE_DLL
87
88 #if defined(USE_DLFCN) && !defined(NO_DLOPEN_NULL)
89
90 #include <dlfcn.h>
91
92 static void *
93 pr_FindSymbolInProg(const char *name)
94 {
95 void *h;
96 void *sym;
97
98 h = dlopen(0, RTLD_LAZY);
99 if (h == NULL)
100 return NULL;
101 sym = dlsym(h, name);
102 (void)dlclose(h);
103 return sym;
104 }
105
106 #elif defined(USE_HPSHL)
107
108 #include <dl.h>
109
110 static void *
111 pr_FindSymbolInProg(const char *name)
112 {
113 shl_t h = NULL;
114 void *sym;
115
116 if (shl_findsym(&h, name, TYPE_DATA, &sym) == -1)
117 return NULL;
118 return sym;
119 }
120
121 #elif defined(USE_MACH_DYLD) || defined(NO_DLOPEN_NULL)
122
123 static void *
124 pr_FindSymbolInProg(const char *name)
125 {
126 /* FIXME: not implemented */
127 return NULL;
128 }
129
130 #else
131
132 #error "The zone allocator is not supported on this platform"
133
134 #endif
135
136 #else /* !defined(HAVE_DLL) */
137
138 static void *
139 pr_FindSymbolInProg(const char *name)
140 {
141 /* can't be implemented */
142 return NULL;
143 }
144
145 #endif /* HAVE_DLL */
146
147 void
148 _PR_InitZones(void)
149 {
150 int i, j;
151 char *envp;
152 PRBool *sym;
153
154 if ((sym = (PRBool *)pr_FindSymbolInProg("nspr_use_zone_allocator")) != NULL) {
155 use_zone_allocator = *sym;
156 } else if ((envp = getenv("NSPR_USE_ZONE_ALLOCATOR")) != NULL) {
157 use_zone_allocator = (atoi(envp) == 1);
158 }
159
160 if (!use_zone_allocator)
161 return;
162
163 for (j = 0; j < THREAD_POOLS; j++) {
164 for (i = 0; i < MEM_ZONES; i++) {
165 MemoryZone *mz = &zones[i][j];
166 int rv = pthread_mutex_init(&mz->lock, NULL);
167 PR_ASSERT(0 == rv);
168 if (rv != 0) {
169 goto loser;
170 }
171 mz->blockSize = 16 << ( 2 * i);
172 }
173 }
174 return;
175
176 loser:
177 _PR_DestroyZones();
178 return;
179 }
180
181 PR_IMPLEMENT(void)
182 PR_FPrintZoneStats(PRFileDesc *debug_out)
183 {
184 int i, j;
185
186 for (j = 0; j < THREAD_POOLS; j++) {
187 for (i = 0; i < MEM_ZONES; i++) {
188 MemoryZone *mz = &zones[i][j];
189 MemoryZone zone = *mz;
190 if (zone.elements || zone.misses || zone.hits) {
191 PR_fprintf(debug_out,
192 "pool: %d, zone: %d, size: %d, free: %d, hit: %d, miss: %d, contend: %d\n",
193 j, i, zone.blockSize, zone.elements,
194 zone.hits, zone.misses, zone.contention);
195 }
196 }
197 }
198 }
199
200 static void *
201 pr_ZoneMalloc(PRUint32 size)
202 {
203 void *rv;
204 unsigned int zone;
205 size_t blockSize;
206 MemBlockHdr *mb, *mt;
207 MemoryZone *mz;
208
209 /* Always allocate a non-zero amount of bytes */
210 if (size < 1) {
211 size = 1;
212 }
213 for (zone = 0, blockSize = 16; zone < MEM_ZONES; ++zone, blockSize <<= 2) {
214 if (size <= blockSize) {
215 break;
216 }
217 }
218 if (zone < MEM_ZONES) {
219 pthread_t me = pthread_self();
220 unsigned int pool = (PRUptrdiff)me % THREAD_POOLS;
221 PRUint32 wasLocked;
222 mz = &zones[zone][pool];
223 wasLocked = mz->locked;
224 pthread_mutex_lock(&mz->lock);
225 mz->locked = 1;
226 if (wasLocked)
227 mz->contention++;
228 if (mz->head) {
229 mb = mz->head;
230 PR_ASSERT(mb->s.magic == ZONE_MAGIC);
231 PR_ASSERT(mb->s.zone == mz);
232 PR_ASSERT(mb->s.blockSize == blockSize);
233 PR_ASSERT(mz->blockSize == blockSize);
234
235 mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize);
236 PR_ASSERT(mt->s.magic == ZONE_MAGIC);
237 PR_ASSERT(mt->s.zone == mz);
238 PR_ASSERT(mt->s.blockSize == blockSize);
239
240 mz->hits++;
241 mz->elements--;
242 mz->head = mb->s.next; /* take off free list */
243 mz->locked = 0;
244 pthread_mutex_unlock(&mz->lock);
245
246 mt->s.next = mb->s.next = NULL;
247 mt->s.requestedSize = mb->s.requestedSize = size;
248
249 rv = (void *)(mb + 1);
250 return rv;
251 }
252
253 mz->misses++;
254 mz->locked = 0;
255 pthread_mutex_unlock(&mz->lock);
256
257 mb = (MemBlockHdr *)malloc(blockSize + 2 * (sizeof *mb));
258 if (!mb) {
259 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
260 return NULL;
261 }
262 mb->s.next = NULL;
263 mb->s.zone = mz;
264 mb->s.magic = ZONE_MAGIC;
265 mb->s.blockSize = blockSize;
266 mb->s.requestedSize = size;
267
268 mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize);
269 memcpy(mt, mb, sizeof *mb);
270
271 rv = (void *)(mb + 1);
272 return rv;
273 }
274
275 /* size was too big. Create a block with no zone */
276 blockSize = (size & 15) ? size + 16 - (size & 15) : size;
277 mb = (MemBlockHdr *)malloc(blockSize + 2 * (sizeof *mb));
278 if (!mb) {
279 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
280 return NULL;
281 }
282 mb->s.next = NULL;
283 mb->s.zone = NULL;
284 mb->s.magic = ZONE_MAGIC;
285 mb->s.blockSize = blockSize;
286 mb->s.requestedSize = size;
287
288 mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize);
289 memcpy(mt, mb, sizeof *mb);
290
291 rv = (void *)(mb + 1);
292 return rv;
293 }
294
295
296 static void *
297 pr_ZoneCalloc(PRUint32 nelem, PRUint32 elsize)
298 {
299 PRUint32 size = nelem * elsize;
300 void *p = pr_ZoneMalloc(size);
301 if (p) {
302 memset(p, 0, size);
303 }
304 return p;
305 }
306
307 static void *
308 pr_ZoneRealloc(void *oldptr, PRUint32 bytes)
309 {
310 void *rv;
311 MemBlockHdr *mb;
312 int ours;
313 MemBlockHdr phony;
314
315 if (!oldptr)
316 return pr_ZoneMalloc(bytes);
317 mb = (MemBlockHdr *)((char *)oldptr - (sizeof *mb));
318 if (mb->s.magic != ZONE_MAGIC) {
319 /* Maybe this just came from ordinary malloc */
320 #ifdef DEBUG
321 fprintf(stderr,
322 "Warning: reallocing memory block %p from ordinary malloc\n",
323 oldptr);
324 #endif
325 /*
326 * We are going to realloc oldptr. If realloc succeeds, the
327 * original value of oldptr will point to freed memory. So this
328 * function must not fail after a successfull realloc call. We
329 * must perform any operation that may fail before the realloc
330 * call.
331 */
332 rv = pr_ZoneMalloc(bytes); /* this may fail */
333 if (!rv) {
334 return rv;
335 }
336
337 /* We don't know how big it is. But we can fix that. */
338 oldptr = realloc(oldptr, bytes);
339 /*
340 * If realloc returns NULL, this function loses the original
341 * value of oldptr. This isn't a leak because the caller of
342 * this function still has the original value of oldptr.
343 */
344 if (!oldptr) {
345 if (bytes) {
346 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
347 pr_ZoneFree(rv);
348 return oldptr;
349 }
350 }
351 phony.s.requestedSize = bytes;
352 mb = &phony;
353 ours = 0;
354 } else {
355 size_t blockSize = mb->s.blockSize;
356 MemBlockHdr *mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize);
357
358 PR_ASSERT(mt->s.magic == ZONE_MAGIC);
359 PR_ASSERT(mt->s.zone == mb->s.zone);
360 PR_ASSERT(mt->s.blockSize == blockSize);
361
362 if (bytes <= blockSize) {
363 /* The block is already big enough. */
364 mt->s.requestedSize = mb->s.requestedSize = bytes;
365 return oldptr;
366 }
367 ours = 1;
368 rv = pr_ZoneMalloc(bytes);
369 if (!rv) {
370 return rv;
371 }
372 }
373
374 if (oldptr && mb->s.requestedSize)
375 memcpy(rv, oldptr, mb->s.requestedSize);
376 if (ours)
377 pr_ZoneFree(oldptr);
378 else if (oldptr)
379 free(oldptr);
380 return rv;
381 }
382
383 static void
384 pr_ZoneFree(void *ptr)
385 {
386 MemBlockHdr *mb, *mt;
387 MemoryZone *mz;
388 size_t blockSize;
389 PRUint32 wasLocked;
390
391 if (!ptr)
392 return;
393
394 mb = (MemBlockHdr *)((char *)ptr - (sizeof *mb));
395
396 if (mb->s.magic != ZONE_MAGIC) {
397 /* maybe this came from ordinary malloc */
398 #ifdef DEBUG
399 fprintf(stderr,
400 "Warning: freeing memory block %p from ordinary malloc\n", ptr);
401 #endif
402 free(ptr);
403 return;
404 }
405
406 blockSize = mb->s.blockSize;
407 mz = mb->s.zone;
408 mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize);
409 PR_ASSERT(mt->s.magic == ZONE_MAGIC);
410 PR_ASSERT(mt->s.zone == mz);
411 PR_ASSERT(mt->s.blockSize == blockSize);
412 if (!mz) {
413 PR_ASSERT(blockSize > 65536);
414 /* This block was not in any zone. Just free it. */
415 free(mb);
416 return;
417 }
418 PR_ASSERT(mz->blockSize == blockSize);
419 wasLocked = mz->locked;
420 pthread_mutex_lock(&mz->lock);
421 mz->locked = 1;
422 if (wasLocked)
423 mz->contention++;
424 mt->s.next = mb->s.next = mz->head; /* put on head of list */
425 mz->head = mb;
426 mz->elements++;
427 mz->locked = 0;
428 pthread_mutex_unlock(&mz->lock);
429 }
430
431 PR_IMPLEMENT(void *) PR_Malloc(PRUint32 size)
432 {
433 if (!_pr_initialized) _PR_ImplicitInitialization();
434
435 return use_zone_allocator ? pr_ZoneMalloc(size) : malloc(size);
436 }
437
438 PR_IMPLEMENT(void *) PR_Calloc(PRUint32 nelem, PRUint32 elsize)
439 {
440 if (!_pr_initialized) _PR_ImplicitInitialization();
441
442 return use_zone_allocator ?
443 pr_ZoneCalloc(nelem, elsize) : calloc(nelem, elsize);
444 }
445
446 PR_IMPLEMENT(void *) PR_Realloc(void *ptr, PRUint32 size)
447 {
448 if (!_pr_initialized) _PR_ImplicitInitialization();
449
450 return use_zone_allocator ? pr_ZoneRealloc(ptr, size) : realloc(ptr, size);
451 }
452
453 PR_IMPLEMENT(void) PR_Free(void *ptr)
454 {
455 if (use_zone_allocator)
456 pr_ZoneFree(ptr);
457 else
458 free(ptr);
459 }
460
461 #else /* !defined(_PR_ZONE_ALLOCATOR) */
462
463 /*
464 ** The PR_Malloc, PR_Calloc, PR_Realloc, and PR_Free functions simply
465 ** call their libc equivalents now. This may seem redundant, but it
466 ** ensures that we are calling into the same runtime library. On
467 ** Win32, it is possible to have multiple runtime libraries (e.g.,
468 ** objects compiled with /MD and /MDd) in the same process, and
469 ** they maintain separate heaps, which cannot be mixed.
470 */
471 PR_IMPLEMENT(void *) PR_Malloc(PRUint32 size)
472 {
473 #if defined (WIN16)
474 return PR_MD_malloc( (size_t) size);
475 #else
476 return malloc(size);
477 #endif
478 }
479
480 PR_IMPLEMENT(void *) PR_Calloc(PRUint32 nelem, PRUint32 elsize)
481 {
482 #if defined (WIN16)
483 return PR_MD_calloc( (size_t)nelem, (size_t)elsize );
484
485 #else
486 return calloc(nelem, elsize);
487 #endif
488 }
489
490 PR_IMPLEMENT(void *) PR_Realloc(void *ptr, PRUint32 size)
491 {
492 #if defined (WIN16)
493 return PR_MD_realloc( ptr, (size_t) size);
494 #else
495 return realloc(ptr, size);
496 #endif
497 }
498
499 PR_IMPLEMENT(void) PR_Free(void *ptr)
500 {
501 #if defined (WIN16)
502 PR_MD_free( ptr );
503 #else
504 free(ptr);
505 #endif
506 }
507
508 #endif /* _PR_ZONE_ALLOCATOR */
509
510 /*
511 ** Complexity alert!
512 **
513 ** If malloc/calloc/free (etc.) were implemented to use pr lock's then
514 ** the entry points could block when called if some other thread had the
515 ** lock.
516 **
517 ** Most of the time this isn't a problem. However, in the case that we
518 ** are using the thread safe malloc code after PR_Init but before
519 ** PR_AttachThread has been called (on a native thread that nspr has yet
520 ** to be told about) we could get royally screwed if the lock was busy
521 ** and we tried to context switch the thread away. In this scenario
522 ** PR_CURRENT_THREAD() == NULL
523 **
524 ** To avoid this unfortunate case, we use the low level locking
525 ** facilities for malloc protection instead of the slightly higher level
526 ** locking. This makes malloc somewhat faster so maybe it's a good thing
527 ** anyway.
528 */
529 #ifdef _PR_OVERRIDE_MALLOC
530
531 /* Imports */
532 extern void *_PR_UnlockedMalloc(size_t size);
533 extern void *_PR_UnlockedMemalign(size_t alignment, size_t size);
534 extern void _PR_UnlockedFree(void *ptr);
535 extern void *_PR_UnlockedRealloc(void *ptr, size_t size);
536 extern void *_PR_UnlockedCalloc(size_t n, size_t elsize);
537
538 static PRBool _PR_malloc_initialised = PR_FALSE;
539
540 #ifdef _PR_PTHREADS
541 static pthread_mutex_t _PR_MD_malloc_crustylock;
542
543 #define _PR_Lock_Malloc() { \
544 if(PR_TRUE == _PR_malloc_initialised) { \
545 PRStatus rv; \
546 rv = pthread_mutex_lock(&_PR_MD_malloc_crustylock); \
547 PR_ASSERT(0 == rv); \
548 }
549
550 #define _PR_Unlock_Malloc() if(PR_TRUE == _PR_malloc_initialised) { \
551 PRStatus rv; \
552 rv = pthread_mutex_unlock(&_PR_MD_malloc_crustylock); \
553 PR_ASSERT(0 == rv); \
554 } \
555 }
556 #else /* _PR_PTHREADS */
557 static _MDLock _PR_MD_malloc_crustylock;
558
559 #ifdef IRIX
560 #define _PR_Lock_Malloc() { \
561 PRIntn _is; \
562 if(PR_TRUE == _PR_malloc_initialised) { \
563 if (_PR_MD_GET_ATTACHED_THREAD() && \
564 !_PR_IS_NATIVE_THREAD( \
565 _PR_MD_GET_ATTACHED_THREAD())) \
566 _PR_INTSOFF(_is); \
567 _PR_MD_LOCK(&_PR_MD_malloc_crustylock); \
568 }
569
570 #define _PR_Unlock_Malloc() if(PR_TRUE == _PR_malloc_initialised) { \
571 _PR_MD_UNLOCK(&_PR_MD_malloc_crustylock); \
572 if (_PR_MD_GET_ATTACHED_THREAD() && \
573 !_PR_IS_NATIVE_THREAD( \
574 _PR_MD_GET_ATTACHED_THREAD())) \
575 _PR_INTSON(_is); \
576 } \
577 }
578 #else /* IRIX */
579 #define _PR_Lock_Malloc() { \
580 PRIntn _is; \
581 if(PR_TRUE == _PR_malloc_initialised) { \
582 if (_PR_MD_CURRENT_THREAD() && \
583 !_PR_IS_NATIVE_THREAD( \
584 _PR_MD_CURRENT_THREAD())) \
585 _PR_INTSOFF(_is); \
586 _PR_MD_LOCK(&_PR_MD_malloc_crustylock); \
587 }
588
589 #define _PR_Unlock_Malloc() if(PR_TRUE == _PR_malloc_initialised) { \
590 _PR_MD_UNLOCK(&_PR_MD_malloc_crustylock); \
591 if (_PR_MD_CURRENT_THREAD() && \
592 !_PR_IS_NATIVE_THREAD( \
593 _PR_MD_CURRENT_THREAD())) \
594 _PR_INTSON(_is); \
595 } \
596 }
597 #endif /* IRIX */
598 #endif /* _PR_PTHREADS */
599
600 PR_IMPLEMENT(PRStatus) _PR_MallocInit(void)
601 {
602 PRStatus rv = PR_SUCCESS;
603
604 if( PR_TRUE == _PR_malloc_initialised ) return PR_SUCCESS;
605
606 #ifdef _PR_PTHREADS
607 {
608 int status;
609 pthread_mutexattr_t mattr;
610
611 status = _PT_PTHREAD_MUTEXATTR_INIT(&mattr);
612 PR_ASSERT(0 == status);
613 status = _PT_PTHREAD_MUTEX_INIT(_PR_MD_malloc_crustylock, mattr);
614 PR_ASSERT(0 == status);
615 status = _PT_PTHREAD_MUTEXATTR_DESTROY(&mattr);
616 PR_ASSERT(0 == status);
617 }
618 #else /* _PR_PTHREADS */
619 _MD_NEW_LOCK(&_PR_MD_malloc_crustylock);
620 #endif /* _PR_PTHREADS */
621
622 if( PR_SUCCESS == rv )
623 {
624 _PR_malloc_initialised = PR_TRUE;
625 }
626
627 return rv;
628 }
629
630 void *malloc(size_t size)
631 {
632 void *p;
633 _PR_Lock_Malloc();
634 p = _PR_UnlockedMalloc(size);
635 _PR_Unlock_Malloc();
636 return p;
637 }
638
639 #if defined(IRIX)
640 void *memalign(size_t alignment, size_t size)
641 {
642 void *p;
643 _PR_Lock_Malloc();
644 p = _PR_UnlockedMemalign(alignment, size);
645 _PR_Unlock_Malloc();
646 return p;
647 }
648
649 void *valloc(size_t size)
650 {
651 return(memalign(sysconf(_SC_PAGESIZE),size));
652 }
653 #endif /* IRIX */
654
655 void free(void *ptr)
656 {
657 _PR_Lock_Malloc();
658 _PR_UnlockedFree(ptr);
659 _PR_Unlock_Malloc();
660 }
661
662 void *realloc(void *ptr, size_t size)
663 {
664 void *p;
665 _PR_Lock_Malloc();
666 p = _PR_UnlockedRealloc(ptr, size);
667 _PR_Unlock_Malloc();
668 return p;
669 }
670
671 void *calloc(size_t n, size_t elsize)
672 {
673 void *p;
674 _PR_Lock_Malloc();
675 p = _PR_UnlockedCalloc(n, elsize);
676 _PR_Unlock_Malloc();
677 return p;
678 }
679
680 void cfree(void *p)
681 {
682 _PR_Lock_Malloc();
683 _PR_UnlockedFree(p);
684 _PR_Unlock_Malloc();
685 }
686
687 void _PR_InitMem(void)
688 {
689 PRStatus rv;
690 rv = _PR_MallocInit();
691 PR_ASSERT(PR_SUCCESS == rv);
692 }
693
694 #endif /* _PR_OVERRIDE_MALLOC */
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)