--- old/make/hotspot/src/native/dtrace/generateJvmOffsets.cpp 2018-07-18 23:08:47.036706425 -0400 +++ new/make/hotspot/src/native/dtrace/generateJvmOffsets.cpp 2018-07-18 23:08:46.796706434 -0400 @@ -212,7 +212,7 @@ GEN_VALUE(AccessFlags_NATIVE, JVM_ACC_NATIVE); GEN_VALUE(ConstMethod_has_linenumber_table, ConstMethod::_has_linenumber_table); GEN_OFFS(AccessFlags, _flags); - GEN_OFFS(Symbol, _length); + GEN_OFFS(Symbol, _length_and_refcount); GEN_OFFS(Symbol, _body); printf("\n"); --- old/src/hotspot/os/solaris/dtrace/jhelper.d 2018-07-18 23:08:47.360706414 -0400 +++ new/src/hotspot/os/solaris/dtrace/jhelper.d 2018-07-18 23:08:47.140706422 -0400 @@ -111,7 +111,7 @@ copyin_offset(OFFSET_HeapBlockHeader_used); copyin_offset(OFFSET_oopDesc_metadata); - copyin_offset(OFFSET_Symbol_length); + copyin_offset(OFFSET_Symbol_length_and_refcount); copyin_offset(OFFSET_Symbol_body); copyin_offset(OFFSET_Method_constMethod); @@ -463,15 +463,16 @@ /* The symbol is a CPSlot and has lower bit set to indicate metadata */ this->nameSymbol &= (~1); /* remove metadata lsb */ + /* Because sparc is big endian, the top half length is at the correct offset. */ this->nameSymbolLength = copyin_uint16(this->nameSymbol + - OFFSET_Symbol_length); + OFFSET_Symbol_length_and_refcount); this->signatureSymbol = copyin_ptr(this->constantPool + this->signatureIndex * sizeof (pointer) + SIZE_ConstantPool); this->signatureSymbol &= (~1); /* remove metadata lsb */ this->signatureSymbolLength = copyin_uint16(this->signatureSymbol + - OFFSET_Symbol_length); + OFFSET_Symbol_length_and_refcount); this->klassPtr = copyin_ptr(this->constantPool + OFFSET_ConstantPool_pool_holder); @@ -480,7 +481,7 @@ OFFSET_Klass_name); this->klassSymbolLength = copyin_uint16(this->klassSymbol + - OFFSET_Symbol_length); + OFFSET_Symbol_length_and_refcount); /* * Enough for three strings, plus the '.', plus the trailing '\0'. --- old/src/hotspot/share/classfile/compactHashtable.inline.hpp 2018-07-18 23:08:47.640706404 -0400 +++ new/src/hotspot/share/classfile/compactHashtable.inline.hpp 2018-07-18 23:08:47.440706411 -0400 @@ -36,7 +36,7 @@ u4 offset, const char* name, int len) { Symbol* sym = (Symbol*)(_base_address + offset); if (sym->equals(name, len)) { - assert(sym->refcount() == -1, "must be shared"); + assert(sym->refcount() == PERM_REFCOUNT, "must be shared"); return sym; } --- old/src/hotspot/share/classfile/symbolTable.cpp 2018-07-18 23:08:47.964706393 -0400 +++ new/src/hotspot/share/classfile/symbolTable.cpp 2018-07-18 23:08:47.732706401 -0400 @@ -212,9 +212,8 @@ count++; // count all entries in this bucket, not just ones with same hash if (e->hash() == hash) { Symbol* sym = e->literal(); - if (sym->equals(name, len)) { + if (sym->equals(name, len) && sym->try_increment_refcount()) { // something is referencing this symbol now. - sym->increment_refcount(); return sym; } } --- old/src/hotspot/share/classfile/systemDictionary.cpp 2018-07-18 23:08:48.300706381 -0400 +++ new/src/hotspot/share/classfile/systemDictionary.cpp 2018-07-18 23:08:48.072706389 -0400 @@ -2226,12 +2226,14 @@ ClassLoaderData* loader_data2 = class_loader_data(class_loader2); Symbol* constraint_name = NULL; + // Needs to be in same scope as constraint_name in case a Symbol is created and + // assigned to constraint_name. + FieldArrayInfo fd; if (!FieldType::is_array(class_name)) { constraint_name = class_name; } else { // For array classes, their Klass*s are not kept in the // constraint table. The element classes are. - FieldArrayInfo fd; BasicType t = FieldType::get_array_info(class_name, fd, CHECK_(false)); // primitive types always pass if (t != T_OBJECT) { --- old/src/hotspot/share/oops/symbol.cpp 2018-07-18 23:08:48.668706369 -0400 +++ new/src/hotspot/share/oops/symbol.cpp 2018-07-18 23:08:48.440706376 -0400 @@ -34,11 +34,22 @@ #include "runtime/atomic.hpp" #include "runtime/os.hpp" +uint32_t Symbol::pack_length_and_refcount(int length, int refcount) { + STATIC_ASSERT(max_symbol_length == ((1 << 16) - 1)); + STATIC_ASSERT(PERM_REFCOUNT == ((1 << 16) - 1)); + assert(length >= 0, "negative length"); + assert(length <= max_symbol_length, "too long symbol"); + assert(refcount >= 0, "negative refcount"); + assert(refcount <= PERM_REFCOUNT, "invalid refcount"); + uint32_t hi = length; + uint32_t lo = refcount; + return (hi << 16) | lo; +} + Symbol::Symbol(const u1* name, int length, int refcount) { - _refcount = refcount; - _length = length; + _length_and_refcount = pack_length_and_refcount(length, refcount); _identity_hash = (short)os::random(); - for (int i = 0; i < _length; i++) { + for (int i = 0; i < length; i++) { byte_at_put(i, name[i]); } } @@ -207,26 +218,66 @@ return AltHashing::murmur3_32(seed, (const jbyte*)as_C_string(), utf8_length()); } +// Increment refcount while checking for zero. If the Symbol's refcount becomes zero +// a thread could be concurrently removing the Symbol. This is used during SymbolTable +// lookup to avoid reviving a dead Symbol. +bool Symbol::try_increment_refcount() { + uint32_t found = _length_and_refcount; + while (true) { + uint32_t old_value = found; + int refc = extract_refcount(old_value); + if (refc == PERM_REFCOUNT) { + return true; // sticky max or created permanent + } else if (refc == 0) { + return false; // dead, can't revive. + } else { + found = Atomic::cmpxchg(old_value + 1, &_length_and_refcount, old_value); + if (found == old_value) { + return true; // successfully updated. + } + // refcount changed, try again. + } + } +} + +// The increment_refcount() is called when not doing lookup. It is assumed that you +// have a symbol with a non-zero refcount and it can't become zero while referenced by +// this caller. void Symbol::increment_refcount() { - // Only increment the refcount if non-negative. If negative either - // overflow has occurred or it is a permanent symbol in a read only - // shared archive. - if (_refcount >= 0) { // not a permanent symbol - Atomic::inc(&_refcount); + if (refcount() != PERM_REFCOUNT) { // not a permanent symbol + if (!try_increment_refcount()) { +#ifdef ASSERT + print(); + fatal("refcount has gone to zero"); +#endif + } NOT_PRODUCT(Atomic::inc(&_total_count);) } } +// Decrement refcount potentially while racing increment, so we need +// to check the value after attempting to decrement so that if another +// thread increments to PERM_REFCOUNT the value is not decremented. void Symbol::decrement_refcount() { - if (_refcount >= 0) { // not a permanent symbol - short new_value = Atomic::add(short(-1), &_refcount); + uint32_t found = _length_and_refcount; + while (true) { + uint32_t old_value = found; + int refc = extract_refcount(old_value); + if (refc == PERM_REFCOUNT) { + return; // refcount is permanent, permanent is sticky + } else if (refc == 0) { #ifdef ASSERT - if (new_value == -1) { // we have transitioned from 0 -> -1 print(); - assert(false, "reference count underflow for symbol"); - } + fatal("refcount underflow"); #endif - (void)new_value; + return; + } else { + found = Atomic::cmpxchg(old_value - 1, &_length_and_refcount, old_value); + if (found == old_value) { + return; // successfully updated. + } + // refcount changed, try again. + } } } --- old/src/hotspot/share/oops/symbol.hpp 2018-07-18 23:08:48.964706358 -0400 +++ new/src/hotspot/share/oops/symbol.hpp 2018-07-18 23:08:48.756706365 -0400 @@ -96,9 +96,9 @@ // type without virtual functions. class ClassLoaderData; -// Set _refcount to PERM_REFCOUNT to prevent the Symbol from being GC'ed. +// Set _refcount to PERM_REFCOUNT to prevent the Symbol from being freed. #ifndef PERM_REFCOUNT -#define PERM_REFCOUNT -1 +#define PERM_REFCOUNT ((1 << 16) - 1) #endif class Symbol : public MetaspaceObj { @@ -107,15 +107,15 @@ friend class MoveSymbols; private: - ATOMIC_SHORT_PAIR( - volatile short _refcount, // needs atomic operation - unsigned short _length // number of UTF8 characters in the symbol (does not need atomic op) - ); + + // This is an int because it needs atomic operation on the refcount. Mask length + // in high half word. length is the number of UTF8 characters in the symbol + volatile uint32_t _length_and_refcount; short _identity_hash; jbyte _body[2]; enum { - // max_symbol_length is constrained by type of _length + // max_symbol_length must fit into the top 16 bits of _length_and_refcount max_symbol_length = (1 << 16) -1 }; @@ -129,7 +129,7 @@ } void byte_at_put(int index, int value) { - assert(index >=0 && index < _length, "symbol index overflow"); + assert(index >=0 && index < length(), "symbol index overflow"); _body[index] = value; } @@ -140,6 +140,12 @@ void operator delete(void* p); + static int extract_length(uint32_t value) { return value >> 16; } + static int extract_refcount(uint32_t value) { return value & 0xffff; } + static uint32_t pack_length_and_refcount(int length, int refcount); + + int length() const { return extract_length(_length_and_refcount); } + public: // Low-level access (used with care, since not GC-safe) const jbyte* base() const { return &_body[0]; } @@ -155,28 +161,29 @@ unsigned identity_hash() const { unsigned addr_bits = (unsigned)((uintptr_t)this >> (LogMinObjAlignmentInBytes + 3)); return ((unsigned)_identity_hash & 0xffff) | - ((addr_bits ^ (_length << 8) ^ (( _body[0] << 8) | _body[1])) << 16); + ((addr_bits ^ (length() << 8) ^ (( _body[0] << 8) | _body[1])) << 16); } // For symbol table alternate hashing unsigned int new_hash(juint seed); // Reference counting. See comments above this class for when to use. - int refcount() const { return _refcount; } + int refcount() const { return extract_refcount(_length_and_refcount); } + bool try_increment_refcount(); void increment_refcount(); void decrement_refcount(); bool is_permanent() { - return (_refcount == PERM_REFCOUNT); + return (refcount() == PERM_REFCOUNT); } int byte_at(int index) const { - assert(index >=0 && index < _length, "symbol index overflow"); + assert(index >=0 && index < length(), "symbol index overflow"); return base()[index]; } const jbyte* bytes() const { return base(); } - int utf8_length() const { return _length; } + int utf8_length() const { return length(); } // Compares the symbol with a string. bool equals(const char* str, int len) const { --- old/src/hotspot/share/runtime/vmStructs.cpp 2018-07-18 23:08:49.296706347 -0400 +++ new/src/hotspot/share/runtime/vmStructs.cpp 2018-07-18 23:08:49.064706355 -0400 @@ -330,9 +330,8 @@ nonstatic_field(ConstMethod, _size_of_parameters, u2) \ nonstatic_field(ObjArrayKlass, _element_klass, Klass*) \ nonstatic_field(ObjArrayKlass, _bottom_klass, Klass*) \ - volatile_nonstatic_field(Symbol, _refcount, short) \ + volatile_nonstatic_field(Symbol, _length_and_refcount, unsigned int) \ nonstatic_field(Symbol, _identity_hash, short) \ - nonstatic_field(Symbol, _length, unsigned short) \ unchecked_nonstatic_field(Symbol, _body, sizeof(jbyte)) /* NOTE: no type */ \ nonstatic_field(Symbol, _body[0], jbyte) \ nonstatic_field(TypeArrayKlass, _max_length, jint) \ --- old/src/java.base/solaris/native/libjvm_db/libjvm_db.c 2018-07-18 23:08:49.684706333 -0400 +++ new/src/java.base/solaris/native/libjvm_db/libjvm_db.c 2018-07-18 23:08:49.448706341 -0400 @@ -552,7 +552,8 @@ CHECK_FAIL(err); // The symbol is a CPSlot and has lower bit set to indicate metadata nameSymbol &= (~1); // remove metadata lsb - err = ps_pread(J->P, nameSymbol + OFFSET_Symbol_length, &nameSymbolLength, 2); + // The length is in the top half of the word. + err = ps_pread(J->P, nameSymbol + OFFSET_Symbol_length_and_refcount, &nameSymbolLength, 2); CHECK_FAIL(err); nameString = (char*)calloc(nameSymbolLength + 1, 1); err = ps_pread(J->P, nameSymbol + OFFSET_Symbol_body, nameString, nameSymbolLength); @@ -564,7 +565,7 @@ err = read_pointer(J, constantPool + signatureIndex * POINTER_SIZE + SIZE_ConstantPool, &signatureSymbol); CHECK_FAIL(err); signatureSymbol &= (~1); // remove metadata lsb - err = ps_pread(J->P, signatureSymbol + OFFSET_Symbol_length, &signatureSymbolLength, 2); + err = ps_pread(J->P, signatureSymbol + OFFSET_Symbol_length_and_refcount, &signatureSymbolLength, 2); CHECK_FAIL(err); signatureString = (char*)calloc(signatureSymbolLength + 1, 1); err = ps_pread(J->P, signatureSymbol + OFFSET_Symbol_body, signatureString, signatureSymbolLength); @@ -575,7 +576,7 @@ CHECK_FAIL(err); err = read_pointer(J, klassPtr + OFFSET_Klass_name, &klassSymbol); CHECK_FAIL(err); - err = ps_pread(J->P, klassSymbol + OFFSET_Symbol_length, &klassSymbolLength, 2); + err = ps_pread(J->P, klassSymbol + OFFSET_Symbol_length_and_refcount, &klassSymbolLength, 2); CHECK_FAIL(err); klassString = (char*)calloc(klassSymbolLength + 1, 1); err = ps_pread(J->P, klassSymbol + OFFSET_Symbol_body, klassString, klassSymbolLength); --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Symbol.java 2018-07-18 23:08:50.024706321 -0400 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Symbol.java 2018-07-18 23:08:49.804706329 -0400 @@ -45,7 +45,7 @@ private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { Type type = db.lookupType("Symbol"); - length = type.getCIntegerField("_length"); + length = type.getCIntegerField("_length_and_refcount"); baseOffset = type.getField("_body").getOffset(); idHash = type.getCIntegerField("_identity_hash"); } @@ -75,7 +75,10 @@ private static CIntegerField length; // Accessors for declared fields - public long getLength() { return length.getValue(this.addr); } + public long getLength() { + long i = length.getValue(this.addr); + return (i >> 16) & 0xffff; + } public byte getByteAt(long index) { return addr.getJByteAt(baseOffset + index); --- old/test/hotspot/gtest/classfile/test_symbolTable.cpp 2018-07-18 23:08:50.312706311 -0400 +++ new/test/hotspot/gtest/classfile/test_symbolTable.cpp 2018-07-18 23:08:50.108706318 -0400 @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "classfile/symbolTable.hpp" +#include "threadHelper.inline.hpp" #include "unittest.hpp" TEST_VM(SymbolTable, temp_new_symbol) { @@ -74,4 +75,76 @@ } ASSERT_EQ(xyz->refcount(), xyzcount - 1) << "Should have been decremented by dtor in inner scope"; + + // Test overflowing refcount making symbol permanent + Symbol* bigsym = SymbolTable::new_symbol("bigsym", CATCH); + for (int i = 0; i < PERM_REFCOUNT + 100; i++) { + bigsym->increment_refcount(); + } + ASSERT_EQ(bigsym->refcount(), PERM_REFCOUNT) << "should not have overflowed"; + + // Test that PERM_REFCOUNT is sticky + for (int i = 0; i < PERM_REFCOUNT + 100; i++) { + bigsym->decrement_refcount(); + } + ASSERT_EQ(bigsym->refcount(), PERM_REFCOUNT) << "should be sticky"; +} + +// TODO: Make two threads one decrementing the refcount and the other trying to increment. +// try_increment_refcount should return false + +#define SYM_NAME_LENGTH 30 +static char symbol_name[SYM_NAME_LENGTH]; + +class SymbolThread : public JavaTestThread { + public: + SymbolThread(Semaphore* post) : JavaTestThread(post) {} + virtual ~SymbolThread() {} + void main_run() { + Thread* THREAD = Thread::current(); + for (int i = 0; i < 1000; i++) { + TempNewSymbol sym = SymbolTable::new_symbol(symbol_name, CATCH); + // Create and destroy new symbol + EXPECT_TRUE(sym->refcount() != 0) << "Symbol refcount unexpectedly zeroed"; + } + } +}; + +#define SYM_TEST_THREAD_COUNT 5 + +class DriverSymbolThread : public JavaTestThread { +public: + Semaphore _done; + DriverSymbolThread(Semaphore* post) : JavaTestThread(post) { }; + virtual ~DriverSymbolThread(){} + + void main_run() { + Semaphore done(0); + + Thread* THREAD = Thread::current(); + + // Find a symbol where there will probably be only one instance. + for (int i = 0; i < 100; i++) { + snprintf(symbol_name, SYM_NAME_LENGTH, "some_symbol%d", i); + TempNewSymbol ts = SymbolTable::new_symbol(symbol_name, CATCH); + if (ts->refcount() == 1) { + EXPECT_TRUE(ts->refcount() == 1) << "Symbol is just created"; + break; // found a unique symbol + } + } + + SymbolThread* st[SYM_TEST_THREAD_COUNT]; + for (int i = 0; i < SYM_TEST_THREAD_COUNT; i++) { + st[i] = new SymbolThread(&done); + st[i]->doit(); + } + + for (int i = 0; i < 4; i++) { + done.wait(); + } + } +}; + +TEST_VM(SymbolTable, test_symbol_refcount_parallel) { + mt_test_doer(); } --- old/test/hotspot/gtest/utilities/test_concurrentHashtable.cpp 2018-07-18 23:08:50.612706301 -0400 +++ new/test/hotspot/gtest/utilities/test_concurrentHashtable.cpp 2018-07-18 23:08:50.396706308 -0400 @@ -29,7 +29,7 @@ #include "runtime/vm_operations.hpp" #include "utilities/concurrentHashTable.inline.hpp" #include "utilities/concurrentHashTableTasks.inline.hpp" -#include "utilitiesHelper.inline.hpp" +#include "threadHelper.inline.hpp" #include "unittest.hpp" // NOTE: On win32 gtest asserts are not mt-safe. --- old/test/hotspot/gtest/utilities/test_globalCounter.cpp 2018-07-18 23:08:50.924706290 -0400 +++ new/test/hotspot/gtest/utilities/test_globalCounter.cpp 2018-07-18 23:08:50.716706297 -0400 @@ -27,7 +27,7 @@ #include "runtime/os.hpp" #include "utilities/globalCounter.hpp" #include "utilities/globalCounter.inline.hpp" -#include "utilitiesHelper.inline.hpp" +#include "threadHelper.inline.hpp" #define GOOD_VALUE 1337 #define BAD_VALUE 4711 --- old/test/hotspot/gtest/utilities/utilitiesHelper.inline.hpp 2018-07-18 23:08:51.364706275 -0400 +++ /dev/null 2018-07-12 14:05:37.184008350 -0400 @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef GTEST_UTILITIES_HELPER_INLINE_HPP -#define GTEST_UTILITIES_HELPER_INLINE_HPP - -#include "runtime/mutex.hpp" -#include "runtime/semaphore.hpp" -#include "runtime/thread.hpp" -#include "runtime/vmThread.hpp" -#include "runtime/vm_operations.hpp" -#include "unittest.hpp" - -class VM_StopSafepoint : public VM_Operation { -public: - Semaphore* _test_complete; - VM_StopSafepoint(Semaphore* wait_for) : _test_complete(wait_for) {} - VMOp_Type type() const { return VMOp_None; } - Mode evaluation_mode() const { return _no_safepoint; } - bool is_cheap_allocated() const { return false; } - void doit() { _test_complete->wait(); } -}; - -// This class and thread keep the non-safepoint op running while we do our testing. -class VMThreadBlocker : public JavaThread { -public: - Semaphore* _unblock; - Semaphore* _done; - VMThreadBlocker(Semaphore* ub, Semaphore* done) : _unblock(ub), _done(done) { - } - virtual ~VMThreadBlocker() {} - void run() { - this->set_thread_state(_thread_in_vm); - { - MutexLocker ml(Threads_lock); - Threads::add(this); - } - VM_StopSafepoint ss(_unblock); - VMThread::execute(&ss); - _done->signal(); - Threads::remove(this); - this->smr_delete(); - } - void doit() { - if (os::create_thread(this, os::os_thread)) { - os::start_thread(this); - } else { - ASSERT_TRUE(false); - } - } -}; - -// For testing in a real JavaThread. -class JavaTestThread : public JavaThread { -public: - Semaphore* _post; - JavaTestThread(Semaphore* post) - : _post(post) { - } - virtual ~JavaTestThread() {} - - void prerun() { - this->set_thread_state(_thread_in_vm); - { - MutexLocker ml(Threads_lock); - Threads::add(this); - } - { - MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); - } - } - - virtual void main_run() = 0; - - void run() { - prerun(); - main_run(); - postrun(); - } - - void postrun() { - Threads::remove(this); - _post->signal(); - this->smr_delete(); - } - - void doit() { - if (os::create_thread(this, os::os_thread)) { - os::start_thread(this); - } else { - ASSERT_TRUE(false); - } - } -}; - -template -class SingleTestThread : public JavaTestThread { -public: - FUNC& _f; - SingleTestThread(Semaphore* post, FUNC& f) - : JavaTestThread(post), _f(f) { - } - - virtual ~SingleTestThread(){} - - virtual void main_run() { - _f(this); - } -}; - -template -static void nomt_test_doer(TESTFUNC &f) { - Semaphore post, block_done, vmt_done; - VMThreadBlocker* blocker = new VMThreadBlocker(&block_done, &vmt_done); - blocker->doit(); - SingleTestThread* stt = new SingleTestThread(&post, f); - stt->doit(); - post.wait(); - block_done.signal(); - vmt_done.wait(); -} - -template -static void mt_test_doer() { - Semaphore post, block_done, vmt_done; - VMThreadBlocker* blocker = new VMThreadBlocker(&block_done, &vmt_done); - blocker->doit(); - RUNNER* runner = new RUNNER(&post); - runner->doit(); - post.wait(); - block_done.signal(); - vmt_done.wait(); -} - -#endif // include guard --- /dev/null 2018-07-12 14:05:37.184008350 -0400 +++ new/test/hotspot/gtest/threadHelper.inline.hpp 2018-07-18 23:08:51.016706287 -0400 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef GTEST_THREADHELPER_INLINE_HPP +#define GTEST_THREADHELPER_INLINE_HPP + +#include "runtime/mutex.hpp" +#include "runtime/semaphore.hpp" +#include "runtime/thread.hpp" +#include "runtime/vmThread.hpp" +#include "runtime/vm_operations.hpp" +#include "unittest.hpp" + +class VM_StopSafepoint : public VM_Operation { +public: + Semaphore* _test_complete; + VM_StopSafepoint(Semaphore* wait_for) : _test_complete(wait_for) {} + VMOp_Type type() const { return VMOp_None; } + Mode evaluation_mode() const { return _no_safepoint; } + bool is_cheap_allocated() const { return false; } + void doit() { _test_complete->wait(); } +}; + +// This class and thread keep the non-safepoint op running while we do our testing. +class VMThreadBlocker : public JavaThread { +public: + Semaphore* _unblock; + Semaphore* _done; + VMThreadBlocker(Semaphore* ub, Semaphore* done) : _unblock(ub), _done(done) { + } + virtual ~VMThreadBlocker() {} + void run() { + this->set_thread_state(_thread_in_vm); + { + MutexLocker ml(Threads_lock); + Threads::add(this); + } + VM_StopSafepoint ss(_unblock); + VMThread::execute(&ss); + _done->signal(); + Threads::remove(this); + this->smr_delete(); + } + void doit() { + if (os::create_thread(this, os::os_thread)) { + os::start_thread(this); + } else { + ASSERT_TRUE(false); + } + } +}; + +// For testing in a real JavaThread. +class JavaTestThread : public JavaThread { +public: + Semaphore* _post; + JavaTestThread(Semaphore* post) + : _post(post) { + } + virtual ~JavaTestThread() {} + + void prerun() { + this->set_thread_state(_thread_in_vm); + { + MutexLocker ml(Threads_lock); + Threads::add(this); + } + { + MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); + } + } + + virtual void main_run() = 0; + + void run() { + prerun(); + main_run(); + postrun(); + } + + void postrun() { + Threads::remove(this); + _post->signal(); + this->smr_delete(); + } + + void doit() { + if (os::create_thread(this, os::os_thread)) { + os::start_thread(this); + } else { + ASSERT_TRUE(false); + } + } +}; + +template +class SingleTestThread : public JavaTestThread { +public: + FUNC& _f; + SingleTestThread(Semaphore* post, FUNC& f) + : JavaTestThread(post), _f(f) { + } + + virtual ~SingleTestThread(){} + + virtual void main_run() { + _f(this); + } +}; + +template +static void nomt_test_doer(TESTFUNC &f) { + Semaphore post, block_done, vmt_done; + VMThreadBlocker* blocker = new VMThreadBlocker(&block_done, &vmt_done); + blocker->doit(); + SingleTestThread* stt = new SingleTestThread(&post, f); + stt->doit(); + post.wait(); + block_done.signal(); + vmt_done.wait(); +} + +template +static void mt_test_doer() { + Semaphore post, block_done, vmt_done; + VMThreadBlocker* blocker = new VMThreadBlocker(&block_done, &vmt_done); + blocker->doit(); + RUNNER* runner = new RUNNER(&post); + runner->doit(); + post.wait(); + block_done.signal(); + vmt_done.wait(); +} + +#endif // include guard