// 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("Hello, world!"); 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(""); } 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 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 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 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 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 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 m_next; GCMember 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 workerClass() const { return NULL; } virtual GCRef workerDomainClass() const { return NULL; } virtual GCRef 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; cpolicy.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<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 '' 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 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 '=' 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 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 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