/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
** File: arena.c
** Description: Testing arenas
**
*/
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include "nspr.h"
#include "plarena.h"
#include "plgetopt.h"
PRLogModuleInfo* tLM;
PRIntn threadCount = 0;
PRMonitor* tMon;
PRBool failed_already = PR_FALSE;
/* Arguments from the command line with default values */
PRIntn debug_mode = 0;
PRIntn poolMin = 4096;
PRIntn poolMax = (100 * 4096);
PRIntn arenaMin = 40;
PRIntn arenaMax = (100 * 40);
PRIntn stressIterations = 15;
PRIntn maxAlloc = (1024 * 1024);
PRIntn stressThreads = 4;
void DumpAll(
void) {
return; }
/*
** Test Arena allocation.
*/
static void ArenaAllocate(
void) {
PLArenaPool ap;
void* ptr;
PRInt32 i;
PL_InitArenaPool(&ap,
"AllocArena", 2048,
sizeof(
double));
PR_LOG(tLM, PR_LOG_DEBUG,
(
"AA, InitPool -- Pool: %p. first: %p, current: %p, size: %d", &ap,
ap.first, ap.current, ap.arenasize));
for (i = 0; i < 150; i++) {
PL_ARENA_ALLOCATE(ptr, &ap, 512);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"AA, after alloc -- Pool: %p. first: %p, current: %p, size: %d",
&ap, ap.first, ap.current, ap.arenasize));
PR_LOG(tLM, PR_LOG_DEBUG, (
"AA -- Pool: %p. alloc: %p ", &ap, ptr));
}
PL_FreeArenaPool(&ap);
for (i = 0; i < 221; i++) {
PL_ARENA_ALLOCATE(ptr, &ap, 512);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"AA, after alloc -- Pool: %p. first: %p, current: %p, size: %d",
&ap, ap.first, ap.current, ap.arenasize));
PR_LOG(tLM, PR_LOG_DEBUG, (
"AA -- Pool: %p. alloc: %p ", &ap, ptr));
}
PL_FreeArenaPool(&ap);
return;
}
/* end ArenaGrow() */
/*
** Test Arena grow.
*/
static void ArenaGrow(
void) {
PLArenaPool ap;
void* ptr;
PRInt32 i;
PL_InitArenaPool(&ap,
"TheArena", 4096,
sizeof(
double));
PL_ARENA_ALLOCATE(ptr, &ap, 512);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"Before growth -- Pool: %p. alloc: %p ", &ap, ptr));
for (i = 0; i < 10; i++) {
PL_ARENA_GROW(ptr, &ap, 512, 7000);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"After growth -- Pool: %p. alloc: %p ", &ap, ptr));
}
return;
}
/* end ArenaGrow() */
/*
** Test arena Mark and Release.
*/
static void MarkAndRelease(
void) {
PLArenaPool ap;
void* ptr = NULL;
void *mark0, *mark1;
PRIntn i;
PL_InitArenaPool(&ap,
"TheArena", 4096,
sizeof(
double));
mark0 = PL_ARENA_MARK(&ap);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"mark0. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p, m0: %p",
&ap, ap.first.next, ap.current, ap.arenasize, ptr, mark0));
for (i = 0; i < 201; i++) {
PL_ARENA_ALLOCATE(ptr, &ap, 512);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"mr. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", &ap,
ap.first.next, ap.current, ap.arenasize, ptr));
}
mark1 = PL_ARENA_MARK(&ap);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"mark1. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p, m1: %p",
&ap, ap.first.next, ap.current, ap.arenasize, ptr, mark1));
for (i = 0; i < 225; i++) {
PL_ARENA_ALLOCATE(ptr, &ap, 512);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"mr. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", &ap,
ap.first.next, ap.current, ap.arenasize, ptr));
}
PL_ARENA_RELEASE(&ap, mark1);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"Release-1: %p -- Pool: %p. first: %p, current: %p, size: %d", mark1,
&ap, ap.first, ap.current, ap.arenasize));
for (i = 0; i < 20; i++) {
PL_ARENA_ALLOCATE(ptr, &ap, 512);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"mr. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", &ap,
ap.first.next, ap.current, ap.arenasize, ptr));
}
PL_ARENA_RELEASE(&ap, mark1);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"Release-1. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", &ap,
ap.first.next, ap.current, ap.arenasize, ptr));
PL_ARENA_RELEASE(&ap, mark0);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"Release-0. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", &ap,
ap.first.next, ap.current, ap.arenasize, ptr));
PL_FreeArenaPool(&ap);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"Free. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", &ap,
ap.first.next, ap.current, ap.arenasize, ptr));
PL_FinishArenaPool(&ap);
PR_LOG(tLM, PR_LOG_DEBUG,
(
"Finish. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", &ap,
ap.first.next, ap.current, ap.arenasize, ptr));
return;
}
/* end MarkAndRelease() */
/*
** RandSize() returns a random number in the range
** min..max, rounded to the next doubleword
**
*/
static PRIntn RandSize(PRIntn min, PRIntn max) {
PRIntn sz = (rand() % (max - min)) + min +
sizeof(
double);
sz &= ~
sizeof(
double) - 1;
return (sz);
}
/*
** StressThread()
** A bunch of these beat on individual arenas
** This tests the free_list protection.
**
*/
static void PR_CALLBACK StressThread(
void* arg) {
PLArenaPool ap;
PRIntn i;
PRIntn sz;
void* ptr;
PRThread* tp = PR_GetCurrentThread();
PR_LOG(tLM, PR_LOG_DEBUG,
(
"Stress Thread %p started\n", PR_GetCurrentThread()));
PL_InitArenaPool(&ap,
"TheArena", RandSize(poolMin, poolMax),
sizeof(
double));
for (i = 0; i < stressIterations; i++) {
PRIntn allocated = 0;
while (allocated < maxAlloc) {
sz = RandSize(arenaMin, arenaMax);
PL_ARENA_ALLOCATE(ptr, &ap, sz);
if (ptr == NULL) {
PR_LOG(
tLM, PR_LOG_ERROR,
(
"ARENA_ALLOCATE() returned NULL\n\tAllocated: %d\n", allocated));
break;
}
allocated += sz;
}
PR_LOG(tLM, PR_LOG_DEBUG,
(
"Stress thread %p finished one iteration\n", tp));
PL_FreeArenaPool(&ap);
}
PR_LOG(tLM, PR_LOG_DEBUG, (
"Stress thread %p finished all iteration\n", tp));
PL_FinishArenaPool(&ap);
PR_LOG(tLM, PR_LOG_DEBUG, (
"Stress thread %p after FinishArenaPool()\n", tp));
/* That's all folks! let's quit */
PR_EnterMonitor(tMon);
threadCount--;
PR_Notify(tMon);
PR_ExitMonitor(tMon);
return;
}
/*
** Stress()
** Flog the hell out of arenas multi-threaded.
** Do NOT pass an individual arena to another thread.
**
*/
static void Stress(
void) {
PRThread* tt;
PRIntn i;
tMon = PR_NewMonitor();
for (i = 0; i < stressThreads; i++) {
PR_EnterMonitor(tMon);
tt = PR_CreateThread(PR_USER_THREAD, StressThread, NULL, PR_PRIORITY_NORMAL,
PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
threadCount++;
PR_ExitMonitor(tMon);
}
/* Wait for all threads to exit */
PR_EnterMonitor(tMon);
while (threadCount != 0) {
PR_Wait(tMon, PR_INTERVAL_NO_TIMEOUT);
}
PR_ExitMonitor(tMon);
PR_DestroyMonitor(tMon);
return;
}
/* end Stress() */
/*
** EvaluateResults()
** uses failed_already to display results and set program
** exit code.
*/
static PRIntn EvaluateResults(
void) {
PRIntn rc = 0;
if (failed_already == PR_TRUE) {
PR_LOG(tLM, PR_LOG_DEBUG, (
"FAIL\n"));
rc = 1;
}
else {
PR_LOG(tLM, PR_LOG_DEBUG, (
"PASS\n"));
}
return (rc);
}
/* EvaluateResults() */
void Help(
void) {
printf(
"arena [options]\n");
printf(
"where options are:\n");
printf(
"-p minimum size of an arena pool. Default(%d)\n", poolMin);
printf(
"-P maximum size of an arena pool. Default(%d)\n", poolMax);
printf(
"-a minimum size of an arena allocation. Default(%d)\n",
arenaMin);
printf(
"-A maximum size of an arena allocation. Default(%d)\n",
arenaMax);
printf(
"-i number of iterations in a stress thread. Default(%d)\n",
stressIterations);
printf(
"-s maximum allocation for a single stress thread. Default(%d)\n",
maxAlloc);
printf(
"-t number of stress threads. Default(%d)\n", stressThreads);
printf(
"-d enable debug mode\n");
printf(
"\n");
exit(1);
}
PRIntn main(PRIntn argc,
char* argv[]) {
PLOptStatus os;
PLOptState* opt = PL_CreateOptState(argc, argv,
"dhp:P:a:A:i:s:t:");
while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
if (PL_OPT_BAD == os) {
continue;
}
switch (opt->option) {
case 'a':
/* arena Min size */
arenaMin = atol(opt->value);
break;
case 'A':
/* arena Max size */
arenaMax = atol(opt->value);
break;
case 'p':
/* pool Min size */
poolMin = atol(opt->value);
break;
case 'P':
/* pool Max size */
poolMax = atol(opt->value);
break;
case 'i':
/* Iterations in stress tests */
stressIterations = atol(opt->value);
break;
case 's':
/* storage to get per iteration */
maxAlloc = atol(opt->value);
break;
case 't':
/* Number of stress threads to create */
stressThreads = atol(opt->value);
break;
case 'd':
/* debug mode */
debug_mode = 1;
break;
case 'h':
/* help */
default:
Help();
}
/* end switch() */
}
/* end while() */
PL_DestroyOptState(opt);
srand((
unsigned)time(NULL));
/* seed random number generator */
tLM = PR_NewLogModule(
"testcase");
#if 0
ArenaAllocate();
ArenaGrow();
#endif
MarkAndRelease();
Stress();
return (EvaluateResults());
}
/* end main() */
/* arena.c */