Source code drop for the Actionscript virtual machine in the Adobe Flash Player for December 2013
5830 lines
194 KiB
C++
5830 lines
194 KiB
C++
// Generated from ST_avmplus_basics.st, ST_avmplus_builtins.st, ST_avmplus_peephole.st, ST_avmplus_vector_accessors.st, ST_mmgc_543560.st, ST_mmgc_575631.st, ST_mmgc_580603.st, ST_mmgc_603411.st, ST_mmgc_637993.st, ST_mmgc_basics.st, ST_mmgc_dependent.st, ST_mmgc_exact.st, ST_mmgc_externalalloc.st, ST_mmgc_finalize_uninit.st, ST_mmgc_fixedmalloc_findbeginning.st, ST_mmgc_gcheap.st, ST_mmgc_gcoption.st, ST_mmgc_mmfx_array.st, ST_mmgc_threads.st, ST_mmgc_weakref.st, ST_nanojit_codealloc.st, ST_vmbase_concurrency.st, ST_vmbase_safepoints.st, ST_vmpi_threads.st, ST_workers_Buffer.st, ST_workers_NoSyncSingleItemBuffer.st, ST_workers_Promise.st
|
|
// Generated from ST_avmplus_basics.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_avmplus_basics {
|
|
class ST_avmplus_basics : public Selftest {
|
|
public:
|
|
ST_avmplus_basics(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
void test3();
|
|
void test4();
|
|
void test5();
|
|
void test6();
|
|
void test7();
|
|
};
|
|
ST_avmplus_basics::ST_avmplus_basics(AvmCore* core)
|
|
: Selftest(core, "avmplus", "basics", ST_avmplus_basics::ST_names,ST_avmplus_basics::ST_explicits)
|
|
{}
|
|
const char* ST_avmplus_basics::ST_names[] = {"unsigned_int","signed_int","equalsLatin1","containsLatin1","indexOfLatin1","matchesLatin1","matchesLatin1_caseless","bug562101", NULL };
|
|
const bool ST_avmplus_basics::ST_explicits[] = {false,false,false,false,false,false,false,false, false };
|
|
void ST_avmplus_basics::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
case 3: test3(); return;
|
|
case 4: test4(); return;
|
|
case 5: test5(); return;
|
|
case 6: test6(); return;
|
|
case 7: test7(); return;
|
|
}
|
|
}
|
|
void ST_avmplus_basics::test0() {
|
|
|
|
// Does right shift of unsigned quantities work?
|
|
// line 14 "ST_avmplus_basics.st"
|
|
verifyPass((int)(~0U >> 1) > 0, "(int)(~0U >> 1) > 0", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_avmplus_basics::test1() {
|
|
|
|
// Does right shift of signed quantities work?
|
|
// line 19 "ST_avmplus_basics.st"
|
|
verifyPass((-1 >> 1) == -1, "(-1 >> 1) == -1", __FILE__, __LINE__);
|
|
|
|
// verify that the "latin1" literal string calls work properly for hi-bit latin1 chars
|
|
}
|
|
void ST_avmplus_basics::test2() {
|
|
Stringp s = core->newConstantStringLatin1("ev\xADident");
|
|
bool equals = s->equalsLatin1("ev\xADident");
|
|
// line 25 "ST_avmplus_basics.st"
|
|
verifyPass(equals == true, "equals == true", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_avmplus_basics::test3() {
|
|
Stringp s = core->newConstantStringLatin1("ev\xADident");
|
|
bool found = s->containsLatin1("\xAD");
|
|
// line 30 "ST_avmplus_basics.st"
|
|
verifyPass(found == true, "found == true", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_avmplus_basics::test4() {
|
|
Stringp s = core->newConstantStringLatin1("ev\xADident");
|
|
int index = s->indexOfLatin1("\xAD");
|
|
// line 35 "ST_avmplus_basics.st"
|
|
verifyPass(index == 2, "index == 2", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_avmplus_basics::test5() {
|
|
Stringp s = core->newConstantStringLatin1("ev\xADident");
|
|
bool matches1 = s->matchesLatin1("\xADi", 2, 2);
|
|
// line 40 "ST_avmplus_basics.st"
|
|
verifyPass(matches1 == true, "matches1 == true", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_avmplus_basics::test6() {
|
|
Stringp s = core->newConstantStringLatin1("ev\xADident");
|
|
bool matches2 = s->matchesLatin1_caseless("\xADIDENT", 2, 2);
|
|
// line 45 "ST_avmplus_basics.st"
|
|
verifyPass(matches2 == true, "matches2 == true", __FILE__, __LINE__);
|
|
|
|
|
|
}
|
|
void ST_avmplus_basics::test7() {
|
|
// XMLParser omits the last char of a DOCTYPE node
|
|
Stringp str = core->newConstantStringLatin1("<?xml version=\"1.0\"?><!DOCTYPE greeting SYSTEM><greeting>Hello, world!</greeting>");
|
|
XMLParser parser(core, str);
|
|
MMgc::GC *gc = core->GetGC();
|
|
XMLTag tag(gc);
|
|
int m_status;
|
|
bool pass = false;
|
|
while ((m_status = parser.getNext(tag)) == XMLParser::kNoError)
|
|
{
|
|
switch (tag.nodeType)
|
|
{
|
|
case XMLTag::kDocTypeDeclaration:
|
|
{
|
|
pass = false;
|
|
pass = tag.text->equalsLatin1("<!DOCTYPE greeting SYSTEM>");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
// line 68 "ST_avmplus_basics.st"
|
|
verifyPass(pass == true, "pass == true", __FILE__, __LINE__);
|
|
|
|
// FIXME: this needs a "register this object with the GC" mechanism; this abuse of the GCRoot mechanism
|
|
// is no longer allowed
|
|
//%%test bug610022
|
|
// Stringp str = core->newConstantStringLatin1("some string that is likely to be unique");
|
|
// WeakRefList<String> list(core->GetGC(), 0);
|
|
// We are going to skip scanning the stack (so that "str" won't hold the string in place)
|
|
// but that means we need a root to ensure that "list" doesn't also get collected.
|
|
//
|
|
//MMgc::GCRoot root(core->GetGC(), &list, sizeof(list));
|
|
//list.add(str);
|
|
//str = NULL;
|
|
//core->GetGC()->Collect(/*scanStack*/false);
|
|
//int removed = list.removeCollectedItems();
|
|
//int count = list.length();
|
|
//%%verify removed == 1 && count == 0
|
|
|
|
|
|
|
|
}
|
|
void create_avmplus_basics(AvmCore* core) { new ST_avmplus_basics(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_avmplus_builtins.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_avmplus_builtins {
|
|
class ST_avmplus_builtins : public Selftest {
|
|
public:
|
|
ST_avmplus_builtins(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
};
|
|
ST_avmplus_builtins::ST_avmplus_builtins(AvmCore* core)
|
|
: Selftest(core, "avmplus", "builtins", ST_avmplus_builtins::ST_names,ST_avmplus_builtins::ST_explicits)
|
|
{}
|
|
const char* ST_avmplus_builtins::ST_names[] = {"WeakValueHashTable", NULL };
|
|
const bool ST_avmplus_builtins::ST_explicits[] = {false, false };
|
|
void ST_avmplus_builtins::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
}
|
|
}
|
|
void ST_avmplus_builtins::test0() {
|
|
WeakValueHashtable* tbl = WeakValueHashtable::create(core->gc);
|
|
String* fhtagn = String::createLatin1(core, "Fhtagn!");
|
|
MMgc::GCObjectLock* fhtagn_lock = core->gc->LockObject(fhtagn);
|
|
fhtagn = NULL;
|
|
for ( int i=0 ; i < 500 ; i++ ) {
|
|
tbl->add(atomFromIntptrValue(i), (i & 1) ? String::createLatin1(core, "Cthulhu!")->atom() : ((String*)core->gc->GetLockedObject(fhtagn_lock))->atom());
|
|
}
|
|
core->gc->Collect();
|
|
core->gc->Collect();
|
|
int sum = 0;
|
|
for ( int i=0 ; i < 500 ; i++ ) {
|
|
Atom a = tbl->get(atomFromIntptrValue(i));
|
|
if (a != AtomConstants::undefinedAtom)
|
|
sum++;
|
|
}
|
|
core->gc->UnlockObject(fhtagn_lock);
|
|
printf("fhtagn sum: %d\n", sum);
|
|
|
|
// Retain at least 250, but it would be unreasonable to retain more than 300
|
|
// line 31 "ST_avmplus_builtins.st"
|
|
verifyPass(sum >= 250 && sum <= 300, "sum >= 250 && sum <= 300", __FILE__, __LINE__);
|
|
|
|
}
|
|
void create_avmplus_builtins(AvmCore* core) { new ST_avmplus_builtins(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_avmplus_peephole.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
#if defined AVMPLUS_PEEPHOLE_OPTIMIZER
|
|
namespace avmplus {
|
|
namespace ST_avmplus_peephole {
|
|
class ST_avmplus_peephole : public Selftest {
|
|
public:
|
|
ST_avmplus_peephole(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
virtual void epilogue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
|
|
private:
|
|
#ifdef AVMPLUS_DIRECT_THREADED
|
|
void** opcode_labels; // the name is not arbitrary
|
|
#endif
|
|
|
|
};
|
|
ST_avmplus_peephole::ST_avmplus_peephole(AvmCore* core)
|
|
: Selftest(core, "avmplus", "peephole", ST_avmplus_peephole::ST_names,ST_avmplus_peephole::ST_explicits)
|
|
{}
|
|
const char* ST_avmplus_peephole::ST_names[] = {"get2locals", NULL };
|
|
const bool ST_avmplus_peephole::ST_explicits[] = {false, false };
|
|
void ST_avmplus_peephole::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
}
|
|
}
|
|
void ST_avmplus_peephole::prologue() {
|
|
|
|
#ifdef AVMPLUS_DIRECT_THREADED
|
|
opcode_labels = interpGetOpcodeLabels();
|
|
#endif
|
|
|
|
}
|
|
void ST_avmplus_peephole::epilogue() {
|
|
|
|
#ifdef AVMPLUS_DIRECT_THREADED
|
|
opcode_labels = NULL; // interpGetOpcodeLables() returns a pointer to static data
|
|
#endif
|
|
|
|
}
|
|
void ST_avmplus_peephole::test0() {
|
|
|
|
WordcodeEmitter* t = new WordcodeEmitter(core, NULL);
|
|
|
|
t->emitOp1(WOP_getlocal, 5);
|
|
t->emitOp1(WOP_getlocal, 4);
|
|
t->emitOp1(WOP_getlocal, 65536);
|
|
t->emitOp1(WOP_getlocal, 7);
|
|
t->emitOp1(WOP_getlocal, 6);
|
|
uintptr_t* code;
|
|
uint32_t len = (uint32_t)t->epilogue(&code);
|
|
|
|
// line 43 "ST_avmplus_peephole.st"
|
|
verifyPass(len == 6, "len == 6", __FILE__, __LINE__);
|
|
// line 44 "ST_avmplus_peephole.st"
|
|
verifyPass(code[0] == NEW_OPCODE(WOP_get2locals), "code[0] == NEW_OPCODE(WOP_get2locals)", __FILE__, __LINE__);
|
|
// line 45 "ST_avmplus_peephole.st"
|
|
verifyPass(code[1] == ((4 << 16) | 5), "code[1] == ((4 << 16) | 5)", __FILE__, __LINE__);
|
|
// line 46 "ST_avmplus_peephole.st"
|
|
verifyPass(code[2] == NEW_OPCODE(WOP_getlocal), "code[2] == NEW_OPCODE(WOP_getlocal)", __FILE__, __LINE__);
|
|
// line 47 "ST_avmplus_peephole.st"
|
|
verifyPass(code[3] == 65536, "code[3] == 65536", __FILE__, __LINE__);
|
|
// line 48 "ST_avmplus_peephole.st"
|
|
verifyPass(code[4] == NEW_OPCODE(WOP_get2locals), "code[4] == NEW_OPCODE(WOP_get2locals)", __FILE__, __LINE__);
|
|
// line 49 "ST_avmplus_peephole.st"
|
|
verifyPass(code[5] == ((6 << 16) | 7), "code[5] == ((6 << 16) | 7)", __FILE__, __LINE__);
|
|
|
|
delete t;
|
|
|
|
|
|
}
|
|
void create_avmplus_peephole(AvmCore* core) { new ST_avmplus_peephole(core); }
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Generated from ST_avmplus_vector_accessors.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
// Bugzilla 609145 - VectorObject needs fast inline getter/setters
|
|
// Make sure the APIs, which are used by the Flash Player and AIR only, do not disappear.
|
|
//
|
|
// NOTE, the following comment is stale and we can fix the code, see the code for
|
|
// VectorAccessor further down for how to access a toplevel.
|
|
//
|
|
// We can't test them because we don't have access to a Toplevel*, but we can reference
|
|
// them, and a link error will ensue if they disappear.
|
|
//
|
|
// Code coverage will get worse with this test, not better. C'est la guerre.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_avmplus_vector_accessors {
|
|
|
|
int32_t reference_getUintPropertyFast(IntVectorObject* obj, uint32_t x)
|
|
{
|
|
return obj->getUintPropertyFast(x);
|
|
}
|
|
|
|
void reference_setUintPropertyFast(IntVectorObject* obj, uint32_t x, int32_t v)
|
|
{
|
|
obj->setUintPropertyFast(x, v);
|
|
}
|
|
|
|
class ST_avmplus_vector_accessors : public Selftest {
|
|
public:
|
|
ST_avmplus_vector_accessors(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
void test3();
|
|
void test4();
|
|
};
|
|
ST_avmplus_vector_accessors::ST_avmplus_vector_accessors(AvmCore* core)
|
|
: Selftest(core, "avmplus", "vector_accessors", ST_avmplus_vector_accessors::ST_names,ST_avmplus_vector_accessors::ST_explicits)
|
|
{}
|
|
const char* ST_avmplus_vector_accessors::ST_names[] = {"getOrSetUintPropertyFast","DataListAccessor_on_int","DataListAccessor_on_float4","VectorAccessor_on_int","VectorAccessor_on_float4", NULL };
|
|
const bool ST_avmplus_vector_accessors::ST_explicits[] = {false,false,false,false,false, false };
|
|
void ST_avmplus_vector_accessors::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
case 3: test3(); return;
|
|
case 4: test4(); return;
|
|
}
|
|
}
|
|
void ST_avmplus_vector_accessors::test0() {
|
|
|
|
// line 36 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_avmplus_vector_accessors::test1() {
|
|
|
|
DataList<int> dl(core->GetGC(), 3);
|
|
dl.add(1);
|
|
dl.add(1);
|
|
dl.add(2);
|
|
dl.add(3);
|
|
dl.add(5);
|
|
dl.add(8);
|
|
dl.add(13);
|
|
DataListAccessor<int> dla(&dl);
|
|
int* xs = dla.addr();
|
|
|
|
// line 51 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[0] == 1, "xs[0] == 1", __FILE__, __LINE__);
|
|
// line 52 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[1] == 1, "xs[1] == 1", __FILE__, __LINE__);
|
|
// line 53 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[2] == 2, "xs[2] == 2", __FILE__, __LINE__);
|
|
// line 54 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[3] == 3, "xs[3] == 3", __FILE__, __LINE__);
|
|
// line 55 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[4] == 5, "xs[4] == 5", __FILE__, __LINE__);
|
|
// line 56 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[5] == 8, "xs[5] == 8", __FILE__, __LINE__);
|
|
// line 57 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[6] == 13, "xs[6] == 13", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_avmplus_vector_accessors::test2() {
|
|
|
|
#ifdef VMCFG_FLOAT
|
|
|
|
DataList<float4_t, 16> dl4(core->GetGC(), 3);
|
|
float4_t x0 = { 1,1,2,3 };
|
|
float4_t x1 = { 5,8,13,21 };
|
|
float4_t x2 = { 34,55,89,144 };
|
|
dl4.add(x0);
|
|
dl4.add(x1);
|
|
dl4.add(x2);
|
|
DataListAccessor<float4_t,16> dla4(&dl4);
|
|
float4_t* x4s = dla4.addr();
|
|
|
|
// line 73 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(f4_eq_i(x4s[0], x0) == 1, "f4_eq_i(x4s[0], x0) == 1", __FILE__, __LINE__);
|
|
// line 74 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(f4_eq_i(x4s[1], x1) == 1, "f4_eq_i(x4s[1], x1) == 1", __FILE__, __LINE__);
|
|
// line 75 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(f4_eq_i(x4s[2], x2) == 1, "f4_eq_i(x4s[2], x2) == 1", __FILE__, __LINE__);
|
|
|
|
#else
|
|
|
|
// line 79 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
|
|
#endif
|
|
|
|
}
|
|
void ST_avmplus_vector_accessors::test3() {
|
|
|
|
#ifdef AVMSHELL_BUILD
|
|
|
|
avmshell::ShellCore* c = (avmshell::ShellCore*)core;
|
|
avmshell::ShellToplevel* top = c->shell_toplevel;
|
|
IntVectorObject* vec = top->intVectorClass()->newVector();
|
|
|
|
vec->_setNativeUintProperty(0, 1);
|
|
vec->_setNativeUintProperty(1, 1);
|
|
vec->_setNativeUintProperty(2, 2);
|
|
vec->_setNativeUintProperty(3, 3);
|
|
vec->_setNativeUintProperty(4, 5);
|
|
vec->_setNativeUintProperty(5, 8);
|
|
vec->_setNativeUintProperty(6, 13);
|
|
|
|
IntVectorAccessor va(vec);
|
|
int* xs = va.addr();
|
|
|
|
// line 102 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[0] == 1, "xs[0] == 1", __FILE__, __LINE__);
|
|
// line 103 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[1] == 1, "xs[1] == 1", __FILE__, __LINE__);
|
|
// line 104 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[2] == 2, "xs[2] == 2", __FILE__, __LINE__);
|
|
// line 105 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[3] == 3, "xs[3] == 3", __FILE__, __LINE__);
|
|
// line 106 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[4] == 5, "xs[4] == 5", __FILE__, __LINE__);
|
|
// line 107 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[5] == 8, "xs[5] == 8", __FILE__, __LINE__);
|
|
// line 108 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(xs[6] == 13, "xs[6] == 13", __FILE__, __LINE__);
|
|
|
|
#else
|
|
|
|
// line 112 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
|
|
#endif // AVMSHELL_BUILD
|
|
|
|
}
|
|
void ST_avmplus_vector_accessors::test4() {
|
|
|
|
#if defined VMCFG_FLOAT && defined AVMSHELL_BUILD
|
|
|
|
avmshell::ShellCore* c = (avmshell::ShellCore*)core;
|
|
avmshell::ShellToplevel* top = c->shell_toplevel;
|
|
Float4VectorObject* vec = top->float4VectorClass()->newVector();
|
|
|
|
float4_t x0 = { 1,1,2,3 };
|
|
float4_t x1 = { 5,8,13,21 };
|
|
float4_t x2 = { 34,55,89,144 };
|
|
|
|
vec->_setNativeUintProperty(0, x0);
|
|
vec->_setNativeUintProperty(1, x1);
|
|
vec->_setNativeUintProperty(2, x2);
|
|
|
|
Float4VectorAccessor va(vec);
|
|
float4_t* x4s = va.addr();
|
|
|
|
// line 135 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(f4_eq_i(x4s[0], x0) == 1, "f4_eq_i(x4s[0], x0) == 1", __FILE__, __LINE__);
|
|
// line 136 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(f4_eq_i(x4s[1], x1) == 1, "f4_eq_i(x4s[1], x1) == 1", __FILE__, __LINE__);
|
|
// line 137 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(f4_eq_i(x4s[2], x2) == 1, "f4_eq_i(x4s[2], x2) == 1", __FILE__, __LINE__);
|
|
|
|
#else
|
|
|
|
// line 141 "ST_avmplus_vector_accessors.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
|
|
#endif // AVMSHELL_BUILD
|
|
|
|
}
|
|
void create_avmplus_vector_accessors(AvmCore* core) { new ST_avmplus_vector_accessors(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_543560.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
// Bugzilla 543560 - here we risk deleting an object that is still on the mark stack because
|
|
// of how we perform large-object splitting. The setup is that user code that deletes the object
|
|
// gets to run after the first part of the large object has been popped off the mark stack
|
|
// but before the rest has been handled.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
#if defined AVMPLUS_WIN32
|
|
#if !defined VMCFG_ARM
|
|
namespace avmplus {
|
|
namespace ST_mmgc_bugzilla_543560 {
|
|
class ST_mmgc_bugzilla_543560 : public Selftest {
|
|
public:
|
|
ST_mmgc_bugzilla_543560(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
virtual void epilogue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
private:
|
|
MMgc::GC *gc;
|
|
|
|
};
|
|
ST_mmgc_bugzilla_543560::ST_mmgc_bugzilla_543560(AvmCore* core)
|
|
: Selftest(core, "mmgc", "bugzilla_543560", ST_mmgc_bugzilla_543560::ST_names,ST_mmgc_bugzilla_543560::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_bugzilla_543560::ST_names[] = {"bugzilla_543560", NULL };
|
|
const bool ST_mmgc_bugzilla_543560::ST_explicits[] = {false, false };
|
|
void ST_mmgc_bugzilla_543560::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_bugzilla_543560::prologue() {
|
|
MMgc::GCConfig config;
|
|
gc = new MMgc::GC(MMgc::GCHeap::GetGCHeap(), config);
|
|
|
|
}
|
|
void ST_mmgc_bugzilla_543560::epilogue() {
|
|
delete gc;
|
|
|
|
}
|
|
using namespace MMgc;
|
|
|
|
// allocate a bunch big things
|
|
// allocate a bunch of small things ( to pump incremental mark.... )
|
|
// explicitly free one of the big things
|
|
// ... crash
|
|
|
|
struct BigThing;
|
|
|
|
static inline unsigned getSerial()
|
|
{
|
|
static unsigned g_counter = 0;
|
|
unsigned result = g_counter;
|
|
++g_counter;
|
|
return result;
|
|
}
|
|
|
|
struct BigThing : public MMgc::GCFinalizedObject
|
|
{
|
|
BigThing() : m_next(0), m_prev(0), m_serial(getSerial())
|
|
{
|
|
VMPI_memset(&m_data, 0, sizeof(m_data));
|
|
}
|
|
|
|
virtual ~BigThing()
|
|
{
|
|
//printf("~BigThing: %u 0x%08X 0x%08X\n", m_serial, this, this + 1);
|
|
}
|
|
GCMember<BigThing> m_next;
|
|
GCMember<BigThing> m_prev;
|
|
unsigned m_serial;
|
|
char m_data[512 * 1024];
|
|
};
|
|
|
|
BigThing* makeBigThings(MMgc::GC* gc, size_t howMany)
|
|
{
|
|
BigThing* first = 0;
|
|
BigThing* curr = 0;
|
|
for (unsigned i = 0; i < howMany; ++i) {
|
|
BigThing* newThing = new (gc) BigThing();
|
|
if (!first) {
|
|
first = newThing;
|
|
}
|
|
else {
|
|
curr->m_next = newThing;
|
|
newThing->m_prev = curr;
|
|
}
|
|
curr = newThing;
|
|
}
|
|
return first;
|
|
}
|
|
|
|
struct SmallThing : public MMgc::GCFinalizedObject
|
|
{
|
|
char m_data[200];
|
|
virtual ~SmallThing()
|
|
{
|
|
}
|
|
};
|
|
|
|
struct MyRoot : public MMgc::GCRoot
|
|
{
|
|
MyRoot(MMgc::GC* gc) : MMgc::GCRoot(gc) {}
|
|
BigThing* m_bigThings;
|
|
};
|
|
|
|
void ST_mmgc_bugzilla_543560::test0() {
|
|
MMGC_GCENTER(gc);
|
|
|
|
MyRoot* theRoot = 0;
|
|
{
|
|
BigThing* volatile bigThings = makeBigThings(gc, 400);
|
|
theRoot = new MyRoot(gc);
|
|
theRoot->m_bigThings = bigThings;
|
|
}
|
|
BigThing* volatile middle = theRoot->m_bigThings;
|
|
for (int j = 0 ; j < 150; ++j)
|
|
middle = middle->m_next;
|
|
|
|
middle->m_prev->m_next = 0;
|
|
for (int j = 0; j < 50; ++j)
|
|
middle = middle->m_next;
|
|
|
|
middle->m_prev = 0;
|
|
gc->Collect();
|
|
gc->Collect();
|
|
|
|
for (int j = 0; j < 100000; ++j) {
|
|
//printf("j: %d\n", j);
|
|
for (int i = 0; i < 500; ++i) {
|
|
new (gc) SmallThing();
|
|
if ((theRoot->m_bigThings) && (MMgc::GC::GetMark(theRoot->m_bigThings))) {
|
|
while (theRoot->m_bigThings != 0) {
|
|
BigThing* curr = theRoot->m_bigThings;
|
|
theRoot->m_bigThings = theRoot->m_bigThings->m_next;
|
|
delete curr;
|
|
}
|
|
MMgc::GCHeap::GetGCHeap()->Decommit();
|
|
}
|
|
}
|
|
|
|
}
|
|
delete theRoot;
|
|
|
|
// Will crash if it fails so the %%verify is just token
|
|
// line 135 "ST_mmgc_543560.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
|
|
}
|
|
void create_mmgc_bugzilla_543560(AvmCore* core) { new ST_mmgc_bugzilla_543560(core); }
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_575631.st
|
|
// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5)
|
|
//
|
|
// 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/.
|
|
|
|
// Bugzilla 565631 - We occasionally interleave invoking finalizers
|
|
// and clearing mark bits in GCAlloc::Finalize; so a finalizer can
|
|
// observe a live object that does not have its mark bit set.
|
|
//
|
|
// This complicates things because we want to ensure that unmarked
|
|
// weakly-referenced objects are resurrected by the GC if the weak
|
|
// reference is dereferenced during presweep, but we do not want to
|
|
// schedule collection work (or set bits that are supposed to be
|
|
// unmarked) during finalization.
|
|
//
|
|
// (Long term we might want to get rid of the interleaving of
|
|
// finalization and mark-bit clearing. Short term, lets just
|
|
// try to detect this on our own.)
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_mmgc_bugzilla_575631 {
|
|
using namespace MMgc;
|
|
|
|
// Upon destruction, start reading weak refs of "friends" near and far
|
|
class Snoopy : public GCFinalizedObject
|
|
{
|
|
public:
|
|
Snoopy(int key, GCWeakRef** refs, int len)
|
|
: key(key), friends(refs), len(len)
|
|
{
|
|
++alive_count;
|
|
}
|
|
~Snoopy();
|
|
static int alive_count;
|
|
private:
|
|
int key;
|
|
GCWeakRef** friends;
|
|
int len;
|
|
};
|
|
|
|
// To take D samples from an array of N elems, walk thru by floor(N/D)
|
|
// steps (but avoid the pathological case when the floor is zero).
|
|
int compute_stride(int numerator, int denominator)
|
|
{
|
|
int delta = numerator / denominator;
|
|
return (delta > 0) ? delta : 1;
|
|
}
|
|
|
|
class ST_mmgc_bugzilla_575631 : public Selftest {
|
|
public:
|
|
ST_mmgc_bugzilla_575631(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
// collecting twice is only "sure" way to gc in presence of incrementality
|
|
void collect2() { core->gc->Collect(); core->gc->Collect(); }
|
|
|
|
};
|
|
ST_mmgc_bugzilla_575631::ST_mmgc_bugzilla_575631(AvmCore* core)
|
|
: Selftest(core, "mmgc", "bugzilla_575631", ST_mmgc_bugzilla_575631::ST_names,ST_mmgc_bugzilla_575631::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_bugzilla_575631::ST_names[] = {"drizzle", NULL };
|
|
const bool ST_mmgc_bugzilla_575631::ST_explicits[] = {false, false };
|
|
void ST_mmgc_bugzilla_575631::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
}
|
|
}
|
|
|
|
/*static*/ int Snoopy::alive_count = 0;
|
|
|
|
const int arr_len = 1000;
|
|
const int lookups_per_destruct = 10;
|
|
const int destructs = 10;
|
|
Snoopy::~Snoopy()
|
|
{
|
|
int delta = compute_stride(arr_len, lookups_per_destruct);
|
|
|
|
for ( int i = 1 ; i < arr_len ; i += delta ) {
|
|
int idx = (key + i) % len;
|
|
// printf("referencing ref[%d] from Snoopy(%d)\n", idx, key);
|
|
friends[idx]->get();
|
|
}
|
|
--alive_count;
|
|
}
|
|
|
|
void ST_mmgc_bugzilla_575631::test0() {
|
|
{
|
|
GC* gc = core->gc;
|
|
|
|
Snoopy* objs[arr_len];
|
|
GCWeakRef* refs[arr_len];
|
|
|
|
// initial setup:
|
|
for (int i=0 ; i < arr_len; ++i ) {
|
|
objs[i] = new (gc) Snoopy(i, refs, arr_len);
|
|
refs[i] = objs[i]->GetWeakRef();
|
|
}
|
|
|
|
collect2();
|
|
|
|
int delta = compute_stride(arr_len, destructs);
|
|
|
|
for (int i=0; i < arr_len; i += delta) {
|
|
objs[i] = NULL;
|
|
collect2();
|
|
}
|
|
|
|
// not assert failing within get() is passing the test.
|
|
// line 99 "ST_mmgc_575631.st"
|
|
verifyPass(1, "1", __FILE__, __LINE__);
|
|
; // (make my auto-indenter happy)
|
|
|
|
// cleanup code; letting ~Snoopy occur outside test extent is big no-no.
|
|
{
|
|
for (int i=0; i < arr_len; ++i ) {
|
|
if (! refs[i]->isNull())
|
|
delete objs[i];
|
|
}
|
|
|
|
// if something went wrong above and some Snoopy's are still alive,
|
|
// we'll get burned during their destructors. Make sure that
|
|
// does not happen.
|
|
// line 112 "ST_mmgc_575631.st"
|
|
verifyPass((Snoopy::alive_count == 0), "(Snoopy::alive_count == 0)", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
}
|
|
|
|
}
|
|
void create_mmgc_bugzilla_575631(AvmCore* core) { new ST_mmgc_bugzilla_575631(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_580603.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
// Bugzilla 580603 - adversarial mmgc: dispersive w.r.t. address space
|
|
// Bugzilla 445780 - Page map needs to be sparse
|
|
//
|
|
// Tommy: "problems in 64-bit linux b/c VMPI_allocateAlignedMemory provides
|
|
// memory from low 32 bit-addresses, and mmap is giving us addresses from
|
|
// 0x7fxxxxxxxxxxxxxx so we need a 4 GB page map."
|
|
//
|
|
// This self-test attempts to replicate the problem described above
|
|
// (by coordinating with an adversarial gcheap) thus illustrating the
|
|
// need for a sparse page map.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
#if defined AVMPLUS_64BIT && defined DEBUG
|
|
namespace avmplus {
|
|
namespace ST_mmgc_bugzilla_580603 {
|
|
using namespace MMgc;
|
|
|
|
class ST_mmgc_bugzilla_580603 : public Selftest {
|
|
public:
|
|
ST_mmgc_bugzilla_580603(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
virtual void epilogue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
|
|
GC *gc;
|
|
GCHeap *heap;
|
|
|
|
size_t saved_dispersiveAdversarial;
|
|
size_t saved_heapLimit;
|
|
|
|
char *min_addr;
|
|
char *max_start;
|
|
char *max_addr;
|
|
size_t min_size;
|
|
size_t max_size;
|
|
|
|
static const int m_num_sizes = 17;
|
|
static const int m_num_iters = 2;
|
|
static const int m_init_size = 16;
|
|
static const int m_dfactor = 2;
|
|
|
|
void print_minmax()
|
|
{
|
|
intptr_t delta = max_addr - min_addr;
|
|
intptr_t v = delta;
|
|
unsigned r = 0; // r will be floor(log_2(v))
|
|
|
|
while (v >>= 1)
|
|
r++;
|
|
|
|
printf("min_size: 0x%15lx max_size: 0x%15lx\n",
|
|
(unsigned long)min_size, (unsigned long)max_size);
|
|
printf("min_addr: 0x%15lx max_addr: 0x%15lx delta:% 15ld (>= 2^%u)\n",
|
|
(unsigned long)min_addr, (unsigned long)max_addr, delta, r);
|
|
fflush(NULL);
|
|
}
|
|
|
|
enum alloc_method_t { via_gc, via_heap };
|
|
|
|
const char* method_string(alloc_method_t m)
|
|
{
|
|
switch (m) {
|
|
case via_gc: return "via_gc";
|
|
case via_heap: return "via_heap";
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
char *my_alloc(size_t size, alloc_method_t m)
|
|
{
|
|
char *ret;
|
|
|
|
// Took heap-zeroing and heap-profiling out of flags;
|
|
// avoid wasting time mapping in memory to initialize to 0.
|
|
int heapFlags = (MMgc::GCHeap::kExpand);
|
|
size_t sizeInPages = (size+(GCHeap::kBlockSize-1))/GCHeap::kBlockSize;
|
|
|
|
switch (m) {
|
|
case via_gc: ret = (char*)gc->Alloc(size); break;
|
|
case via_heap:
|
|
ret = (char*)heap->Alloc(sizeInPages, heapFlags);
|
|
break;
|
|
default: ret = 0; break;
|
|
}
|
|
|
|
if (size < min_size)
|
|
min_size = size;
|
|
if (size > max_size)
|
|
max_size = size;
|
|
if (ret < min_addr)
|
|
min_addr = ret;
|
|
if (ret > max_start)
|
|
max_start = ret;
|
|
if (ret+size > max_addr)
|
|
max_addr = ret+size;
|
|
|
|
// printf("my_alloc(%10ld, %10s) => %p\n", size, method_string(m), ret);
|
|
// fflush(NULL);
|
|
return ret;
|
|
}
|
|
|
|
void my_free(char *p, alloc_method_t m)
|
|
{
|
|
switch (m) {
|
|
case via_gc: gc->Free(p); return;
|
|
case via_heap: heap->FreeNoProfile(p); return;
|
|
default: AvmAssert(p == 0); return;
|
|
}
|
|
}
|
|
|
|
// fills recv[] with the addresses of a series of N allocated and
|
|
// freed blocks, where N = (m_num_iters * m_num_sizes).
|
|
//
|
|
// For each iteration:
|
|
//
|
|
// 1. allocate m_num_sizes blocks of distinct (and exponentially
|
|
// growing) size.
|
|
// 2. after finishing the allocations from (1.), free the blocks
|
|
// allocated in the *previous* iteration.
|
|
//
|
|
// The intention is to allocate blocks covering a wide range of
|
|
// sizes in order to tickle the memory subsystem's ability to
|
|
// track blocks of different sizes. Keeping each iteration's
|
|
// blocks alive across the next iteration is meant to ensure
|
|
// that the set of allocated addresses have many wide gaps,
|
|
// stressing the memory subsystem's ability to represent
|
|
// the meta-data for disparately allocated ranges.
|
|
//
|
|
// The loop attempts to keep the block sizes distinct both within any
|
|
// particular iteration (the exponential growth) and also across the
|
|
// distinct iterations. This was an artifact of trying to force the
|
|
// memory subsystem to be unable to return back blocks that had been
|
|
// previously freed. It may or may not still be necessary with the
|
|
// addition of the dispersiveAdversarial gcheap config mode; it does
|
|
// not seem to hurt the effectiveness of this test, since one can
|
|
// observe OOM failures when using PageMap::Uniform using this test;
|
|
// see notes in prologue code below.
|
|
|
|
void do_allocs(char **recv, alloc_method_t m)
|
|
{
|
|
size_t init = m_init_size;
|
|
int num_iters = m_num_iters;
|
|
int num_sizes = m_num_sizes;
|
|
int dfactor = m_dfactor;
|
|
for (int j=num_iters-1; j >= 0; j--) {
|
|
for (int i=0, factor=1; i < num_sizes; i++, factor*=dfactor) {
|
|
size_t size = factor*init+num_iters-j;
|
|
// size_t size = factor*init;
|
|
char *result = my_alloc(size, m);
|
|
recv[j*num_sizes+i] = result;
|
|
}
|
|
if (j < num_iters - 1) {
|
|
for (int i=0; i < num_sizes; i++) {
|
|
my_free(recv[(j+1)*num_sizes+i], m);
|
|
}
|
|
}
|
|
}
|
|
for (int i=0; i < num_sizes; i++) {
|
|
my_free(recv[0*num_sizes+i], m);
|
|
}
|
|
}
|
|
|
|
|
|
};
|
|
ST_mmgc_bugzilla_580603::ST_mmgc_bugzilla_580603(AvmCore* core)
|
|
: Selftest(core, "mmgc", "bugzilla_580603", ST_mmgc_bugzilla_580603::ST_names,ST_mmgc_bugzilla_580603::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_bugzilla_580603::ST_names[] = {"alloc_loop_mmgc_viaheap_far","alloc_loop_mmgc_viagc_far", NULL };
|
|
const bool ST_mmgc_bugzilla_580603::ST_explicits[] = {false,false, false };
|
|
void ST_mmgc_bugzilla_580603::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_bugzilla_580603::prologue() {
|
|
{
|
|
min_addr = (char*)(void*)-1;
|
|
min_size = (size_t)(void*)-1;
|
|
max_addr = 0;
|
|
max_size = 0;
|
|
|
|
// based on ST_mmgc_basics.st
|
|
heap = MMgc::GCHeap::GetGCHeap();
|
|
|
|
// Constants determining test parameters; see comments below.
|
|
const int k1 = 40;
|
|
const int k2 = 15;
|
|
|
|
// filler that GCHeap attempts to insert between allocations,
|
|
// in bytes (not that the precise number matters).
|
|
saved_dispersiveAdversarial = heap->Config().dispersiveAdversarial;
|
|
heap->Config().dispersiveAdversarial = size_t(1) << k1;
|
|
// limit is measured in pages; 1 page = kBlockSize bytes.
|
|
// its default value is absurdly large; cut it down to something
|
|
// where we'll see a failure without first hosing the host machine.
|
|
saved_heapLimit = heap->Config().heapLimit;
|
|
heap->Config().heapLimit = ((1 << k2)-1);
|
|
|
|
// Some pairs of (k1,k2) for filler = 2**k1 and limit = 2**k2 - 1,
|
|
// where the (decreasing) value of k2 has just passed threshold to
|
|
// expose OOM from PageMap::Uniform (i.e. limit = 2**(k2+1) - 1
|
|
// won't OOM); test bed is 64-bit DEBUG avmshell on Mac OS X 10.6.4.
|
|
//
|
|
// k1, k2
|
|
// ------
|
|
// 43, 18
|
|
// 42, 17
|
|
// 41, 16
|
|
// 40, 15
|
|
// 39, 14
|
|
// 38, 13
|
|
// 37, 12
|
|
// 36, 12
|
|
// 35, 11
|
|
// 34, 11
|
|
//
|
|
// For k1 < 34, test won't OOM for k2 >= 11.
|
|
// For k2 < 11, test will OOM w/o dispersiveAdversarial at all.
|
|
|
|
gc = core->gc;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_bugzilla_580603::epilogue() {
|
|
{
|
|
// restore original values to limit disruption to remaining selftests
|
|
heap->Config().heapLimit = this->saved_heapLimit;
|
|
heap->Config().dispersiveAdversarial = saved_dispersiveAdversarial;
|
|
}
|
|
|
|
}
|
|
|
|
void ST_mmgc_bugzilla_580603::test0() {
|
|
{
|
|
char *h[3];
|
|
h[0] = my_alloc( 256*GCHeap::kBlockSize, via_heap);
|
|
h[1] = my_alloc( 256*GCHeap::kBlockSize, via_heap);
|
|
my_free(h[0], via_heap);
|
|
my_free(h[1], via_heap);
|
|
// print_minmax();
|
|
|
|
char *g[m_num_iters*m_num_sizes]; (void)g;
|
|
do_allocs(g, via_heap);
|
|
|
|
// print_minmax();
|
|
|
|
// line 242 "ST_mmgc_580603.st"
|
|
verifyPass(1, "1", __FILE__, __LINE__);
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_bugzilla_580603::test1() {
|
|
{
|
|
char *h[3];
|
|
h[0] = my_alloc( 256*GCHeap::kBlockSize, via_heap);
|
|
h[1] = my_alloc( 256*GCHeap::kBlockSize, via_heap);
|
|
my_free(h[0], via_heap);
|
|
my_free(h[1], via_heap);
|
|
// print_minmax();
|
|
|
|
char *g[m_num_iters*m_num_sizes]; (void)g;
|
|
do_allocs(g, via_gc);
|
|
|
|
// print_minmax();
|
|
|
|
// line 259 "ST_mmgc_580603.st"
|
|
verifyPass(1, "1", __FILE__, __LINE__);
|
|
}
|
|
|
|
}
|
|
void create_mmgc_bugzilla_580603(AvmCore* core) { new ST_mmgc_bugzilla_580603(core); }
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_603411.st
|
|
// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
|
|
//
|
|
// 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/.
|
|
|
|
// Bug 603411 - SECURITY: AvmCore dtor needs to clear CallStackNode chain
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
#if defined DEBUGGER
|
|
namespace avmplus {
|
|
namespace ST_mmgc_bugzilla_603411 {
|
|
|
|
class MyDebugger : public Debugger
|
|
{
|
|
public:
|
|
MyDebugger(AvmCore *core, avmplus::Debugger::TraceLevel tracelevel) : Debugger(core, tracelevel) {}
|
|
~MyDebugger() {}
|
|
|
|
virtual void enterDebugger() {}
|
|
virtual bool filterException(avmplus::Exception*, bool) { return false; }
|
|
virtual bool hitWatchpoint() {return false; }
|
|
};
|
|
|
|
|
|
class MyToplevel: public avmplus::Toplevel
|
|
{
|
|
public:
|
|
MyToplevel(avmplus::AbcEnv* env)
|
|
: Toplevel(env) {}
|
|
virtual GCRef<avmplus::ClassClosure> workerClass() const
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
virtual GCRef<avmplus::ClassClosure> workerDomainClass() const
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
virtual GCRef<avmplus::ScriptObject> constructWorkerObject() const
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
};
|
|
|
|
class MyAvmCore : public avmplus::AvmCore
|
|
{
|
|
public:
|
|
MyAvmCore(MMgc::GC* gc) : AvmCore(gc, kApiVersionSeries_FP) { initBuiltinPool(1); }
|
|
~MyAvmCore() {}
|
|
|
|
virtual void interrupt(Toplevel*, InterruptReason) { }
|
|
virtual void stackOverflow(Toplevel*) { }
|
|
virtual avmplus::String* readFileForEval(avmplus::String*, avmplus::String*) { return NULL; }
|
|
virtual avmplus::ApiVersion getDefaultAPI() { return kApiVersion_VM_INTERNAL; }
|
|
virtual avmplus::Toplevel* createTopLevel(avmplus::AbcEnv*)
|
|
{
|
|
return NULL;
|
|
}
|
|
Debugger* createDebugger(int tracelevel) { return new (this->gc) MyDebugger(this, (avmplus::Debugger::_TraceLevel)tracelevel); }
|
|
|
|
virtual avmplus::Toplevel* createToplevel(avmplus::AbcEnv* env) {
|
|
return new (gc) MyToplevel(env);
|
|
}
|
|
|
|
};
|
|
|
|
|
|
class MyTestClass
|
|
{
|
|
public:
|
|
MyTestClass(MyAvmCore* core) : m_core(core)
|
|
{
|
|
(void)core;
|
|
}
|
|
|
|
void testAvmCoreDelete()
|
|
{
|
|
// create CallStackNode
|
|
CallStackNode csn(m_core, "Date");
|
|
|
|
// delete core
|
|
delete m_core;
|
|
}
|
|
private:
|
|
MyAvmCore* m_core;
|
|
};
|
|
|
|
class ST_mmgc_bugzilla_603411 : public Selftest {
|
|
public:
|
|
ST_mmgc_bugzilla_603411(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
virtual void epilogue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
private:
|
|
MyTestClass* testClass;
|
|
MyAvmCore* testCore;
|
|
};
|
|
ST_mmgc_bugzilla_603411::ST_mmgc_bugzilla_603411(AvmCore* core)
|
|
: Selftest(core, "mmgc", "bugzilla_603411", ST_mmgc_bugzilla_603411::ST_names,ST_mmgc_bugzilla_603411::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_bugzilla_603411::ST_names[] = {"test1", NULL };
|
|
const bool ST_mmgc_bugzilla_603411::ST_explicits[] = {false, false };
|
|
void ST_mmgc_bugzilla_603411::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_bugzilla_603411::prologue() {
|
|
|
|
}
|
|
void ST_mmgc_bugzilla_603411::epilogue() {
|
|
testClass = NULL;
|
|
testCore = NULL;
|
|
|
|
}
|
|
void ST_mmgc_bugzilla_603411::test0() {
|
|
// create our own core
|
|
testCore = new MyAvmCore(core->gc);
|
|
testClass = new MyTestClass(testCore);
|
|
testClass->testAvmCoreDelete();
|
|
|
|
// line 108 "ST_mmgc_603411.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
|
|
}
|
|
void create_mmgc_bugzilla_603411(AvmCore* core) { new ST_mmgc_bugzilla_603411(core); }
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_637993.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
// Bugzilla 637993: rehashing a GCHashtable in the midst of iteration
|
|
// is unsound; here we check that we are guarding against it.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_mmgc_bugzilla_637993 {
|
|
class ST_mmgc_bugzilla_637993 : public Selftest {
|
|
public:
|
|
ST_mmgc_bugzilla_637993(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
void test3();
|
|
void test4();
|
|
|
|
const static size_t elem_count = 1000;
|
|
int32_t *elems;
|
|
MMgc::GCHashtable m_table;
|
|
|
|
void add_first_half() {
|
|
for (size_t i=0; i < elem_count/2; i++)
|
|
m_table.put(&elems[i], &elems[i+1]);
|
|
}
|
|
|
|
void add_second_half() {
|
|
for (size_t i=elem_count/2; i < elem_count; i++)
|
|
m_table.put(&elems[i], &elems[i+1]);
|
|
}
|
|
|
|
};
|
|
ST_mmgc_bugzilla_637993::ST_mmgc_bugzilla_637993(AvmCore* core)
|
|
: Selftest(core, "mmgc", "bugzilla_637993", ST_mmgc_bugzilla_637993::ST_names,ST_mmgc_bugzilla_637993::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_bugzilla_637993::ST_names[] = {"delete_during_iteration_okay_if_norehash","delete_during_iteration_asserts_if_rehash","rehash_after_iteration_succeeds","rehash_during_iteration_assert_fails_1","rehash_during_iteration_assert_fails_2", NULL };
|
|
const bool ST_mmgc_bugzilla_637993::ST_explicits[] = {false,true,false,true,true, false };
|
|
void ST_mmgc_bugzilla_637993::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
case 3: test3(); return;
|
|
case 4: test4(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_bugzilla_637993::test0() {
|
|
elems = new int32_t[elem_count];
|
|
add_first_half();
|
|
{
|
|
MMgc::GCHashtable::Iterator it(&m_table);
|
|
while (it.nextKey()) {
|
|
m_table.remove(it.value(), /*allowrehash=*/false);
|
|
}
|
|
}
|
|
m_table.clear();
|
|
delete elems;
|
|
// line 41 "ST_mmgc_637993.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
;
|
|
|
|
}
|
|
void ST_mmgc_bugzilla_637993::test1() {
|
|
elems = new int32_t[elem_count];
|
|
add_first_half();
|
|
{
|
|
MMgc::GCHashtable::Iterator it(&m_table);
|
|
while (it.nextKey()) {
|
|
m_table.remove(it.value());
|
|
}
|
|
}
|
|
m_table.clear();
|
|
delete elems;
|
|
// line 55 "ST_mmgc_637993.st"
|
|
verifyPass(false, "false", __FILE__, __LINE__);
|
|
;
|
|
|
|
// This test is a trivial success; it is meant to be compared against
|
|
// the cases that *fail* below, in order to make it clear what is
|
|
// wrong with the intentionally asserting cases.
|
|
}
|
|
void ST_mmgc_bugzilla_637993::test2() {
|
|
elems = new int32_t[elem_count];
|
|
add_first_half();
|
|
{
|
|
MMgc::GCHashtable::Iterator it(&m_table);
|
|
it.nextKey();
|
|
it.nextKey();
|
|
}
|
|
add_second_half(); // rule satisfied; (Iterator is out of scope).
|
|
m_table.clear();
|
|
delete elems;
|
|
// line 72 "ST_mmgc_637993.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
;
|
|
|
|
// (This test should definitely assert.)
|
|
}
|
|
void ST_mmgc_bugzilla_637993::test3() {
|
|
elems = new int32_t[elem_count];
|
|
add_first_half();
|
|
{
|
|
MMgc::GCHashtable::Iterator it(&m_table);
|
|
it.nextKey();
|
|
add_second_half(); // this is where we break the rule
|
|
it.nextKey();
|
|
}
|
|
|
|
m_table.clear();
|
|
delete elems;
|
|
// we should never get here, the assertion should happen up above.
|
|
// line 89 "ST_mmgc_637993.st"
|
|
verifyPass(false, "false", __FILE__, __LINE__);
|
|
;
|
|
|
|
// This test will assert even though the iteration is "done",
|
|
// because the rule is that we cannot modify the hashtable while
|
|
// any iterator is still "in scope"
|
|
}
|
|
void ST_mmgc_bugzilla_637993::test4() {
|
|
elems = new int32_t[elem_count];
|
|
add_first_half();
|
|
{
|
|
MMgc::GCHashtable::Iterator it(&m_table);
|
|
it.nextKey();
|
|
it.nextKey();
|
|
add_second_half(); // this is where we break the rule
|
|
}
|
|
|
|
m_table.clear();
|
|
delete elems;
|
|
// we should never get here, the assertion should happen up above.
|
|
// line 108 "ST_mmgc_637993.st"
|
|
verifyPass(false, "false", __FILE__, __LINE__);
|
|
;
|
|
|
|
}
|
|
void create_mmgc_bugzilla_637993(AvmCore* core) { new ST_mmgc_bugzilla_637993(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_basics.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_mmgc_basics {
|
|
class ST_mmgc_basics : public Selftest {
|
|
public:
|
|
ST_mmgc_basics(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
virtual void epilogue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
void test3();
|
|
void test4();
|
|
void test5();
|
|
void test6();
|
|
void test7();
|
|
void test8();
|
|
void test9();
|
|
void test10();
|
|
void test11();
|
|
void test12();
|
|
void test13();
|
|
void test14();
|
|
void test15();
|
|
void test16();
|
|
void test17();
|
|
private:
|
|
MMgc::GC *gc;
|
|
MMgc::FixedAlloc *fa;
|
|
MMgc::FixedMalloc *fm;
|
|
|
|
};
|
|
ST_mmgc_basics::ST_mmgc_basics(AvmCore* core)
|
|
: Selftest(core, "mmgc", "basics", ST_mmgc_basics::ST_names,ST_mmgc_basics::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_basics::ST_names[] = {"create_gc_instance","create_gc_object","get_bytesinuse","collect","getgcheap","fixedAlloc","fixedMalloc","gcheap","gcheapAlign","gcmethods","finalizerAlloc","finalizerDelete","nestedGCs","collectDormantGC","lockObject","regression_551169","blacklisting","get_bytesinusefast", NULL };
|
|
const bool ST_mmgc_basics::ST_explicits[] = {false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false, false };
|
|
void ST_mmgc_basics::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
case 3: test3(); return;
|
|
case 4: test4(); return;
|
|
case 5: test5(); return;
|
|
case 6: test6(); return;
|
|
case 7: test7(); return;
|
|
case 8: test8(); return;
|
|
case 9: test9(); return;
|
|
case 10: test10(); return;
|
|
case 11: test11(); return;
|
|
case 12: test12(); return;
|
|
case 13: test13(); return;
|
|
case 14: test14(); return;
|
|
case 15: test15(); return;
|
|
case 16: test16(); return;
|
|
case 17: test17(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_basics::prologue() {
|
|
MMgc::GCConfig config;
|
|
gc=new MMgc::GC(MMgc::GCHeap::GetGCHeap(), config);
|
|
if (gc==NULL) {
|
|
MMgc::GCHeap::Init();
|
|
gc=new MMgc::GC(MMgc::GCHeap::GetGCHeap(), config);
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_basics::epilogue() {
|
|
delete gc;
|
|
|
|
}
|
|
using namespace MMgc;
|
|
class DeleteInFinalizer : public GCFinalizedObject {
|
|
public:
|
|
// note "small" is a reserved identifier in Visual C++ for Windows Mobile (#defined to be 'char')
|
|
DeleteInFinalizer(GCFinalizedObject *big, GCFinalizedObject *small_) : big(big), small_(small_) {};
|
|
~DeleteInFinalizer() { delete big; delete small_; }
|
|
private:
|
|
GCFinalizedObject *big;
|
|
GCFinalizedObject *small_;
|
|
};
|
|
|
|
// Any small object would do
|
|
class AllocInFinalizer2 : public GCObject {
|
|
public:
|
|
void* dummy;
|
|
};
|
|
|
|
class AllocInFinalizer : public GCFinalizedObject {
|
|
public:
|
|
AllocInFinalizer() {}
|
|
~AllocInFinalizer() { new (GC::GetGC(this)) AllocInFinalizer2(); }
|
|
};
|
|
|
|
class LockableObject : public GCFinalizedObject {
|
|
public:
|
|
LockableObject(int* counter) : counter(counter) {}
|
|
virtual ~LockableObject() {
|
|
*counter = *counter + 1;
|
|
}
|
|
int * const counter;
|
|
};
|
|
|
|
class LockerAndUnlocker
|
|
{
|
|
public:
|
|
static const int numlocked = 100;
|
|
static GCObjectLock* lock[numlocked];
|
|
static GCObjectLock* lock2[numlocked];
|
|
static int counter;
|
|
|
|
static bool createAndLockObjects(GC* gc) {
|
|
counter = 0;
|
|
for ( int i=0 ; i < numlocked ; i++ )
|
|
lock[i] = gc->LockObject(new (gc) LockableObject(&counter));
|
|
return true;
|
|
}
|
|
|
|
static bool lockLevel2(GC* gc) {
|
|
for ( int i=0 ; i < numlocked ; i++ )
|
|
lock2[i] = gc->LockObject(gc->GetLockedObject(lock[i]));
|
|
return true;
|
|
}
|
|
|
|
static bool testLocksHeld(GC* gc, int level) {
|
|
if (counter != 0)
|
|
return false;
|
|
|
|
int held = 0;
|
|
for ( int i=0 ; i < numlocked ; i++ )
|
|
held += bool(gc->GetLockedObject(lock[i]) != NULL);
|
|
if (level > 1)
|
|
for ( int i=0 ; i < numlocked ; i++ )
|
|
held += bool(gc->GetLockedObject(lock2[i]) != NULL);
|
|
|
|
if (held != level*numlocked)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool testLocksNotHeld(GC*) {
|
|
// At least some of the destructors should have run...
|
|
if (counter < numlocked/2)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool unlockLevel2(GC* gc) {
|
|
for ( int i=0 ; i < numlocked ; i++ )
|
|
gc->UnlockObject(lock2[i]);
|
|
return true;
|
|
}
|
|
|
|
static bool unlockLevel1(GC* gc) {
|
|
for ( int i=0 ; i < numlocked ; i++ ) {
|
|
#ifdef MMGC_HEAP_GRAPH
|
|
gc->addToBlacklist(gc->GetLockedObject(lock[i]));
|
|
#endif
|
|
gc->UnlockObject(lock[i]);
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
GCObjectLock* LockerAndUnlocker::lock[numlocked];
|
|
GCObjectLock* LockerAndUnlocker::lock2[numlocked];
|
|
int LockerAndUnlocker::counter = 0;
|
|
|
|
void ST_mmgc_basics::test0() {
|
|
// line 128 "ST_mmgc_basics.st"
|
|
verifyPass(gc != NULL, "gc != NULL", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_mmgc_basics::test1() {
|
|
MMGC_GCENTER(gc);
|
|
MyGCObject *mygcobject;
|
|
mygcobject = (MyGCObject *)new (gc) MyGCObject();
|
|
// line 134 "ST_mmgc_basics.st"
|
|
verifyPass(mygcobject!=NULL, "mygcobject!=NULL", __FILE__, __LINE__);
|
|
mygcobject->i=10;
|
|
// line 136 "ST_mmgc_basics.st"
|
|
verifyPass(mygcobject->i==10, "mygcobject->i==10", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_mmgc_basics::test2() {
|
|
MMGC_GCENTER(gc);
|
|
MyGCObject *mygcobject;
|
|
int inuse=(int)gc->GetBytesInUse();
|
|
mygcobject = (MyGCObject *)new (gc) MyGCObject();
|
|
// AvmLog("bytes in use before %d after %d\n",inuse,(int)gc->GetBytesInUse());
|
|
// line 144 "ST_mmgc_basics.st"
|
|
verifyPass(gc->GetBytesInUse()==inuse + sizeof(MyGCObject) + DebugSize(), "gc->GetBytesInUse()==inuse + sizeof(MyGCObject) + DebugSize()", __FILE__, __LINE__);
|
|
delete mygcobject;
|
|
|
|
}
|
|
void ST_mmgc_basics::test3() {
|
|
MMGC_GCENTER(gc);
|
|
MyGCObject *mygcobject;
|
|
int inuse=(int)gc->GetBytesInUse();
|
|
mygcobject = (MyGCObject *)new (gc) MyGCObject();
|
|
// line 152 "ST_mmgc_basics.st"
|
|
verifyPass((int)gc->GetBytesInUse()>inuse, "(int)gc->GetBytesInUse()>inuse", __FILE__, __LINE__);
|
|
delete mygcobject;
|
|
// AvmLog("collect: inuse=%d current=%d\n",inuse,(int)gc->GetBytesInUse());
|
|
gc->Collect();
|
|
// AvmLog("collect: inuse=%d current=%d\n",inuse,(int)gc->GetBytesInUse());
|
|
// line 157 "ST_mmgc_basics.st"
|
|
verifyPass((int)gc->GetBytesInUse()<=inuse, "(int)gc->GetBytesInUse()<=inuse", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_mmgc_basics::test4() {
|
|
// line 160 "ST_mmgc_basics.st"
|
|
verifyPass(gc->GetGCHeap()!=NULL, "gc->GetGCHeap()!=NULL", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_mmgc_basics::test5() {
|
|
MMgc::FixedAlloc *fa;
|
|
fa=new MMgc::FixedAlloc(2048,MMgc::GCHeap::GetGCHeap());
|
|
// line 165 "ST_mmgc_basics.st"
|
|
verifyPass((int)fa->GetMaxAlloc()==0, "(int)fa->GetMaxAlloc()==0", __FILE__, __LINE__);
|
|
// line 166 "ST_mmgc_basics.st"
|
|
verifyPass((int)fa->GetNumBlocks()==0, "(int)fa->GetNumBlocks()==0", __FILE__, __LINE__);
|
|
void *data1=fa->Alloc(2048);
|
|
// line 168 "ST_mmgc_basics.st"
|
|
verifyPass(MMgc::FixedAlloc::GetFixedAlloc(data1)==fa, "MMgc::FixedAlloc::GetFixedAlloc(data1)==fa", __FILE__, __LINE__);
|
|
// line 169 "ST_mmgc_basics.st"
|
|
verifyPass(fa->GetBytesInUse()==DebugSize()+2048, "fa->GetBytesInUse()==DebugSize()+2048", __FILE__, __LINE__);
|
|
// line 170 "ST_mmgc_basics.st"
|
|
verifyPass(fa->GetItemSize()==2048, "fa->GetItemSize()==2048", __FILE__, __LINE__);
|
|
void *data2=fa->Alloc(2048);
|
|
// line 172 "ST_mmgc_basics.st"
|
|
verifyPass(MMgc::FixedAlloc::GetFixedAlloc(data2)==fa, "MMgc::FixedAlloc::GetFixedAlloc(data2)==fa", __FILE__, __LINE__);
|
|
// AvmLog("fa->GetItemSize=%d\n",(int)fa->GetItemSize());
|
|
// line 174 "ST_mmgc_basics.st"
|
|
verifyPass((int)fa->GetItemSize()==2048, "(int)fa->GetItemSize()==2048", __FILE__, __LINE__);
|
|
fa->Free(data1);
|
|
// line 176 "ST_mmgc_basics.st"
|
|
verifyPass((int)fa->GetItemSize()==2048, "(int)fa->GetItemSize()==2048", __FILE__, __LINE__);
|
|
// line 177 "ST_mmgc_basics.st"
|
|
verifyPass((int)fa->GetMaxAlloc()==1, "(int)fa->GetMaxAlloc()==1", __FILE__, __LINE__);
|
|
// line 178 "ST_mmgc_basics.st"
|
|
verifyPass((int)fa->GetNumBlocks()==1, "(int)fa->GetNumBlocks()==1", __FILE__, __LINE__);
|
|
fa->Free(data2);
|
|
delete fa;
|
|
|
|
}
|
|
void ST_mmgc_basics::test6() {
|
|
fm=MMgc::FixedMalloc::GetFixedMalloc();
|
|
int start=(int)fm->GetBytesInUse();
|
|
int starttotal=(int)fm->GetTotalSize();
|
|
// AvmLog("fm->GetBytesInUse()=%d\n",(int)fm->GetBytesInUse());
|
|
// line 187 "ST_mmgc_basics.st"
|
|
verifyPass((int)fm->GetBytesInUse()==start, "(int)fm->GetBytesInUse()==start", __FILE__, __LINE__);
|
|
// AvmLog("fm->GetTotalSize()=%d\n",(int)fm->GetTotalSize());
|
|
// line 189 "ST_mmgc_basics.st"
|
|
verifyPass((int)fm->GetTotalSize()==starttotal, "(int)fm->GetTotalSize()==starttotal", __FILE__, __LINE__);
|
|
void *obj=fm->Alloc(8192);
|
|
// AvmLog("fm->GetBytesInUse()=%d\n",(int)fm->GetBytesInUse());
|
|
// %%verify fm->GetBytesInUse()==start + 8192 + MMgc::DebugSize()
|
|
// AvmLog("fm->GetTotalSize()=%d\n",(int)fm->GetTotalSize());
|
|
// %%verify (int)fm->GetTotalSize()==starttotal+2
|
|
fm->Free(obj);
|
|
// AvmLog("fm->GetBytesInUse()=%d\n",(int)fm->GetBytesInUse());
|
|
// line 197 "ST_mmgc_basics.st"
|
|
verifyPass((int)fm->GetBytesInUse()==start, "(int)fm->GetBytesInUse()==start", __FILE__, __LINE__);
|
|
// AvmLog("fm->GetTotalSize()=%d\n",(int)fm->GetTotalSize());
|
|
// line 199 "ST_mmgc_basics.st"
|
|
verifyPass((int)fm->GetTotalSize()==starttotal, "(int)fm->GetTotalSize()==starttotal", __FILE__, __LINE__);
|
|
obj=fm->Calloc(1024,10);
|
|
// AvmLog("fm->GetBytesInUse()=%d\n",(int)fm->GetBytesInUse());
|
|
// FixedMalloc is currently (as of redux 3229) tracking large allocs using a list of
|
|
// small objects, in some debug modes. So we can't have a precise test here.
|
|
// line 204 "ST_mmgc_basics.st"
|
|
verifyPass((int)fm->GetBytesInUse()>=start+1024*12 && (int)fm->GetBytesInUse()<=start+1024*12+64, "(int)fm->GetBytesInUse()>=start+1024*12 && (int)fm->GetBytesInUse()<=start+1024*12+64", __FILE__, __LINE__);
|
|
// AvmLog("fm->GetTotalSize()=%d\n",(int)fm->GetTotalSize());
|
|
// line 206 "ST_mmgc_basics.st"
|
|
verifyPass((int)fm->GetTotalSize()==starttotal+3, "(int)fm->GetTotalSize()==starttotal+3", __FILE__, __LINE__);
|
|
fm->Free(obj);
|
|
// line 208 "ST_mmgc_basics.st"
|
|
verifyPass((int)fm->GetBytesInUse()==start, "(int)fm->GetBytesInUse()==start", __FILE__, __LINE__);
|
|
// line 209 "ST_mmgc_basics.st"
|
|
verifyPass((int)fm->GetTotalSize()==starttotal, "(int)fm->GetTotalSize()==starttotal", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_mmgc_basics::test7() {
|
|
MMgc::GCHeap *gh=MMgc::GCHeap::GetGCHeap();
|
|
int startfreeheap=(int)gh->GetFreeHeapSize();
|
|
// %%verify (int)gh->GetTotalHeapSize()==128
|
|
// AvmLog("gh->GetFreeHeapSize()=%d\n",(int)gh->GetFreeHeapSize());
|
|
// line 216 "ST_mmgc_basics.st"
|
|
verifyPass((int)gh->GetFreeHeapSize()==startfreeheap, "(int)gh->GetFreeHeapSize()==startfreeheap", __FILE__, __LINE__);
|
|
//gh->Config().heapLimit = 1024;
|
|
// %%verify (int)gh->GetTotalHeapSize()==128
|
|
// AvmLog("gh->GetFreeHeapSize()=%d\n",(int)gh->GetFreeHeapSize());
|
|
// line 220 "ST_mmgc_basics.st"
|
|
verifyPass((int)gh->GetFreeHeapSize()==startfreeheap, "(int)gh->GetFreeHeapSize()==startfreeheap", __FILE__, __LINE__);
|
|
void *data = gh->Alloc(10,MMgc::GCHeap::kExpand | MMgc::GCHeap::kZero);
|
|
// line 222 "ST_mmgc_basics.st"
|
|
verifyPass((int)gh->GetTotalHeapSize()>startfreeheap, "(int)gh->GetTotalHeapSize()>startfreeheap", __FILE__, __LINE__);
|
|
// AvmLog("gh->GetFreeHeapSize()=%d\n",(int)gh->GetFreeHeapSize());
|
|
gh->FreeNoProfile(data);
|
|
|
|
}
|
|
void ST_mmgc_basics::test8() {
|
|
MMgc::GCHeap *gh=MMgc::GCHeap::GetGCHeap();
|
|
|
|
// Tricky: try to provoke some internal asserts
|
|
void *d[1000];
|
|
for ( unsigned i=0 ; i < ARRAY_SIZE(d) ; i++ ) {
|
|
d[i] = gh->Alloc(1);
|
|
void *data = gh->Alloc(10,MMgc::GCHeap::flags_Alloc, 4);
|
|
gh->Free(data);
|
|
}
|
|
for ( unsigned i=0 ; i < ARRAY_SIZE(d) ; i++ )
|
|
gh->Free(d[i]);
|
|
|
|
//
|
|
for ( size_t k=2 ; k <= 256 ; k *= 2 ) {
|
|
void *data = gh->Alloc(10,MMgc::GCHeap::flags_Alloc, k);
|
|
// line 242 "ST_mmgc_basics.st"
|
|
verifyPass(((uintptr_t)data & (k*MMgc::GCHeap::kBlockSize - 1)) == 0, "((uintptr_t)data & (k*MMgc::GCHeap::kBlockSize - 1)) == 0", __FILE__, __LINE__);
|
|
// line 243 "ST_mmgc_basics.st"
|
|
verifyPass(gh->Size(data) == 10, "gh->Size(data) == 10", __FILE__, __LINE__);
|
|
gh->Free(data);
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_basics::test9() {
|
|
MMGC_GCENTER(gc);
|
|
MyGCObject *mygcobject;
|
|
mygcobject = (MyGCObject *)new (gc) MyGCObject();
|
|
// line 251 "ST_mmgc_basics.st"
|
|
verifyPass((MyGCObject *)gc->FindBeginningGuarded(mygcobject)==mygcobject, "(MyGCObject *)gc->FindBeginningGuarded(mygcobject)==mygcobject", __FILE__, __LINE__);
|
|
// line 252 "ST_mmgc_basics.st"
|
|
verifyPass((MyGCObject *)gc->FindBeginningFast(mygcobject)==mygcobject, "(MyGCObject *)gc->FindBeginningFast(mygcobject)==mygcobject", __FILE__, __LINE__);
|
|
|
|
// Bugzilla 542529 - in debug mode we would assert here due to logic flaws in the allocatr
|
|
}
|
|
void ST_mmgc_basics::test10() {
|
|
MMGC_GCENTER(gc);
|
|
new (gc) AllocInFinalizer();
|
|
gc->Collect(false);
|
|
// line 259 "ST_mmgc_basics.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_mmgc_basics::test11() {
|
|
MMGC_GCENTER(gc);
|
|
new (gc) DeleteInFinalizer(new (gc, 100) GCFinalizedObject(), new (gc) GCFinalizedObject());
|
|
//delete m; delete m; // this verifies we crash, it does
|
|
gc->Collect(false);
|
|
// line 266 "ST_mmgc_basics.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
GCFinalizedObject *gcfo = new (gc) GCFinalizedObject();
|
|
gcfo->~GCFinalizedObject();
|
|
gcfo->~GCFinalizedObject(); // this used to be a deleteing dtor and would crash, not anymore
|
|
|
|
|
|
|
|
}
|
|
void ST_mmgc_basics::test12() {
|
|
GCConfig config;
|
|
GC *gcb = new GC(GCHeap::GetGCHeap(), config);
|
|
MMGC_GCENTER(gc);
|
|
void *a = gc->Alloc(8);
|
|
{
|
|
MMGC_GCENTER(gcb);
|
|
a = gcb->Alloc(8);
|
|
{
|
|
MMGC_GCENTER(gc);
|
|
a = gc->Alloc(8);
|
|
}
|
|
a = gcb->Alloc(8);
|
|
}
|
|
a = gc->Alloc(8);
|
|
// just fishing for asserts/hangs/crashes
|
|
// line 289 "ST_mmgc_basics.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
delete gcb;
|
|
|
|
}
|
|
void ST_mmgc_basics::test13() {
|
|
{
|
|
GCConfig config;
|
|
GC *gcb = new GC(GCHeap::GetGCHeap(), config);
|
|
{
|
|
MMGC_GCENTER(gcb);
|
|
gcb->Alloc(8);
|
|
}
|
|
|
|
// this will cause a Collection in gcb
|
|
GCHeap::SignalExternalFreeMemory();
|
|
delete gcb;
|
|
|
|
// just fishing for asserts/hangs/crashes
|
|
// line 306 "ST_mmgc_basics.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_basics::test14() {
|
|
{
|
|
GCConfig config;
|
|
GC *gc = new GC(GCHeap::GetGCHeap(), config);
|
|
MMGC_GCENTER(gc);
|
|
|
|
// line 315 "ST_mmgc_basics.st"
|
|
verifyPass(LockerAndUnlocker::createAndLockObjects(gc), "LockerAndUnlocker::createAndLockObjects(gc)", __FILE__, __LINE__);
|
|
gc->Collect();
|
|
gc->Collect();
|
|
// line 318 "ST_mmgc_basics.st"
|
|
verifyPass(LockerAndUnlocker::testLocksHeld(gc, 1), "LockerAndUnlocker::testLocksHeld(gc, 1)", __FILE__, __LINE__);
|
|
gc->Collect();
|
|
gc->Collect();
|
|
// line 321 "ST_mmgc_basics.st"
|
|
verifyPass(LockerAndUnlocker::lockLevel2(gc), "LockerAndUnlocker::lockLevel2(gc)", __FILE__, __LINE__);
|
|
gc->Collect();
|
|
gc->Collect();
|
|
// line 324 "ST_mmgc_basics.st"
|
|
verifyPass(LockerAndUnlocker::testLocksHeld(gc, 2), "LockerAndUnlocker::testLocksHeld(gc, 2)", __FILE__, __LINE__);
|
|
gc->Collect();
|
|
gc->Collect();
|
|
// line 327 "ST_mmgc_basics.st"
|
|
verifyPass(LockerAndUnlocker::unlockLevel2(gc), "LockerAndUnlocker::unlockLevel2(gc)", __FILE__, __LINE__);
|
|
gc->Collect();
|
|
gc->Collect();
|
|
// line 330 "ST_mmgc_basics.st"
|
|
verifyPass(LockerAndUnlocker::testLocksHeld(gc, 1), "LockerAndUnlocker::testLocksHeld(gc, 1)", __FILE__, __LINE__);
|
|
gc->Collect();
|
|
gc->Collect();
|
|
// line 333 "ST_mmgc_basics.st"
|
|
verifyPass(LockerAndUnlocker::unlockLevel1(gc), "LockerAndUnlocker::unlockLevel1(gc)", __FILE__, __LINE__);
|
|
|
|
// Bug 637695: be aware: conservative retention may foil verify below
|
|
gc->Collect();
|
|
gc->Collect();
|
|
// line 338 "ST_mmgc_basics.st"
|
|
verifyPass(LockerAndUnlocker::testLocksNotHeld(gc), "LockerAndUnlocker::testLocksNotHeld(gc)", __FILE__, __LINE__);
|
|
|
|
delete gc;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_basics::test15() {
|
|
{
|
|
GCConfig config;
|
|
GC *testGC = new GC(GCHeap::GetGCHeap(), config);
|
|
{
|
|
MMGC_GCENTER(testGC);
|
|
testGC->StartIncrementalMark();
|
|
// self test for tricky GCRoot deletion logic
|
|
// do this a bunch, idea is to try to hit GetItemAbove border edge cases
|
|
//GCMarkStack& ms = testGC->m_incrementalWork;
|
|
for(int i=0;i<10000;i++) {
|
|
//GCRoot *fauxRoot = new GCRoot(testGC, new char[GC::kMarkItemSplitThreshold*2], GC::kMarkItemSplitThreshold*2);
|
|
//testGC->MarkAllRoots();
|
|
// tail of fauxRoot is on stack
|
|
//uintptr_t sentinel = fauxRoot->GetMarkStackSentinelPointer();
|
|
//if(sentinel) {
|
|
// const void* ptr;
|
|
// ms.Read_RootProtector(sentinel, ptr);
|
|
// %%verify ptr == fauxRoot
|
|
// uintptr_t tail = ms.GetItemAbove(sentinel);
|
|
// %%verify ms.GetEndAt(tail) == fauxRoot->End()
|
|
// %%verify sentinel != 0
|
|
//}
|
|
//delete [] (char*)fauxRoot->Get();
|
|
//delete fauxRoot;
|
|
//if(sentinel) {
|
|
// %%verify ms.P(sentinel) == GCMarkStack::kDeadItem
|
|
// %%verify ms.GetSentinel1TypeAt(ms.GetItemAbove(sentinel)) == GCMarkStack::kDeadItem
|
|
//}
|
|
// line 372 "ST_mmgc_basics.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
}
|
|
testGC->Mark();
|
|
testGC->ClearMarkStack();
|
|
testGC->ClearMarks();
|
|
}
|
|
delete testGC;
|
|
}
|
|
|
|
|
|
}
|
|
void ST_mmgc_basics::test16() {
|
|
#ifdef MMGC_HEAP_GRAPH
|
|
GCConfig config;
|
|
GC *gc = new GC(GCHeap::GetGCHeap(), config);
|
|
MMGC_GCENTER(gc);
|
|
MyGCObject *mygcobject;
|
|
mygcobject = (MyGCObject *)new (gc) MyGCObject();
|
|
gc->addToBlacklist(mygcobject);
|
|
gc->Collect();
|
|
gc->removeFromBlacklist(mygcobject);
|
|
delete gc;
|
|
#endif
|
|
// line 394 "ST_mmgc_basics.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_mmgc_basics::test17() {
|
|
MMGC_GCENTER(gc);
|
|
MyGCObject *mygcobject;
|
|
mygcobject = (MyGCObject *) new (gc) MyGCObject();
|
|
// line 400 "ST_mmgc_basics.st"
|
|
verifyPass(gc->GetBytesInUse() == gc->GetBytesInUseFast(), "gc->GetBytesInUse() == gc->GetBytesInUseFast()", __FILE__, __LINE__);
|
|
delete mygcobject;
|
|
// line 402 "ST_mmgc_basics.st"
|
|
verifyPass(gc->GetBytesInUse() == gc->GetBytesInUseFast(), "gc->GetBytesInUse() == gc->GetBytesInUseFast()", __FILE__, __LINE__);
|
|
gc->Collect();
|
|
// line 404 "ST_mmgc_basics.st"
|
|
verifyPass(gc->GetBytesInUse() == gc->GetBytesInUseFast(), "gc->GetBytesInUse() == gc->GetBytesInUseFast()", __FILE__, __LINE__);
|
|
MyGCLargeObject *mygclargeobject;
|
|
mygclargeobject = (MyGCLargeObject *) new (gc) MyGCLargeObject();
|
|
// line 407 "ST_mmgc_basics.st"
|
|
verifyPass(gc->GetBytesInUse() == gc->GetBytesInUseFast(), "gc->GetBytesInUse() == gc->GetBytesInUseFast()", __FILE__, __LINE__);
|
|
delete mygclargeobject;
|
|
// line 409 "ST_mmgc_basics.st"
|
|
verifyPass(gc->GetBytesInUse() == gc->GetBytesInUseFast(), "gc->GetBytesInUse() == gc->GetBytesInUseFast()", __FILE__, __LINE__);
|
|
gc->Collect();
|
|
// line 411 "ST_mmgc_basics.st"
|
|
verifyPass(gc->GetBytesInUse() == gc->GetBytesInUseFast() , "gc->GetBytesInUse() == gc->GetBytesInUseFast() ", __FILE__, __LINE__);
|
|
|
|
|
|
}
|
|
void create_mmgc_basics(AvmCore* core) { new ST_mmgc_basics(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_dependent.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
// Bugzilla 543560 - here we risk deleting an object that is still on the mark stack because
|
|
// of how we perform large-object splitting. The setup is that user code that deletes the object
|
|
// gets to run after the first part of the large object has been popped off the mark stack
|
|
// but before the rest has been handled.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_mmgc_dependent {
|
|
using namespace MMgc;
|
|
|
|
static const size_t nbytes = 16*1024*1024;
|
|
|
|
class DependentAllocHolder : public GCFinalizedObject {
|
|
public:
|
|
|
|
DependentAllocHolder() {
|
|
memory = (char*)FixedMalloc::GetFixedMalloc()->Alloc(nbytes);
|
|
GC::GetGC(this)->SignalDependentAllocation(nbytes);
|
|
}
|
|
|
|
virtual ~DependentAllocHolder() {
|
|
FixedMalloc::GetFixedMalloc()->Free(memory);
|
|
memory = NULL;
|
|
GC::GetGC(this)->SignalDependentDeallocation(nbytes);
|
|
}
|
|
private:
|
|
char* memory;
|
|
};
|
|
|
|
class ST_mmgc_dependent : public Selftest {
|
|
public:
|
|
ST_mmgc_dependent(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
virtual void epilogue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
private:
|
|
MMgc::GC *gc;
|
|
|
|
};
|
|
ST_mmgc_dependent::ST_mmgc_dependent(AvmCore* core)
|
|
: Selftest(core, "mmgc", "dependent", ST_mmgc_dependent::ST_names,ST_mmgc_dependent::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_dependent::ST_names[] = {"dependent_alloc","dependent_memory_total","dependent_memory_unknown", NULL };
|
|
const bool ST_mmgc_dependent::ST_explicits[] = {false,false,false, false };
|
|
void ST_mmgc_dependent::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_dependent::prologue() {
|
|
GCConfig config;
|
|
gc = new GC(GCHeap::GetGCHeap(), config);
|
|
|
|
}
|
|
void ST_mmgc_dependent::epilogue() {
|
|
delete gc;
|
|
|
|
}
|
|
void ST_mmgc_dependent::test0() {
|
|
size_t maxheap = 0;
|
|
{
|
|
MMGC_GCENTER(gc);
|
|
|
|
int count = 100;
|
|
for (int c=0; c<count; c++) {
|
|
(void)(new (gc) DependentAllocHolder());
|
|
size_t heapsize = gc->policy.blocksOwnedByGC() * GCHeap::kBlockSize;
|
|
// printf("%lu\n", (unsigned long)heapsize);
|
|
if (heapsize > maxheap)
|
|
maxheap = heapsize;
|
|
}
|
|
}
|
|
|
|
// This is tricky to get right but for this test the 16MB blocks will dominate
|
|
// completely. So assume that heap size must stay below L*2*16MB for the
|
|
// L that applies at 32MB.
|
|
|
|
// line 68 "ST_mmgc_dependent.st"
|
|
verifyPass(size_t(gc->policy.queryLoadForHeapsize(double(2*nbytes)) * 2.0 * double(nbytes)) >= maxheap, "size_t(gc->policy.queryLoadForHeapsize(double(2*nbytes)) * 2.0 * double(nbytes)) >= maxheap", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_mmgc_dependent::test1() {
|
|
#if defined VMCFG_TELEMETRY && defined AVMSHELL_BUILD
|
|
size_t depMem_start,depMem_end;
|
|
depMem_start = depMem_end = 0;
|
|
MMgc::GC* gc = core->gc;
|
|
|
|
// Check total dependent memory consistency
|
|
for(int i = 0; i < MMgc::typeCount; i++)
|
|
depMem_start += gc->getDependentMemory((MMgc::DependentMemoryType)i);
|
|
// line 79 "ST_mmgc_dependent.st"
|
|
verifyPass(depMem_start == gc->policy.dependentAllocation, "depMem_start == gc->policy.dependentAllocation", __FILE__, __LINE__);
|
|
|
|
// Check byteArray type dependent memory
|
|
avmshell::ShellCore* c = (avmshell::ShellCore*)core;
|
|
avmshell::ShellToplevel* top = c->shell_toplevel;
|
|
|
|
size_t byteArray_bytes1 = gc->getDependentMemory(MMgc::typeByteArray);
|
|
ByteArrayObject* byteArray = top->byteArrayClass()->constructByteArray();
|
|
byteArray->writeBoolean(false);
|
|
byteArray->writeDouble(3.14);
|
|
|
|
// Allocate known type dependent memory
|
|
size_t byteArray_bytes2 = gc->getDependentMemory(MMgc::typeByteArray);
|
|
// line 92 "ST_mmgc_dependent.st"
|
|
verifyPass(byteArray_bytes2 > byteArray_bytes1, "byteArray_bytes2 > byteArray_bytes1", __FILE__, __LINE__);
|
|
|
|
// Allocate unknown dependent memory
|
|
DependentAllocHolder* obj = new (gc) DependentAllocHolder();
|
|
|
|
size_t byteArray_bytes3 = gc->getDependentMemory(MMgc::typeByteArray);
|
|
// line 98 "ST_mmgc_dependent.st"
|
|
verifyPass(byteArray_bytes3 == byteArray_bytes2, "byteArray_bytes3 == byteArray_bytes2", __FILE__, __LINE__);
|
|
byteArray->clear();
|
|
|
|
// line 101 "ST_mmgc_dependent.st"
|
|
verifyPass(gc->getDependentMemory(MMgc::typeByteArray) == byteArray_bytes1, "gc->getDependentMemory(MMgc::typeByteArray) == byteArray_bytes1", __FILE__, __LINE__);
|
|
delete obj;
|
|
|
|
// Consistency check
|
|
for(int i = 0; i < MMgc::typeCount; i++)
|
|
depMem_end += gc->getDependentMemory((MMgc::DependentMemoryType)i);
|
|
// line 107 "ST_mmgc_dependent.st"
|
|
verifyPass(depMem_end == gc->policy.dependentAllocation, "depMem_end == gc->policy.dependentAllocation", __FILE__, __LINE__);
|
|
// line 108 "ST_mmgc_dependent.st"
|
|
verifyPass(depMem_end == depMem_start, "depMem_end == depMem_start", __FILE__, __LINE__);
|
|
|
|
#if !defined DEBUG && !defined DEBUGGER
|
|
// Get memory for out of bounds type
|
|
// getDependentMemory has assert; skip in debug build
|
|
// line 113 "ST_mmgc_dependent.st"
|
|
verifyPass(gc->getDependentMemory(MMgc::typeCount) == 0, "gc->getDependentMemory(MMgc::typeCount) == 0", __FILE__, __LINE__);
|
|
#endif
|
|
// line 115 "ST_mmgc_dependent.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_mmgc_dependent::test2() {
|
|
#if defined VMCFG_TELEMETRY && defined AVMSHELL_BUILD
|
|
// Check unknown type dependent memory
|
|
MMgc::GC* gc = core->gc;
|
|
size_t unknownDependentMem_start = gc->getDependentMemory(MMgc::typeUnknown);
|
|
DependentAllocHolder* obj = new (gc) DependentAllocHolder();
|
|
size_t unknownDependentMem_end = gc->getDependentMemory(MMgc::typeUnknown);
|
|
// line 125 "ST_mmgc_dependent.st"
|
|
verifyPass((unknownDependentMem_end - unknownDependentMem_start) == nbytes, "(unknownDependentMem_end - unknownDependentMem_start) == nbytes", __FILE__, __LINE__);
|
|
|
|
delete obj;
|
|
// line 128 "ST_mmgc_dependent.st"
|
|
verifyPass(unknownDependentMem_start == gc->getDependentMemory(MMgc::typeUnknown), "unknownDependentMem_start == gc->getDependentMemory(MMgc::typeUnknown)", __FILE__, __LINE__);
|
|
#else
|
|
// line 130 "ST_mmgc_dependent.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
|
|
}
|
|
void create_mmgc_dependent(AvmCore* core) { new ST_mmgc_dependent(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_exact.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_mmgc_exact {
|
|
|
|
class Cthulhu : public MMgc::GCTraceableObject
|
|
{
|
|
public:
|
|
static Cthulhu* create(MMgc::GC* gc) {
|
|
return new (gc, MMgc::kExact) Cthulhu();
|
|
}
|
|
|
|
virtual bool gcTrace(MMgc::GC* gc, size_t cursor) {
|
|
(void)gc;
|
|
// Incorrectly say that there's more to do even when the object is small
|
|
if (cursor == 0)
|
|
return true;
|
|
// Concede
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// This test triggers a particular edge case in GC::SplitExactGCObject where
|
|
// a small object that returns 'true' from its gcTrace method is synchronously
|
|
// marked until the method returns 'false'; a 'true' return is only ever expected
|
|
// from the gcTrace methods of large objects.
|
|
|
|
class ST_mmgc_exact : public Selftest {
|
|
public:
|
|
ST_mmgc_exact(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
};
|
|
ST_mmgc_exact::ST_mmgc_exact(AvmCore* core)
|
|
: Selftest(core, "mmgc", "exact", ST_mmgc_exact::ST_names,ST_mmgc_exact::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_exact::ST_names[] = {"IncorrectlySplitSmallObject", NULL };
|
|
const bool ST_mmgc_exact::ST_explicits[] = {false, false };
|
|
void ST_mmgc_exact::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_exact::test0() {
|
|
// Create an object that stays alive so that its tracer will be called
|
|
MMgc::GCObjectLock* lock = core->gc->LockObject(Cthulhu::create(core->gc));
|
|
core->gc->Collect();
|
|
core->gc->Collect();
|
|
core->gc->UnlockObject(lock);
|
|
|
|
// line 42 "ST_mmgc_exact.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
|
|
|
|
}
|
|
void create_mmgc_exact(AvmCore* core) { new ST_mmgc_exact(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_externalalloc.st
|
|
// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_mmgc_externalalloc {
|
|
using namespace MMgc;
|
|
class MyCallback : public MMgc::OOMCallback
|
|
{
|
|
public:
|
|
MMgc::MemoryStatus status;
|
|
|
|
MyCallback()
|
|
{
|
|
status = MMgc::GCHeap::GetGCHeap()->GetStatus();
|
|
MMgc::GCHeap::GetGCHeap()->AddOOMCallback(this);
|
|
}
|
|
|
|
void memoryStatusChange(MMgc::MemoryStatus /*oldStatus*/, MMgc::MemoryStatus newStatus)
|
|
{
|
|
status = newStatus;
|
|
}
|
|
};
|
|
|
|
class ST_mmgc_externalalloc : public Selftest {
|
|
public:
|
|
ST_mmgc_externalalloc(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
virtual void epilogue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
private:
|
|
MyCallback *cb;
|
|
|
|
};
|
|
ST_mmgc_externalalloc::ST_mmgc_externalalloc(AvmCore* core)
|
|
: Selftest(core, "mmgc", "externalalloc", ST_mmgc_externalalloc::ST_names,ST_mmgc_externalalloc::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_externalalloc::ST_names[] = {"externalAllocation", NULL };
|
|
const bool ST_mmgc_externalalloc::ST_explicits[] = {false, false };
|
|
void ST_mmgc_externalalloc::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_externalalloc::prologue() {
|
|
cb = new MyCallback();
|
|
|
|
}
|
|
void ST_mmgc_externalalloc::epilogue() {
|
|
cb = NULL;
|
|
|
|
}
|
|
void ST_mmgc_externalalloc::test0() {
|
|
MMGC_GCENTER(core->gc);
|
|
size_t softlimit = GCHeap::GetGCHeap()->Config().heapSoftLimit;
|
|
// Remove the heapSoftLimit, this should ensure that we are not in a MMgc::kMemSoftLimit state,
|
|
// and hopefully we are in a kMemNormal state otherwise we are in a kMemAbort state and all bets are off.
|
|
GCHeap::GetGCHeap()->Config().heapSoftLimit = 0;
|
|
// line 45 "ST_mmgc_externalalloc.st"
|
|
verifyPass(cb->status==MMgc::kMemNormal, "cb->status==MMgc::kMemNormal", __FILE__, __LINE__);
|
|
GCHeap::GetGCHeap()->Config().heapSoftLimit = GCHeap::GetGCHeap()->GetTotalHeapSize();
|
|
MMgc::GCHeap::SignalExternalAllocation(1024*1024);
|
|
// line 48 "ST_mmgc_externalalloc.st"
|
|
verifyPass(cb->status==MMgc::kMemSoftLimit, "cb->status==MMgc::kMemSoftLimit", __FILE__, __LINE__);
|
|
MMgc::GCHeap::SignalExternalDeallocation(1024*1024);
|
|
// line 50 "ST_mmgc_externalalloc.st"
|
|
verifyPass(cb->status==MMgc::kMemNormal, "cb->status==MMgc::kMemNormal", __FILE__, __LINE__);
|
|
GCHeap::GetGCHeap()->Config().heapSoftLimit = softlimit;
|
|
softlimit = 0;
|
|
|
|
|
|
|
|
}
|
|
void create_mmgc_externalalloc(AvmCore* core) { new ST_mmgc_externalalloc(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_finalize_uninit.st
|
|
// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// 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/.
|
|
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
|
|
// Bugzilla 573737 - a throw from an argument to a constructor can
|
|
// cause an object to be allocated on the gc-heap before it has been
|
|
// fully initialized. (In particular, its vtable could be missing.)
|
|
// Since finalizers use the virtual destructor method, a missing
|
|
// vtable is a problem.
|
|
//
|
|
// Its a little tricky to test this because the "order of evaluation
|
|
// to an operator new() to get memory and the evaluation of arguments
|
|
// to constructors is undefined"; see details below.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_mmgc_finalize_uninit {
|
|
|
|
using namespace MMgc;
|
|
|
|
class D : public GCFinalizedObject
|
|
{
|
|
public:
|
|
D(int dummyArgument) { (void)dummyArgument; }
|
|
~D() { ++finalized; }
|
|
static int finalized_count() { return D::finalized; }
|
|
static void reset_finalized_count() { finalized = 0; }
|
|
// "usual" overload of placement new used in mmgc client code
|
|
void * operator new(size_t size, GC *gc) {
|
|
return GCFinalizedObject::operator new(size, gc);
|
|
}
|
|
// hack to explicitly order new-allocation + ctor-arg eval; see below
|
|
// (but reallys its just good ol' placement new!)
|
|
void * operator new(size_t size, GC *gc, void *raw) {
|
|
(void)size; (void)gc;
|
|
return raw;
|
|
}
|
|
private:
|
|
static int finalized;
|
|
};
|
|
|
|
/*static*/
|
|
int D::finalized;
|
|
|
|
int deathValue(AvmCore *core)
|
|
{
|
|
core->throwAtom(atomFromIntptrValue(1)); /* won't return */
|
|
|
|
return 0; /* silence compiler warnings */
|
|
}
|
|
|
|
// Test checks that finalizers themselves have not broken; here,
|
|
// constructor argument subexpression evaluates cleanly.
|
|
class ST_mmgc_finalize_uninit : public Selftest {
|
|
public:
|
|
ST_mmgc_finalize_uninit(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
void test3();
|
|
};
|
|
ST_mmgc_finalize_uninit::ST_mmgc_finalize_uninit(AvmCore* core)
|
|
: Selftest(core, "mmgc", "finalize_uninit", ST_mmgc_finalize_uninit::ST_names,ST_mmgc_finalize_uninit::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_finalize_uninit::ST_names[] = {"check_finalizers_still_work","original_death","desugared_check_finalizers_still_work","desugared_death", NULL };
|
|
const bool ST_mmgc_finalize_uninit::ST_explicits[] = {false,false,false,false, false };
|
|
void ST_mmgc_finalize_uninit::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
case 3: test3(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_finalize_uninit::test0() {
|
|
{
|
|
D::reset_finalized_count();
|
|
D* d;
|
|
int i;
|
|
|
|
// loop to alloc many (and subsequently reclaim >=expected percentage)
|
|
for (i = 0; i < 100; i++) {
|
|
d = new (core->gc) D(1);
|
|
}
|
|
(void) d;
|
|
|
|
core->gc->Collect(); // finish any prior incremental work ...
|
|
core->gc->Collect(); // ... and ensure we got fresh + complete gc.
|
|
|
|
// printf("D::finalized_count(): %d\n", D::finalized_count());
|
|
|
|
// line 75 "ST_mmgc_finalize_uninit.st"
|
|
verifyPass((D::finalized_count() > 90), "(D::finalized_count() > 90)", __FILE__, __LINE__);
|
|
}
|
|
|
|
// Test illustrates of the kind of code that exposed the original bug;
|
|
// here, constructor argument subexpression throws.
|
|
}
|
|
void ST_mmgc_finalize_uninit::test1() {
|
|
{
|
|
D* d;
|
|
volatile int i;
|
|
|
|
// Here, constructor argument subexpression throws
|
|
|
|
// Just one allocation attempt alone would risk false retention of
|
|
// intermediate values, so loop a bit to ensure that *some* D's,
|
|
// if allocated at all, will be considered garbage.
|
|
for (i = 0; i < 5; i++) {
|
|
TRY (core, kCatchAction_Ignore) {
|
|
d = new (core->gc) D(deathValue(core));
|
|
} CATCH (Exception *e) {
|
|
(void)e;
|
|
}
|
|
END_CATCH
|
|
END_TRY
|
|
}
|
|
(void) d;
|
|
|
|
// if things go badly, one of the collections below will segfault
|
|
// during finalization.
|
|
core->gc->Collect();
|
|
core->gc->Collect();
|
|
|
|
// (not dying is passing.)
|
|
// line 107 "ST_mmgc_finalize_uninit.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
}
|
|
|
|
// C++ standard says "The order of evaluation to an operator new() to
|
|
// get memory and the evaluation of arguments to constructors is
|
|
// undefined."
|
|
//
|
|
// Unfortunately, it is difficult to directly express the particular
|
|
// order of evaluation that exposes the bug in question, because
|
|
// allocation and construction are tied together.
|
|
//
|
|
// So, here we manually decompose the tests above to control
|
|
// evaluation order of operator new() and constructor arguments, to
|
|
// express suitable evil (namely: allocation; args eval; construction)
|
|
// for selftest.
|
|
//
|
|
// The desugaring iteself is:
|
|
// desugar[[ new (gc-exp) D(arg-exp) ]]
|
|
// ==> mem = D::operator new(sizeof(D), arg), new (gc-exp, mem) D(arg-exp)
|
|
|
|
// Test illustrates desugaring is "sound"; keep in sync with
|
|
// check_finalizers_still_work above.
|
|
}
|
|
void ST_mmgc_finalize_uninit::test2() {
|
|
{
|
|
D::reset_finalized_count();
|
|
D* d;
|
|
int i;
|
|
|
|
// loop to alloc many (and subsequently reclaim >=expected percentage)
|
|
for (i = 0; i < 100; i++) {
|
|
// d = new (core->gc) D(s);
|
|
void *mem = D::operator new(sizeof(D), core->gc);
|
|
d = new (core->gc, mem) D(1);
|
|
}
|
|
(void) d;
|
|
|
|
core->gc->Collect(); // finish any prior incremental work ...
|
|
core->gc->Collect(); // ... and ensure we got fresh + complete gc.
|
|
|
|
// printf("D::finalized_count(): %d\n", D::finalized_count());
|
|
|
|
// line 148 "ST_mmgc_finalize_uninit.st"
|
|
verifyPass((D::finalized_count() > 90), "(D::finalized_count() > 90)", __FILE__, __LINE__);
|
|
}
|
|
|
|
|
|
// Test forces evil order of evaluation via desugaring of
|
|
// construction; keep in sync with original_death above.
|
|
}
|
|
void ST_mmgc_finalize_uninit::test3() {
|
|
{
|
|
D* d;
|
|
volatile int i;
|
|
|
|
// Here, constructor argument subexpression throws
|
|
|
|
// Just one allocation attempt alone would risk false retention of
|
|
// intermediate values, so loop a bit to ensure that *some* D's,
|
|
// which are forcibly allocated here, will be considered garbage.
|
|
for (i = 0; i < 5; i++) {
|
|
TRY (core, kCatchAction_Ignore) {
|
|
// d = new (core->gc) D(deathValue());
|
|
void *mem = D::operator new(sizeof(D), core->gc);
|
|
d = new (core->gc, mem) D(deathValue(core));
|
|
} CATCH (Exception *e) {
|
|
(void)e;
|
|
}
|
|
END_CATCH
|
|
END_TRY
|
|
}
|
|
(void) d;
|
|
|
|
// if things go badly, one of the collections below will segfault
|
|
// during finalization.
|
|
core->gc->Collect();
|
|
core->gc->Collect();
|
|
|
|
// (not dying is passing.)
|
|
// line 183 "ST_mmgc_finalize_uninit.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
}
|
|
|
|
}
|
|
void create_mmgc_finalize_uninit(AvmCore* core) { new ST_mmgc_finalize_uninit(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_fixedmalloc_findbeginning.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
// Bugzilla 663508: Add FixedMalloc::FindBeginning
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_mmgc_fixedmalloc_findbeginning {
|
|
using namespace MMgc;
|
|
|
|
class ST_mmgc_fixedmalloc_findbeginning : public Selftest {
|
|
public:
|
|
ST_mmgc_fixedmalloc_findbeginning(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
void test3();
|
|
void test4();
|
|
void test5();
|
|
void test6();
|
|
void test7();
|
|
void test8();
|
|
void test9();
|
|
void test10();
|
|
void test11();
|
|
|
|
MMgc::FixedMalloc *fm;
|
|
|
|
// Allocates object of size sz and tests FixedMalloc::FindBeginning
|
|
// at various addresses within the allocated object. The sampled
|
|
// addresses are meant to cover the following interesting edge
|
|
// cases: at or near the object's start, the middle of the object,
|
|
// and at or near the object's end.
|
|
//
|
|
// If all the tests successively identify the object's start, then
|
|
// returns 0. Otherwise returns a numeric code identifying which
|
|
// tests failed.
|
|
int allocateVerifyAndFree(size_t sz)
|
|
{
|
|
int retval = 0;
|
|
(void)sz;
|
|
|
|
char *obj = (char*)fm->Alloc(sz);
|
|
|
|
retval |= checkLookups(obj, obj, sz);
|
|
retval = retval << 2;
|
|
if (sz > 1) retval |= checkLookups(obj+1, obj, sz);
|
|
retval = retval << 2;
|
|
if (sz > 3) retval |= checkLookups(obj+3, obj, sz);
|
|
retval = retval << 2;
|
|
retval |= checkLookups(obj+sz/2, obj, sz);
|
|
retval = retval << 2;
|
|
retval |= checkLookups(obj+sz-1, obj, sz);
|
|
retval = retval << 2;
|
|
|
|
fm->Free(obj);
|
|
|
|
return retval;
|
|
}
|
|
|
|
// Returns 0 if both test of FixedMalloc::FindBeginning and
|
|
// FixedMalloc::FindBeginningAndSize pass.
|
|
// Returns 1 if the first test (of FindBeginning) fails.
|
|
// Returns 2 if the second test (of FindBeginningAndSize) fails.
|
|
int checkLookups(const void* probe, const void* realStart, size_t sz)
|
|
{
|
|
int retval = 0;
|
|
const void* begin_recv;
|
|
size_t size_recv;
|
|
size_t roundup_actual_size;
|
|
|
|
if (sz <= (size_t)FixedMalloc::kLargestAlloc)
|
|
roundup_actual_size = fm->FindAllocatorForSize(sz)->GetItemSize();
|
|
else
|
|
roundup_actual_size =
|
|
roundUp(sz + DebugSize(), GCHeap::kBlockSize) - DebugSize();
|
|
|
|
if (fm->FindBeginning(probe) != realStart)
|
|
retval |= 1;
|
|
if (!fm->FindBeginningAndSize(probe, begin_recv, size_recv) ||
|
|
(begin_recv != realStart) ||
|
|
(size_recv != roundup_actual_size))
|
|
retval |= 3;
|
|
|
|
return retval;
|
|
}
|
|
|
|
size_t roundUp(size_t s, size_t inc) {
|
|
return (((s + inc - 1) / inc) * inc);
|
|
}
|
|
|
|
};
|
|
ST_mmgc_fixedmalloc_findbeginning::ST_mmgc_fixedmalloc_findbeginning(AvmCore* core)
|
|
: Selftest(core, "mmgc", "fixedmalloc_findbeginning", ST_mmgc_fixedmalloc_findbeginning::ST_names,ST_mmgc_fixedmalloc_findbeginning::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_fixedmalloc_findbeginning::ST_names[] = {"findbeginnings_small0","findbeginnings_small1","findbeginnings_small2","findbeginnings_small3","findbeginnings_almost_large1","findbeginnings_almost_large2","findbeginnings_large","findbeginnings_almost_multiblock1","findbeginnings_almost_multiblock2","findbeginnings_barely_multiblock","findbeginnings_multiblock_two","findbeginnings_multiblock_ten", NULL };
|
|
const bool ST_mmgc_fixedmalloc_findbeginning::ST_explicits[] = {false,false,false,false,false,false,false,false,false,false,false,false, false };
|
|
void ST_mmgc_fixedmalloc_findbeginning::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
case 3: test3(); return;
|
|
case 4: test4(); return;
|
|
case 5: test5(); return;
|
|
case 6: test6(); return;
|
|
case 7: test7(); return;
|
|
case 8: test8(); return;
|
|
case 9: test9(); return;
|
|
case 10: test10(); return;
|
|
case 11: test11(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_fixedmalloc_findbeginning::prologue() {
|
|
fm = MMgc::FixedMalloc::GetFixedMalloc();
|
|
|
|
}
|
|
|
|
void ST_mmgc_fixedmalloc_findbeginning::test0() {
|
|
{
|
|
size_t sz = FixedMalloc::kSizeClasses[0];
|
|
// line 91 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_fixedmalloc_findbeginning::test1() {
|
|
{
|
|
size_t sz = FixedMalloc::kSizeClasses[1];
|
|
// line 98 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_fixedmalloc_findbeginning::test2() {
|
|
{
|
|
size_t sz = FixedMalloc::kSizeClasses[2];
|
|
// line 105 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_fixedmalloc_findbeginning::test3() {
|
|
{
|
|
size_t sz = FixedMalloc::kSizeClasses[3];
|
|
// line 112 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_fixedmalloc_findbeginning::test4() {
|
|
{
|
|
size_t sz = FixedMalloc::kLargestAlloc - 1;
|
|
sz = sz - DebugSize();
|
|
// line 120 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
sz = sz + DebugSize();
|
|
// line 123 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_fixedmalloc_findbeginning::test5() {
|
|
{
|
|
size_t sz = FixedMalloc::kLargestAlloc;
|
|
sz = sz - DebugSize();
|
|
// line 131 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
sz = sz + DebugSize();
|
|
// line 134 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_fixedmalloc_findbeginning::test6() {
|
|
{
|
|
size_t sz = FixedMalloc::kLargestAlloc+1;
|
|
sz = sz - DebugSize();
|
|
// line 142 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
sz = sz + DebugSize();
|
|
// line 145 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_fixedmalloc_findbeginning::test7() {
|
|
{
|
|
size_t sz = GCHeap::kBlockSize-1;
|
|
sz = sz - DebugSize();
|
|
// line 153 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
sz = sz + DebugSize();
|
|
// line 156 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_fixedmalloc_findbeginning::test8() {
|
|
{
|
|
size_t sz = GCHeap::kBlockSize;
|
|
sz = sz - DebugSize();
|
|
// line 164 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
sz = sz + DebugSize();
|
|
// line 167 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_fixedmalloc_findbeginning::test9() {
|
|
{
|
|
size_t sz = GCHeap::kBlockSize+1;
|
|
sz = sz - DebugSize();
|
|
// line 175 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
sz = sz + DebugSize();
|
|
// line 178 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_fixedmalloc_findbeginning::test10() {
|
|
{
|
|
size_t sz = GCHeap::kBlockSize*2;
|
|
sz = sz - DebugSize();
|
|
// line 186 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
sz = sz + DebugSize();
|
|
// line 189 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_fixedmalloc_findbeginning::test11() {
|
|
{
|
|
size_t sz = GCHeap::kBlockSize*10;
|
|
sz = sz - DebugSize();
|
|
// line 197 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
sz = sz + DebugSize();
|
|
// line 200 "ST_mmgc_fixedmalloc_findbeginning.st"
|
|
verifyPass(allocateVerifyAndFree(sz) == 0, "allocateVerifyAndFree(sz) == 0", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void create_mmgc_fixedmalloc_findbeginning(AvmCore* core) { new ST_mmgc_fixedmalloc_findbeginning(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_gcheap.st
|
|
// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_mmgc_gcheap {
|
|
class ST_mmgc_gcheap : public Selftest {
|
|
public:
|
|
ST_mmgc_gcheap(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
};
|
|
ST_mmgc_gcheap::ST_mmgc_gcheap(AvmCore* core)
|
|
: Selftest(core, "mmgc", "gcheap", ST_mmgc_gcheap::ST_names,ST_mmgc_gcheap::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_gcheap::ST_names[] = {"largeAlloc","largeAllocAlignment", NULL };
|
|
const bool ST_mmgc_gcheap::ST_explicits[] = {false,false, false };
|
|
void ST_mmgc_gcheap::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
}
|
|
}
|
|
using namespace MMgc;
|
|
|
|
void ST_mmgc_gcheap::test0() {
|
|
GCHeap *heap = GCHeap::GetGCHeap();
|
|
for(int i=1;i<4;i++) {
|
|
void *item = heap->Alloc(GCHeap::kOSAllocThreshold*i);
|
|
// line 17 "ST_mmgc_gcheap.st"
|
|
verifyPass(heap->Size(item) == GCHeap::kOSAllocThreshold*i, "heap->Size(item) == GCHeap::kOSAllocThreshold*i", __FILE__, __LINE__);
|
|
heap->Free(item);
|
|
}
|
|
// line 20 "ST_mmgc_gcheap.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_mmgc_gcheap::test1() {
|
|
GCHeap *heap = GCHeap::GetGCHeap();
|
|
for(int i=1;i<10;i++) {
|
|
void *item = heap->Alloc(GCHeap::kOSAllocThreshold*i, GCHeap::flags_Alloc, 1<<i);
|
|
// line 26 "ST_mmgc_gcheap.st"
|
|
verifyPass(heap->Size(item) == GCHeap::kOSAllocThreshold*i, "heap->Size(item) == GCHeap::kOSAllocThreshold*i", __FILE__, __LINE__);
|
|
// line 27 "ST_mmgc_gcheap.st"
|
|
verifyPass(uintptr_t(((GCHeap::kBlockSize<<(i-1))-1) & uintptr_t(item)) == 0, "uintptr_t(((GCHeap::kBlockSize<<(i-1))-1) & uintptr_t(item)) == 0", __FILE__, __LINE__);
|
|
heap->Free(item);
|
|
}
|
|
|
|
|
|
}
|
|
void create_mmgc_gcheap(AvmCore* core) { new ST_mmgc_gcheap(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_gcoption.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_mmgc_gcoption {
|
|
using namespace MMgc;
|
|
|
|
class ST_mmgc_gcoption : public Selftest {
|
|
public:
|
|
ST_mmgc_gcoption(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
virtual void epilogue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
void test3();
|
|
void test4();
|
|
bool m_ret;
|
|
bool m_wrong;
|
|
GCHeap *m_heap;
|
|
GCHeapConfig m_config_orig;
|
|
bool isParamOption(const char* line) {
|
|
return m_heap->config.IsGCOptionWithParam(line);
|
|
}
|
|
bool notParamOption(const char* line) {
|
|
return !isParamOption(line);
|
|
}
|
|
bool approxEqual(double x, double y) {
|
|
double delta1 = x - y;
|
|
// abs() does not produce correct results at e.g. DBL_MAX.
|
|
double delta2 = (delta1 > 0) ? delta1 : -delta1;
|
|
return (delta2 < 0.0001);
|
|
}
|
|
void parseApply(const char* line, const char* optExtra=0)
|
|
{
|
|
m_ret = m_heap->config.ParseAndApplyOption(line, m_wrong, optExtra);
|
|
}
|
|
bool parsedCorrectly() {
|
|
return m_ret && !m_wrong;
|
|
}
|
|
bool gcoptionButIncorrectFormat() {
|
|
return m_ret && m_wrong;
|
|
}
|
|
|
|
void saveOrigHeapConfig() {
|
|
memcpy(&m_config_orig, &m_heap->config, sizeof(GCHeapConfig));
|
|
}
|
|
|
|
void restoreHeapConfig() {
|
|
memcpy(&m_heap->config, &m_config_orig, sizeof(GCHeapConfig));
|
|
}
|
|
|
|
bool configUnchanged() {
|
|
const char* c1 = (const char*)&m_heap->config;
|
|
const char* c2 = (const char*)&m_config_orig;
|
|
bool noChangeSeen = true;
|
|
for (size_t i=0; i < sizeof(GCHeapConfig); i++) {
|
|
if (*c1 != *c2)
|
|
noChangeSeen = false;
|
|
}
|
|
return noChangeSeen;
|
|
}
|
|
|
|
};
|
|
ST_mmgc_gcoption::ST_mmgc_gcoption(AvmCore* core)
|
|
: Selftest(core, "mmgc", "gcoption", ST_mmgc_gcoption::ST_names,ST_mmgc_gcoption::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_gcoption::ST_names[] = {"detect_parameterized_options","parse_memstats","parse_memlimit","parse_gcbehavior_gcsummary_eagersweep","parse_load_gcwork", NULL };
|
|
const bool ST_mmgc_gcoption::ST_explicits[] = {false,false,false,false,false, false };
|
|
void ST_mmgc_gcoption::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
case 3: test3(); return;
|
|
case 4: test4(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_gcoption::prologue() {
|
|
{
|
|
m_heap = GCHeap::GetGCHeap();
|
|
saveOrigHeapConfig();
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_gcoption::epilogue() {
|
|
{
|
|
restoreHeapConfig();
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_gcoption::test0() {
|
|
{
|
|
// line 75 "ST_mmgc_gcoption.st"
|
|
verifyPass(isParamOption("-memlimit"), "isParamOption(\"-memlimit\")", __FILE__, __LINE__);
|
|
// line 76 "ST_mmgc_gcoption.st"
|
|
verifyPass(isParamOption("-load"), "isParamOption(\"-load\")", __FILE__, __LINE__);
|
|
// line 77 "ST_mmgc_gcoption.st"
|
|
verifyPass(isParamOption("-loadCeiling"), "isParamOption(\"-loadCeiling\")", __FILE__, __LINE__);
|
|
// line 78 "ST_mmgc_gcoption.st"
|
|
verifyPass(isParamOption("-gcwork"), "isParamOption(\"-gcwork\")", __FILE__, __LINE__);
|
|
// line 79 "ST_mmgc_gcoption.st"
|
|
verifyPass(isParamOption("-gcstack"), "isParamOption(\"-gcstack\")", __FILE__, __LINE__);
|
|
|
|
// line 81 "ST_mmgc_gcoption.st"
|
|
verifyPass(notParamOption("-memlimit=10"), "notParamOption(\"-memlimit=10\")", __FILE__, __LINE__);
|
|
// line 82 "ST_mmgc_gcoption.st"
|
|
verifyPass(notParamOption("-load 1.5"), "notParamOption(\"-load 1.5\")", __FILE__, __LINE__);
|
|
// line 83 "ST_mmgc_gcoption.st"
|
|
verifyPass(notParamOption("-loadCeiling 1.5"), "notParamOption(\"-loadCeiling 1.5\")", __FILE__, __LINE__);
|
|
// line 84 "ST_mmgc_gcoption.st"
|
|
verifyPass(notParamOption("-gcwork 1.5"), "notParamOption(\"-gcwork 1.5\")", __FILE__, __LINE__);
|
|
// line 85 "ST_mmgc_gcoption.st"
|
|
verifyPass(notParamOption("-gcstack 10"), "notParamOption(\"-gcstack 10\")", __FILE__, __LINE__);
|
|
|
|
// line 87 "ST_mmgc_gcoption.st"
|
|
verifyPass(notParamOption("-not_an_option_and_never_will_be"), "notParamOption(\"-not_an_option_and_never_will_be\")", __FILE__, __LINE__);
|
|
// line 88 "ST_mmgc_gcoption.st"
|
|
verifyPass(notParamOption("-not_an_option_and_never_will_be 10"), "notParamOption(\"-not_an_option_and_never_will_be 10\")", __FILE__, __LINE__);
|
|
// line 89 "ST_mmgc_gcoption.st"
|
|
verifyPass(notParamOption("-not_an_option_and_never_will_be=10"), "notParamOption(\"-not_an_option_and_never_will_be=10\")", __FILE__, __LINE__);
|
|
;
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_gcoption::test1() {
|
|
{
|
|
// sanity checks:
|
|
// - make sure our configUnchanged check is sane
|
|
// - make sure our restoreHeapConfig works.
|
|
// line 98 "ST_mmgc_gcoption.st"
|
|
verifyPass(configUnchanged(), "configUnchanged()", __FILE__, __LINE__);
|
|
memset(&m_heap->config, 0xfe, sizeof(GCHeapConfig));
|
|
// line 100 "ST_mmgc_gcoption.st"
|
|
verifyPass(!configUnchanged(), "!configUnchanged()", __FILE__, __LINE__);
|
|
restoreHeapConfig();
|
|
// line 102 "ST_mmgc_gcoption.st"
|
|
verifyPass(configUnchanged(), "configUnchanged()", __FILE__, __LINE__);
|
|
;
|
|
|
|
parseApply("-memstats");
|
|
// line 106 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 107 "ST_mmgc_gcoption.st"
|
|
verifyPass(m_heap->config.gcstats && m_heap->config.autoGCStats, "m_heap->config.gcstats && m_heap->config.autoGCStats", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
parseApply("-memstats-verbose");
|
|
// line 112 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 113 "ST_mmgc_gcoption.st"
|
|
verifyPass(m_heap->config.gcstats && m_heap->config.autoGCStats && m_heap->config.verbose, "m_heap->config.gcstats && m_heap->config.autoGCStats && m_heap->config.verbose", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
}
|
|
}
|
|
void ST_mmgc_gcoption::test2() {
|
|
{
|
|
parseApply("-memlimit 10");
|
|
// line 120 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 121 "ST_mmgc_gcoption.st"
|
|
verifyPass(m_heap->config.heapLimit == 10, "m_heap->config.heapLimit == 10", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
parseApply("-memlimit=11");
|
|
// line 126 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 127 "ST_mmgc_gcoption.st"
|
|
verifyPass(m_heap->config.heapLimit == 11, "m_heap->config.heapLimit == 11", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
parseApply("-memlimit = 12");
|
|
// line 132 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 133 "ST_mmgc_gcoption.st"
|
|
verifyPass(m_heap->config.heapLimit == 12, "m_heap->config.heapLimit == 12", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
parseApply("-memlimit");
|
|
// line 138 "ST_mmgc_gcoption.st"
|
|
verifyPass(gcoptionButIncorrectFormat(), "gcoptionButIncorrectFormat()", __FILE__, __LINE__);
|
|
// line 139 "ST_mmgc_gcoption.st"
|
|
verifyPass(m_heap->config.heapLimit == m_config_orig.heapLimit, "m_heap->config.heapLimit == m_config_orig.heapLimit", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
parseApply("-memlimit", "13");
|
|
// line 144 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 145 "ST_mmgc_gcoption.st"
|
|
verifyPass(m_heap->config.heapLimit == 13, "m_heap->config.heapLimit == 13", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
}
|
|
}
|
|
void ST_mmgc_gcoption::test3() {
|
|
{
|
|
#ifdef MMGC_POLICY_PROFILING
|
|
parseApply("-gcbehavior");
|
|
// line 153 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 154 "ST_mmgc_gcoption.st"
|
|
verifyPass((m_heap->config.gcbehavior == 2), "(m_heap->config.gcbehavior == 2)", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
parseApply("-gcsummary");
|
|
// line 159 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 160 "ST_mmgc_gcoption.st"
|
|
verifyPass((m_heap->config.gcbehavior == 1), "(m_heap->config.gcbehavior == 1)", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
#endif
|
|
|
|
parseApply("-eagersweep");
|
|
// line 166 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 167 "ST_mmgc_gcoption.st"
|
|
verifyPass(m_heap->config.eagerSweeping, "m_heap->config.eagerSweeping", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
}
|
|
}
|
|
void ST_mmgc_gcoption::test4() {
|
|
{
|
|
parseApply("-load 7.0");
|
|
// line 174 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 175 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoad[0], 7.0), "approxEqual(m_heap->config.gcLoad[0], 7.0)", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
// test load with '<space><param>'
|
|
parseApply("-load 6.0,10,5.0");
|
|
// line 181 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 182 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoad[0], 6.0), "approxEqual(m_heap->config.gcLoad[0], 6.0)", __FILE__, __LINE__);
|
|
// line 183 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoadCutoff[0], 10.0), "approxEqual(m_heap->config.gcLoadCutoff[0], 10.0)", __FILE__, __LINE__);
|
|
// line 184 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoad[1], 5.0), "approxEqual(m_heap->config.gcLoad[1], 5.0)", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
// test load with separate <param>
|
|
parseApply("-load", "8.0,20.5,7.0");
|
|
// line 190 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 191 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoad[0], 8.0), "approxEqual(m_heap->config.gcLoad[0], 8.0)", __FILE__, __LINE__);
|
|
// line 192 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoadCutoff[0], 20.5), "approxEqual(m_heap->config.gcLoadCutoff[0], 20.5)", __FILE__, __LINE__);
|
|
// line 193 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoad[1], 7.0), "approxEqual(m_heap->config.gcLoad[1], 7.0)", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
// test load with '=<param>'
|
|
parseApply("-load=10.0,30.5,9.0");
|
|
// line 199 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 200 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoad[0], 10.0), "approxEqual(m_heap->config.gcLoad[0], 10.0)", __FILE__, __LINE__);
|
|
// line 201 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoadCutoff[0], 30.5), "approxEqual(m_heap->config.gcLoadCutoff[0], 30.5)", __FILE__, __LINE__);
|
|
// line 202 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoad[1], 9.0), "approxEqual(m_heap->config.gcLoad[1], 9.0)", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
// Max load pairs is 7
|
|
parseApply("-load 1.5,1.5,2,2,3,3,4,4,5,5,6,6,7,7,8,8");
|
|
// line 208 "ST_mmgc_gcoption.st"
|
|
verifyPass(gcoptionButIncorrectFormat(), "gcoptionButIncorrectFormat()", __FILE__, __LINE__);
|
|
// line 209 "ST_mmgc_gcoption.st"
|
|
verifyPass(configUnchanged(), "configUnchanged()", __FILE__, __LINE__);
|
|
restoreHeapConfig();
|
|
|
|
// Ensure that the last load value is ignored
|
|
parseApply("-load=10.0,30.0,9.0,60.0");
|
|
// line 214 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 215 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoad[0], 10.0), "approxEqual(m_heap->config.gcLoad[0], 10.0)", __FILE__, __LINE__);
|
|
// line 216 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoadCutoff[0], 30.0), "approxEqual(m_heap->config.gcLoadCutoff[0], 30.0)", __FILE__, __LINE__);
|
|
// line 217 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoad[1], 9.0), "approxEqual(m_heap->config.gcLoad[1], 9.0)", __FILE__, __LINE__);
|
|
// line 218 "ST_mmgc_gcoption.st"
|
|
verifyPass(!approxEqual(m_heap->config.gcLoadCutoff[1], 60.0), "!approxEqual(m_heap->config.gcLoadCutoff[1], 60.0)", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
parseApply("-load");
|
|
// line 223 "ST_mmgc_gcoption.st"
|
|
verifyPass(gcoptionButIncorrectFormat(), "gcoptionButIncorrectFormat()", __FILE__, __LINE__);
|
|
// line 224 "ST_mmgc_gcoption.st"
|
|
verifyPass(configUnchanged(), "configUnchanged()", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
// L (load) must be > 1
|
|
parseApply("-load 1,30");
|
|
// line 230 "ST_mmgc_gcoption.st"
|
|
verifyPass(gcoptionButIncorrectFormat(), "gcoptionButIncorrectFormat()", __FILE__, __LINE__);
|
|
// line 231 "ST_mmgc_gcoption.st"
|
|
verifyPass(configUnchanged(), "configUnchanged()", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
parseApply("-load badvalue");
|
|
// line 236 "ST_mmgc_gcoption.st"
|
|
verifyPass(gcoptionButIncorrectFormat(), "gcoptionButIncorrectFormat()", __FILE__, __LINE__);
|
|
// line 237 "ST_mmgc_gcoption.st"
|
|
verifyPass(configUnchanged(), "configUnchanged()", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
|
|
parseApply("-loadCeiling 11.5");
|
|
// line 243 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 244 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcLoadCeiling, 11.5), "approxEqual(m_heap->config.gcLoadCeiling, 11.5)", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
parseApply("-gcwork 12.5");
|
|
// line 249 "ST_mmgc_gcoption.st"
|
|
verifyPass(gcoptionButIncorrectFormat(), "gcoptionButIncorrectFormat()", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
parseApply("-gcwork 0.123456");
|
|
// line 254 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 255 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcEfficiency, .123456), "approxEqual(m_heap->config.gcEfficiency, .123456)", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
parseApply("-gcwork=0.23456");
|
|
// line 260 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 261 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcEfficiency, .23456), "approxEqual(m_heap->config.gcEfficiency, .23456)", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
parseApply("-gcwork", "0.3456");
|
|
// line 266 "ST_mmgc_gcoption.st"
|
|
verifyPass(parsedCorrectly(), "parsedCorrectly()", __FILE__, __LINE__);
|
|
// line 267 "ST_mmgc_gcoption.st"
|
|
verifyPass(approxEqual(m_heap->config.gcEfficiency, .3456), "approxEqual(m_heap->config.gcEfficiency, .3456)", __FILE__, __LINE__);
|
|
;
|
|
restoreHeapConfig();
|
|
|
|
}
|
|
|
|
}
|
|
void create_mmgc_gcoption(AvmCore* core) { new ST_mmgc_gcoption(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_mmfx_array.st
|
|
// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_mmgc_mmfx_array {
|
|
class ST_mmgc_mmfx_array : public Selftest {
|
|
public:
|
|
ST_mmgc_mmfx_array(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
};
|
|
ST_mmgc_mmfx_array::ST_mmgc_mmfx_array(AvmCore* core)
|
|
: Selftest(core, "mmgc", "mmfx_array", ST_mmgc_mmfx_array::ST_names,ST_mmgc_mmfx_array::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_mmfx_array::ST_names[] = {"mmfx_new_array_oom","mmfx_new_array_opts_canFail","mmfx_new_array_opts_oom", NULL };
|
|
const bool ST_mmgc_mmfx_array::ST_explicits[] = {true,true,true, false };
|
|
void ST_mmgc_mmfx_array::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
}
|
|
}
|
|
using namespace MMgc;
|
|
|
|
// There are different failure modes.
|
|
// 1) if you call mmfx_new_array() it can OOM:
|
|
// -> it should OOM and shutdown
|
|
// 2) if you call mmfx_new_array_opts(canFail) and it OOMs:
|
|
// -> it should return NULL and NOT OOM
|
|
// 3) if you call mmfx_new_aray_opts() without the canFail flag:
|
|
// -> it should OOM and shutdown
|
|
|
|
|
|
void ST_mmgc_mmfx_array::test0() {
|
|
// This will cause an OOM abort and exit, run with -memlimit 1024
|
|
int* m_ints = mmfx_new_array(int, 1048576);
|
|
mmfx_delete_array(m_ints);
|
|
m_ints = NULL;
|
|
// line 27 "ST_mmgc_mmfx_array.st"
|
|
verifyPass(false, "false", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_mmgc_mmfx_array::test1() {
|
|
// This should fail and return NULL, run with -memlimit 1024
|
|
int* m_ints = mmfx_new_array_opt(int, 1048576, MMgc::kCanFail);
|
|
// line 32 "ST_mmgc_mmfx_array.st"
|
|
verifyPass(m_ints == NULL, "m_ints == NULL", __FILE__, __LINE__);
|
|
mmfx_delete_array(m_ints);
|
|
m_ints = NULL;
|
|
|
|
}
|
|
void ST_mmgc_mmfx_array::test2() {
|
|
// This will cause an OOM abort and exit, run with -memlimit 1024
|
|
int* m_ints = mmfx_new_array_opt(int, 1048576, MMgc::kZero);
|
|
mmfx_delete_array(m_ints);
|
|
m_ints = NULL;
|
|
// line 41 "ST_mmgc_mmfx_array.st"
|
|
verifyPass(false, "false", __FILE__, __LINE__);
|
|
|
|
|
|
|
|
}
|
|
void create_mmgc_mmfx_array(AvmCore* core) { new ST_mmgc_mmfx_array(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_threads.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
#if defined VMCFG_WORKERTHREADS
|
|
namespace avmplus {
|
|
namespace ST_mmgc_threads {
|
|
class ST_mmgc_threads : public Selftest {
|
|
public:
|
|
ST_mmgc_threads(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
virtual void epilogue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
|
|
private:
|
|
MMgc::GC *gc;
|
|
MMgc::FixedAlloc *fa;
|
|
MMgc::FixedMalloc *fm;
|
|
bool waiting;
|
|
bool result;
|
|
bool isDead;
|
|
pthread_t pthread;
|
|
pthread_mutex_t pmutex;
|
|
pthread_cond_t pcond;
|
|
|
|
static void* slaveRunner(void *arg)
|
|
{
|
|
((ST_mmgc_threads*)arg)->slaveRun();
|
|
return NULL;
|
|
}
|
|
|
|
void slaveRun()
|
|
{
|
|
wait();
|
|
{
|
|
MMGC_ENTER_VOID;
|
|
MMGC_GCENTER(gc);
|
|
result &= !isDead;
|
|
gc->ReapZCT();
|
|
result &= !isDead;
|
|
gc->Collect();
|
|
result &= !isDead;
|
|
}
|
|
kick();
|
|
}
|
|
|
|
void startSlave()
|
|
{
|
|
pthread_create(&pthread, NULL, slaveRunner, this);
|
|
}
|
|
|
|
void kick()
|
|
{
|
|
pthread_mutex_lock (&pmutex);
|
|
while(!waiting) {
|
|
pthread_mutex_unlock (&pmutex);
|
|
usleep(100);
|
|
pthread_mutex_lock (&pmutex);
|
|
}
|
|
pthread_cond_signal (&pcond);
|
|
while(waiting) {
|
|
pthread_mutex_unlock (&pmutex);
|
|
usleep(100);
|
|
pthread_mutex_lock (&pmutex);
|
|
}
|
|
pthread_mutex_unlock (&pmutex);
|
|
}
|
|
|
|
void wait()
|
|
{
|
|
pthread_mutex_lock (&pmutex);
|
|
GCAssert(waiting == false);
|
|
waiting = true;
|
|
pthread_cond_wait (&pcond, &pmutex);
|
|
waiting = false;
|
|
pthread_mutex_unlock (&pmutex);
|
|
}
|
|
|
|
static void kickAndWait(void* arg)
|
|
{
|
|
ST_mmgc_threads* self = (ST_mmgc_threads*)arg;
|
|
self->kick();
|
|
self->wait();
|
|
}
|
|
|
|
};
|
|
ST_mmgc_threads::ST_mmgc_threads(AvmCore* core)
|
|
: Selftest(core, "mmgc", "threads", ST_mmgc_threads::ST_names,ST_mmgc_threads::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_threads::ST_names[] = {"mmgc_gc_root_thread", NULL };
|
|
const bool ST_mmgc_threads::ST_explicits[] = {false, false };
|
|
void ST_mmgc_threads::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_threads::prologue() {
|
|
MMgc::GCConfig config;
|
|
gc=new MMgc::GC(MMgc::GCHeap::GetGCHeap(), config);
|
|
if (gc==NULL) {
|
|
MMgc::GCHeap::Init();
|
|
gc=new MMgc::GC(MMgc::GCHeap::GetGCHeap(), config);
|
|
}
|
|
pthread_mutex_init(&pmutex, NULL);
|
|
pthread_cond_init(&pcond, NULL);
|
|
result = true;
|
|
isDead = false;
|
|
waiting = false;
|
|
|
|
}
|
|
void ST_mmgc_threads::epilogue() {
|
|
pthread_mutex_destroy(&pmutex);
|
|
pthread_cond_destroy(&pcond);
|
|
delete gc;
|
|
|
|
}
|
|
using namespace MMgc;
|
|
|
|
class RCObjectNotifier : public RCObject
|
|
{
|
|
public:
|
|
RCObjectNotifier(bool *isDead) : isDead(isDead) {}
|
|
~RCObjectNotifier() { *isDead = true; isDead = NULL; }
|
|
bool *isDead;
|
|
};
|
|
|
|
void ST_mmgc_threads::test0() {
|
|
startSlave();
|
|
MMGC_GCENTER(gc);
|
|
RCObjectNotifier *obj = new (gc) RCObjectNotifier(&isDead);
|
|
{
|
|
gc->CreateRootFromCurrentStack(kickAndWait, this);
|
|
}
|
|
|
|
// line 122 "ST_mmgc_threads.st"
|
|
verifyPass(result, "result", __FILE__, __LINE__);
|
|
|
|
// line 124 "ST_mmgc_threads.st"
|
|
verifyPass(!isDead, "!isDead", __FILE__, __LINE__);
|
|
gc->ReapZCT();
|
|
// line 126 "ST_mmgc_threads.st"
|
|
verifyPass(!isDead, "!isDead", __FILE__, __LINE__);
|
|
gc->Collect();
|
|
// line 128 "ST_mmgc_threads.st"
|
|
verifyPass(!isDead, "!isDead", __FILE__, __LINE__);
|
|
|
|
pthread_join(pthread, NULL);
|
|
|
|
printf("Ignore this: %d\n", *obj->isDead);
|
|
|
|
}
|
|
void create_mmgc_threads(AvmCore* core) { new ST_mmgc_threads(core); }
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Generated from ST_mmgc_weakref.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_mmgc_weakref {
|
|
using namespace MMgc;
|
|
|
|
class C : public GCFinalizedObject
|
|
{
|
|
public:
|
|
C(int key) : key(key) {}
|
|
~C() { key = -1; }
|
|
int key;
|
|
GCMember<C> next;
|
|
};
|
|
|
|
class MyCallback : public GCCallback
|
|
{
|
|
public:
|
|
MyCallback(GC* gc, GCWeakRef** refs, C** objs, int n)
|
|
: GCCallback(gc)
|
|
, refs(refs)
|
|
, objs(objs)
|
|
, n(n)
|
|
{
|
|
}
|
|
|
|
virtual void presweep() {
|
|
for ( int i=1 ; i < 1000 ; i+=2 )
|
|
objs[i]->next = (C*)(void *)(refs[i-1]->get());
|
|
}
|
|
|
|
private:
|
|
GCWeakRef** refs;
|
|
C** objs;
|
|
int n;
|
|
};
|
|
|
|
class D : public GCFinalizedObject
|
|
{
|
|
public:
|
|
~D() { GC::GetWeakRef(this); }
|
|
};
|
|
|
|
class ST_mmgc_weakref : public Selftest {
|
|
public:
|
|
ST_mmgc_weakref(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
};
|
|
ST_mmgc_weakref::ST_mmgc_weakref(AvmCore* core)
|
|
: Selftest(core, "mmgc", "weakref", ST_mmgc_weakref::ST_names,ST_mmgc_weakref::ST_explicits)
|
|
{}
|
|
const char* ST_mmgc_weakref::ST_names[] = {"unmarked_object_presweep","unmarked_object_finalize", NULL };
|
|
const bool ST_mmgc_weakref::ST_explicits[] = {false,true, false };
|
|
void ST_mmgc_weakref::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
}
|
|
}
|
|
void ST_mmgc_weakref::test0() {
|
|
|
|
// Bugzilla 572331 - it's possible for a weak reference to yield up a pointer to an unmarked
|
|
// object in a presweep handler, and for that pointer to be stored into the heap even with
|
|
// a WB macro, without the barrier triggering.
|
|
|
|
{
|
|
GC* gc = core->gc;
|
|
|
|
// Stack allocated storage, so automatically these arrays are roots
|
|
|
|
GCWeakRef* refs[1000];
|
|
C* objs[1000];
|
|
|
|
// Create weak and strong references to 1000 objects
|
|
|
|
for ( int i=0 ; i < 1000 ; i++ ) {
|
|
objs[i] = new (gc) C(i);
|
|
refs[i] = objs[i]->GetWeakRef();
|
|
}
|
|
|
|
// Get the collector into a reasonable state.
|
|
|
|
gc->Collect();
|
|
gc->Collect();
|
|
|
|
// Remove the even-numbered strong refs.
|
|
|
|
for ( int i=0 ; i < 1000 ; i+=2 )
|
|
objs[i] = NULL;
|
|
|
|
// Introduce mischief. The presweep callback will extract
|
|
// pointers from the even-numbered weak refs and insert them into
|
|
// the objects in the odd-numbered slots (which are strongly
|
|
// held). Most of the objects referenced from the even-numbered
|
|
// weak refs should be weakly held and unmarked. We hope to catch
|
|
// the collector picking up an unmarked object.
|
|
|
|
MyCallback* cb = new MyCallback(gc, refs, objs, 1000);
|
|
|
|
// Now trigger the collector again.
|
|
|
|
gc->Collect();
|
|
|
|
// Prevent more mischief from happening.
|
|
|
|
delete cb;
|
|
|
|
// Now reference all the odd-numbered objects and check the
|
|
// integrity of their 'next' objects. There's a chance this may
|
|
// crash.
|
|
|
|
for ( int i=1 ; i < 1000 ; i+= 2 ) {
|
|
// line 104 "ST_mmgc_weakref.st"
|
|
verifyPass(objs[i]->next->key == i-1, "objs[i]->next->key == i-1", __FILE__, __LINE__);
|
|
}
|
|
|
|
// Clean up
|
|
VMPI_memset(refs, 0, sizeof(refs));
|
|
VMPI_memset(objs, 0, sizeof(objs));
|
|
}
|
|
|
|
}
|
|
void ST_mmgc_weakref::test1() {
|
|
|
|
// Bugzilla 647155 - ditto as the previous test, but now it's the object's destructor that tries
|
|
// to store a pointer to an unmarked object (in this case the object itself) into a weak ref.
|
|
// This will assert in debug builds, so the test is marked "explicit" for that reason: in a
|
|
// debug build we want to verify that the assert is hit, in a release build we want to verify that
|
|
// the test does not crash.
|
|
|
|
{
|
|
GC* gc = core->gc;
|
|
|
|
// Stack allocated storage, so automatically these arrays are roots
|
|
|
|
D* objs[1000];
|
|
|
|
// Create strong references to 1000 objects
|
|
|
|
for ( int i=0 ; i < 1000 ; i++ ) {
|
|
objs[i] = new (gc) D();
|
|
}
|
|
|
|
// Get the collector into a reasonable state.
|
|
|
|
gc->Collect();
|
|
gc->Collect();
|
|
|
|
// Remove the even-numbered strong refs.
|
|
|
|
for ( int i=0 ; i < 1000 ; i+=2 )
|
|
objs[i] = NULL;
|
|
|
|
// Now trigger the collector again.
|
|
|
|
gc->Collect();
|
|
|
|
// line 147 "ST_mmgc_weakref.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
}
|
|
|
|
}
|
|
void create_mmgc_weakref(AvmCore* core) { new ST_mmgc_weakref(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_nanojit_codealloc.st
|
|
// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5)
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_nanojit_codealloc {
|
|
|
|
// This conditionalization is clumsy, but we don't seem to have any other
|
|
// mechanism to enable a selftest conditionally upon the configuration.
|
|
|
|
#ifdef VMCFG_NANOJIT
|
|
|
|
using nanojit::NIns;
|
|
using nanojit::CodeList;
|
|
using nanojit::CodeAlloc;
|
|
|
|
static bool verbose = false;
|
|
|
|
class Randomizer {
|
|
private:
|
|
|
|
TRandomFast seed;
|
|
|
|
public:
|
|
|
|
Randomizer()
|
|
{
|
|
MathUtils::initRandom(&seed);
|
|
}
|
|
|
|
// Return integer in range [min,max).
|
|
|
|
uint32_t range(uint32_t min, uint32_t max)
|
|
{
|
|
return min + MathUtils::Random(max - min, &seed);
|
|
}
|
|
};
|
|
|
|
class CodeAllocDriver {
|
|
public:
|
|
|
|
struct CodeListProfile {
|
|
uint32_t largefreq; // 1 in 'largefreq' chance codelist will be "large"
|
|
uint32_t smallmin; // Minimum size of "small" codelist
|
|
uint32_t smallmax; // Maximum size of "small" codelist
|
|
uint32_t largemin; // Minimum size of "large" codelist
|
|
uint32_t largemax; // Maximum size of "large" coelist
|
|
};
|
|
|
|
struct AllocationProfile {
|
|
uint32_t limit; // Largest block allocation permitted, zero for no limit
|
|
uint32_t leftfreq; // 1 in 'leftfreq' chance returned space will start at beginning of block
|
|
};
|
|
|
|
CodeAllocDriver(uint32_t max_codelists, CodeListProfile& codelist_profile, AllocationProfile& alloc_profile);
|
|
|
|
void run(uint32_t iterations);
|
|
|
|
private:
|
|
|
|
Randomizer rand;
|
|
uint32_t n_active; // Maximum number of simultaneously-allocated codelists
|
|
CodeListProfile cp; // Parameters controlling distribution of codelist sizes
|
|
AllocationProfile ap; // Parameters controlling allocation of each codelist
|
|
|
|
void makeCodeList(uint32_t index, CodeAlloc& alloc, CodeList*& code);
|
|
};
|
|
|
|
CodeAllocDriver::CodeAllocDriver(uint32_t max_codelists, CodeListProfile& codelist_profile, AllocationProfile& alloc_profile)
|
|
: n_active(max_codelists), cp(codelist_profile), ap(alloc_profile)
|
|
{}
|
|
|
|
// Create a codelist at the specified index in the codelist pool.
|
|
// The length of the codelist is generated pseudo-randomly.
|
|
|
|
void CodeAllocDriver::makeCodeList(uint32_t index, CodeAlloc& alloc, CodeList*& code)
|
|
{
|
|
NIns* start;
|
|
NIns* end;
|
|
|
|
int32_t size = ((rand.range(0, cp.largefreq) > 0)
|
|
? rand.range(cp.smallmin, cp.smallmax)
|
|
: rand.range(cp.largemin, cp.largemax));
|
|
if (verbose)
|
|
AvmLog("code-heap-test: codelist %d size %d\n", index, size);
|
|
while (size > 0) {
|
|
alloc.alloc(start, end, ap.limit);
|
|
uint32_t blksize = uint32_t(end - start);
|
|
if (verbose)
|
|
AvmLog("code-heap-test: alloc %x-%x %d\n", start, start, blksize);
|
|
size -= blksize;
|
|
if (size < 0) {
|
|
// Last allocated block was not completely used, so we will return some of it.
|
|
// We choose randomly whether to return a prefix of the block, or a segment out
|
|
// of the middle. This doesn't correspond precisely to normal usage in nanojit.
|
|
// Note that if we are not retaining at least two bytes, we cannot split in the middle.
|
|
int32_t excess = -size;
|
|
if (rand.range(0, ap.leftfreq) > 0 || blksize - excess < 2) {
|
|
// Hole starts at left
|
|
// Note that hole at right is deliberately not handled in CodegenAlloc.cpp, results in assert.
|
|
if (verbose)
|
|
AvmLog("code-heap-test: hole l %x-%x %d\n", start, start + excess, excess);
|
|
alloc.addRemainder(code, start, end, start, start + excess);
|
|
} else {
|
|
// Hole will go in middle
|
|
uint32_t offset = rand.range(1, blksize - excess);
|
|
if (verbose)
|
|
AvmLog("code-heap-test: hole m %x-%x %d\n", start + offset, start + offset + excess, excess);
|
|
alloc.addRemainder(code, start, end, start + offset, start + offset + excess);
|
|
}
|
|
} else {
|
|
// Add entire block to codelist.
|
|
CodeAlloc::add(code, start, end);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Repeatedly construct and destroy codelists.
|
|
// We maintain a pool of codelists. On each iteration, we replace one
|
|
// of the existing codelists with a new one, freeing the old.
|
|
|
|
void CodeAllocDriver::run(uint32_t iterations)
|
|
{
|
|
nanojit::Config config;
|
|
// Enable page protection flag checking during test code
|
|
config.check_page_flags = true;
|
|
|
|
CodeAlloc alloc(&config);
|
|
|
|
CodeList** codelists = mmfx_new_array(CodeList*, n_active);
|
|
|
|
VMPI_memset(codelists, 0, n_active * sizeof(CodeList*));
|
|
|
|
for (uint32_t i = 0; i < iterations; i++) {
|
|
uint32_t victim = rand.range(0, n_active);
|
|
alloc.freeAll(codelists[victim]);
|
|
makeCodeList(victim, alloc, codelists[victim]);
|
|
//alloc.logStats();
|
|
debug_only( alloc.sanity_check(); )
|
|
}
|
|
|
|
mmfx_delete_array(codelists);
|
|
}
|
|
|
|
#endif /* VMCFG_NANOJIT */
|
|
|
|
class ST_nanojit_codealloc : public Selftest {
|
|
public:
|
|
ST_nanojit_codealloc(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
};
|
|
ST_nanojit_codealloc::ST_nanojit_codealloc(AvmCore* core)
|
|
: Selftest(core, "nanojit", "codealloc", ST_nanojit_codealloc::ST_names,ST_nanojit_codealloc::ST_explicits)
|
|
{}
|
|
const char* ST_nanojit_codealloc::ST_names[] = {"allocfree", NULL };
|
|
const bool ST_nanojit_codealloc::ST_explicits[] = {false, false };
|
|
void ST_nanojit_codealloc::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
}
|
|
}
|
|
|
|
#ifdef VMCFG_NANOJIT
|
|
|
|
typedef CodeAllocDriver::CodeListProfile CodeListProfile;
|
|
typedef CodeAllocDriver::AllocationProfile AllocationProfile;
|
|
|
|
static CodeListProfile cp = { 5, 1, 1*1024, 1, 16*1024 };
|
|
|
|
static AllocationProfile ap[] = { { 0, 2 },
|
|
#ifndef NANOJIT_64BIT
|
|
// 128-byte minimum is too small on 64-bit platorms.
|
|
{ 128, 2 },
|
|
#endif
|
|
{ 512, 2 },
|
|
{ 1024, 2 },
|
|
{ 2048, 2 },
|
|
{ 4096, 2 } };
|
|
|
|
static uint32_t n_ap = sizeof(ap) / sizeof(AllocationProfile);
|
|
|
|
#endif /* VMCFG_NANOJIT */
|
|
|
|
void ST_nanojit_codealloc::test0() {
|
|
|
|
#ifdef VMCFG_NANOJIT
|
|
|
|
for (uint32_t i = 0; i < n_ap; i++) {
|
|
CodeAllocDriver* driver = mmfx_new(CodeAllocDriver(20, cp, ap[i]));
|
|
#if defined(DEBUG) && !(defined(VMCFG_IA32) || defined(VMCFG_AMD64))
|
|
// This test is very slow in debug builds, due to the calls to sanity_check().
|
|
// Run an abbreviated version of the test except on desktop platforms.
|
|
driver->run(200);
|
|
#else
|
|
driver->run(20000);
|
|
#endif
|
|
mmfx_delete(driver);
|
|
}
|
|
|
|
#endif /* VMCFG_NANOJIT */
|
|
|
|
// We pass if we don't crash or assert.
|
|
// line 194 "ST_nanojit_codealloc.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
|
|
|
|
}
|
|
void create_nanojit_codealloc(AvmCore* core) { new ST_nanojit_codealloc(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_vmbase_concurrency.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_vmbase_concurrency {
|
|
using namespace MMgc;
|
|
using namespace vmbase;
|
|
|
|
// We use the same testing method as that for ST_vmpi_threads:
|
|
// Each construct is tested by (1) using it in the implementation of a mutator that
|
|
// modifies a counter for a fixed number of iterations, and then (2) running
|
|
// duplicates of that mutator in parallel. The final counter value ends up in
|
|
// sharedCounter, which is guarded by m_monitor (except for in CASTest).
|
|
// Each test checks that the sharedCounter ends up with a statically determined
|
|
// end value.
|
|
// (Description courtesy of Felix)
|
|
|
|
// We need a specific namespace as ST_vmpi_threads uses the same test class names
|
|
namespace selftestconcurrency {
|
|
|
|
class ThreadTestBase : public Runnable {
|
|
public:
|
|
ThreadTestBase(int iterations) : m_iterations(iterations), sharedCounter(0) {
|
|
}
|
|
virtual ~ThreadTestBase() {
|
|
}
|
|
|
|
protected:
|
|
WaitNotifyMonitor m_monitor;
|
|
const int m_iterations;
|
|
|
|
public:
|
|
int sharedCounter;
|
|
};
|
|
|
|
class TestRunner {
|
|
public:
|
|
struct ThreadRecord {
|
|
VMThread* thread;
|
|
bool startupOk;
|
|
};
|
|
|
|
TestRunner(int threadQty, bool doJoin = true) : m_threadQty(threadQty), m_doJoin(doJoin) {
|
|
}
|
|
virtual ~TestRunner() {
|
|
}
|
|
|
|
void runTest(ThreadTestBase& test) {
|
|
|
|
m_threads = mmfx_new_array(ThreadRecord, m_threadQty);
|
|
|
|
// Start up the threads
|
|
for (int i = 0; i < m_threadQty; i++) {
|
|
m_threads[i].thread = mmfx_new(VMThread(&test));
|
|
m_threads[i].startupOk = m_threads[i].thread->start();
|
|
}
|
|
|
|
// ...then block until they all terminate
|
|
for (int i = 0; i < m_threadQty; i++) {
|
|
if (m_doJoin && m_threads[i].startupOk) {
|
|
m_threads[i].thread->join();
|
|
}
|
|
mmfx_delete(m_threads[i].thread);
|
|
}
|
|
|
|
mmfx_delete_array(m_threads);
|
|
}
|
|
|
|
private:
|
|
const int m_threadQty;
|
|
ThreadRecord* m_threads;
|
|
bool m_doJoin;
|
|
};
|
|
|
|
class MutexTest : public ThreadTestBase {
|
|
public:
|
|
MutexTest(int iterations) : ThreadTestBase(iterations) {}
|
|
virtual ~MutexTest() {}
|
|
|
|
virtual void run() {
|
|
AvmAssert(m_iterations % 2 == 0);
|
|
for (int i = 0; i < m_iterations/2; i++) {
|
|
SCOPE_LOCK(m_monitor) {
|
|
SCOPE_LOCK(m_monitor) {
|
|
sharedCounter++;
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < m_iterations/2; i++) {
|
|
SCOPE_LOCK_NAMED(locker, m_monitor) {
|
|
SCOPE_LOCK_NAMED(locker, m_monitor) {
|
|
sharedCounter++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
class ConditionTest : public ThreadTestBase {
|
|
public:
|
|
ConditionTest(int iterations, int threadQty) : ThreadTestBase(iterations), m_threadQty(threadQty) {}
|
|
virtual ~ConditionTest() {}
|
|
|
|
virtual void run() {
|
|
|
|
AvmAssert(m_threadQty >= 2);
|
|
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
SCOPE_LOCK_NAMED(locker, m_monitor) {
|
|
sharedCounter++;
|
|
// If there's another thread still active then wait.
|
|
if (m_threadQty > 1) {
|
|
locker.notify();
|
|
locker.wait();
|
|
}
|
|
// This thread has finished, so let's wake everyone else up
|
|
if (i == m_iterations - 1) {
|
|
--m_threadQty;
|
|
locker.notifyAll();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private:
|
|
int m_threadQty;
|
|
};
|
|
|
|
class AtomicCounterTest : public ThreadTestBase {
|
|
public:
|
|
AtomicCounterTest(int iterations, int threadQty) : ThreadTestBase(iterations), m_threadQty(threadQty) {}
|
|
virtual ~AtomicCounterTest() {}
|
|
|
|
virtual void run() {
|
|
|
|
AvmAssert(m_iterations % 4 == 0);
|
|
|
|
for (int i = 0; i < m_iterations/4; i++) {
|
|
m_counter++;
|
|
}
|
|
for (int i = 0; i < m_iterations/4; i++) {
|
|
m_counter--;
|
|
}
|
|
for (int i = 0; i < m_iterations/4; i++) {
|
|
++m_counter;
|
|
}
|
|
for (int i = 0; i < m_iterations/4; i++) {
|
|
--m_counter;
|
|
}
|
|
|
|
SCOPE_LOCK(m_monitor) {
|
|
if (--m_threadQty == 0) {
|
|
sharedCounter = m_counter;
|
|
}
|
|
}
|
|
}
|
|
private:
|
|
AtomicCounter32 m_counter;
|
|
int m_threadQty;
|
|
};
|
|
|
|
class CASTest : public ThreadTestBase {
|
|
public:
|
|
CASTest(int iterations, bool withBarrier) : ThreadTestBase(iterations), m_withBarrier(withBarrier) {}
|
|
virtual ~CASTest() {}
|
|
|
|
virtual void run() {
|
|
if (m_withBarrier) {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
int32_t current, next;
|
|
do {
|
|
current = sharedCounter;
|
|
next = current + 1;
|
|
} while (!AtomicOps::compareAndSwap32WithBarrier(current, next, &sharedCounter));
|
|
}
|
|
} else {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
int32_t current, next;
|
|
do {
|
|
current = sharedCounter;
|
|
next = current + 1;
|
|
} while (!AtomicOps::compareAndSwap32(current, next, &sharedCounter));
|
|
}
|
|
}
|
|
}
|
|
private:
|
|
bool m_withBarrier;
|
|
};
|
|
|
|
/**
|
|
* We protect a shared counter with a Dekker-style lock that has been made
|
|
* sequentially consistent with memory barriers.
|
|
*
|
|
* The idea is that if the barriers are correct, then two threads can compete
|
|
* to update the counter n times each, so that the final counter value is 2n. If
|
|
* the final value is not 2n, then the barriers have failed to ensure sequential
|
|
* consistency.
|
|
*
|
|
* FIXME: bug 609943
|
|
* This seems way too complicated. We have to be confident in the algorithm
|
|
* before considering the barrier implementations, and I'm not convinced as yet.
|
|
* Is there something simpler?
|
|
* Note that the barriers below are extremely conservative.
|
|
*
|
|
* This is test is not actually run. The verifyPass below just returns true.
|
|
*/
|
|
class MemoryBarrierTest : public ThreadTestBase {
|
|
public:
|
|
MemoryBarrierTest(int iterations) : ThreadTestBase(iterations), m_thread0(0), m_thread1(0), m_turn(NULL) {}
|
|
virtual ~MemoryBarrierTest() {}
|
|
|
|
virtual void run() {
|
|
|
|
volatile int* me;
|
|
volatile int* other;
|
|
volatile int* const counterp = &sharedCounter;
|
|
|
|
SCOPE_LOCK(m_monitor) {
|
|
if (m_turn == NULL) {
|
|
me = &m_thread0;
|
|
other = &m_thread1;
|
|
m_turn = me;
|
|
} else {
|
|
me = &m_thread1;
|
|
other = &m_thread0;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
// Dekker lock
|
|
*me = 1;
|
|
MemoryBarrier::readWrite();
|
|
while (*other == 1) {
|
|
MemoryBarrier::readWrite();
|
|
if (m_turn == other) {
|
|
MemoryBarrier::readWrite();
|
|
*me = 0;
|
|
MemoryBarrier::readWrite();
|
|
while (m_turn == other) {
|
|
MemoryBarrier::readWrite();
|
|
}
|
|
MemoryBarrier::readWrite();
|
|
*me = 1;
|
|
MemoryBarrier::readWrite();
|
|
}
|
|
}
|
|
MemoryBarrier::readWrite();
|
|
(*counterp)++;
|
|
MemoryBarrier::readWrite();
|
|
m_turn = other;
|
|
MemoryBarrier::readWrite();
|
|
*me = 0;
|
|
MemoryBarrier::readWrite();
|
|
}
|
|
}
|
|
private:
|
|
volatile int m_thread0;
|
|
volatile int m_thread1;
|
|
volatile int* volatile m_turn;
|
|
};
|
|
|
|
class ConditionWithWaitTest : public ThreadTestBase {
|
|
public:
|
|
ConditionWithWaitTest(int iterations) : ThreadTestBase(iterations) {}
|
|
virtual ~ConditionWithWaitTest() {}
|
|
|
|
virtual void run() {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
SCOPE_LOCK_NAMED(locker, m_monitor) {
|
|
sharedCounter++;
|
|
locker.wait(1);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class SleepTest : public ThreadTestBase {
|
|
public:
|
|
SleepTest(int iterations) : ThreadTestBase(iterations) {}
|
|
virtual ~SleepTest() {}
|
|
|
|
virtual void run() {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
SCOPE_LOCK(m_monitor) {
|
|
sharedCounter++;
|
|
}
|
|
VMThread::sleep(1);
|
|
}
|
|
}
|
|
};
|
|
|
|
class VMThreadLocalTest : public ThreadTestBase {
|
|
public:
|
|
VMThreadLocalTest(int iterations) : ThreadTestBase(iterations) {}
|
|
virtual ~VMThreadLocalTest() {}
|
|
|
|
virtual void run() {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
m_localCounter.set(m_localCounter.get() + 1);
|
|
}
|
|
SCOPE_LOCK(m_monitor) {
|
|
sharedCounter += (int)m_localCounter;
|
|
}
|
|
}
|
|
private:
|
|
VMThreadLocal<uintptr_t> m_localCounter;
|
|
};
|
|
}
|
|
|
|
// This needs to be at least 2 for ConditionTest
|
|
#define THREAD_QTY 4
|
|
#define ITERATIONS 100000
|
|
|
|
using namespace selftestconcurrency;
|
|
|
|
class ST_vmbase_concurrency : public Selftest {
|
|
public:
|
|
ST_vmbase_concurrency(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
void test3();
|
|
void test4();
|
|
void test5();
|
|
void test6();
|
|
void test7();
|
|
void test8();
|
|
void test9();
|
|
};
|
|
ST_vmbase_concurrency::ST_vmbase_concurrency(AvmCore* core)
|
|
: Selftest(core, "vmbase", "concurrency", ST_vmbase_concurrency::ST_names,ST_vmbase_concurrency::ST_explicits)
|
|
{}
|
|
const char* ST_vmbase_concurrency::ST_names[] = {"mutexes","conditions","atomic_counter","compare_and_swap_without_barrier","compare_and_swap_with_barrier","memory_barrier","condition_with_wait","sleep","vmthreadlocal","join", NULL };
|
|
const bool ST_vmbase_concurrency::ST_explicits[] = {false,false,false,false,false,false,false,false,false,false, false };
|
|
void ST_vmbase_concurrency::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
case 3: test3(); return;
|
|
case 4: test4(); return;
|
|
case 5: test5(); return;
|
|
case 6: test6(); return;
|
|
case 7: test7(); return;
|
|
case 8: test8(); return;
|
|
case 9: test9(); return;
|
|
}
|
|
}
|
|
void ST_vmbase_concurrency::test0() {
|
|
#ifndef UNDER_CE
|
|
TestRunner runner(THREAD_QTY);
|
|
MutexTest test(ITERATIONS);
|
|
runner.runTest(test);
|
|
// line 328 "ST_vmbase_concurrency.st"
|
|
verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmbase_concurrency::test1() {
|
|
#ifndef UNDER_CE
|
|
TestRunner runner(THREAD_QTY);
|
|
ConditionTest test(ITERATIONS, THREAD_QTY);
|
|
runner.runTest(test);
|
|
// line 336 "ST_vmbase_concurrency.st"
|
|
verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmbase_concurrency::test2() {
|
|
#ifndef UNDER_CE
|
|
TestRunner runner(THREAD_QTY);
|
|
AtomicCounterTest test(ITERATIONS, THREAD_QTY);
|
|
runner.runTest(test);
|
|
// line 344 "ST_vmbase_concurrency.st"
|
|
verifyPass(test.sharedCounter == 0, "test.sharedCounter == 0", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmbase_concurrency::test3() {
|
|
#ifndef UNDER_CE
|
|
TestRunner runner(THREAD_QTY);
|
|
CASTest test(ITERATIONS, false);
|
|
runner.runTest(test);
|
|
// line 352 "ST_vmbase_concurrency.st"
|
|
verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmbase_concurrency::test4() {
|
|
#ifndef UNDER_CE
|
|
TestRunner runner(THREAD_QTY);
|
|
CASTest test(ITERATIONS, true);
|
|
runner.runTest(test);
|
|
// line 360 "ST_vmbase_concurrency.st"
|
|
verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmbase_concurrency::test5() {
|
|
#ifndef UNDER_CE
|
|
/* This test is failing on Windows and Mac OSX 10.4.
|
|
* For Windows, see bug 609820.
|
|
* For Mac, are the 10.4 APIs not reliable?
|
|
* It could also be the test, or the compiler!
|
|
* FIXME: bug 609943 Selftests to stress memory barriers (fences)
|
|
|
|
// Note that the memory barrier test is based on a Dekker lock, so we
|
|
// only ever use 2 threads.
|
|
TestRunner runner(2);
|
|
MemoryBarrierTest test(ITERATIONS);
|
|
runner.runTest(test);
|
|
// line 376 "ST_vmbase_concurrency.st"
|
|
verifyPass(test.sharedCounter == 2 * ITERATIONS, "test.sharedCounter == 2 * ITERATIONS", __FILE__, __LINE__);
|
|
*/
|
|
|
|
// line 379 "ST_vmbase_concurrency.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmbase_concurrency::test6() {
|
|
#ifndef UNDER_CE
|
|
TestRunner runner(THREAD_QTY);
|
|
ConditionWithWaitTest test(2000); // Use 2000 iterations with a 1 ms wait
|
|
runner.runTest(test);
|
|
// line 387 "ST_vmbase_concurrency.st"
|
|
verifyPass(test.sharedCounter == THREAD_QTY * 2000, "test.sharedCounter == THREAD_QTY * 2000", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmbase_concurrency::test7() {
|
|
#ifndef UNDER_CE
|
|
TestRunner runner(THREAD_QTY);
|
|
SleepTest test(2000); // Use 2000 iterations with a 1 ms sleep
|
|
runner.runTest(test);
|
|
// line 395 "ST_vmbase_concurrency.st"
|
|
verifyPass(test.sharedCounter == THREAD_QTY * 2000, "test.sharedCounter == THREAD_QTY * 2000", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmbase_concurrency::test8() {
|
|
#ifndef UNDER_CE
|
|
TestRunner runner(THREAD_QTY);
|
|
VMThreadLocalTest test(ITERATIONS);
|
|
runner.runTest(test);
|
|
// line 403 "ST_vmbase_concurrency.st"
|
|
verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmbase_concurrency::test9() {
|
|
#ifndef UNDER_CE
|
|
// We should be able to run the dtor of a non-started VMThread.
|
|
{
|
|
VMThread vmthread;
|
|
}
|
|
// Run the mutex test but call the VMThread dtors without joining first
|
|
TestRunner runner(THREAD_QTY, false);
|
|
MutexTest test(ITERATIONS);
|
|
runner.runTest(test);
|
|
// line 416 "ST_vmbase_concurrency.st"
|
|
verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
void create_vmbase_concurrency(AvmCore* core) { new ST_vmbase_concurrency(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_vmbase_safepoints.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
#if defined VMCFG_SAFEPOINTS && defined BUG_754918
|
|
namespace avmplus {
|
|
namespace ST_vmbase_safepoints {
|
|
using namespace MMgc;
|
|
using namespace vmbase;
|
|
|
|
/**
|
|
* Base class for tests run by a TestRunner.
|
|
* Sub-classes should override runInSafepointManagerContext().
|
|
*/
|
|
class SafepointTestBase : public Runnable
|
|
{
|
|
public:
|
|
SafepointTestBase(SafepointManager& safepointManager, int iterations, int threadQty)
|
|
: m_safepointManager(safepointManager)
|
|
, m_iterations(iterations)
|
|
, m_threadQty(threadQty)
|
|
, sharedCounter(0)
|
|
{
|
|
}
|
|
|
|
virtual ~SafepointTestBase()
|
|
{
|
|
}
|
|
|
|
virtual void run()
|
|
{
|
|
// Enter the safepoint context shared by the primordial and worker threads
|
|
SafepointRecord spRecord;
|
|
m_safepointManager.enter(&spRecord);
|
|
|
|
runInSafepointManagerContext();
|
|
|
|
// Cleanup
|
|
m_safepointManager.leave(&spRecord);
|
|
}
|
|
|
|
// Tests should override this function
|
|
virtual void runInSafepointManagerContext() = 0;
|
|
|
|
int threadQty()
|
|
{
|
|
return m_threadQty;
|
|
}
|
|
|
|
protected:
|
|
SafepointManager& m_safepointManager;
|
|
WaitNotifyMonitor m_monitor;
|
|
const int m_iterations;
|
|
const int m_threadQty;
|
|
|
|
public:
|
|
int sharedCounter;
|
|
};
|
|
|
|
/**
|
|
* Creates the given number of VMThreads, and passes each the SafepointTestBase to execute.
|
|
* The TestRunner contains the SafepointManager with which the primordial and each worker
|
|
* thread will register their topmost SafepointRecord.
|
|
*/
|
|
class TestRunner
|
|
{
|
|
public:
|
|
struct ThreadRecord
|
|
{
|
|
VMThread* thread;
|
|
bool startupOk;
|
|
};
|
|
|
|
TestRunner(bool doJoin = true)
|
|
: m_doJoin(doJoin)
|
|
{
|
|
}
|
|
|
|
virtual ~TestRunner()
|
|
{
|
|
}
|
|
|
|
void runTest(SafepointTestBase& test)
|
|
{
|
|
// Enter the safepoint context shared by the primordial and worker threads
|
|
SafepointRecord spRecord;
|
|
m_safepointManager.enter(&spRecord);
|
|
|
|
m_threads = mmfx_new_array(ThreadRecord, test.threadQty());
|
|
|
|
// Start up the threads.
|
|
// Each should also enter m_safepointManager
|
|
for (int i = 0; i < test.threadQty(); i++) {
|
|
m_threads[i].thread = mmfx_new(VMThread(&test));
|
|
m_threads[i].startupOk = m_threads[i].thread->start();
|
|
}
|
|
|
|
// ...then block until they all terminate.
|
|
// Whilst performing the join, this thread will
|
|
// be implicitly safepointed.
|
|
for (int i = 0; i < test.threadQty(); i++) {
|
|
if (m_doJoin && m_threads[i].startupOk) {
|
|
m_threads[i].thread->join();
|
|
}
|
|
mmfx_delete(m_threads[i].thread);
|
|
}
|
|
|
|
mmfx_delete_array(m_threads);
|
|
|
|
// Cleanup
|
|
m_safepointManager.leave(&spRecord);
|
|
}
|
|
|
|
SafepointManager& safepointManager()
|
|
{
|
|
return m_safepointManager;
|
|
}
|
|
|
|
private:
|
|
ThreadRecord* m_threads;
|
|
bool m_doJoin;
|
|
SafepointManager m_safepointManager;
|
|
};
|
|
|
|
/**
|
|
* Threads increment a shared counter.
|
|
* Thread safety is guaranteed by only performing the increment within a safepoint task.
|
|
*/
|
|
class SimpleTest : public SafepointTestBase
|
|
{
|
|
class SimpleSafepointTask : public SafepointTask
|
|
{
|
|
public:
|
|
SimpleSafepointTask(int& counter)
|
|
: m_counter(counter)
|
|
{
|
|
}
|
|
|
|
void run()
|
|
{
|
|
// We're in the safepoint task, so update
|
|
// the shared counter;
|
|
m_counter++;
|
|
}
|
|
|
|
private:
|
|
int& m_counter;
|
|
};
|
|
|
|
public:
|
|
SimpleTest(SafepointManager& safepointManager, int iterations, int threadQty)
|
|
: SafepointTestBase(safepointManager, iterations, threadQty)
|
|
{
|
|
}
|
|
|
|
virtual ~SimpleTest()
|
|
{
|
|
}
|
|
|
|
// Thread entrypoint
|
|
virtual void runInSafepointManagerContext()
|
|
{
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
// Increment shared counter in a safepoint task
|
|
SimpleSafepointTask task(sharedCounter);
|
|
m_safepointManager.requestSafepointTask(task);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Producer-consumer with safepoints.
|
|
*
|
|
* Producer threads each increment a *private* counter in a tight
|
|
* loop which includes a safepoint.
|
|
*
|
|
* A single consumer thread executes a tight loop which continually
|
|
* requests that the producers be safepointed. In the safepoint task
|
|
* the consumer 'steals' and then zeros the private counter of the
|
|
* producers. Each stolen counter value is added to the consumer's
|
|
* private counter.
|
|
*
|
|
* This continues until the producers have executed a fixed number of
|
|
* iterations. At the end of this process the consumer should have
|
|
* a deterministic private counter value.
|
|
*
|
|
*/
|
|
class ProducerConsumerTest : public SafepointTestBase
|
|
{
|
|
public:
|
|
|
|
class ConsumerSafepointTask : public SafepointTask
|
|
{
|
|
public:
|
|
ConsumerSafepointTask(int* producerCounters, int numProducers)
|
|
: m_producerCounters(producerCounters)
|
|
, m_numProducers(numProducers)
|
|
, m_producerStolenCounter(0)
|
|
{
|
|
}
|
|
|
|
void run()
|
|
{
|
|
// We're in the safepoint task, so we can steal and then zero
|
|
// the producers' counters.
|
|
// Note that we don't know how many producer threads are still
|
|
// alive, m_numProducers is just the length of the array
|
|
// we inspect.
|
|
for (int i = 0; i < m_numProducers; i++) {
|
|
m_producerStolenCounter += m_producerCounters[i];
|
|
m_producerCounters[i] = 0;
|
|
}
|
|
}
|
|
|
|
int producerStolenCounter()
|
|
{
|
|
return m_producerStolenCounter;
|
|
}
|
|
|
|
private:
|
|
int* const m_producerCounters;
|
|
const int m_numProducers;
|
|
int m_producerStolenCounter;
|
|
};
|
|
|
|
public:
|
|
ProducerConsumerTest(SafepointManager& safepointManager, int iterations, int threadQty)
|
|
: SafepointTestBase(safepointManager, iterations, threadQty)
|
|
, m_producerCounts(mmfx_new_array_opt(int, threadQty - 1, kZero))
|
|
{
|
|
}
|
|
|
|
virtual ~ProducerConsumerTest()
|
|
{
|
|
}
|
|
|
|
// Thread entrypoint
|
|
virtual void runInSafepointManagerContext()
|
|
{
|
|
// Wait for all the threads to startup to force as much
|
|
// contention as possible.
|
|
const int threadIndex = m_threadIn++;
|
|
SCOPE_LOCK_SP_NAMED(locker, m_monitor) {
|
|
locker.notifyAll();
|
|
while (m_threadIn < m_threadQty) {
|
|
locker.wait();
|
|
}
|
|
}
|
|
|
|
// Setup one of the threads as the consumer, the others are producers
|
|
if (threadIndex == m_threadQty - 1) {
|
|
// The consumer continually makes SafepointTask requests on
|
|
// the SafepointManager, leading to safepointed producers.
|
|
// In each SafepointTask the consumer takes each producer's
|
|
// currently accrued, private counter, and adds it
|
|
// to its own total count, then zeroes the producers counter.
|
|
int consumerCount = 0;
|
|
enum State
|
|
{
|
|
RUNNING, DRAINING, DONE
|
|
};
|
|
State state = RUNNING;
|
|
do {
|
|
ConsumerSafepointTask task(m_producerCounts, m_threadQty - 1);
|
|
m_safepointManager.requestSafepointTask(task);
|
|
consumerCount += task.producerStolenCounter();
|
|
if (state == DRAINING) {
|
|
state = DONE;
|
|
} else if (m_threadOut == m_threadQty - 1) {
|
|
state = DRAINING;
|
|
}
|
|
} while (state != DONE);
|
|
|
|
// sharedCounter is verified by the selftest runner
|
|
sharedCounter = consumerCount + m_iterations;
|
|
|
|
// Can't do this in the dtor, if the selftest verify fails, it will
|
|
// throw an exception and the dtor will not run.
|
|
mmfx_delete_array(m_producerCounts);
|
|
} else {
|
|
// The producers increment their private counter, but we make sure that they
|
|
// reach an explicit safepoint every iteration.
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
SAFEPOINT_POLL_FAST(m_safepointManager);
|
|
m_producerCounts[threadIndex]++;
|
|
if (i % 100 == 0)
|
|
VMPI_threadYield(); // Try to allow some interesting interleavings
|
|
}
|
|
}
|
|
m_threadOut++;
|
|
}
|
|
private:
|
|
int* m_producerCounts;
|
|
AtomicCounter32 m_threadIn;
|
|
AtomicCounter32 m_threadOut;
|
|
};
|
|
|
|
/**
|
|
* Producer-consumer with nested/recursive (arbitrary depth) SafepointManagers.
|
|
*
|
|
* This test uses the same idea as ProducerConsumerTest, but instead of
|
|
* having only one SafepointManager which is entered by all the producer
|
|
* threads, there are N SafepointManagers which the producers continually
|
|
* nest in and out of. Each producer maintains a private counter for each
|
|
* SafepointManager nesting level, which it only increments when in the
|
|
* context of the corresponding SafepointManager.
|
|
*
|
|
* Similar to the producers, the consumer continually nests in and out of
|
|
* the stack of SafepointManagers. At each nesting level the consumer
|
|
* requests that the producers be safepointed with respect to the
|
|
* SafepointManager at that nesting level. In the safepoint task
|
|
* the consumer 'steals' and then zeros the private counter of the
|
|
* producers for that level. Each stolen counter value is added to
|
|
* the consumer's private counter.
|
|
*
|
|
* This continues until the producers have executed a fixed number of
|
|
* iterations for each nesting level. At the end of this process the
|
|
* consumer should have a deterministic private counter value.
|
|
*
|
|
* Note, to test both recursive and nested SafepointManager entry,
|
|
* the SafepointManager used for the final nesting level is the
|
|
* same as the first.
|
|
*/
|
|
class NestedProducerConsumerTest : public SafepointTestBase
|
|
{
|
|
|
|
public:
|
|
NestedProducerConsumerTest(SafepointManager& safepointManager, int iterations, int threadQty, int nestingDepth)
|
|
: SafepointTestBase(safepointManager, iterations, threadQty)
|
|
, m_nestingDepth(nestingDepth)
|
|
{
|
|
m_nestedManagers = mmfx_new_array(SafepointManager, m_nestingDepth);
|
|
m_producerCounters = mmfx_new_array(int*, m_nestingDepth + 1);
|
|
for (int i = 0; i < m_nestingDepth + 1; i++) {
|
|
m_producerCounters[i] = mmfx_new_array_opt(int, threadQty - 1, kZero);
|
|
}
|
|
}
|
|
|
|
virtual ~NestedProducerConsumerTest()
|
|
{
|
|
}
|
|
|
|
int nestedConsume(int depth)
|
|
{
|
|
if (depth == -1) {
|
|
// Fish for asserts:
|
|
// At the deepest nesting level recurse into the oldest SafepointManager on the stack.
|
|
SafepointRecord record;
|
|
m_safepointManager.enter(&record);
|
|
ProducerConsumerTest::ConsumerSafepointTask task(m_producerCounters[0], m_threadQty - 1);
|
|
m_safepointManager.requestSafepointTask(task);
|
|
m_safepointManager.leave(&record);
|
|
return task.producerStolenCounter();;
|
|
}
|
|
SafepointRecord record;
|
|
m_nestedManagers[depth].enter(&record);
|
|
ProducerConsumerTest::ConsumerSafepointTask task(m_producerCounters[depth + 1], m_threadQty - 1);
|
|
m_nestedManagers[depth].requestSafepointTask(task);
|
|
int consumerCount = task.producerStolenCounter();
|
|
|
|
// Next SafepointManager
|
|
consumerCount += nestedConsume(depth - 1);
|
|
|
|
m_nestedManagers[depth].leave(&record);
|
|
return consumerCount;
|
|
}
|
|
|
|
void nestedProduce(int depth, int iterations, int threadIndex)
|
|
{
|
|
if (depth == -1) {
|
|
// Fish for asserts:
|
|
// At the deepest nesting level recurse into the oldest SafepointManager on the stack.
|
|
SafepointRecord record;
|
|
m_safepointManager.enter(&record);
|
|
for (int i = 0; i < iterations; i++) {
|
|
SAFEPOINT_POLL_FAST(m_safepointManager);
|
|
m_producerCounters[0][threadIndex]++;
|
|
}
|
|
m_safepointManager.leave(&record);
|
|
return;
|
|
}
|
|
SafepointRecord record;
|
|
m_nestedManagers[depth].enter(&record);
|
|
for (int i = 0; i < iterations; i++) {
|
|
SAFEPOINT_POLL_FAST(m_nestedManagers[depth]);
|
|
m_producerCounters[depth + 1][threadIndex]++;
|
|
}
|
|
VMPI_threadYield(); // Try to allow some interesting interleavings
|
|
|
|
// Next SafepointManager
|
|
nestedProduce(depth - 1, iterations, threadIndex);
|
|
|
|
m_nestedManagers[depth].leave(&record);
|
|
}
|
|
|
|
// Thread entrypoint
|
|
virtual void runInSafepointManagerContext()
|
|
{
|
|
// Wait for all the threads to startup to force as much
|
|
// contention as possible.
|
|
const int threadIndex = m_threadIn++;
|
|
SCOPE_LOCK_SP_NAMED(locker, m_monitor) {
|
|
locker.notifyAll();
|
|
while (m_threadIn < m_threadQty) {
|
|
locker.wait();
|
|
}
|
|
}
|
|
|
|
// Setup one of the threads as the consumer, the others are producers
|
|
if (threadIndex == m_threadQty - 1) {
|
|
// The consumer continually makes SafepointTask requests on
|
|
// the SafepointManager at each nesting level, leading to
|
|
// safepointed producers. In each SafepointTask the consumer
|
|
// takes each producer's currently accrued, private counter at
|
|
// the current nesting depth, and adds it to its own total count,
|
|
// then zeroes the producers counter.
|
|
int consumerCount = 0;
|
|
enum State
|
|
{
|
|
RUNNING, DRAINING, DONE
|
|
};
|
|
State state = RUNNING;
|
|
do {
|
|
ProducerConsumerTest::ConsumerSafepointTask task(m_producerCounters[0], m_threadQty - 1);
|
|
m_safepointManager.requestSafepointTask(task);
|
|
consumerCount += task.producerStolenCounter();
|
|
consumerCount += nestedConsume(m_nestingDepth - 1);
|
|
if (state == DRAINING) {
|
|
state = DONE;
|
|
} else if (m_threadOut == m_threadQty - 1) {
|
|
state = DRAINING;
|
|
}
|
|
} while (state != DONE);
|
|
|
|
// sharedCounter is verified by the selftest runner
|
|
sharedCounter = consumerCount + m_iterations;
|
|
|
|
// Can't do this in the dtor, if the selftest verify fails, it will
|
|
// throw an exception and the dtor will not run.
|
|
for (int i = 0; i < m_nestingDepth + 1; i++) {
|
|
mmfx_delete_array(m_producerCounters[i]);
|
|
}
|
|
mmfx_delete_array(m_producerCounters);
|
|
mmfx_delete_array(m_nestedManagers);
|
|
} else {
|
|
// How many increments should we perform each time we visit
|
|
// a nesting depth?
|
|
const int incPerDepth = m_iterations / 1000;
|
|
const int iterations = m_iterations / (incPerDepth * (m_nestingDepth + 2));
|
|
// The producers increment their private counter at each nesting depth,
|
|
// but we make sure that they reach an explicit safepoint every iteration.
|
|
for (int i = 0; i < iterations; i++) {
|
|
for (int j = 0; j < incPerDepth; j++) {
|
|
SAFEPOINT_POLL_FAST(m_safepointManager);
|
|
m_producerCounters[0][threadIndex]++;
|
|
}
|
|
nestedProduce(m_nestingDepth - 1, incPerDepth, threadIndex);
|
|
VMPI_threadYield(); // Try to allow some interesting interleavings
|
|
}
|
|
const int balance = m_iterations % (incPerDepth * (m_nestingDepth + 2));
|
|
for (int i = 0; i < balance; i++) {
|
|
SAFEPOINT_POLL_FAST(m_safepointManager);
|
|
m_producerCounters[0][threadIndex]++;
|
|
}
|
|
}
|
|
m_threadOut++;
|
|
}
|
|
private:
|
|
int** m_producerCounters;
|
|
const int m_nestingDepth;
|
|
SafepointManager* m_nestedManagers;
|
|
AtomicCounter32 m_threadIn;
|
|
AtomicCounter32 m_threadOut;
|
|
};
|
|
|
|
// Must be >=2 for ProducerConsumerTest and NestedProducerConsumerTest
|
|
#define THREAD_QTY 4
|
|
|
|
// Must be >= 1000 for NestedProducerConsumerTest
|
|
#define ITERATIONS 100000
|
|
|
|
// For NestedProducerConsumerTest
|
|
#define NESTING_DEPTH 7
|
|
|
|
class ST_vmbase_safepoints : public Selftest {
|
|
public:
|
|
ST_vmbase_safepoints(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
};
|
|
ST_vmbase_safepoints::ST_vmbase_safepoints(AvmCore* core)
|
|
: Selftest(core, "vmbase", "safepoints", ST_vmbase_safepoints::ST_names,ST_vmbase_safepoints::ST_explicits)
|
|
{}
|
|
const char* ST_vmbase_safepoints::ST_names[] = {"simple","producer_consumer","nested_producer_consumer", NULL };
|
|
const bool ST_vmbase_safepoints::ST_explicits[] = {false,false,false, false };
|
|
void ST_vmbase_safepoints::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
}
|
|
}
|
|
void ST_vmbase_safepoints::test0() {
|
|
#ifndef UNDER_CE
|
|
TestRunner runner(THREAD_QTY!=0);
|
|
SimpleTest test(runner.safepointManager(), ITERATIONS, THREAD_QTY);
|
|
runner.runTest(test);
|
|
// line 496 "ST_vmbase_safepoints.st"
|
|
verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmbase_safepoints::test1() {
|
|
#ifndef UNDER_CE
|
|
TestRunner runner(THREAD_QTY!=0);
|
|
ProducerConsumerTest test(runner.safepointManager(), ITERATIONS, THREAD_QTY);
|
|
runner.runTest(test);
|
|
// line 504 "ST_vmbase_safepoints.st"
|
|
verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmbase_safepoints::test2() {
|
|
#ifndef UNDER_CE
|
|
TestRunner runner(THREAD_QTY!=0);
|
|
NestedProducerConsumerTest test(runner.safepointManager(), ITERATIONS * 100, THREAD_QTY, NESTING_DEPTH);
|
|
runner.runTest(test);
|
|
// line 512 "ST_vmbase_safepoints.st"
|
|
verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS * 100, "test.sharedCounter == THREAD_QTY * ITERATIONS * 100", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
|
|
|
|
|
|
}
|
|
void create_vmbase_safepoints(AvmCore* core) { new ST_vmbase_safepoints(core); }
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Generated from ST_vmpi_threads.st
|
|
// -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
//
|
|
// 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/.
|
|
|
|
// Bugzilla 543560 - here we risk deleting an object that is still on the mark stack because
|
|
// of how we perform large-object splitting. The setup is that user code that deletes the object
|
|
// gets to run after the first part of the large object has been popped off the mark stack
|
|
// but before the rest has been handled.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_vmpi_threads {
|
|
using namespace MMgc;
|
|
|
|
class ThreadTestBase {
|
|
public:
|
|
struct ThreadRecord {
|
|
vmpi_thread_t thread;
|
|
int id;
|
|
bool startupOk;
|
|
};
|
|
|
|
ThreadTestBase(int threadQty, int iterations) : m_threadQty(threadQty), m_iterations(iterations), sharedCounter(0) {
|
|
VMPI_recursiveMutexInit(&m_mutex);
|
|
VMPI_condVarInit(&m_condition);
|
|
}
|
|
virtual ~ThreadTestBase() {
|
|
VMPI_recursiveMutexDestroy(&m_mutex);
|
|
VMPI_condVarDestroy(&m_condition);
|
|
}
|
|
|
|
void runTest() {
|
|
|
|
ThreadRecord* threads = mmfx_new_array(ThreadRecord, m_threadQty);
|
|
|
|
// Start up the threads
|
|
for (int i = 0; i < m_threadQty; i++) {
|
|
threads[i].id = i;
|
|
threads[i].startupOk = VMPI_threadCreate(&threads[i].thread, NULL, _workerThreadEntry, this);
|
|
}
|
|
|
|
// ...then block until they all terminate
|
|
for (int i = 0; i < m_threadQty; i++) {
|
|
if (threads[i].startupOk) {
|
|
VMPI_threadJoin(threads[i].thread);
|
|
}
|
|
}
|
|
|
|
mmfx_delete_array(threads);
|
|
}
|
|
|
|
static vmpi_thread_rtn_t VMPI_THREAD_START_CC detachbleThreadEntry(vmpi_thread_arg_t) {
|
|
VMPI_threadDetach(VMPI_currentThread());
|
|
return NULL;
|
|
}
|
|
|
|
static vmpi_thread_rtn_t VMPI_THREAD_START_CC joinableThreadEntry(vmpi_thread_arg_t) {
|
|
return NULL;
|
|
}
|
|
|
|
protected:
|
|
virtual void workerThreadEntry() {}
|
|
|
|
private:
|
|
static vmpi_thread_rtn_t VMPI_THREAD_START_CC _workerThreadEntry(vmpi_thread_arg_t arg) {
|
|
((ThreadTestBase*)arg)->workerThreadEntry();
|
|
return NULL;
|
|
}
|
|
|
|
protected:
|
|
const int m_threadQty;
|
|
vmpi_mutex_t m_mutex;
|
|
vmpi_condvar_t m_condition;
|
|
const int m_iterations;
|
|
|
|
public:
|
|
int sharedCounter;
|
|
};
|
|
|
|
class MutexTest : public ThreadTestBase {
|
|
public:
|
|
MutexTest(int threadQty, int iterations) : ThreadTestBase(threadQty, iterations) {}
|
|
virtual ~MutexTest() {}
|
|
|
|
virtual void workerThreadEntry() {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
VMPI_recursiveMutexLock(&m_mutex);
|
|
VMPI_recursiveMutexLock(&m_mutex);
|
|
sharedCounter++;
|
|
VMPI_recursiveMutexUnlock(&m_mutex);
|
|
VMPI_recursiveMutexUnlock(&m_mutex);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class ConditionTest : public ThreadTestBase {
|
|
public:
|
|
ConditionTest(int threadQty, int iterations) : ThreadTestBase(threadQty, iterations), m_finishedQty(0) {}
|
|
virtual ~ConditionTest() {}
|
|
|
|
virtual void workerThreadEntry() {
|
|
if (m_threadQty < 2) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
VMPI_recursiveMutexLock(&m_mutex);
|
|
sharedCounter++;
|
|
|
|
// If there's another thread still active then wait.
|
|
if (m_finishedQty < m_threadQty - 1) {
|
|
VMPI_condVarSignal(&m_condition);
|
|
VMPI_condVarWait(&m_condition, &m_mutex);
|
|
}
|
|
|
|
// This thread has finished, so let's wake everyone else up
|
|
if (i == m_iterations - 1) {
|
|
m_finishedQty++;
|
|
VMPI_condVarBroadcast(&m_condition);
|
|
}
|
|
VMPI_recursiveMutexUnlock(&m_mutex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private:
|
|
int m_finishedQty;
|
|
};
|
|
|
|
class AtomicIncrementTest : public ThreadTestBase {
|
|
public:
|
|
AtomicIncrementTest(int threadQty, int iterations, bool withBarrier) : ThreadTestBase(threadQty, iterations), m_withBarrier(withBarrier) {}
|
|
virtual ~AtomicIncrementTest() {}
|
|
|
|
virtual void workerThreadEntry() {
|
|
if (m_withBarrier) {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
VMPI_atomicIncAndGet32WithBarrier(&sharedCounter);
|
|
}
|
|
} else {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
VMPI_atomicIncAndGet32(&sharedCounter);
|
|
}
|
|
}
|
|
}
|
|
private:
|
|
bool m_withBarrier;
|
|
};
|
|
|
|
class AtomicDecrementTest : public ThreadTestBase {
|
|
public:
|
|
AtomicDecrementTest(int threadQty, int iterations, bool withBarrier) : ThreadTestBase(threadQty, iterations), m_withBarrier(withBarrier) {}
|
|
virtual ~AtomicDecrementTest() {}
|
|
|
|
virtual void workerThreadEntry() {
|
|
if (m_withBarrier) {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
VMPI_atomicDecAndGet32WithBarrier(&sharedCounter);
|
|
}
|
|
} else {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
VMPI_atomicDecAndGet32(&sharedCounter);
|
|
}
|
|
}
|
|
}
|
|
private:
|
|
bool m_withBarrier;
|
|
};
|
|
|
|
class CASTest : public ThreadTestBase {
|
|
public:
|
|
CASTest(int threadQty, int iterations, bool withBarrier) : ThreadTestBase(threadQty, iterations), m_withBarrier(withBarrier) {}
|
|
virtual ~CASTest() {}
|
|
|
|
virtual void workerThreadEntry() {
|
|
if (m_withBarrier) {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
int32_t current, next;
|
|
do {
|
|
current = sharedCounter;
|
|
next = current + 1;
|
|
} while (!VMPI_compareAndSwap32WithBarrier(current, next, &sharedCounter));
|
|
}
|
|
} else {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
int32_t current, next;
|
|
do {
|
|
current = sharedCounter;
|
|
next = current + 1;
|
|
} while (!VMPI_compareAndSwap32(current, next, &sharedCounter));
|
|
}
|
|
}
|
|
}
|
|
private:
|
|
bool m_withBarrier;
|
|
};
|
|
|
|
/**
|
|
* We protect a shared counter with a Dekker-style lock that has been made
|
|
* sequentially consistent with memory barriers.
|
|
*
|
|
* The idea is that if the barriers are correct, then two threads can compete
|
|
* to update the counter n times each, so that the final counter value is 2n. If
|
|
* the final value is not 2n, then the barriers have failed to ensure sequential
|
|
* consistency.
|
|
*
|
|
* FIXME: bug 609943
|
|
* This seems way too complicated. We have to be confident in the algorithm
|
|
* before considering the barrier implementations, and I'm not convinced as yet.
|
|
* Is there something simpler?
|
|
*
|
|
* This is test is not actually run. The verifyPass below just returns true.
|
|
*/
|
|
class MemoryBarrierTest : public ThreadTestBase {
|
|
public:
|
|
MemoryBarrierTest(int iterations) : ThreadTestBase(2, iterations), m_thread0(0), m_thread1(0), m_turn(NULL) {}
|
|
virtual ~MemoryBarrierTest() {}
|
|
|
|
virtual void workerThreadEntry() {
|
|
|
|
volatile int* me;
|
|
volatile int* other;
|
|
volatile int* const counterp = &sharedCounter;
|
|
|
|
VMPI_recursiveMutexLock(&m_mutex);
|
|
if (m_turn == NULL) {
|
|
me = &m_thread0;
|
|
other = &m_thread1;
|
|
m_turn = me;
|
|
} else {
|
|
me = &m_thread1;
|
|
other = &m_thread0;
|
|
}
|
|
VMPI_recursiveMutexUnlock(&m_mutex);
|
|
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
// Dekker lock
|
|
*me = 1;
|
|
VMPI_memoryBarrier();
|
|
while (*other == 1) {
|
|
VMPI_memoryBarrier();
|
|
if (m_turn == other) {
|
|
VMPI_memoryBarrier();
|
|
*me = 0;
|
|
VMPI_memoryBarrier();
|
|
while (m_turn == other) {
|
|
VMPI_memoryBarrier();
|
|
}
|
|
VMPI_memoryBarrier();
|
|
*me = 1;
|
|
VMPI_memoryBarrier();
|
|
}
|
|
}
|
|
VMPI_memoryBarrier();
|
|
(*counterp)++;
|
|
VMPI_memoryBarrier();
|
|
m_turn = other;
|
|
VMPI_memoryBarrier();
|
|
*me = 0;
|
|
VMPI_memoryBarrier();
|
|
}
|
|
}
|
|
private:
|
|
volatile int m_thread0;
|
|
volatile int m_thread1;
|
|
volatile int* volatile m_turn;
|
|
};
|
|
|
|
class TryLockTest : public ThreadTestBase {
|
|
public:
|
|
TryLockTest(int threadQty, int iterations) : ThreadTestBase(threadQty, iterations) {}
|
|
virtual ~TryLockTest() {}
|
|
|
|
virtual void workerThreadEntry() {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
int counter = 0;
|
|
while (!VMPI_recursiveMutexTryLock(&m_mutex)) {
|
|
++counter;
|
|
if((counter & 63) == 0) {
|
|
VMPI_threadSleep(1);
|
|
}
|
|
}
|
|
sharedCounter++;
|
|
VMPI_recursiveMutexUnlock(&m_mutex);
|
|
}
|
|
}
|
|
};
|
|
|
|
class ConditionWithWaitTest : public ThreadTestBase {
|
|
public:
|
|
ConditionWithWaitTest(int threadQty, int iterations) : ThreadTestBase(threadQty, iterations) {}
|
|
virtual ~ConditionWithWaitTest() {}
|
|
|
|
virtual void workerThreadEntry() {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
VMPI_recursiveMutexLock(&m_mutex);
|
|
sharedCounter++;
|
|
VMPI_condVarTimedWait(&m_condition, &m_mutex, 1);
|
|
VMPI_recursiveMutexUnlock(&m_mutex);
|
|
}
|
|
}
|
|
};
|
|
|
|
class SleepTest : public ThreadTestBase {
|
|
public:
|
|
SleepTest(int threadQty, int iterations) : ThreadTestBase(threadQty, iterations) {}
|
|
virtual ~SleepTest() {}
|
|
|
|
virtual void workerThreadEntry() {
|
|
for (int i = 0; i < m_iterations; i++) {
|
|
VMPI_recursiveMutexLock(&m_mutex);
|
|
sharedCounter++;
|
|
VMPI_recursiveMutexUnlock(&m_mutex);
|
|
VMPI_threadSleep(1);
|
|
}
|
|
}
|
|
};
|
|
|
|
// This needs to be at least 2 for ConditionTest
|
|
#define THREAD_QTY 4
|
|
|
|
#define ITERATIONS 100000
|
|
|
|
class ST_vmpi_threads : public Selftest {
|
|
public:
|
|
ST_vmpi_threads(AvmCore* core);
|
|
virtual void run(int n);
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
void test3();
|
|
void test4();
|
|
void test5();
|
|
void test6();
|
|
void test7();
|
|
void test8();
|
|
void test9();
|
|
void test10();
|
|
void test11();
|
|
void test12();
|
|
void test13();
|
|
};
|
|
ST_vmpi_threads::ST_vmpi_threads(AvmCore* core)
|
|
: Selftest(core, "vmpi", "threads", ST_vmpi_threads::ST_names,ST_vmpi_threads::ST_explicits)
|
|
{}
|
|
const char* ST_vmpi_threads::ST_names[] = {"mutexes","conditions","atomic_increment_without_barrier","atomic_decrement_without_barrier","compare_and_swap_without_barrier","atomic_increment_with_barrier","atomic_decrement_with_barrier","compare_and_swap_with_barrier","memory_barrier","try_lock","condition_with_wait","sleep","detach","thread_attributes", NULL };
|
|
const bool ST_vmpi_threads::ST_explicits[] = {false,false,false,false,false,false,false,false,false,false,false,false,false,false, false };
|
|
void ST_vmpi_threads::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
case 3: test3(); return;
|
|
case 4: test4(); return;
|
|
case 5: test5(); return;
|
|
case 6: test6(); return;
|
|
case 7: test7(); return;
|
|
case 8: test8(); return;
|
|
case 9: test9(); return;
|
|
case 10: test10(); return;
|
|
case 11: test11(); return;
|
|
case 12: test12(); return;
|
|
case 13: test13(); return;
|
|
}
|
|
}
|
|
void ST_vmpi_threads::test0() {
|
|
#ifndef UNDER_CE
|
|
MutexTest mutexTest(THREAD_QTY, ITERATIONS);
|
|
mutexTest.runTest();
|
|
// line 334 "ST_vmpi_threads.st"
|
|
verifyPass(mutexTest.sharedCounter == THREAD_QTY * ITERATIONS, "mutexTest.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test1() {
|
|
#ifndef UNDER_CE
|
|
ConditionTest conditionTest(THREAD_QTY, ITERATIONS);
|
|
conditionTest.runTest();
|
|
// line 341 "ST_vmpi_threads.st"
|
|
verifyPass(conditionTest.sharedCounter == THREAD_QTY * ITERATIONS, "conditionTest.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test2() {
|
|
#ifndef UNDER_CE
|
|
AtomicIncrementTest atomicIncrementTest(THREAD_QTY, ITERATIONS, false);
|
|
atomicIncrementTest.runTest();
|
|
// line 348 "ST_vmpi_threads.st"
|
|
verifyPass(atomicIncrementTest.sharedCounter == THREAD_QTY * ITERATIONS, "atomicIncrementTest.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test3() {
|
|
#ifndef UNDER_CE
|
|
AtomicDecrementTest atomicDecrementTest(THREAD_QTY, ITERATIONS, false);
|
|
atomicDecrementTest.runTest();
|
|
// line 355 "ST_vmpi_threads.st"
|
|
verifyPass(atomicDecrementTest.sharedCounter == -(THREAD_QTY * ITERATIONS), "atomicDecrementTest.sharedCounter == -(THREAD_QTY * ITERATIONS)", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test4() {
|
|
#ifndef UNDER_CE
|
|
CASTest casTest(THREAD_QTY, ITERATIONS, false);
|
|
casTest.runTest();
|
|
// line 362 "ST_vmpi_threads.st"
|
|
verifyPass(casTest.sharedCounter == THREAD_QTY * ITERATIONS, "casTest.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test5() {
|
|
#ifndef UNDER_CE
|
|
AtomicIncrementTest atomicIncrementTest(THREAD_QTY, ITERATIONS, true);
|
|
atomicIncrementTest.runTest();
|
|
// line 369 "ST_vmpi_threads.st"
|
|
verifyPass(atomicIncrementTest.sharedCounter == THREAD_QTY * ITERATIONS, "atomicIncrementTest.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test6() {
|
|
#ifndef UNDER_CE
|
|
AtomicDecrementTest atomicDecrementTest(THREAD_QTY, ITERATIONS, true);
|
|
atomicDecrementTest.runTest();
|
|
// line 376 "ST_vmpi_threads.st"
|
|
verifyPass(atomicDecrementTest.sharedCounter == -(THREAD_QTY * ITERATIONS), "atomicDecrementTest.sharedCounter == -(THREAD_QTY * ITERATIONS)", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test7() {
|
|
#ifndef UNDER_CE
|
|
CASTest casTest(THREAD_QTY, ITERATIONS, true);
|
|
casTest.runTest();
|
|
// line 383 "ST_vmpi_threads.st"
|
|
verifyPass(casTest.sharedCounter == THREAD_QTY * ITERATIONS, "casTest.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test8() {
|
|
#ifndef UNDER_CE
|
|
/* This test is failing on Windows and Mac OSX 10.4.
|
|
* For Windows, see bug 609820.
|
|
* For Mac, are the 10.4 APIs not reliable?
|
|
* It could also be the test, or the compiler!
|
|
* FIXME: bug 609943 Selftests to stress memory barriers (fences)
|
|
|
|
// Note that the memory barrier test is based on a Dekker lock, so we
|
|
// only ever use 2 threads.
|
|
MemoryBarrierTest memoryBarrierTest(ITERATIONS);
|
|
memoryBarrierTest.runTest();
|
|
// line 398 "ST_vmpi_threads.st"
|
|
verifyPass(memoryBarrierTest.sharedCounter == 2 * ITERATIONS, "memoryBarrierTest.sharedCounter == 2 * ITERATIONS", __FILE__, __LINE__);
|
|
|
|
*/
|
|
// line 401 "ST_vmpi_threads.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test9() {
|
|
#ifndef UNDER_CE
|
|
TryLockTest tryLockTest(THREAD_QTY, ITERATIONS);
|
|
tryLockTest.runTest();
|
|
// line 408 "ST_vmpi_threads.st"
|
|
verifyPass(tryLockTest.sharedCounter == THREAD_QTY * ITERATIONS, "tryLockTest.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test10() {
|
|
#ifndef UNDER_CE
|
|
// Use 2000 iterations with a 1 ms wait
|
|
ConditionWithWaitTest conditionWithWaitTest(THREAD_QTY, 2000);
|
|
conditionWithWaitTest.runTest();
|
|
// line 416 "ST_vmpi_threads.st"
|
|
verifyPass(conditionWithWaitTest.sharedCounter == THREAD_QTY * 2000, "conditionWithWaitTest.sharedCounter == THREAD_QTY * 2000", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test11() {
|
|
#ifndef UNDER_CE
|
|
// Use 2000 iterations with a 1 ms sleep
|
|
SleepTest sleepTest(THREAD_QTY, 2000);
|
|
sleepTest.runTest();
|
|
// line 424 "ST_vmpi_threads.st"
|
|
verifyPass(sleepTest.sharedCounter == THREAD_QTY * 2000, "sleepTest.sharedCounter == THREAD_QTY * 2000", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test12() {
|
|
#ifndef UNDER_CE
|
|
// We can't really test for much here...
|
|
vmpi_thread_t thread;
|
|
VMPI_threadCreate(&thread, NULL, ThreadTestBase::detachbleThreadEntry, NULL);
|
|
// line 432 "ST_vmpi_threads.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
}
|
|
void ST_vmpi_threads::test13() {
|
|
#ifndef UNDER_CE
|
|
vmpi_thread_attr_t attr;
|
|
// line 438 "ST_vmpi_threads.st"
|
|
verifyPass(VMPI_threadAttrInit(&attr) == true, "VMPI_threadAttrInit(&attr) == true", __FILE__, __LINE__);
|
|
|
|
VMPI_threadAttrSetGuardSize(&attr, VMPI_threadAttrDefaultGuardSize());
|
|
VMPI_threadAttrSetStackSize(&attr, VMPI_threadAttrDefaultStackSize());
|
|
VMPI_threadAttrSetPriorityLow(&attr);
|
|
VMPI_threadAttrSetPriorityNormal(&attr);
|
|
VMPI_threadAttrSetPriorityHigh(&attr);
|
|
|
|
vmpi_thread_t thread;
|
|
bool started = VMPI_threadCreate(&thread, &attr, ThreadTestBase::joinableThreadEntry, NULL);
|
|
// line 448 "ST_vmpi_threads.st"
|
|
verifyPass(started == true, "started == true", __FILE__, __LINE__);
|
|
if (started) {
|
|
VMPI_threadJoin(thread);
|
|
}
|
|
|
|
// line 453 "ST_vmpi_threads.st"
|
|
verifyPass(VMPI_threadAttrDestroy(&attr) == true, "VMPI_threadAttrDestroy(&attr) == true", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
|
|
}
|
|
void create_vmpi_threads(AvmCore* core) { new ST_vmpi_threads(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Generated from ST_workers_Buffer.st
|
|
// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5)
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
#if !defined DEBUGGER
|
|
namespace avmplus {
|
|
namespace ST_workers_Buffers {
|
|
class ST_workers_Buffers : public Selftest {
|
|
public:
|
|
ST_workers_Buffers(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
virtual void epilogue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
void test3();
|
|
void test4();
|
|
void test5();
|
|
void test6();
|
|
void test7();
|
|
MMgc::GC *gc;
|
|
NoSyncSingleItemBuffer *singleItemBuffer;
|
|
NoSyncMultiItemBuffer *multiItemBuffer;
|
|
NonBlockingMultiItemBuffer *nonBlockMultiItemBuffer;
|
|
Atom *atom;
|
|
|
|
};
|
|
ST_workers_Buffers::ST_workers_Buffers(AvmCore* core)
|
|
: Selftest(core, "workers", "Buffers", ST_workers_Buffers::ST_names,ST_workers_Buffers::ST_explicits)
|
|
{}
|
|
const char* ST_workers_Buffers::ST_names[] = {"NonBlockMultiItemConstructor","NonBlockMultiItemEmptyBuffer","NonBlockMultiItemFullBuffer","SingleConstructor","SingleEmptyBuffer","SingleFullBuffer","MultiConstructor","MultiEmptyBuffer", NULL };
|
|
const bool ST_workers_Buffers::ST_explicits[] = {false,false,false,false,false,false,false,false, false };
|
|
void ST_workers_Buffers::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
case 3: test3(); return;
|
|
case 4: test4(); return;
|
|
case 5: test5(); return;
|
|
case 6: test6(); return;
|
|
case 7: test7(); return;
|
|
}
|
|
}
|
|
void ST_workers_Buffers::prologue() {
|
|
MMgc::GCConfig config;
|
|
gc = new MMgc::GC(MMgc::GCHeap::GetGCHeap(), config);
|
|
//gc=core->GetGC();
|
|
atom=(Atom *)malloc(100);
|
|
singleItemBuffer=new NoSyncSingleItemBuffer(gc);
|
|
multiItemBuffer=new NoSyncMultiItemBuffer();
|
|
nonBlockMultiItemBuffer=new NonBlockingMultiItemBuffer(gc);
|
|
|
|
}
|
|
void ST_workers_Buffers::epilogue() {
|
|
delete singleItemBuffer;
|
|
delete multiItemBuffer;
|
|
delete nonBlockMultiItemBuffer;
|
|
delete gc;
|
|
free(atom);
|
|
|
|
}
|
|
void ST_workers_Buffers::test0() {
|
|
// line 37 "ST_workers_Buffer.st"
|
|
verifyPass(nonBlockMultiItemBuffer!=NULL, "nonBlockMultiItemBuffer!=NULL", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_workers_Buffers::test1() {
|
|
// line 40 "ST_workers_Buffer.st"
|
|
verifyPass(nonBlockMultiItemBuffer->isEmpty(), "nonBlockMultiItemBuffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 41 "ST_workers_Buffer.st"
|
|
verifyPass(!nonBlockMultiItemBuffer->isFull(), "!nonBlockMultiItemBuffer->isFull()", __FILE__, __LINE__);
|
|
// line 42 "ST_workers_Buffer.st"
|
|
verifyPass(!nonBlockMultiItemBuffer->get(atom), "!nonBlockMultiItemBuffer->get(atom)", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_workers_Buffers::test2() {
|
|
bool result;
|
|
for (int i=0;i<14;i++) {
|
|
result=nonBlockMultiItemBuffer->put(String::createLatin1(core,"test string")->atom());
|
|
// line 48 "ST_workers_Buffer.st"
|
|
verifyPass(result, "result", __FILE__, __LINE__);
|
|
// line 49 "ST_workers_Buffer.st"
|
|
verifyPass(!nonBlockMultiItemBuffer->isEmpty(), "!nonBlockMultiItemBuffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 50 "ST_workers_Buffer.st"
|
|
verifyPass(!nonBlockMultiItemBuffer->isFull(), "!nonBlockMultiItemBuffer->isFull()", __FILE__, __LINE__);
|
|
}
|
|
result=nonBlockMultiItemBuffer->put(String::createLatin1(core,"test string")->atom());
|
|
// line 53 "ST_workers_Buffer.st"
|
|
verifyPass(result, "result", __FILE__, __LINE__);
|
|
// line 54 "ST_workers_Buffer.st"
|
|
verifyPass(!nonBlockMultiItemBuffer->isEmpty(), "!nonBlockMultiItemBuffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 55 "ST_workers_Buffer.st"
|
|
verifyPass(nonBlockMultiItemBuffer->isFull(), "nonBlockMultiItemBuffer->isFull()", __FILE__, __LINE__);
|
|
|
|
result=nonBlockMultiItemBuffer->put(String::createLatin1(core,"test string")->atom());
|
|
// line 58 "ST_workers_Buffer.st"
|
|
verifyPass(!result, "!result", __FILE__, __LINE__);
|
|
// line 59 "ST_workers_Buffer.st"
|
|
verifyPass(!nonBlockMultiItemBuffer->isEmpty(), "!nonBlockMultiItemBuffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 60 "ST_workers_Buffer.st"
|
|
verifyPass(nonBlockMultiItemBuffer->isFull(), "nonBlockMultiItemBuffer->isFull()", __FILE__, __LINE__);
|
|
|
|
for (int i=0;i<14;i++) {
|
|
// line 63 "ST_workers_Buffer.st"
|
|
verifyPass(nonBlockMultiItemBuffer->get(atom), "nonBlockMultiItemBuffer->get(atom)", __FILE__, __LINE__);
|
|
// line 64 "ST_workers_Buffer.st"
|
|
verifyPass(!nonBlockMultiItemBuffer->isEmpty(), "!nonBlockMultiItemBuffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 65 "ST_workers_Buffer.st"
|
|
verifyPass(!nonBlockMultiItemBuffer->isFull(), "!nonBlockMultiItemBuffer->isFull()", __FILE__, __LINE__);
|
|
}
|
|
// line 67 "ST_workers_Buffer.st"
|
|
verifyPass(nonBlockMultiItemBuffer->get(atom), "nonBlockMultiItemBuffer->get(atom)", __FILE__, __LINE__);
|
|
// line 68 "ST_workers_Buffer.st"
|
|
verifyPass(nonBlockMultiItemBuffer->isEmpty(), "nonBlockMultiItemBuffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 69 "ST_workers_Buffer.st"
|
|
verifyPass(!nonBlockMultiItemBuffer->isFull(), "!nonBlockMultiItemBuffer->isFull()", __FILE__, __LINE__);
|
|
|
|
// line 71 "ST_workers_Buffer.st"
|
|
verifyPass(!nonBlockMultiItemBuffer->get(atom), "!nonBlockMultiItemBuffer->get(atom)", __FILE__, __LINE__);
|
|
// line 72 "ST_workers_Buffer.st"
|
|
verifyPass(nonBlockMultiItemBuffer->isEmpty(), "nonBlockMultiItemBuffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 73 "ST_workers_Buffer.st"
|
|
verifyPass(!nonBlockMultiItemBuffer->isFull(), "!nonBlockMultiItemBuffer->isFull()", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_workers_Buffers::test3() {
|
|
// line 76 "ST_workers_Buffer.st"
|
|
verifyPass(singleItemBuffer!=NULL, "singleItemBuffer!=NULL", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_workers_Buffers::test4() {
|
|
// line 79 "ST_workers_Buffer.st"
|
|
verifyPass(singleItemBuffer->isEmpty(), "singleItemBuffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 80 "ST_workers_Buffer.st"
|
|
verifyPass(!singleItemBuffer->isFull(), "!singleItemBuffer->isFull()", __FILE__, __LINE__);
|
|
// line 81 "ST_workers_Buffer.st"
|
|
verifyPass(!singleItemBuffer->get(atom), "!singleItemBuffer->get(atom)", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_workers_Buffers::test5() {
|
|
bool result;
|
|
result=singleItemBuffer->put(String::createLatin1(core,"test string")->atom());
|
|
// line 86 "ST_workers_Buffer.st"
|
|
verifyPass(result, "result", __FILE__, __LINE__);
|
|
// line 87 "ST_workers_Buffer.st"
|
|
verifyPass(!singleItemBuffer->isEmpty(), "!singleItemBuffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 88 "ST_workers_Buffer.st"
|
|
verifyPass(singleItemBuffer->isFull(), "singleItemBuffer->isFull()", __FILE__, __LINE__);
|
|
result=singleItemBuffer->put(String::createLatin1(core,"test string")->atom());
|
|
// line 90 "ST_workers_Buffer.st"
|
|
verifyPass(!result, "!result", __FILE__, __LINE__);
|
|
// line 91 "ST_workers_Buffer.st"
|
|
verifyPass(singleItemBuffer->get(atom), "singleItemBuffer->get(atom)", __FILE__, __LINE__);
|
|
// line 92 "ST_workers_Buffer.st"
|
|
verifyPass(singleItemBuffer->isEmpty(), "singleItemBuffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 93 "ST_workers_Buffer.st"
|
|
verifyPass(!singleItemBuffer->isFull(), "!singleItemBuffer->isFull()", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_workers_Buffers::test6() {
|
|
// line 96 "ST_workers_Buffer.st"
|
|
verifyPass(multiItemBuffer!=NULL, "multiItemBuffer!=NULL", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_workers_Buffers::test7() {
|
|
// line 99 "ST_workers_Buffer.st"
|
|
verifyPass(singleItemBuffer->isEmpty(), "singleItemBuffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 100 "ST_workers_Buffer.st"
|
|
verifyPass(!singleItemBuffer->isFull(), "!singleItemBuffer->isFull()", __FILE__, __LINE__);
|
|
// line 101 "ST_workers_Buffer.st"
|
|
verifyPass(!singleItemBuffer->get(atom), "!singleItemBuffer->get(atom)", __FILE__, __LINE__);
|
|
|
|
// Bugzilla: https://bugzilla.mozilla.org/show_bug.cgi?id=758260
|
|
//%%test MultiFullBuffer
|
|
// bool result;
|
|
// for (int i=0;i<15;i++) {
|
|
// ChannelItem item;
|
|
// item.tag = kDoubleType;
|
|
// item.asNumber = 3.14;
|
|
// result=multiItemBuffer->put(item);
|
|
// %%verify result
|
|
// %%verify !multiItemBuffer->isEmpty()
|
|
// %%verify !multiItemBuffer->isFull()
|
|
// }
|
|
// ChannelItem inItem;
|
|
// inItem.asNumber = 3.14;
|
|
// inItem.tag = kDoubleType;
|
|
// result=multiItemBuffer->put(inItem);
|
|
// %%verify result
|
|
// %%verify !multiItemBuffer->isEmpty()
|
|
// %%verify multiItemBuffer->isFull()
|
|
//
|
|
// result=multiItemBuffer->put(inItem);
|
|
// %%verify !result
|
|
// %%verify !multiItemBuffer->isEmpty()
|
|
// %%verify multiItemBuffer->isFull()
|
|
//
|
|
|
|
|
|
}
|
|
void create_workers_Buffers(AvmCore* core) { new ST_workers_Buffers(core); }
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Generated from ST_workers_NoSyncSingleItemBuffer.st
|
|
// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5)
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
#if !defined DEBUGGER
|
|
namespace avmplus {
|
|
namespace ST_workers_NoSyncSingleItemBuffer {
|
|
class ST_workers_NoSyncSingleItemBuffer : public Selftest {
|
|
public:
|
|
ST_workers_NoSyncSingleItemBuffer(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
virtual void epilogue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
void test1();
|
|
void test2();
|
|
void test3();
|
|
MMgc::GC *gc;
|
|
NoSyncSingleItemBuffer *buffer;
|
|
Atom *atom;
|
|
|
|
};
|
|
ST_workers_NoSyncSingleItemBuffer::ST_workers_NoSyncSingleItemBuffer(AvmCore* core)
|
|
: Selftest(core, "workers", "NoSyncSingleItemBuffer", ST_workers_NoSyncSingleItemBuffer::ST_names,ST_workers_NoSyncSingleItemBuffer::ST_explicits)
|
|
{}
|
|
const char* ST_workers_NoSyncSingleItemBuffer::ST_names[] = {"constructor","emptyBuffer","fullBuffer","destroy", NULL };
|
|
const bool ST_workers_NoSyncSingleItemBuffer::ST_explicits[] = {false,false,false,false, false };
|
|
void ST_workers_NoSyncSingleItemBuffer::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
case 1: test1(); return;
|
|
case 2: test2(); return;
|
|
case 3: test3(); return;
|
|
}
|
|
}
|
|
void ST_workers_NoSyncSingleItemBuffer::prologue() {
|
|
gc=core->GetGC();
|
|
atom=(Atom *)malloc(100);
|
|
buffer=new NoSyncSingleItemBuffer(gc);
|
|
|
|
}
|
|
void ST_workers_NoSyncSingleItemBuffer::epilogue() {
|
|
delete buffer;
|
|
free(atom);
|
|
|
|
}
|
|
|
|
void ST_workers_NoSyncSingleItemBuffer::test0() {
|
|
// line 30 "ST_workers_NoSyncSingleItemBuffer.st"
|
|
verifyPass(buffer!=NULL, "buffer!=NULL", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_workers_NoSyncSingleItemBuffer::test1() {
|
|
// line 33 "ST_workers_NoSyncSingleItemBuffer.st"
|
|
verifyPass(buffer->isEmpty(), "buffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 34 "ST_workers_NoSyncSingleItemBuffer.st"
|
|
verifyPass(!buffer->isFull(), "!buffer->isFull()", __FILE__, __LINE__);
|
|
// line 35 "ST_workers_NoSyncSingleItemBuffer.st"
|
|
verifyPass(!buffer->get(atom), "!buffer->get(atom)", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_workers_NoSyncSingleItemBuffer::test2() {
|
|
buffer->put(String::createLatin1(core,"test string")->atom());
|
|
// line 39 "ST_workers_NoSyncSingleItemBuffer.st"
|
|
verifyPass(!buffer->isEmpty(), "!buffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 40 "ST_workers_NoSyncSingleItemBuffer.st"
|
|
verifyPass(buffer->isFull(), "buffer->isFull()", __FILE__, __LINE__);
|
|
// line 41 "ST_workers_NoSyncSingleItemBuffer.st"
|
|
verifyPass(buffer->get(atom), "buffer->get(atom)", __FILE__, __LINE__);
|
|
// line 42 "ST_workers_NoSyncSingleItemBuffer.st"
|
|
verifyPass(buffer->isEmpty(), "buffer->isEmpty()", __FILE__, __LINE__);
|
|
// line 43 "ST_workers_NoSyncSingleItemBuffer.st"
|
|
verifyPass(!buffer->isFull(), "!buffer->isFull()", __FILE__, __LINE__);
|
|
|
|
}
|
|
void ST_workers_NoSyncSingleItemBuffer::test3() {
|
|
buffer->unregisterRoot();
|
|
// line 47 "ST_workers_NoSyncSingleItemBuffer.st"
|
|
verifyPass(true, "true", __FILE__, __LINE__);
|
|
|
|
|
|
|
|
}
|
|
void create_workers_NoSyncSingleItemBuffer(AvmCore* core) { new ST_workers_NoSyncSingleItemBuffer(core); }
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Generated from ST_workers_Promise.st
|
|
// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5)
|
|
// 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/.
|
|
|
|
#include "avmshell.h"
|
|
#ifdef VMCFG_SELFTEST
|
|
namespace avmplus {
|
|
namespace ST_workers_Promise {
|
|
class ST_workers_Promise : public Selftest {
|
|
public:
|
|
ST_workers_Promise(AvmCore* core);
|
|
virtual void run(int n);
|
|
virtual void prologue();
|
|
private:
|
|
static const char* ST_names[];
|
|
static const bool ST_explicits[];
|
|
void test0();
|
|
Isolate *main;
|
|
|
|
};
|
|
ST_workers_Promise::ST_workers_Promise(AvmCore* core)
|
|
: Selftest(core, "workers", "Promise", ST_workers_Promise::ST_names,ST_workers_Promise::ST_explicits)
|
|
{}
|
|
const char* ST_workers_Promise::ST_names[] = {"MainWorker", NULL };
|
|
const bool ST_workers_Promise::ST_explicits[] = {false, false };
|
|
void ST_workers_Promise::run(int n) {
|
|
switch(n) {
|
|
case 0: test0(); return;
|
|
}
|
|
}
|
|
void ST_workers_Promise::prologue() {
|
|
main=core->getIsolate();
|
|
|
|
|
|
}
|
|
void ST_workers_Promise::test0() {
|
|
// verify the main worker can be retrieved from AvmCore
|
|
// line 19 "ST_workers_Promise.st"
|
|
verifyPass(main!=NULL, "main!=NULL", __FILE__, __LINE__);
|
|
uint32_t state=main->getAggregate()->queryState(main);
|
|
// verify state == RUNNING
|
|
//printf("state=%d\n",state);
|
|
// line 23 "ST_workers_Promise.st"
|
|
verifyPass(state==4, "state==4", __FILE__, __LINE__);
|
|
|
|
// line 25 "ST_workers_Promise.st"
|
|
verifyPass(main->getAvmCore() == core, "main->getAvmCore() == core", __FILE__, __LINE__);
|
|
// line 26 "ST_workers_Promise.st"
|
|
verifyPass(main->isParentOf(main) == false, "main->isParentOf(main) == false", __FILE__, __LINE__);
|
|
|
|
|
|
}
|
|
void create_workers_Promise(AvmCore* core) { new ST_workers_Promise(core); }
|
|
}
|
|
}
|
|
#endif
|
|
|