OLD | NEW |
| (Empty) |
1 /* This Source Code Form is subject to the terms of the Mozilla Public | |
2 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
4 | |
5 #ifdef DEBUG | |
6 static const char CVS_ID[] = "@(#) $RCSfile: arena.c,v $ $Revision: 1.15 $ $Date
: 2012/07/06 18:19:32 $"; | |
7 #endif /* DEBUG */ | |
8 | |
9 /* | |
10 * arena.c | |
11 * | |
12 * This contains the implementation of NSS's thread-safe arenas. | |
13 */ | |
14 | |
15 #ifndef BASE_H | |
16 #include "base.h" | |
17 #endif /* BASE_H */ | |
18 | |
19 #ifdef ARENA_THREADMARK | |
20 #include "prthread.h" | |
21 #endif /* ARENA_THREADMARK */ | |
22 | |
23 #include "prlock.h" | |
24 #include "plarena.h" | |
25 | |
26 #include <string.h> | |
27 | |
28 /* | |
29 * NSSArena | |
30 * | |
31 * This is based on NSPR's arena code, but it is threadsafe. | |
32 * | |
33 * The public methods relating to this type are: | |
34 * | |
35 * NSSArena_Create -- constructor | |
36 * NSSArena_Destroy | |
37 * NSS_ZAlloc | |
38 * NSS_ZRealloc | |
39 * NSS_ZFreeIf | |
40 * | |
41 * The nonpublic methods relating to this type are: | |
42 * | |
43 * nssArena_Create -- constructor | |
44 * nssArena_Destroy | |
45 * nssArena_Mark | |
46 * nssArena_Release | |
47 * nssArena_Unmark | |
48 * | |
49 * nss_ZAlloc | |
50 * nss_ZFreeIf | |
51 * nss_ZRealloc | |
52 * | |
53 * In debug builds, the following calls are available: | |
54 * | |
55 * nssArena_verifyPointer | |
56 * nssArena_registerDestructor | |
57 * nssArena_deregisterDestructor | |
58 */ | |
59 | |
60 struct NSSArenaStr { | |
61 PLArenaPool pool; | |
62 PRLock *lock; | |
63 #ifdef ARENA_THREADMARK | |
64 PRThread *marking_thread; | |
65 nssArenaMark *first_mark; | |
66 nssArenaMark *last_mark; | |
67 #endif /* ARENA_THREADMARK */ | |
68 #ifdef ARENA_DESTRUCTOR_LIST | |
69 struct arena_destructor_node *first_destructor; | |
70 struct arena_destructor_node *last_destructor; | |
71 #endif /* ARENA_DESTRUCTOR_LIST */ | |
72 }; | |
73 | |
74 /* | |
75 * nssArenaMark | |
76 * | |
77 * This type is used to mark the current state of an NSSArena. | |
78 */ | |
79 | |
80 struct nssArenaMarkStr { | |
81 PRUint32 magic; | |
82 void *mark; | |
83 #ifdef ARENA_THREADMARK | |
84 nssArenaMark *next; | |
85 #endif /* ARENA_THREADMARK */ | |
86 #ifdef ARENA_DESTRUCTOR_LIST | |
87 struct arena_destructor_node *next_destructor; | |
88 struct arena_destructor_node *prev_destructor; | |
89 #endif /* ARENA_DESTRUCTOR_LIST */ | |
90 }; | |
91 | |
92 #define MARK_MAGIC 0x4d41524b /* "MARK" how original */ | |
93 | |
94 /* | |
95 * But first, the pointer-tracking code | |
96 */ | |
97 #ifdef DEBUG | |
98 extern const NSSError NSS_ERROR_INTERNAL_ERROR; | |
99 | |
100 static nssPointerTracker arena_pointer_tracker; | |
101 | |
102 static PRStatus | |
103 arena_add_pointer | |
104 ( | |
105 const NSSArena *arena | |
106 ) | |
107 { | |
108 PRStatus rv; | |
109 | |
110 rv = nssPointerTracker_initialize(&arena_pointer_tracker); | |
111 if( PR_SUCCESS != rv ) { | |
112 return rv; | |
113 } | |
114 | |
115 rv = nssPointerTracker_add(&arena_pointer_tracker, arena); | |
116 if( PR_SUCCESS != rv ) { | |
117 NSSError e = NSS_GetError(); | |
118 if( NSS_ERROR_NO_MEMORY != e ) { | |
119 nss_SetError(NSS_ERROR_INTERNAL_ERROR); | |
120 } | |
121 | |
122 return rv; | |
123 } | |
124 | |
125 return PR_SUCCESS; | |
126 } | |
127 | |
128 static PRStatus | |
129 arena_remove_pointer | |
130 ( | |
131 const NSSArena *arena | |
132 ) | |
133 { | |
134 PRStatus rv; | |
135 | |
136 rv = nssPointerTracker_remove(&arena_pointer_tracker, arena); | |
137 if( PR_SUCCESS != rv ) { | |
138 nss_SetError(NSS_ERROR_INTERNAL_ERROR); | |
139 } | |
140 | |
141 return rv; | |
142 } | |
143 | |
144 /* | |
145 * nssArena_verifyPointer | |
146 * | |
147 * This method is only present in debug builds. | |
148 * | |
149 * If the specified pointer is a valid pointer to an NSSArena object, | |
150 * this routine will return PR_SUCCESS. Otherwise, it will put an | |
151 * error on the error stack and return PR_FAILURE. | |
152 * | |
153 * The error may be one of the following values: | |
154 * NSS_ERROR_INVALID_ARENA | |
155 * | |
156 * Return value: | |
157 * PR_SUCCESS if the pointer is valid | |
158 * PR_FAILURE if it isn't | |
159 */ | |
160 | |
161 NSS_IMPLEMENT PRStatus | |
162 nssArena_verifyPointer | |
163 ( | |
164 const NSSArena *arena | |
165 ) | |
166 { | |
167 PRStatus rv; | |
168 | |
169 rv = nssPointerTracker_initialize(&arena_pointer_tracker); | |
170 if( PR_SUCCESS != rv ) { | |
171 /* | |
172 * This is a little disingenious. We have to initialize the | |
173 * tracker, because someone could "legitimately" try to verify | |
174 * an arena pointer before one is ever created. And this step | |
175 * might fail, due to lack of memory. But the only way that | |
176 * this step can fail is if it's doing the call_once stuff, | |
177 * (later calls just no-op). And if it didn't no-op, there | |
178 * aren't any valid arenas.. so the argument certainly isn't one. | |
179 */ | |
180 nss_SetError(NSS_ERROR_INVALID_ARENA); | |
181 return PR_FAILURE; | |
182 } | |
183 | |
184 rv = nssPointerTracker_verify(&arena_pointer_tracker, arena); | |
185 if( PR_SUCCESS != rv ) { | |
186 nss_SetError(NSS_ERROR_INVALID_ARENA); | |
187 return PR_FAILURE; | |
188 } | |
189 | |
190 return PR_SUCCESS; | |
191 } | |
192 #endif /* DEBUG */ | |
193 | |
194 #ifdef ARENA_DESTRUCTOR_LIST | |
195 | |
196 struct arena_destructor_node { | |
197 struct arena_destructor_node *next; | |
198 struct arena_destructor_node *prev; | |
199 void (*destructor)(void *argument); | |
200 void *arg; | |
201 }; | |
202 | |
203 /* | |
204 * nssArena_registerDestructor | |
205 * | |
206 * This routine stores a pointer to a callback and an arbitrary | |
207 * pointer-sized argument in the arena, at the current point in | |
208 * the mark stack. If the arena is destroyed, or an "earlier" | |
209 * mark is released, then this destructor will be called at that | |
210 * time. Note that the destructor will be called with the arena | |
211 * locked, which means the destructor may free memory in that | |
212 * arena, but it may not allocate or cause to be allocated any | |
213 * memory. This callback facility was included to support our | |
214 * debug-version pointer-tracker feature; overuse runs counter to | |
215 * the the original intent of arenas. This routine returns a | |
216 * PRStatus value; if successful, it will return PR_SUCCESS. If | |
217 * unsuccessful, it will set an error on the error stack and | |
218 * return PR_FAILURE. | |
219 * | |
220 * The error may be one of the following values: | |
221 * NSS_ERROR_INVALID_ARENA | |
222 * NSS_ERROR_NO_MEMORY | |
223 * | |
224 * Return value: | |
225 * PR_SUCCESS | |
226 * PR_FAILURE | |
227 */ | |
228 | |
229 NSS_IMPLEMENT PRStatus | |
230 nssArena_registerDestructor | |
231 ( | |
232 NSSArena *arena, | |
233 void (*destructor)(void *argument), | |
234 void *arg | |
235 ) | |
236 { | |
237 struct arena_destructor_node *it; | |
238 | |
239 #ifdef NSSDEBUG | |
240 if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { | |
241 return PR_FAILURE; | |
242 } | |
243 #endif /* NSSDEBUG */ | |
244 | |
245 it = nss_ZNEW(arena, struct arena_destructor_node); | |
246 if( (struct arena_destructor_node *)NULL == it ) { | |
247 return PR_FAILURE; | |
248 } | |
249 | |
250 it->prev = arena->last_destructor; | |
251 arena->last_destructor->next = it; | |
252 arena->last_destructor = it; | |
253 it->destructor = destructor; | |
254 it->arg = arg; | |
255 | |
256 if( (nssArenaMark *)NULL != arena->last_mark ) { | |
257 arena->last_mark->prev_destructor = it->prev; | |
258 arena->last_mark->next_destructor = it->next; | |
259 } | |
260 | |
261 return PR_SUCCESS; | |
262 } | |
263 | |
264 NSS_IMPLEMENT PRStatus | |
265 nssArena_deregisterDestructor | |
266 ( | |
267 NSSArena *arena, | |
268 void (*destructor)(void *argument), | |
269 void *arg | |
270 ) | |
271 { | |
272 struct arena_destructor_node *it; | |
273 | |
274 #ifdef NSSDEBUG | |
275 if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { | |
276 return PR_FAILURE; | |
277 } | |
278 #endif /* NSSDEBUG */ | |
279 | |
280 for( it = arena->first_destructor; it; it = it->next ) { | |
281 if( (it->destructor == destructor) && (it->arg == arg) ) { | |
282 break; | |
283 } | |
284 } | |
285 | |
286 if( (struct arena_destructor_node *)NULL == it ) { | |
287 nss_SetError(NSS_ERROR_NOT_FOUND); | |
288 return PR_FAILURE; | |
289 } | |
290 | |
291 if( it == arena->first_destructor ) { | |
292 arena->first_destructor = it->next; | |
293 } | |
294 | |
295 if( it == arena->last_destructor ) { | |
296 arena->last_destructor = it->prev; | |
297 } | |
298 | |
299 if( (struct arena_destructor_node *)NULL != it->prev ) { | |
300 it->prev->next = it->next; | |
301 } | |
302 | |
303 if( (struct arena_destructor_node *)NULL != it->next ) { | |
304 it->next->prev = it->prev; | |
305 } | |
306 | |
307 { | |
308 nssArenaMark *m; | |
309 for( m = arena->first_mark; m; m = m->next ) { | |
310 if( m->next_destructor == it ) { | |
311 m->next_destructor = it->next; | |
312 } | |
313 if( m->prev_destructor == it ) { | |
314 m->prev_destructor = it->prev; | |
315 } | |
316 } | |
317 } | |
318 | |
319 nss_ZFreeIf(it); | |
320 return PR_SUCCESS; | |
321 } | |
322 | |
323 static void | |
324 nss_arena_call_destructor_chain | |
325 ( | |
326 struct arena_destructor_node *it | |
327 ) | |
328 { | |
329 for( ; it ; it = it->next ) { | |
330 (*(it->destructor))(it->arg); | |
331 } | |
332 } | |
333 | |
334 #endif /* ARENA_DESTRUCTOR_LIST */ | |
335 | |
336 /* | |
337 * NSSArena_Create | |
338 * | |
339 * This routine creates a new memory arena. This routine may return | |
340 * NULL upon error, in which case it will have created an error stack. | |
341 * | |
342 * The top-level error may be one of the following values: | |
343 * NSS_ERROR_NO_MEMORY | |
344 * | |
345 * Return value: | |
346 * NULL upon error | |
347 * A pointer to an NSSArena upon success | |
348 */ | |
349 | |
350 NSS_IMPLEMENT NSSArena * | |
351 NSSArena_Create | |
352 ( | |
353 void | |
354 ) | |
355 { | |
356 nss_ClearErrorStack(); | |
357 return nssArena_Create(); | |
358 } | |
359 | |
360 /* | |
361 * nssArena_Create | |
362 * | |
363 * This routine creates a new memory arena. This routine may return | |
364 * NULL upon error, in which case it will have set an error on the | |
365 * error stack. | |
366 * | |
367 * The error may be one of the following values: | |
368 * NSS_ERROR_NO_MEMORY | |
369 * | |
370 * Return value: | |
371 * NULL upon error | |
372 * A pointer to an NSSArena upon success | |
373 */ | |
374 | |
375 NSS_IMPLEMENT NSSArena * | |
376 nssArena_Create | |
377 ( | |
378 void | |
379 ) | |
380 { | |
381 NSSArena *rv = (NSSArena *)NULL; | |
382 | |
383 rv = nss_ZNEW((NSSArena *)NULL, NSSArena); | |
384 if( (NSSArena *)NULL == rv ) { | |
385 nss_SetError(NSS_ERROR_NO_MEMORY); | |
386 return (NSSArena *)NULL; | |
387 } | |
388 | |
389 rv->lock = PR_NewLock(); | |
390 if( (PRLock *)NULL == rv->lock ) { | |
391 (void)nss_ZFreeIf(rv); | |
392 nss_SetError(NSS_ERROR_NO_MEMORY); | |
393 return (NSSArena *)NULL; | |
394 } | |
395 | |
396 /* | |
397 * Arena sizes. The current security code has 229 occurrences of | |
398 * PORT_NewArena. The default chunksizes specified break down as | |
399 * | |
400 * Size Mult. Specified as | |
401 * 512 1 512 | |
402 * 1024 7 1024 | |
403 * 2048 5 2048 | |
404 * 2048 5 CRMF_DEFAULT_ARENA_SIZE | |
405 * 2048 190 DER_DEFAULT_CHUNKSIZE | |
406 * 2048 20 SEC_ASN1_DEFAULT_ARENA_SIZE | |
407 * 4096 1 4096 | |
408 * | |
409 * Obviously this "default chunksize" flexibility isn't very | |
410 * useful to us, so I'll just pick 2048. | |
411 */ | |
412 | |
413 PL_InitArenaPool(&rv->pool, "NSS", 2048, sizeof(double)); | |
414 | |
415 #ifdef DEBUG | |
416 { | |
417 PRStatus st; | |
418 st = arena_add_pointer(rv); | |
419 if( PR_SUCCESS != st ) { | |
420 PL_FinishArenaPool(&rv->pool); | |
421 PR_DestroyLock(rv->lock); | |
422 (void)nss_ZFreeIf(rv); | |
423 return (NSSArena *)NULL; | |
424 } | |
425 } | |
426 #endif /* DEBUG */ | |
427 | |
428 return rv; | |
429 } | |
430 | |
431 /* | |
432 * NSSArena_Destroy | |
433 * | |
434 * This routine will destroy the specified arena, freeing all memory | |
435 * allocated from it. This routine returns a PRStatus value; if | |
436 * successful, it will return PR_SUCCESS. If unsuccessful, it will | |
437 * create an error stack and return PR_FAILURE. | |
438 * | |
439 * The top-level error may be one of the following values: | |
440 * NSS_ERROR_INVALID_ARENA | |
441 * | |
442 * Return value: | |
443 * PR_SUCCESS upon success | |
444 * PR_FAILURE upon failure | |
445 */ | |
446 | |
447 NSS_IMPLEMENT PRStatus | |
448 NSSArena_Destroy | |
449 ( | |
450 NSSArena *arena | |
451 ) | |
452 { | |
453 nss_ClearErrorStack(); | |
454 | |
455 #ifdef DEBUG | |
456 if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { | |
457 return PR_FAILURE; | |
458 } | |
459 #endif /* DEBUG */ | |
460 | |
461 return nssArena_Destroy(arena); | |
462 } | |
463 | |
464 /* | |
465 * nssArena_Destroy | |
466 * | |
467 * This routine will destroy the specified arena, freeing all memory | |
468 * allocated from it. This routine returns a PRStatus value; if | |
469 * successful, it will return PR_SUCCESS. If unsuccessful, it will | |
470 * set an error on the error stack and return PR_FAILURE. | |
471 * | |
472 * The error may be one of the following values: | |
473 * NSS_ERROR_INVALID_ARENA | |
474 * | |
475 * Return value: | |
476 * PR_SUCCESS | |
477 * PR_FAILURE | |
478 */ | |
479 | |
480 NSS_IMPLEMENT PRStatus | |
481 nssArena_Destroy | |
482 ( | |
483 NSSArena *arena | |
484 ) | |
485 { | |
486 PRLock *lock; | |
487 | |
488 #ifdef NSSDEBUG | |
489 if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { | |
490 return PR_FAILURE; | |
491 } | |
492 #endif /* NSSDEBUG */ | |
493 | |
494 if( (PRLock *)NULL == arena->lock ) { | |
495 /* Just got destroyed */ | |
496 nss_SetError(NSS_ERROR_INVALID_ARENA); | |
497 return PR_FAILURE; | |
498 } | |
499 PR_Lock(arena->lock); | |
500 | |
501 #ifdef DEBUG | |
502 if( PR_SUCCESS != arena_remove_pointer(arena) ) { | |
503 PR_Unlock(arena->lock); | |
504 return PR_FAILURE; | |
505 } | |
506 #endif /* DEBUG */ | |
507 | |
508 #ifdef ARENA_DESTRUCTOR_LIST | |
509 /* Note that the arena is locked at this time */ | |
510 nss_arena_call_destructor_chain(arena->first_destructor); | |
511 #endif /* ARENA_DESTRUCTOR_LIST */ | |
512 | |
513 PL_FinishArenaPool(&arena->pool); | |
514 lock = arena->lock; | |
515 arena->lock = (PRLock *)NULL; | |
516 PR_Unlock(lock); | |
517 PR_DestroyLock(lock); | |
518 (void)nss_ZFreeIf(arena); | |
519 return PR_SUCCESS; | |
520 } | |
521 | |
522 static void *nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size); | |
523 | |
524 /* | |
525 * nssArena_Mark | |
526 * | |
527 * This routine "marks" the current state of an arena. Space | |
528 * allocated after the arena has been marked can be freed by | |
529 * releasing the arena back to the mark with nssArena_Release, | |
530 * or committed by calling nssArena_Unmark. When successful, | |
531 * this routine returns a valid nssArenaMark pointer. This | |
532 * routine may return NULL upon error, in which case it will | |
533 * have set an error on the error stack. | |
534 * | |
535 * The error may be one of the following values: | |
536 * NSS_ERROR_INVALID_ARENA | |
537 * NSS_ERROR_NO_MEMORY | |
538 * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD | |
539 * | |
540 * Return value: | |
541 * NULL upon failure | |
542 * An nssArenaMark pointer upon success | |
543 */ | |
544 | |
545 NSS_IMPLEMENT nssArenaMark * | |
546 nssArena_Mark | |
547 ( | |
548 NSSArena *arena | |
549 ) | |
550 { | |
551 nssArenaMark *rv; | |
552 void *p; | |
553 | |
554 #ifdef NSSDEBUG | |
555 if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { | |
556 return (nssArenaMark *)NULL; | |
557 } | |
558 #endif /* NSSDEBUG */ | |
559 | |
560 if( (PRLock *)NULL == arena->lock ) { | |
561 /* Just got destroyed */ | |
562 nss_SetError(NSS_ERROR_INVALID_ARENA); | |
563 return (nssArenaMark *)NULL; | |
564 } | |
565 PR_Lock(arena->lock); | |
566 | |
567 #ifdef ARENA_THREADMARK | |
568 if( (PRThread *)NULL == arena->marking_thread ) { | |
569 /* Unmarked. Store our thread ID */ | |
570 arena->marking_thread = PR_GetCurrentThread(); | |
571 /* This call never fails. */ | |
572 } else { | |
573 /* Marked. Verify it's the current thread */ | |
574 if( PR_GetCurrentThread() != arena->marking_thread ) { | |
575 PR_Unlock(arena->lock); | |
576 nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); | |
577 return (nssArenaMark *)NULL; | |
578 } | |
579 } | |
580 #endif /* ARENA_THREADMARK */ | |
581 | |
582 p = PL_ARENA_MARK(&arena->pool); | |
583 /* No error possible */ | |
584 | |
585 /* Do this after the mark */ | |
586 rv = (nssArenaMark *)nss_zalloc_arena_locked(arena, sizeof(nssArenaMark)); | |
587 if( (nssArenaMark *)NULL == rv ) { | |
588 PR_Unlock(arena->lock); | |
589 nss_SetError(NSS_ERROR_NO_MEMORY); | |
590 return (nssArenaMark *)NULL; | |
591 } | |
592 | |
593 #ifdef ARENA_THREADMARK | |
594 if ( (nssArenaMark *)NULL == arena->first_mark) { | |
595 arena->first_mark = rv; | |
596 arena->last_mark = rv; | |
597 } else { | |
598 arena->last_mark->next = rv; | |
599 arena->last_mark = rv; | |
600 } | |
601 #endif /* ARENA_THREADMARK */ | |
602 | |
603 rv->mark = p; | |
604 rv->magic = MARK_MAGIC; | |
605 | |
606 #ifdef ARENA_DESTRUCTOR_LIST | |
607 rv->prev_destructor = arena->last_destructor; | |
608 #endif /* ARENA_DESTRUCTOR_LIST */ | |
609 | |
610 PR_Unlock(arena->lock); | |
611 | |
612 return rv; | |
613 } | |
614 | |
615 /* | |
616 * nss_arena_unmark_release | |
617 * | |
618 * This static routine implements the routines nssArena_Release | |
619 * ans nssArena_Unmark, which are almost identical. | |
620 */ | |
621 | |
622 static PRStatus | |
623 nss_arena_unmark_release | |
624 ( | |
625 NSSArena *arena, | |
626 nssArenaMark *arenaMark, | |
627 PRBool release | |
628 ) | |
629 { | |
630 void *inner_mark; | |
631 | |
632 #ifdef NSSDEBUG | |
633 if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { | |
634 return PR_FAILURE; | |
635 } | |
636 #endif /* NSSDEBUG */ | |
637 | |
638 if( MARK_MAGIC != arenaMark->magic ) { | |
639 nss_SetError(NSS_ERROR_INVALID_ARENA_MARK); | |
640 return PR_FAILURE; | |
641 } | |
642 | |
643 if( (PRLock *)NULL == arena->lock ) { | |
644 /* Just got destroyed */ | |
645 nss_SetError(NSS_ERROR_INVALID_ARENA); | |
646 return PR_FAILURE; | |
647 } | |
648 PR_Lock(arena->lock); | |
649 | |
650 #ifdef ARENA_THREADMARK | |
651 if( (PRThread *)NULL != arena->marking_thread ) { | |
652 if( PR_GetCurrentThread() != arena->marking_thread ) { | |
653 PR_Unlock(arena->lock); | |
654 nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); | |
655 return PR_FAILURE; | |
656 } | |
657 } | |
658 #endif /* ARENA_THREADMARK */ | |
659 | |
660 if( MARK_MAGIC != arenaMark->magic ) { | |
661 /* Just got released */ | |
662 PR_Unlock(arena->lock); | |
663 nss_SetError(NSS_ERROR_INVALID_ARENA_MARK); | |
664 return PR_FAILURE; | |
665 } | |
666 | |
667 arenaMark->magic = 0; | |
668 inner_mark = arenaMark->mark; | |
669 | |
670 #ifdef ARENA_THREADMARK | |
671 { | |
672 nssArenaMark **pMark = &arena->first_mark; | |
673 nssArenaMark *rest; | |
674 nssArenaMark *last = (nssArenaMark *)NULL; | |
675 | |
676 /* Find this mark */ | |
677 while( *pMark != arenaMark ) { | |
678 last = *pMark; | |
679 pMark = &(*pMark)->next; | |
680 } | |
681 | |
682 /* Remember the pointer, then zero it */ | |
683 rest = (*pMark)->next; | |
684 *pMark = (nssArenaMark *)NULL; | |
685 | |
686 arena->last_mark = last; | |
687 | |
688 /* Invalidate any later marks being implicitly released */ | |
689 for( ; (nssArenaMark *)NULL != rest; rest = rest->next ) { | |
690 rest->magic = 0; | |
691 } | |
692 | |
693 /* If we just got rid of the first mark, clear the thread ID */ | |
694 if( (nssArenaMark *)NULL == arena->first_mark ) { | |
695 arena->marking_thread = (PRThread *)NULL; | |
696 } | |
697 } | |
698 #endif /* ARENA_THREADMARK */ | |
699 | |
700 if( release ) { | |
701 #ifdef ARENA_DESTRUCTOR_LIST | |
702 if( (struct arena_destructor_node *)NULL != arenaMark->prev_destructor ) { | |
703 arenaMark->prev_destructor->next = (struct arena_destructor_node *)NULL; | |
704 } | |
705 arena->last_destructor = arenaMark->prev_destructor; | |
706 | |
707 /* Note that the arena is locked at this time */ | |
708 nss_arena_call_destructor_chain(arenaMark->next_destructor); | |
709 #endif /* ARENA_DESTRUCTOR_LIST */ | |
710 | |
711 PR_ARENA_RELEASE(&arena->pool, inner_mark); | |
712 /* No error return */ | |
713 } | |
714 | |
715 PR_Unlock(arena->lock); | |
716 return PR_SUCCESS; | |
717 } | |
718 | |
719 /* | |
720 * nssArena_Release | |
721 * | |
722 * This routine invalidates and releases all memory allocated from | |
723 * the specified arena after the point at which the specified mark | |
724 * was obtained. This routine returns a PRStatus value; if successful, | |
725 * it will return PR_SUCCESS. If unsuccessful, it will set an error | |
726 * on the error stack and return PR_FAILURE. | |
727 * | |
728 * The error may be one of the following values: | |
729 * NSS_ERROR_INVALID_ARENA | |
730 * NSS_ERROR_INVALID_ARENA_MARK | |
731 * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD | |
732 * | |
733 * Return value: | |
734 * PR_SUCCESS | |
735 * PR_FAILURE | |
736 */ | |
737 | |
738 NSS_IMPLEMENT PRStatus | |
739 nssArena_Release | |
740 ( | |
741 NSSArena *arena, | |
742 nssArenaMark *arenaMark | |
743 ) | |
744 { | |
745 return nss_arena_unmark_release(arena, arenaMark, PR_TRUE); | |
746 } | |
747 | |
748 /* | |
749 * nssArena_Unmark | |
750 * | |
751 * This routine "commits" the indicated mark and any marks after | |
752 * it, making them unreleasable. Note that any earlier marks can | |
753 * still be released, and such a release will invalidate these | |
754 * later unmarked regions. If an arena is to be safely shared by | |
755 * more than one thread, all marks must be either released or | |
756 * unmarked. This routine returns a PRStatus value; if successful, | |
757 * it will return PR_SUCCESS. If unsuccessful, it will set an error | |
758 * on the error stack and return PR_FAILURE. | |
759 * | |
760 * The error may be one of the following values: | |
761 * NSS_ERROR_INVALID_ARENA | |
762 * NSS_ERROR_INVALID_ARENA_MARK | |
763 * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD | |
764 * | |
765 * Return value: | |
766 * PR_SUCCESS | |
767 * PR_FAILURE | |
768 */ | |
769 | |
770 NSS_IMPLEMENT PRStatus | |
771 nssArena_Unmark | |
772 ( | |
773 NSSArena *arena, | |
774 nssArenaMark *arenaMark | |
775 ) | |
776 { | |
777 return nss_arena_unmark_release(arena, arenaMark, PR_FALSE); | |
778 } | |
779 | |
780 /* | |
781 * We prefix this header to all allocated blocks. It is a multiple | |
782 * of the alignment size. Note that this usage of a header may make | |
783 * purify spew bogus warnings about "potentially leaked blocks" of | |
784 * memory; if that gets too annoying we can add in a pointer to the | |
785 * header in the header itself. There's not a lot of safety here; | |
786 * maybe we should add a magic value? | |
787 */ | |
788 struct pointer_header { | |
789 NSSArena *arena; | |
790 PRUint32 size; | |
791 }; | |
792 | |
793 static void * | |
794 nss_zalloc_arena_locked | |
795 ( | |
796 NSSArena *arena, | |
797 PRUint32 size | |
798 ) | |
799 { | |
800 void *p; | |
801 void *rv; | |
802 struct pointer_header *h; | |
803 PRUint32 my_size = size + sizeof(struct pointer_header); | |
804 PR_ARENA_ALLOCATE(p, &arena->pool, my_size); | |
805 if( (void *)NULL == p ) { | |
806 nss_SetError(NSS_ERROR_NO_MEMORY); | |
807 return (void *)NULL; | |
808 } | |
809 /* | |
810 * Do this before we unlock. This way if the user is using | |
811 * an arena in one thread while destroying it in another, he'll | |
812 * fault/FMR in his code, not ours. | |
813 */ | |
814 h = (struct pointer_header *)p; | |
815 h->arena = arena; | |
816 h->size = size; | |
817 rv = (void *)((char *)h + sizeof(struct pointer_header)); | |
818 (void)nsslibc_memset(rv, 0, size); | |
819 return rv; | |
820 } | |
821 | |
822 /* | |
823 * NSS_ZAlloc | |
824 * | |
825 * This routine allocates and zeroes a section of memory of the | |
826 * size, and returns to the caller a pointer to that memory. If | |
827 * the optional arena argument is non-null, the memory will be | |
828 * obtained from that arena; otherwise, the memory will be obtained | |
829 * from the heap. This routine may return NULL upon error, in | |
830 * which case it will have set an error upon the error stack. The | |
831 * value specified for size may be zero; in which case a valid | |
832 * zero-length block of memory will be allocated. This block may | |
833 * be expanded by calling NSS_ZRealloc. | |
834 * | |
835 * The error may be one of the following values: | |
836 * NSS_ERROR_INVALID_ARENA | |
837 * NSS_ERROR_NO_MEMORY | |
838 * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD | |
839 * | |
840 * Return value: | |
841 * NULL upon error | |
842 * A pointer to the new segment of zeroed memory | |
843 */ | |
844 | |
845 NSS_IMPLEMENT void * | |
846 NSS_ZAlloc | |
847 ( | |
848 NSSArena *arenaOpt, | |
849 PRUint32 size | |
850 ) | |
851 { | |
852 return nss_ZAlloc(arenaOpt, size); | |
853 } | |
854 | |
855 /* | |
856 * nss_ZAlloc | |
857 * | |
858 * This routine allocates and zeroes a section of memory of the | |
859 * size, and returns to the caller a pointer to that memory. If | |
860 * the optional arena argument is non-null, the memory will be | |
861 * obtained from that arena; otherwise, the memory will be obtained | |
862 * from the heap. This routine may return NULL upon error, in | |
863 * which case it will have set an error upon the error stack. The | |
864 * value specified for size may be zero; in which case a valid | |
865 * zero-length block of memory will be allocated. This block may | |
866 * be expanded by calling nss_ZRealloc. | |
867 * | |
868 * The error may be one of the following values: | |
869 * NSS_ERROR_INVALID_ARENA | |
870 * NSS_ERROR_NO_MEMORY | |
871 * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD | |
872 * | |
873 * Return value: | |
874 * NULL upon error | |
875 * A pointer to the new segment of zeroed memory | |
876 */ | |
877 | |
878 NSS_IMPLEMENT void * | |
879 nss_ZAlloc | |
880 ( | |
881 NSSArena *arenaOpt, | |
882 PRUint32 size | |
883 ) | |
884 { | |
885 struct pointer_header *h; | |
886 PRUint32 my_size = size + sizeof(struct pointer_header); | |
887 | |
888 if( my_size < sizeof(struct pointer_header) ) { | |
889 /* Wrapped */ | |
890 nss_SetError(NSS_ERROR_NO_MEMORY); | |
891 return (void *)NULL; | |
892 } | |
893 | |
894 if( (NSSArena *)NULL == arenaOpt ) { | |
895 /* Heap allocation, no locking required. */ | |
896 h = (struct pointer_header *)PR_Calloc(1, my_size); | |
897 if( (struct pointer_header *)NULL == h ) { | |
898 nss_SetError(NSS_ERROR_NO_MEMORY); | |
899 return (void *)NULL; | |
900 } | |
901 | |
902 h->arena = (NSSArena *)NULL; | |
903 h->size = size; | |
904 /* We used calloc: it's already zeroed */ | |
905 | |
906 return (void *)((char *)h + sizeof(struct pointer_header)); | |
907 } else { | |
908 void *rv; | |
909 /* Arena allocation */ | |
910 #ifdef NSSDEBUG | |
911 if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { | |
912 return (void *)NULL; | |
913 } | |
914 #endif /* NSSDEBUG */ | |
915 | |
916 if( (PRLock *)NULL == arenaOpt->lock ) { | |
917 /* Just got destroyed */ | |
918 nss_SetError(NSS_ERROR_INVALID_ARENA); | |
919 return (void *)NULL; | |
920 } | |
921 PR_Lock(arenaOpt->lock); | |
922 | |
923 #ifdef ARENA_THREADMARK | |
924 if( (PRThread *)NULL != arenaOpt->marking_thread ) { | |
925 if( PR_GetCurrentThread() != arenaOpt->marking_thread ) { | |
926 nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); | |
927 PR_Unlock(arenaOpt->lock); | |
928 return (void *)NULL; | |
929 } | |
930 } | |
931 #endif /* ARENA_THREADMARK */ | |
932 | |
933 rv = nss_zalloc_arena_locked(arenaOpt, size); | |
934 | |
935 PR_Unlock(arenaOpt->lock); | |
936 return rv; | |
937 } | |
938 /*NOTREACHED*/ | |
939 } | |
940 | |
941 /* | |
942 * NSS_ZFreeIf | |
943 * | |
944 * If the specified pointer is non-null, then the region of memory | |
945 * to which it points -- which must have been allocated with | |
946 * NSS_ZAlloc -- will be zeroed and released. This routine | |
947 * returns a PRStatus value; if successful, it will return PR_SUCCESS. | |
948 * If unsuccessful, it will set an error on the error stack and return | |
949 * PR_FAILURE. | |
950 * | |
951 * The error may be one of the following values: | |
952 * NSS_ERROR_INVALID_POINTER | |
953 * | |
954 * Return value: | |
955 * PR_SUCCESS | |
956 * PR_FAILURE | |
957 */ | |
958 NSS_IMPLEMENT PRStatus | |
959 NSS_ZFreeIf | |
960 ( | |
961 void *pointer | |
962 ) | |
963 { | |
964 return nss_ZFreeIf(pointer); | |
965 } | |
966 | |
967 /* | |
968 * nss_ZFreeIf | |
969 * | |
970 * If the specified pointer is non-null, then the region of memory | |
971 * to which it points -- which must have been allocated with | |
972 * nss_ZAlloc -- will be zeroed and released. This routine | |
973 * returns a PRStatus value; if successful, it will return PR_SUCCESS. | |
974 * If unsuccessful, it will set an error on the error stack and return | |
975 * PR_FAILURE. | |
976 * | |
977 * The error may be one of the following values: | |
978 * NSS_ERROR_INVALID_POINTER | |
979 * | |
980 * Return value: | |
981 * PR_SUCCESS | |
982 * PR_FAILURE | |
983 */ | |
984 | |
985 NSS_IMPLEMENT PRStatus | |
986 nss_ZFreeIf | |
987 ( | |
988 void *pointer | |
989 ) | |
990 { | |
991 struct pointer_header *h; | |
992 | |
993 if( (void *)NULL == pointer ) { | |
994 return PR_SUCCESS; | |
995 } | |
996 | |
997 h = (struct pointer_header *)((char *)pointer | |
998 - sizeof(struct pointer_header)); | |
999 | |
1000 /* Check any magic here */ | |
1001 | |
1002 if( (NSSArena *)NULL == h->arena ) { | |
1003 /* Heap */ | |
1004 (void)nsslibc_memset(pointer, 0, h->size); | |
1005 PR_Free(h); | |
1006 return PR_SUCCESS; | |
1007 } else { | |
1008 /* Arena */ | |
1009 #ifdef NSSDEBUG | |
1010 if( PR_SUCCESS != nssArena_verifyPointer(h->arena) ) { | |
1011 return PR_FAILURE; | |
1012 } | |
1013 #endif /* NSSDEBUG */ | |
1014 | |
1015 if( (PRLock *)NULL == h->arena->lock ) { | |
1016 /* Just got destroyed.. so this pointer is invalid */ | |
1017 nss_SetError(NSS_ERROR_INVALID_POINTER); | |
1018 return PR_FAILURE; | |
1019 } | |
1020 PR_Lock(h->arena->lock); | |
1021 | |
1022 (void)nsslibc_memset(pointer, 0, h->size); | |
1023 | |
1024 /* No way to "free" it within an NSPR arena. */ | |
1025 | |
1026 PR_Unlock(h->arena->lock); | |
1027 return PR_SUCCESS; | |
1028 } | |
1029 /*NOTREACHED*/ | |
1030 } | |
1031 | |
1032 /* | |
1033 * NSS_ZRealloc | |
1034 * | |
1035 * This routine reallocates a block of memory obtained by calling | |
1036 * nss_ZAlloc or nss_ZRealloc. The portion of memory | |
1037 * between the new and old sizes -- which is either being newly | |
1038 * obtained or released -- is in either case zeroed. This routine | |
1039 * may return NULL upon failure, in which case it will have placed | |
1040 * an error on the error stack. | |
1041 * | |
1042 * The error may be one of the following values: | |
1043 * NSS_ERROR_INVALID_POINTER | |
1044 * NSS_ERROR_NO_MEMORY | |
1045 * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD | |
1046 * | |
1047 * Return value: | |
1048 * NULL upon error | |
1049 * A pointer to the replacement segment of memory | |
1050 */ | |
1051 | |
1052 NSS_EXTERN void * | |
1053 NSS_ZRealloc | |
1054 ( | |
1055 void *pointer, | |
1056 PRUint32 newSize | |
1057 ) | |
1058 { | |
1059 return nss_ZRealloc(pointer, newSize); | |
1060 } | |
1061 | |
1062 /* | |
1063 * nss_ZRealloc | |
1064 * | |
1065 * This routine reallocates a block of memory obtained by calling | |
1066 * nss_ZAlloc or nss_ZRealloc. The portion of memory | |
1067 * between the new and old sizes -- which is either being newly | |
1068 * obtained or released -- is in either case zeroed. This routine | |
1069 * may return NULL upon failure, in which case it will have placed | |
1070 * an error on the error stack. | |
1071 * | |
1072 * The error may be one of the following values: | |
1073 * NSS_ERROR_INVALID_POINTER | |
1074 * NSS_ERROR_NO_MEMORY | |
1075 * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD | |
1076 * | |
1077 * Return value: | |
1078 * NULL upon error | |
1079 * A pointer to the replacement segment of memory | |
1080 */ | |
1081 | |
1082 NSS_EXTERN void * | |
1083 nss_ZRealloc | |
1084 ( | |
1085 void *pointer, | |
1086 PRUint32 newSize | |
1087 ) | |
1088 { | |
1089 NSSArena *arena; | |
1090 struct pointer_header *h, *new_h; | |
1091 PRUint32 my_newSize = newSize + sizeof(struct pointer_header); | |
1092 void *rv; | |
1093 | |
1094 if( my_newSize < sizeof(struct pointer_header) ) { | |
1095 /* Wrapped */ | |
1096 nss_SetError(NSS_ERROR_NO_MEMORY); | |
1097 return (void *)NULL; | |
1098 } | |
1099 | |
1100 if( (void *)NULL == pointer ) { | |
1101 nss_SetError(NSS_ERROR_INVALID_POINTER); | |
1102 return (void *)NULL; | |
1103 } | |
1104 | |
1105 h = (struct pointer_header *)((char *)pointer | |
1106 - sizeof(struct pointer_header)); | |
1107 | |
1108 /* Check any magic here */ | |
1109 | |
1110 if( newSize == h->size ) { | |
1111 /* saves thrashing */ | |
1112 return pointer; | |
1113 } | |
1114 | |
1115 arena = h->arena; | |
1116 if (!arena) { | |
1117 /* Heap */ | |
1118 new_h = (struct pointer_header *)PR_Calloc(1, my_newSize); | |
1119 if( (struct pointer_header *)NULL == new_h ) { | |
1120 nss_SetError(NSS_ERROR_NO_MEMORY); | |
1121 return (void *)NULL; | |
1122 } | |
1123 | |
1124 new_h->arena = (NSSArena *)NULL; | |
1125 new_h->size = newSize; | |
1126 rv = (void *)((char *)new_h + sizeof(struct pointer_header)); | |
1127 | |
1128 if( newSize > h->size ) { | |
1129 (void)nsslibc_memcpy(rv, pointer, h->size); | |
1130 (void)nsslibc_memset(&((char *)rv)[ h->size ], | |
1131 0, (newSize - h->size)); | |
1132 } else { | |
1133 (void)nsslibc_memcpy(rv, pointer, newSize); | |
1134 } | |
1135 | |
1136 (void)nsslibc_memset(pointer, 0, h->size); | |
1137 h->size = 0; | |
1138 PR_Free(h); | |
1139 | |
1140 return rv; | |
1141 } else { | |
1142 void *p; | |
1143 /* Arena */ | |
1144 #ifdef NSSDEBUG | |
1145 if (PR_SUCCESS != nssArena_verifyPointer(arena)) { | |
1146 return (void *)NULL; | |
1147 } | |
1148 #endif /* NSSDEBUG */ | |
1149 | |
1150 if (!arena->lock) { | |
1151 /* Just got destroyed.. so this pointer is invalid */ | |
1152 nss_SetError(NSS_ERROR_INVALID_POINTER); | |
1153 return (void *)NULL; | |
1154 } | |
1155 PR_Lock(arena->lock); | |
1156 | |
1157 #ifdef ARENA_THREADMARK | |
1158 if (arena->marking_thread) { | |
1159 if (PR_GetCurrentThread() != arena->marking_thread) { | |
1160 PR_Unlock(arena->lock); | |
1161 nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); | |
1162 return (void *)NULL; | |
1163 } | |
1164 } | |
1165 #endif /* ARENA_THREADMARK */ | |
1166 | |
1167 if( newSize < h->size ) { | |
1168 /* | |
1169 * We have no general way of returning memory to the arena | |
1170 * (mark/release doesn't work because things may have been | |
1171 * allocated after this object), so the memory is gone | |
1172 * anyway. We might as well just return the same pointer to | |
1173 * the user, saying "yeah, uh-hunh, you can only use less of | |
1174 * it now." We'll zero the leftover part, of course. And | |
1175 * in fact we might as well *not* adjust h->size-- this way, | |
1176 * if the user reallocs back up to something not greater than | |
1177 * the original size, then voila, there's the memory! This | |
1178 * way a thrash big/small/big/small doesn't burn up the arena. | |
1179 */ | |
1180 char *extra = &((char *)pointer)[ newSize ]; | |
1181 (void)nsslibc_memset(extra, 0, (h->size - newSize)); | |
1182 PR_Unlock(arena->lock); | |
1183 return pointer; | |
1184 } | |
1185 | |
1186 PR_ARENA_ALLOCATE(p, &arena->pool, my_newSize); | |
1187 if( (void *)NULL == p ) { | |
1188 PR_Unlock(arena->lock); | |
1189 nss_SetError(NSS_ERROR_NO_MEMORY); | |
1190 return (void *)NULL; | |
1191 } | |
1192 | |
1193 new_h = (struct pointer_header *)p; | |
1194 new_h->arena = arena; | |
1195 new_h->size = newSize; | |
1196 rv = (void *)((char *)new_h + sizeof(struct pointer_header)); | |
1197 if (rv != pointer) { | |
1198 (void)nsslibc_memcpy(rv, pointer, h->size); | |
1199 (void)nsslibc_memset(pointer, 0, h->size); | |
1200 } | |
1201 (void)nsslibc_memset(&((char *)rv)[ h->size ], 0, (newSize - h->size)); | |
1202 h->arena = (NSSArena *)NULL; | |
1203 h->size = 0; | |
1204 PR_Unlock(arena->lock); | |
1205 return rv; | |
1206 } | |
1207 /*NOTREACHED*/ | |
1208 } | |
1209 | |
1210 PRStatus | |
1211 nssArena_Shutdown(void) | |
1212 { | |
1213 PRStatus rv = PR_SUCCESS; | |
1214 #ifdef DEBUG | |
1215 rv = nssPointerTracker_finalize(&arena_pointer_tracker); | |
1216 #endif | |
1217 return rv; | |
1218 } | |
OLD | NEW |