Merge
This commit is contained in:
commit
50862707c8
1
.hgtags
1
.hgtags
@ -369,3 +369,4 @@ d53037a90c441cb528dc41c30827985de0e67c62 jdk-9+123
|
||||
2a5697a98620c4f40e4a1a71478464399b8878de jdk-9+124
|
||||
3aa52182b3ad7c5b3a61cf05a59dd07e4c5884e5 jdk-9+125
|
||||
03e7b2c5ae345be3caf981d76ceb3efe5ff447f8 jdk-9+126
|
||||
8e45018bde9de4ad15b972ae62874bba52dba2d5 jdk-9+127
|
||||
|
@ -369,3 +369,4 @@ cae471d3b87783e0a3deea658e1e1c84b2485b6c jdk-9+121
|
||||
f80c841ae2545eaf9acd2724bccc305d98cefbe2 jdk-9+124
|
||||
9aa7d40f3a453f51e47f4c1b19eff5740a74a9f8 jdk-9+125
|
||||
3a58466296d36944454756ef01e7513ac5e14a16 jdk-9+126
|
||||
8fa686245bd2a072ece3392743460030f0854520 jdk-9+127
|
||||
|
@ -369,3 +369,4 @@ e33a34cc551907617d8129c4faaf1a5a7e61d21c jdk-9+123
|
||||
45121d5afb9d5bfadab75378572ad96832e0809e jdk-9+124
|
||||
1d48e67d1b91eb9f72e49e69a4021edb85e357fc jdk-9+125
|
||||
c7f5ba08fcd4b8416e62c21229f9a07c95498919 jdk-9+126
|
||||
8fab452b6f4710762ba1d8e55fd62db00b1355fe jdk-9+127
|
||||
|
@ -529,3 +529,4 @@ af6b4ad908e732d23021f12e8322b204433d5cf6 jdk-9+122
|
||||
479631362b4930be985245ea063d87d821a472eb jdk-9+124
|
||||
bb640b49741af3f57f9994129934c46fc173219f jdk-9+125
|
||||
adc8c84b7cf8c540d920182f78a2bc982366432a jdk-9+126
|
||||
352357128f602dcf0426b1cbe011a4685a4d9f97 jdk-9+127
|
||||
|
@ -45,7 +45,8 @@ ifeq ($(call check-jvm-feature, dtrace), true)
|
||||
$(DTRACE_GENSRC_DIR)/%.h: $(DTRACE_SOURCE_DIR)/%.d
|
||||
$(call LogInfo, Generating dtrace header file $(@F))
|
||||
$(call MakeDir, $(@D) $(DTRACE_SUPPORT_DIR))
|
||||
$(call ExecuteWithLog, $(DTRACE_SUPPORT_DIR)/$(@F).d, $(CC) -E $(DTRACE_CPP_FLAGS) $< > $(DTRACE_SUPPORT_DIR)/$(@F).d)
|
||||
$(call ExecuteWithLog, $(DTRACE_SUPPORT_DIR)/$(@F).d, \
|
||||
( $(CC) -E $(DTRACE_CPP_FLAGS) $< > $(DTRACE_SUPPORT_DIR)/$(@F).d ) )
|
||||
$(call ExecuteWithLog, $@, $(DTRACE) $(DTRACE_FLAGS) -h -o $@ -s $(DTRACE_SUPPORT_DIR)/$(@F).d)
|
||||
|
||||
# Process all .d files in DTRACE_SOURCE_DIR. They are:
|
||||
|
@ -68,7 +68,7 @@ ifeq ($(call check-jvm-feature, dtrace), true)
|
||||
$1: $$(BUILD_DTRACE_GEN_OFFSETS)
|
||||
$$(call LogInfo, Generating dtrace $2 file $$(@F))
|
||||
$$(call MakeDir, $$(@D))
|
||||
$$(call ExecuteWithLog, $$@, $$(DTRACE_GEN_OFFSETS_TOOL) -$$(strip $2) > $$@)
|
||||
$$(call ExecuteWithLog, $$@, ( $$(DTRACE_GEN_OFFSETS_TOOL) -$$(strip $2) > $$@ ) )
|
||||
|
||||
TARGETS += $1
|
||||
endef
|
||||
|
@ -16,9 +16,9 @@
|
||||
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
package com.sun.hotspot.tools.compiler;
|
||||
|
@ -501,13 +501,26 @@ ClassLoaderData::~ClassLoaderData() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this class loader data is for the platform class loader.
|
||||
*/
|
||||
// Returns true if this class loader data is for the system class loader.
|
||||
bool ClassLoaderData::is_system_class_loader_data() const {
|
||||
return SystemDictionary::is_system_class_loader(class_loader());
|
||||
}
|
||||
|
||||
// Returns true if this class loader data is for the platform class loader.
|
||||
bool ClassLoaderData::is_platform_class_loader_data() const {
|
||||
return SystemDictionary::is_platform_class_loader(class_loader());
|
||||
}
|
||||
|
||||
// Returns true if this class loader data is one of the 3 builtin
|
||||
// (boot, application/system or platform) class loaders. Note, the
|
||||
// builtin loaders are not freed by a GC.
|
||||
bool ClassLoaderData::is_builtin_class_loader_data() const {
|
||||
Handle classLoaderHandle = class_loader();
|
||||
return (is_the_null_class_loader_data() ||
|
||||
SystemDictionary::is_system_class_loader(classLoaderHandle) ||
|
||||
SystemDictionary::is_platform_class_loader(classLoaderHandle));
|
||||
}
|
||||
|
||||
Metaspace* ClassLoaderData::metaspace_non_null() {
|
||||
assert(!DumpSharedSpaces, "wrong metaspace!");
|
||||
// If the metaspace has not been allocated, create a new one. Might want
|
||||
@ -957,12 +970,6 @@ bool ClassLoaderDataGraph::do_unloading(BoolObjectClosure* is_alive_closure,
|
||||
data = _head;
|
||||
while (data != NULL) {
|
||||
if (data->is_alive(is_alive_closure)) {
|
||||
if (data->packages_defined()) {
|
||||
data->packages()->purge_all_package_exports();
|
||||
}
|
||||
if (data->modules_defined()) {
|
||||
data->modules()->purge_all_module_reads();
|
||||
}
|
||||
// clean metaspace
|
||||
if (walk_all_metadata) {
|
||||
data->classes_do(InstanceKlass::purge_previous_versions);
|
||||
@ -990,6 +997,23 @@ bool ClassLoaderDataGraph::do_unloading(BoolObjectClosure* is_alive_closure,
|
||||
}
|
||||
|
||||
if (seen_dead_loader) {
|
||||
// Walk a ModuleEntry's reads and a PackageEntry's exports lists
|
||||
// to determine if there are modules on those lists that are now
|
||||
// dead and should be removed. A module's life cycle is equivalent
|
||||
// to its defining class loader's life cycle. Since a module is
|
||||
// considered dead if its class loader is dead, these walks must
|
||||
// occur after each class loader's aliveness is determined.
|
||||
data = _head;
|
||||
while (data != NULL) {
|
||||
if (data->packages_defined()) {
|
||||
data->packages()->purge_all_package_exports();
|
||||
}
|
||||
if (data->modules_defined()) {
|
||||
data->modules()->purge_all_module_reads();
|
||||
}
|
||||
data = data->next();
|
||||
}
|
||||
|
||||
post_class_unload_events();
|
||||
}
|
||||
|
||||
|
@ -270,7 +270,9 @@ class ClassLoaderData : public CHeapObj<mtClass> {
|
||||
bool is_the_null_class_loader_data() const {
|
||||
return this == _the_null_class_loader_data;
|
||||
}
|
||||
bool is_system_class_loader_data() const;
|
||||
bool is_platform_class_loader_data() const;
|
||||
bool is_builtin_class_loader_data() const;
|
||||
|
||||
// The Metaspace is created lazily so may be NULL. This
|
||||
// method will allocate a Metaspace if needed.
|
||||
|
@ -248,7 +248,7 @@ inline void SimpleCompactHashtable::iterate(const I& iterator) {
|
||||
} else {
|
||||
u4*entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]);
|
||||
while (entry < entry_max) {
|
||||
iterator.do_value(_base_address, entry[0]);
|
||||
iterator.do_value(_base_address, entry[1]);
|
||||
entry += 2;
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,6 @@
|
||||
|
||||
ModuleEntry* ModuleEntryTable::_javabase_module = NULL;
|
||||
|
||||
|
||||
void ModuleEntry::set_location(Symbol* location) {
|
||||
if (_location != NULL) {
|
||||
// _location symbol's refcounts are managed by ModuleEntry,
|
||||
@ -115,10 +114,35 @@ void ModuleEntry::add_read(ModuleEntry* m) {
|
||||
// Lazily create a module's reads list
|
||||
_reads = new (ResourceObj::C_HEAP, mtModule)GrowableArray<ModuleEntry*>(MODULE_READS_SIZE, true);
|
||||
}
|
||||
|
||||
// Determine, based on this newly established read edge to module m,
|
||||
// if this module's read list should be walked at a GC safepoint.
|
||||
set_read_walk_required(m->loader_data());
|
||||
|
||||
// Establish readability to module m
|
||||
_reads->append_if_missing(m);
|
||||
}
|
||||
}
|
||||
|
||||
// If the module's loader, that a read edge is being established to, is
|
||||
// not the same loader as this module's and is not one of the 3 builtin
|
||||
// class loaders, then this module's reads list must be walked at GC
|
||||
// safepoint. Modules have the same life cycle as their defining class
|
||||
// loaders and should be removed if dead.
|
||||
void ModuleEntry::set_read_walk_required(ClassLoaderData* m_loader_data) {
|
||||
assert_locked_or_safepoint(Module_lock);
|
||||
if (!_must_walk_reads &&
|
||||
loader_data() != m_loader_data &&
|
||||
!m_loader_data->is_builtin_class_loader_data()) {
|
||||
_must_walk_reads = true;
|
||||
if (log_is_enabled(Trace, modules)) {
|
||||
ResourceMark rm;
|
||||
log_trace(modules)("ModuleEntry::set_read_walk_required(): module %s reads list must be walked",
|
||||
(name() != NULL) ? name()->as_C_string() : UNNAMED_MODULE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ModuleEntry::has_reads() const {
|
||||
assert_locked_or_safepoint(Module_lock);
|
||||
return ((_reads != NULL) && !_reads->is_empty());
|
||||
@ -127,14 +151,28 @@ bool ModuleEntry::has_reads() const {
|
||||
// Purge dead module entries out of reads list.
|
||||
void ModuleEntry::purge_reads() {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
|
||||
if (has_reads()) {
|
||||
|
||||
if (_must_walk_reads && has_reads()) {
|
||||
// This module's _must_walk_reads flag will be reset based
|
||||
// on the remaining live modules on the reads list.
|
||||
_must_walk_reads = false;
|
||||
|
||||
if (log_is_enabled(Trace, modules)) {
|
||||
ResourceMark rm;
|
||||
log_trace(modules)("ModuleEntry::purge_reads(): module %s reads list being walked",
|
||||
(name() != NULL) ? name()->as_C_string() : UNNAMED_MODULE);
|
||||
}
|
||||
|
||||
// Go backwards because this removes entries that are dead.
|
||||
int len = _reads->length();
|
||||
for (int idx = len - 1; idx >= 0; idx--) {
|
||||
ModuleEntry* module_idx = _reads->at(idx);
|
||||
ClassLoaderData* cld = module_idx->loader();
|
||||
if (cld->is_unloading()) {
|
||||
ClassLoaderData* cld_idx = module_idx->loader_data();
|
||||
if (cld_idx->is_unloading()) {
|
||||
_reads->delete_at(idx);
|
||||
} else {
|
||||
// Update the need to walk this module's reads based on live modules
|
||||
set_read_walk_required(cld_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -248,7 +286,7 @@ ModuleEntry* ModuleEntryTable::new_entry(unsigned int hash, Handle module_handle
|
||||
entry->set_module(loader_data->add_handle(module_handle));
|
||||
}
|
||||
|
||||
entry->set_loader(loader_data);
|
||||
entry->set_loader_data(loader_data);
|
||||
entry->set_version(version);
|
||||
entry->set_location(location);
|
||||
|
||||
@ -375,11 +413,11 @@ void ModuleEntryTable::print(outputStream* st) {
|
||||
|
||||
void ModuleEntry::print(outputStream* st) {
|
||||
ResourceMark rm;
|
||||
st->print_cr("entry "PTR_FORMAT" name %s module "PTR_FORMAT" loader %s version %s location %s strict %s next "PTR_FORMAT,
|
||||
st->print_cr("entry " PTR_FORMAT " name %s module " PTR_FORMAT " loader %s version %s location %s strict %s next " PTR_FORMAT,
|
||||
p2i(this),
|
||||
name() == NULL ? UNNAMED_MODULE : name()->as_C_string(),
|
||||
p2i(module()),
|
||||
loader()->loader_name(),
|
||||
loader_data()->loader_name(),
|
||||
version() != NULL ? version()->as_C_string() : "NULL",
|
||||
location() != NULL ? location()->as_C_string() : "NULL",
|
||||
BOOL_TO_STR(!can_read_all_unnamed()), p2i(next()));
|
||||
@ -401,5 +439,5 @@ void ModuleEntryTable::verify() {
|
||||
}
|
||||
|
||||
void ModuleEntry::verify() {
|
||||
guarantee(loader() != NULL, "A module entry must be associated with a loader.");
|
||||
guarantee(loader_data() != NULL, "A module entry must be associated with a loader.");
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ class ModuleClosure;
|
||||
// It contains:
|
||||
// - Symbol* containing the module's name.
|
||||
// - pointer to the java.lang.reflect.Module for this module.
|
||||
// - pointer to the java.security.ProtectionDomain shared by classes defined to this module.
|
||||
// - ClassLoaderData*, class loader of this module.
|
||||
// - a growable array containg other module entries that this module can read.
|
||||
// - a flag indicating if this module can read all unnamed modules.
|
||||
@ -54,56 +55,58 @@ private:
|
||||
jobject _module; // java.lang.reflect.Module
|
||||
jobject _pd; // java.security.ProtectionDomain, cached
|
||||
// for shared classes from this module
|
||||
ClassLoaderData* _loader;
|
||||
ClassLoaderData* _loader_data;
|
||||
GrowableArray<ModuleEntry*>* _reads; // list of modules that are readable by this module
|
||||
Symbol* _version; // module version number
|
||||
Symbol* _location; // module location
|
||||
bool _can_read_all_unnamed;
|
||||
bool _has_default_read_edges; // JVMTI redefine/retransform support
|
||||
bool _must_walk_reads; // walk module's reads list at GC safepoints to purge out dead modules
|
||||
TRACE_DEFINE_TRACE_ID_FIELD;
|
||||
enum {MODULE_READS_SIZE = 101}; // Initial size of list of modules that the module can read.
|
||||
|
||||
public:
|
||||
void init() {
|
||||
_module = NULL;
|
||||
_loader = NULL;
|
||||
_loader_data = NULL;
|
||||
_pd = NULL;
|
||||
_reads = NULL;
|
||||
_version = NULL;
|
||||
_location = NULL;
|
||||
_can_read_all_unnamed = false;
|
||||
_has_default_read_edges = false;
|
||||
_must_walk_reads = false;
|
||||
}
|
||||
|
||||
Symbol* name() const { return literal(); }
|
||||
void set_name(Symbol* n) { set_literal(n); }
|
||||
Symbol* name() const { return literal(); }
|
||||
void set_name(Symbol* n) { set_literal(n); }
|
||||
|
||||
jobject module() const { return _module; }
|
||||
void set_module(jobject j) { _module = j; }
|
||||
jobject module() const { return _module; }
|
||||
void set_module(jobject j) { _module = j; }
|
||||
|
||||
// The shared ProtectionDomain reference is set once the VM loads a shared class
|
||||
// originated from the current Module. The referenced ProtectionDomain object is
|
||||
// created by the ClassLoader when loading a class (shared or non-shared) from the
|
||||
// Module for the first time. This ProtectionDomain object is used for all
|
||||
// classes from the Module loaded by the same ClassLoader.
|
||||
Handle shared_protection_domain();
|
||||
void set_shared_protection_domain(ClassLoaderData *loader_data,
|
||||
Handle pd);
|
||||
Handle shared_protection_domain();
|
||||
void set_shared_protection_domain(ClassLoaderData *loader_data, Handle pd);
|
||||
|
||||
ClassLoaderData* loader() const { return _loader; }
|
||||
void set_loader(ClassLoaderData* l) { _loader = l; }
|
||||
ClassLoaderData* loader_data() const { return _loader_data; }
|
||||
void set_loader_data(ClassLoaderData* l) { _loader_data = l; }
|
||||
|
||||
Symbol* version() const { return _version; }
|
||||
void set_version(Symbol* version);
|
||||
Symbol* version() const { return _version; }
|
||||
void set_version(Symbol* version);
|
||||
|
||||
Symbol* location() const { return _location; }
|
||||
void set_location(Symbol* location);
|
||||
Symbol* location() const { return _location; }
|
||||
void set_location(Symbol* location);
|
||||
|
||||
bool can_read(ModuleEntry* m) const;
|
||||
bool has_reads() const;
|
||||
void add_read(ModuleEntry* m);
|
||||
bool can_read(ModuleEntry* m) const;
|
||||
bool has_reads() const;
|
||||
void add_read(ModuleEntry* m);
|
||||
void set_read_walk_required(ClassLoaderData* m_loader_data);
|
||||
|
||||
bool is_named() const { return (literal() != NULL); }
|
||||
bool is_named() const { return (name() != NULL); }
|
||||
|
||||
bool can_read_all_unnamed() const {
|
||||
assert(is_named() || _can_read_all_unnamed == true,
|
||||
@ -178,7 +181,7 @@ private:
|
||||
ModuleEntry* _unnamed_module;
|
||||
|
||||
ModuleEntry* new_entry(unsigned int hash, Handle module_handle, Symbol* name, Symbol* version,
|
||||
Symbol* location, ClassLoaderData* class_loader);
|
||||
Symbol* location, ClassLoaderData* loader_data);
|
||||
void add_entry(int index, ModuleEntry* new_entry);
|
||||
|
||||
int entry_size() const { return BasicHashtable<mtModule>::entry_size(); }
|
||||
|
@ -113,7 +113,7 @@ static PackageEntry* get_package_entry(ModuleEntry* module_entry, jstring packag
|
||||
const char *package_name = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(package));
|
||||
if (package_name == NULL) return NULL;
|
||||
TempNewSymbol pkg_symbol = SymbolTable::new_symbol(package_name, CHECK_NULL);
|
||||
PackageEntryTable* package_entry_table = module_entry->loader()->packages();
|
||||
PackageEntryTable* package_entry_table = module_entry->loader_data()->packages();
|
||||
assert(package_entry_table != NULL, "Unexpected null package entry table");
|
||||
return package_entry_table->lookup_only(pkg_symbol);
|
||||
}
|
||||
@ -868,7 +868,7 @@ void Modules::add_module_package(jobject module, jstring package, TRAPS) {
|
||||
package_name, module_entry->name()->as_C_string());
|
||||
|
||||
TempNewSymbol pkg_symbol = SymbolTable::new_symbol(package_name, CHECK);
|
||||
PackageEntryTable* package_table = module_entry->loader()->packages();
|
||||
PackageEntryTable* package_table = module_entry->loader_data()->packages();
|
||||
assert(package_table != NULL, "Missing package_table");
|
||||
|
||||
bool pkg_exists = false;
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/moduleEntry.hpp"
|
||||
#include "classfile/packageEntry.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
@ -53,12 +54,40 @@ void PackageEntry::add_qexport(ModuleEntry* m) {
|
||||
if (!has_qual_exports_list()) {
|
||||
// Lazily create a package's qualified exports list.
|
||||
// Initial size is small, do not anticipate export lists to be large.
|
||||
_qualified_exports =
|
||||
new (ResourceObj::C_HEAP, mtModule) GrowableArray<ModuleEntry*>(QUAL_EXP_SIZE, true);
|
||||
_qualified_exports = new (ResourceObj::C_HEAP, mtModule) GrowableArray<ModuleEntry*>(QUAL_EXP_SIZE, true);
|
||||
}
|
||||
|
||||
// Determine, based on this newly established export to module m,
|
||||
// if this package's export list should be walked at a GC safepoint.
|
||||
set_export_walk_required(m->loader_data());
|
||||
|
||||
// Establish exportability to module m
|
||||
_qualified_exports->append_if_missing(m);
|
||||
}
|
||||
|
||||
// If the module's loader, that an export is being established to, is
|
||||
// not the same loader as this module's and is not one of the 3 builtin
|
||||
// class loaders, then this package's export list must be walked at GC
|
||||
// safepoint. Modules have the same life cycle as their defining class
|
||||
// loaders and should be removed if dead.
|
||||
void PackageEntry::set_export_walk_required(ClassLoaderData* m_loader_data) {
|
||||
assert_locked_or_safepoint(Module_lock);
|
||||
ModuleEntry* this_pkg_mod = module();
|
||||
if (!_must_walk_exports &&
|
||||
(this_pkg_mod == NULL || this_pkg_mod->loader_data() != m_loader_data) &&
|
||||
!m_loader_data->is_builtin_class_loader_data()) {
|
||||
_must_walk_exports = true;
|
||||
if (log_is_enabled(Trace, modules)) {
|
||||
ResourceMark rm;
|
||||
assert(name() != NULL, "PackageEntry without a valid name");
|
||||
log_trace(modules)("PackageEntry::set_export_walk_required(): package %s defined in module %s, exports list must be walked",
|
||||
name()->as_C_string(),
|
||||
(this_pkg_mod == NULL || this_pkg_mod->name() == NULL) ?
|
||||
UNNAMED_MODULE : this_pkg_mod->name()->as_C_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the package's exported states based on the value of the ModuleEntry.
|
||||
void PackageEntry::set_exported(ModuleEntry* m) {
|
||||
MutexLocker m1(Module_lock);
|
||||
@ -96,14 +125,34 @@ void PackageEntry::set_is_exported_allUnnamed() {
|
||||
// Remove dead module entries within the package's exported list.
|
||||
void PackageEntry::purge_qualified_exports() {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
|
||||
if (_qualified_exports != NULL) {
|
||||
if (_must_walk_exports &&
|
||||
_qualified_exports != NULL &&
|
||||
!_qualified_exports->is_empty()) {
|
||||
ModuleEntry* pkg_module = module();
|
||||
|
||||
// This package's _must_walk_exports flag will be reset based
|
||||
// on the remaining live modules on the exports list.
|
||||
_must_walk_exports = false;
|
||||
|
||||
if (log_is_enabled(Trace, modules)) {
|
||||
ResourceMark rm;
|
||||
assert(name() != NULL, "PackageEntry without a valid name");
|
||||
ModuleEntry* pkg_mod = module();
|
||||
log_trace(modules)("PackageEntry::purge_qualified_exports(): package %s defined in module %s, exports list being walked",
|
||||
name()->as_C_string(),
|
||||
(pkg_mod == NULL || pkg_mod->name() == NULL) ? UNNAMED_MODULE : pkg_mod->name()->as_C_string());
|
||||
}
|
||||
|
||||
// Go backwards because this removes entries that are dead.
|
||||
int len = _qualified_exports->length();
|
||||
for (int idx = len - 1; idx >= 0; idx--) {
|
||||
ModuleEntry* module_idx = _qualified_exports->at(idx);
|
||||
ClassLoaderData* cld = module_idx->loader();
|
||||
if (cld->is_unloading()) {
|
||||
ClassLoaderData* cld_idx = module_idx->loader_data();
|
||||
if (cld_idx->is_unloading()) {
|
||||
_qualified_exports->delete_at(idx);
|
||||
} else {
|
||||
// Update the need to walk this package's exports based on live modules
|
||||
set_export_walk_required(cld_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -297,8 +346,8 @@ void PackageEntryTable::print(outputStream* st) {
|
||||
|
||||
void PackageEntry::print(outputStream* st) {
|
||||
ResourceMark rm;
|
||||
st->print_cr("package entry "PTR_FORMAT" name %s module %s classpath_index "
|
||||
INT32_FORMAT " is_exported_unqualified %d is_exported_allUnnamed %d " "next "PTR_FORMAT,
|
||||
st->print_cr("package entry " PTR_FORMAT " name %s module %s classpath_index "
|
||||
INT32_FORMAT " is_exported_unqualified %d is_exported_allUnnamed %d " "next " PTR_FORMAT,
|
||||
p2i(this), name()->as_C_string(),
|
||||
(module()->is_named() ? module()->name()->as_C_string() : UNNAMED_MODULE),
|
||||
_classpath_index, _is_exported_unqualified, _is_exported_allUnnamed, p2i(next()));
|
||||
|
@ -69,6 +69,7 @@ private:
|
||||
s2 _classpath_index;
|
||||
bool _is_exported_unqualified;
|
||||
bool _is_exported_allUnnamed;
|
||||
bool _must_walk_exports;
|
||||
GrowableArray<ModuleEntry*>* _exported_pending_delete; // transitioned from qualified to unqualified, delete at safepoint
|
||||
GrowableArray<ModuleEntry*>* _qualified_exports;
|
||||
TRACE_DEFINE_TRACE_ID_FIELD;
|
||||
@ -82,6 +83,7 @@ public:
|
||||
_classpath_index = -1;
|
||||
_is_exported_unqualified = false;
|
||||
_is_exported_allUnnamed = false;
|
||||
_must_walk_exports = false;
|
||||
_exported_pending_delete = NULL;
|
||||
_qualified_exports = NULL;
|
||||
}
|
||||
@ -147,6 +149,7 @@ public:
|
||||
|
||||
// add the module to the package's qualified exports
|
||||
void add_qexport(ModuleEntry* m);
|
||||
void set_export_walk_required(ClassLoaderData* m_loader_data);
|
||||
|
||||
PackageEntry* next() const {
|
||||
return (PackageEntry*)HashtableEntry<Symbol*, mtModule>::next();
|
||||
|
@ -175,9 +175,18 @@ bool SystemDictionary::is_parallelDefine(Handle class_loader) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the passed class loader is the platform class loader.
|
||||
*/
|
||||
// Returns true if the passed class loader is the builtin application class loader
|
||||
// or a custom system class loader. A customer system class loader can be
|
||||
// specified via -Djava.system.class.loader.
|
||||
bool SystemDictionary::is_system_class_loader(Handle class_loader) {
|
||||
if (class_loader.is_null()) {
|
||||
return false;
|
||||
}
|
||||
return (class_loader->klass() == SystemDictionary::jdk_internal_loader_ClassLoaders_AppClassLoader_klass() ||
|
||||
class_loader() == _java_system_loader);
|
||||
}
|
||||
|
||||
// Returns true if the passed class loader is the platform class loader.
|
||||
bool SystemDictionary::is_platform_class_loader(Handle class_loader) {
|
||||
if (class_loader.is_null()) {
|
||||
return false;
|
||||
|
@ -660,6 +660,7 @@ public:
|
||||
static instanceKlassHandle load_shared_class(Symbol* class_name,
|
||||
Handle class_loader,
|
||||
TRAPS);
|
||||
static bool is_system_class_loader(Handle class_loader);
|
||||
static bool is_platform_class_loader(Handle class_loader);
|
||||
|
||||
protected:
|
||||
|
@ -48,10 +48,10 @@ void PreservedMarks::restore_and_increment(volatile size_t* const total_size_add
|
||||
|
||||
#ifndef PRODUCT
|
||||
void PreservedMarks::assert_empty() {
|
||||
assert(_stack.is_empty(), "stack expected to be empty, size = "SIZE_FORMAT,
|
||||
assert(_stack.is_empty(), "stack expected to be empty, size = " SIZE_FORMAT,
|
||||
_stack.size());
|
||||
assert(_stack.cache_size() == 0,
|
||||
"stack expected to have no cached segments, cache size = "SIZE_FORMAT,
|
||||
"stack expected to have no cached segments, cache size = " SIZE_FORMAT,
|
||||
_stack.cache_size());
|
||||
}
|
||||
#endif // ndef PRODUCT
|
||||
|
@ -419,21 +419,20 @@ void Rewriter::scan_method(Method* method, bool reverse, bool* invokespecial_err
|
||||
InstanceKlass* klass = method->method_holder();
|
||||
u2 bc_index = Bytes::get_Java_u2(bcp + prefix_length + 1);
|
||||
constantPoolHandle cp(method->constants());
|
||||
Symbol* field_name = cp->name_ref_at(bc_index);
|
||||
Symbol* field_sig = cp->signature_ref_at(bc_index);
|
||||
Symbol* ref_class_name = cp->klass_name_at(cp->klass_ref_index_at(bc_index));
|
||||
|
||||
if (klass->name() == ref_class_name) {
|
||||
Symbol* field_name = cp->name_ref_at(bc_index);
|
||||
Symbol* field_sig = cp->signature_ref_at(bc_index);
|
||||
|
||||
fieldDescriptor fd;
|
||||
klass->find_field(field_name, field_sig, &fd);
|
||||
if (fd.access_flags().is_final()) {
|
||||
if (fd.access_flags().is_static()) {
|
||||
assert(c == Bytecodes::_putstatic, "must be putstatic");
|
||||
if (!method->is_static_initializer()) {
|
||||
fd.set_has_initialized_final_update(true);
|
||||
}
|
||||
} else {
|
||||
assert(c == Bytecodes::_putfield, "must be putfield");
|
||||
if (!method->is_object_initializer()) {
|
||||
fd.set_has_initialized_final_update(true);
|
||||
}
|
||||
|
@ -584,27 +584,26 @@ static bool verify_special_jvm_flags() {
|
||||
// Parses a size specification string.
|
||||
bool Arguments::atojulong(const char *s, julong* result) {
|
||||
julong n = 0;
|
||||
int args_read = 0;
|
||||
bool is_hex = false;
|
||||
// Skip leading 0[xX] for hexadecimal
|
||||
if (*s =='0' && (*(s+1) == 'x' || *(s+1) == 'X')) {
|
||||
s += 2;
|
||||
is_hex = true;
|
||||
args_read = sscanf(s, JULONG_FORMAT_X, &n);
|
||||
} else {
|
||||
args_read = sscanf(s, JULONG_FORMAT, &n);
|
||||
}
|
||||
if (args_read != 1) {
|
||||
|
||||
// First char must be a digit. Don't allow negative numbers or leading spaces.
|
||||
if (!isdigit(*s)) {
|
||||
return false;
|
||||
}
|
||||
while (*s != '\0' && (isdigit(*s) || (is_hex && isxdigit(*s)))) {
|
||||
s++;
|
||||
}
|
||||
// 4705540: illegal if more characters are found after the first non-digit
|
||||
if (strlen(s) > 1) {
|
||||
|
||||
bool is_hex = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'));
|
||||
char* remainder;
|
||||
errno = 0;
|
||||
n = strtoull(s, &remainder, (is_hex ? 16 : 10));
|
||||
if (errno != 0) {
|
||||
return false;
|
||||
}
|
||||
switch (*s) {
|
||||
|
||||
// Fail if no number was read at all or if the remainder contains more than a single non-digit character.
|
||||
if (remainder == s || strlen(remainder) > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (*remainder) {
|
||||
case 'T': case 't':
|
||||
*result = n * G * K;
|
||||
// Check for overflow.
|
||||
|
@ -205,16 +205,39 @@ void VMError::print_stack_trace(outputStream* st, JavaThread* jt,
|
||||
static void print_oom_reasons(outputStream* st) {
|
||||
st->print_cr("# Possible reasons:");
|
||||
st->print_cr("# The system is out of physical RAM or swap space");
|
||||
st->print_cr("# In 32 bit mode, the process size limit was hit");
|
||||
if (UseCompressedOops) {
|
||||
st->print_cr("# The process is running with CompressedOops enabled, and the Java Heap may be blocking the growth of the native heap");
|
||||
}
|
||||
if (LogBytesPerWord == 2) {
|
||||
st->print_cr("# In 32 bit mode, the process size limit was hit");
|
||||
}
|
||||
st->print_cr("# Possible solutions:");
|
||||
st->print_cr("# Reduce memory load on the system");
|
||||
st->print_cr("# Increase physical memory or swap space");
|
||||
st->print_cr("# Check if swap backing store is full");
|
||||
st->print_cr("# Use 64 bit Java on a 64 bit OS");
|
||||
if (LogBytesPerWord == 2) {
|
||||
st->print_cr("# Use 64 bit Java on a 64 bit OS");
|
||||
}
|
||||
st->print_cr("# Decrease Java heap size (-Xmx/-Xms)");
|
||||
st->print_cr("# Decrease number of Java threads");
|
||||
st->print_cr("# Decrease Java thread stack sizes (-Xss)");
|
||||
st->print_cr("# Set larger code cache with -XX:ReservedCodeCacheSize=");
|
||||
if (UseCompressedOops) {
|
||||
switch (Universe::narrow_oop_mode()) {
|
||||
case Universe::UnscaledNarrowOop:
|
||||
st->print_cr("# JVM is running with Unscaled Compressed Oops mode in which the Java heap is");
|
||||
st->print_cr("# placed in the first 4GB address space. The Java Heap base address is the");
|
||||
st->print_cr("# maximum limit for the native heap growth. Please use -XX:HeapBaseMinAddress");
|
||||
st->print_cr("# to set the Java Heap base and to place the Java Heap above 4GB virtual address.");
|
||||
break;
|
||||
case Universe::ZeroBasedNarrowOop:
|
||||
st->print_cr("# JVM is running with Zero Based Compressed Oops mode in which the Java heap is");
|
||||
st->print_cr("# placed in the first 32GB address space. The Java Heap base address is the");
|
||||
st->print_cr("# maximum limit for the native heap growth. Please use -XX:HeapBaseMinAddress");
|
||||
st->print_cr("# to set the Java Heap base and to place the Java Heap above 32GB virtual address.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
st->print_cr("# This output file may be truncated or incomplete.");
|
||||
}
|
||||
|
||||
|
@ -395,6 +395,17 @@ hotspot_jprt = \
|
||||
:hotspot_fast_gc_gcold \
|
||||
:hotspot_fast_runtime \
|
||||
:hotspot_fast_serviceability
|
||||
|
||||
hotspot_runtime_tier2 = \
|
||||
runtime/ \
|
||||
serviceability/ \
|
||||
-:hotspot_fast_runtime \
|
||||
-:hotspot_fast_serviceability \
|
||||
-:hotspot_runtime_tier2_platform_agnostic
|
||||
|
||||
hotspot_runtime_tier2_platform_agnostic = \
|
||||
runtime/SelectionResolution \
|
||||
-:hotspot_fast_runtime
|
||||
|
||||
#All tests that depends on nashorn extension.
|
||||
#
|
||||
|
@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package gc.g1.humongousObjects;
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
import sun.hotspot.WhiteBox;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @test TestNoAllocationsInHRegions
|
||||
* @summary Checks that no additional allocations are made in humongous regions
|
||||
* @requires vm.gc.G1
|
||||
* @library /testlibrary /test/lib /
|
||||
* @modules java.management java.base/jdk.internal.misc
|
||||
* @build sun.hotspot.WhiteBox
|
||||
* gc.testlibrary.Helpers
|
||||
* gc.g1.humongousObjects.TestNoAllocationsInHRegions
|
||||
*
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
*
|
||||
* @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
|
||||
* -XX:G1HeapRegionSize=1M -Xms200m -Xmx200m -XX:MaxTenuringThreshold=0
|
||||
* -Xlog:gc=trace:file=TestNoAllocationsInHRegions10.log
|
||||
* gc.g1.humongousObjects.TestNoAllocationsInHRegions 30 10
|
||||
*
|
||||
* @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
|
||||
* -XX:G1HeapRegionSize=1M -Xms200m -Xmx200m -XX:MaxTenuringThreshold=0
|
||||
* -Xlog:gc=trace:file=TestNoAllocationsInHRegions50.log
|
||||
* gc.g1.humongousObjects.TestNoAllocationsInHRegions 30 50
|
||||
*
|
||||
* @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
|
||||
* -XX:G1HeapRegionSize=1M -Xms200m -Xmx200m -XX:MaxTenuringThreshold=0
|
||||
* -Xlog:gc=trace:file=TestNoAllocationsInHRegions70.log
|
||||
* gc.g1.humongousObjects.TestNoAllocationsInHRegions 30 70
|
||||
*/
|
||||
public class TestNoAllocationsInHRegions {
|
||||
private static final WhiteBox WB = WhiteBox.getWhiteBox();
|
||||
private static final Random RND = Utils.getRandomInstance();
|
||||
private static final int G1_REGION_SIZE = WB.g1RegionSize();
|
||||
private static final int[] HUMONGOUS_SIZES = {G1_REGION_SIZE / 2, G1_REGION_SIZE + 1, G1_REGION_SIZE * 2 + 1};
|
||||
private static final int ALLOC_THREAD_COUNT = 5;
|
||||
|
||||
// We fill specified part of heap with humongous objects - we need public static to prevent escape analysis to
|
||||
// collect this field
|
||||
public static LinkedList<byte[]> humongousAllocations = new LinkedList<>();
|
||||
|
||||
private static volatile boolean shouldStop = false;
|
||||
private static volatile Error error = null;
|
||||
|
||||
static class Allocator implements Runnable {
|
||||
|
||||
private final List<byte[]> liveObjects = new LinkedList<>();
|
||||
private int usedMemory = 0;
|
||||
public final Runnable[] actions;
|
||||
|
||||
/**
|
||||
* Maximum size of simple allocation
|
||||
*/
|
||||
private static final int MAX_ALLOCATION_SIZE = (int) (G1_REGION_SIZE / 2 * 0.9);
|
||||
|
||||
/**
|
||||
* Maximum size of dead (i.e. one which is made unreachable right after allocation) object
|
||||
*/
|
||||
private static final int DEAD_OBJECT_MAX_SIZE = G1_REGION_SIZE / 10;
|
||||
|
||||
public Allocator(int maxAllocationMemory) {
|
||||
|
||||
actions = new Runnable[]{
|
||||
// Allocation
|
||||
() -> {
|
||||
if (maxAllocationMemory - usedMemory != 0) {
|
||||
int arraySize = RND.nextInt(Math.min(maxAllocationMemory - usedMemory,
|
||||
MAX_ALLOCATION_SIZE));
|
||||
|
||||
if (arraySize != 0) {
|
||||
byte[] allocation = new byte[arraySize];
|
||||
liveObjects.add(allocation);
|
||||
usedMemory += arraySize;
|
||||
|
||||
// Sanity check
|
||||
if (WB.g1IsHumongous(allocation)) {
|
||||
String errorMessage = String.format("Test Bug: Byte array of size"
|
||||
+ " %d is expected to be non-humongous but it is humongous",
|
||||
allocation.length);
|
||||
|
||||
System.out.println(errorMessage);
|
||||
error = new Error(errorMessage);
|
||||
shouldStop = true;
|
||||
}
|
||||
|
||||
// Test check
|
||||
if (WB.g1BelongsToHumongousRegion(WB.getObjectAddress(allocation))) {
|
||||
String errorMessage = String.format("Non-humongous allocation of byte array of "
|
||||
+ "length %d and size %d with address %d was made in Humongous Region",
|
||||
allocation.length, WB.getObjectSize(allocation),
|
||||
WB.getObjectAddress(allocation));
|
||||
|
||||
System.out.println(errorMessage);
|
||||
error = new Error(errorMessage);
|
||||
shouldStop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Deallocation
|
||||
() -> {
|
||||
if (liveObjects.size() != 0) {
|
||||
int elementNum = RND.nextInt(liveObjects.size());
|
||||
int shouldFree = liveObjects.get(elementNum).length;
|
||||
liveObjects.remove(elementNum);
|
||||
usedMemory -= shouldFree;
|
||||
}
|
||||
},
|
||||
|
||||
// Dead object allocation
|
||||
() -> {
|
||||
int size = RND.nextInt(DEAD_OBJECT_MAX_SIZE);
|
||||
byte[] deadObject = new byte[size];
|
||||
},
|
||||
|
||||
// Check
|
||||
() -> {
|
||||
List<byte[]> wrongHumongousAllocations = liveObjects.stream()
|
||||
.filter(WB::g1IsHumongous)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (wrongHumongousAllocations.size() > 0) {
|
||||
wrongHumongousAllocations.stream().forEach(a ->
|
||||
System.out.format("Non-humongous allocation of byte array of length %d and"
|
||||
+ " size %d with address %d was made in Humongous Region",
|
||||
a.length, WB.getObjectSize(a), WB.getObjectAddress(a)));
|
||||
error = new Error("Some non-humongous allocations were made to humongous region");
|
||||
shouldStop = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!shouldStop) {
|
||||
actions[RND.nextInt(actions.length)].run();
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 2) {
|
||||
throw new Error("Test Bug: Expected duration (in seconds) and percent of allocated regions were not "
|
||||
+ "provided as command line argument");
|
||||
}
|
||||
|
||||
// test duration
|
||||
long duration = Integer.parseInt(args[0]) * 1000L;
|
||||
// part of heap preallocated with humongous objects (in percents)
|
||||
int percentOfAllocatedHeap = Integer.parseInt(args[1]);
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
long initialFreeRegionsCount = WB.g1NumFreeRegions();
|
||||
int regionsToAllocate = (int) ((double) initialFreeRegionsCount / 100.0 * percentOfAllocatedHeap);
|
||||
long freeRegionLeft = initialFreeRegionsCount - regionsToAllocate;
|
||||
|
||||
System.out.println("Regions to allocate: " + regionsToAllocate + "; regions to left free: " + freeRegionLeft);
|
||||
|
||||
int maxMemoryPerAllocThread = (int) ((Runtime.getRuntime().freeMemory() / 100.0
|
||||
* (100 - percentOfAllocatedHeap)) / ALLOC_THREAD_COUNT * 0.5);
|
||||
|
||||
System.out.println("Using " + maxMemoryPerAllocThread / 1024 + "KB for each of " + ALLOC_THREAD_COUNT
|
||||
+ " allocation threads");
|
||||
|
||||
while (WB.g1NumFreeRegions() > freeRegionLeft) {
|
||||
try {
|
||||
humongousAllocations.add(new byte[HUMONGOUS_SIZES[RND.nextInt(HUMONGOUS_SIZES.length)]]);
|
||||
} catch (OutOfMemoryError oom) {
|
||||
//We got OOM trying to fill heap with humongous objects
|
||||
//It probably means that heap is fragmented which is strange since the test logic should avoid it
|
||||
System.out.println("Warning: OOM while allocating humongous objects - it likely means "
|
||||
+ "that heap is fragmented");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Initial free regions " + initialFreeRegionsCount + "; Free regions left "
|
||||
+ WB.g1NumFreeRegions());
|
||||
|
||||
LinkedList<Thread> threads = new LinkedList<>();
|
||||
|
||||
for (int i = 0; i < ALLOC_THREAD_COUNT; i++) {
|
||||
threads.add(new Thread(new Allocator(maxMemoryPerAllocThread)));
|
||||
}
|
||||
|
||||
threads.stream().forEach(Thread::start);
|
||||
|
||||
while ((System.currentTimeMillis() - startTime < duration) && error == null) {
|
||||
Thread.yield();
|
||||
}
|
||||
|
||||
shouldStop = true;
|
||||
System.out.println("Finished test");
|
||||
if (error != null) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
@ -138,6 +138,41 @@ public enum GC {
|
||||
}
|
||||
},
|
||||
|
||||
MIXED_GC {
|
||||
@Override
|
||||
public Runnable get() {
|
||||
return () -> {
|
||||
WHITE_BOX.youngGC();
|
||||
Helpers.waitTillCMCFinished(WHITE_BOX, 0);
|
||||
WHITE_BOX.youngGC();
|
||||
Helpers.waitTillCMCFinished(WHITE_BOX, 0);
|
||||
|
||||
WHITE_BOX.g1StartConcMarkCycle();
|
||||
Helpers.waitTillCMCFinished(WHITE_BOX, 0);
|
||||
|
||||
WHITE_BOX.youngGC();
|
||||
Helpers.waitTillCMCFinished(WHITE_BOX, 0);
|
||||
// Provoking Mixed GC
|
||||
WHITE_BOX.youngGC();// second evacuation pause will be mixed
|
||||
Helpers.waitTillCMCFinished(WHITE_BOX, 0);
|
||||
};
|
||||
}
|
||||
|
||||
public Consumer<ReferenceInfo<Object[]>> getChecker() {
|
||||
return getCheckerImpl(true, false, true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> shouldContain() {
|
||||
return Arrays.asList(GCTokens.WB_INITIATED_CMC);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> shouldNotContain() {
|
||||
return Arrays.asList(GCTokens.YOUNG_GC);
|
||||
}
|
||||
},
|
||||
|
||||
FULL_GC_MEMORY_PRESSURE {
|
||||
@Override
|
||||
public Runnable get() {
|
||||
|
@ -38,6 +38,9 @@ The test checks that after different type of GC unreachable objects behave as ex
|
||||
non-humongous and humongous objects are not collected since we make 2 Young GC to promote all
|
||||
weak references to Old Gen.
|
||||
|
||||
6. Mixed GC - weakly referenced non-humongous and humongous objects are collected, softly referenced non-humongous and
|
||||
humongous objects are not collected.
|
||||
|
||||
The test gets gc type as a command line argument.
|
||||
Then the test allocates object graph in heap (currently testing scenarios are pre-generated and stored in
|
||||
TestcaseData.getPregeneratedTestcases()) with TestObjectGraphAfterGC::allocateObjectGraph.
|
||||
|
@ -66,6 +66,12 @@ import java.util.stream.Collectors;
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
*
|
||||
* @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
|
||||
* -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=30000 -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0
|
||||
* -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_MIXED_GC.gc.log -XX:MaxTenuringThreshold=1
|
||||
* -XX:G1MixedGCCountTarget=1 -XX:G1OldCSetRegionThresholdPercent=100 -XX:SurvivorRatio=1 -XX:InitiatingHeapOccupancyPercent=0
|
||||
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC MIXED_GC
|
||||
*
|
||||
* @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
|
||||
* -XX:G1HeapRegionSize=1M -Xlog:gc*=debug:file=TestObjectGraphAfterGC_YOUNG_GC.gc.log
|
||||
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC YOUNG_GC
|
||||
*
|
||||
|
71
hotspot/test/native/runtime/test_arguments.cpp
Normal file
71
hotspot/test/native/runtime/test_arguments.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*
|
||||
*/
|
||||
#include "precompiled.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "unittest.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
TEST(arguments, atojulong) {
|
||||
char ullong_max[32];
|
||||
int ret = jio_snprintf(ullong_max, sizeof(ullong_max), JULONG_FORMAT, ULLONG_MAX);
|
||||
ASSERT_NE(-1, ret);
|
||||
|
||||
julong value;
|
||||
const char* invalid_strings[] = {
|
||||
"", "-1", "-100", " 1", "2 ", "3 2", "1.0",
|
||||
"0x4.5", "0x", "0x0x1" "0.001", "4e10", "e"
|
||||
"K", "M", "G", "1MB", "1KM", "AA", "0B",
|
||||
"18446744073709551615K", "17179869184G",
|
||||
"999999999999999999999999999999"
|
||||
};
|
||||
for (uint i = 0; i < ARRAY_SIZE(invalid_strings); i++) {
|
||||
ASSERT_FALSE(Arguments::atojulong(invalid_strings[i], &value))
|
||||
<< "Invalid string '" << invalid_strings[i] << "' parsed without error.";
|
||||
}
|
||||
|
||||
struct {
|
||||
const char* str;
|
||||
julong expected_value;
|
||||
} valid_strings[] = {
|
||||
{ "0", 0 },
|
||||
{ "4711", 4711 },
|
||||
{ "1K", 1ULL * K },
|
||||
{ "1k", 1ULL * K },
|
||||
{ "2M", 2ULL * M },
|
||||
{ "2m", 2ULL * M },
|
||||
{ "4G", 4ULL * G },
|
||||
{ "4g", 4ULL * G },
|
||||
{ "0K", 0 },
|
||||
{ ullong_max, ULLONG_MAX },
|
||||
{ "0xcafebabe", 0xcafebabe },
|
||||
{ "0XCAFEBABE", 0xcafebabe },
|
||||
{ "0XCAFEbabe", 0xcafebabe },
|
||||
{ "0x10K", 0x10 * K }
|
||||
};
|
||||
for (uint i = 0; i < ARRAY_SIZE(valid_strings); i++) {
|
||||
ASSERT_TRUE(Arguments::atojulong(valid_strings[i].str, &value))
|
||||
<< "Valid string '" << valid_strings[i].str << "' did not parse.";
|
||||
ASSERT_EQ(valid_strings[i].expected_value, value);
|
||||
}
|
||||
}
|
55
hotspot/test/runtime/Final/Bad.jasm
Normal file
55
hotspot/test/runtime/Final/Bad.jasm
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
/* Recoded in jasm to provoke an ICCE assigning a non-static final field with putstatic.
|
||||
class Bad {
|
||||
public static final int i; //rewritten
|
||||
//rewritten to: public final int i;
|
||||
static { i = 5; } // putstatic instruction
|
||||
}
|
||||
*/
|
||||
|
||||
super class Bad
|
||||
version 53:0
|
||||
{
|
||||
|
||||
// Remove 'static' keyword
|
||||
public final Field i:I;
|
||||
|
||||
Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
static Method "<clinit>":"()V"
|
||||
stack 1 locals 0
|
||||
{
|
||||
iconst_5;
|
||||
putstatic Field i:"I";
|
||||
return;
|
||||
}
|
||||
|
||||
} // end Class Bad
|
42
hotspot/test/runtime/Final/PutfieldError.java
Normal file
42
hotspot/test/runtime/Final/PutfieldError.java
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test PutfieldError
|
||||
* @bug 8160551
|
||||
* @summary Throw ICCE rather than crashing for nonstatic final field in static initializer
|
||||
* @compile Bad.jasm
|
||||
* @run main PutfieldError
|
||||
*/
|
||||
|
||||
public class PutfieldError {
|
||||
public static void main(java.lang.String[] unused) {
|
||||
try {
|
||||
Bad b = new Bad();
|
||||
System.out.println("Bad.i = " + 5);
|
||||
throw new RuntimeException("ICCE NOT thrown as expected");
|
||||
} catch (IncompatibleClassChangeError icce) {
|
||||
System.out.println("ICCE thrown as expected");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test SharedStringsDedup
|
||||
* @summary Test -Xshare:auto with shared strings and -XX:+UseStringDeduplication
|
||||
* Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows
|
||||
* @requires (sun.arch.data.model != "32") & (os.family != "windows")
|
||||
* @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
|
||||
* @requires vm.gc.G1
|
||||
* @library /testlibrary
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* @run main SharedStringsDedup
|
||||
*/
|
||||
|
||||
import jdk.test.lib.*;
|
||||
import java.io.File;
|
||||
|
||||
// The main purpose is to test the interaction between shared strings
|
||||
// and -XX:+UseStringDeduplication. We run in -Xshare:auto mode so
|
||||
// we don't need to worry about CDS archive mapping failure (which
|
||||
// doesn't happen often so it won't impact coverage).
|
||||
public class SharedStringsDedup {
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Dump
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:SharedArchiveFile=./SharedStringsDedup.jsa",
|
||||
"-XX:+UseCompressedOops", "-XX:+UseG1GC",
|
||||
"-XX:+PrintSharedSpaces",
|
||||
"-Xshare:dump");
|
||||
|
||||
new OutputAnalyzer(pb.start())
|
||||
.shouldContain("Loading classes to share")
|
||||
.shouldContain("Shared string table stats")
|
||||
.shouldHaveExitValue(0);
|
||||
|
||||
// Run with -Xshare:auto
|
||||
pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:SharedArchiveFile=./SharedStringsDedup.jsa",
|
||||
"-XX:+UseCompressedOops", "-XX:+UseG1GC",
|
||||
"-XX:+UseStringDeduplication",
|
||||
"-Xshare:auto",
|
||||
"-version");
|
||||
|
||||
new OutputAnalyzer(pb.start())
|
||||
.shouldMatch("(java|openjdk) version")
|
||||
.shouldHaveExitValue(0);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A custom system ClassLoader to define the module "m2" to during iterations of
|
||||
* differing test runs within the test ModuleStress.java
|
||||
*/
|
||||
public class CustomSystemClassLoader extends ClassLoader {
|
||||
public CustomSystemClassLoader() {
|
||||
super();
|
||||
}
|
||||
public CustomSystemClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import static jdk.test.lib.Asserts.*;
|
||||
|
||||
import java.lang.reflect.Layer;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
//
|
||||
// ClassLoader1 --> defines m1 --> packages p1
|
||||
// ClassLoader2 --> defines m2 --> packages p2
|
||||
// Java System Class Loader --> defines m3 --> packages p3
|
||||
//
|
||||
// m1 can read m2
|
||||
// package p2 in m2 is exported to m1 and m3
|
||||
//
|
||||
// class p1.c1 defined in m1 tries to access p2.c2 defined in m2
|
||||
// Access allowed since m1 can read m2 and package p2 is exported to m1.
|
||||
//
|
||||
public class ModuleNonBuiltinCLMain {
|
||||
|
||||
// Create a Layer over the boot layer.
|
||||
// Define modules within this layer to test access between
|
||||
// publically defined classes within packages of those modules.
|
||||
public void createLayerOnBoot() throws Throwable {
|
||||
|
||||
// Define module: m1
|
||||
// Can read: java.base, m2
|
||||
// Packages: p1
|
||||
// Packages exported: p1 is exported to unqualifiedly
|
||||
ModuleDescriptor descriptor_m1 =
|
||||
new ModuleDescriptor.Builder("m1")
|
||||
.requires("java.base")
|
||||
.requires("m2")
|
||||
.exports("p1")
|
||||
.build();
|
||||
|
||||
// Define module: m2
|
||||
// Can read: java.base, m3
|
||||
// Packages: p2
|
||||
// Packages exported: package p2 is exported to m1 and m3
|
||||
Set<String> targets = new HashSet<>();
|
||||
targets.add("m1");
|
||||
targets.add("m3");
|
||||
ModuleDescriptor descriptor_m2 =
|
||||
new ModuleDescriptor.Builder("m2")
|
||||
.requires("java.base")
|
||||
.requires("m3")
|
||||
.exports("p2", targets)
|
||||
.build();
|
||||
|
||||
// Define module: m3
|
||||
// Can read: java.base
|
||||
// Packages: p3
|
||||
// Packages exported: none
|
||||
ModuleDescriptor descriptor_m3 =
|
||||
new ModuleDescriptor.Builder("m3")
|
||||
.requires("java.base")
|
||||
.build();
|
||||
|
||||
// Set up a ModuleFinder containing all modules for this layer.
|
||||
ModuleFinder finder = ModuleLibrary.of(descriptor_m1, descriptor_m2, descriptor_m3);
|
||||
|
||||
// Resolves "m1"
|
||||
Configuration cf = Layer.boot()
|
||||
.configuration()
|
||||
.resolveRequires(finder, ModuleFinder.of(), Set.of("m1"));
|
||||
|
||||
// map each module to differing user defined class loaders for this test
|
||||
Map<String, ClassLoader> map = new HashMap<>();
|
||||
Loader1 cl1 = new Loader1();
|
||||
Loader2 cl2 = new Loader2();
|
||||
ClassLoader cl3 = ClassLoader.getSystemClassLoader();
|
||||
map.put("m1", cl1);
|
||||
map.put("m2", cl2);
|
||||
map.put("m3", cl3);
|
||||
|
||||
// Create Layer that contains m1 & m2
|
||||
Layer layer = Layer.boot().defineModules(cf, map::get);
|
||||
assertTrue(layer.findLoader("m1") == cl1);
|
||||
assertTrue(layer.findLoader("m2") == cl2);
|
||||
assertTrue(layer.findLoader("m3") == cl3);
|
||||
assertTrue(layer.findLoader("java.base") == null);
|
||||
|
||||
// now use the same loader to load class p1.c1
|
||||
Class p1_c1_class = cl1.loadClass("p1.c1");
|
||||
try {
|
||||
p1_c1_class.newInstance();
|
||||
} catch (IllegalAccessError e) {
|
||||
throw new RuntimeException("Test Failed, an IAE should not be thrown since p2 is exported qualifiedly to m1");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Throwable {
|
||||
ModuleNonBuiltinCLMain test = new ModuleNonBuiltinCLMain();
|
||||
test.createLayerOnBoot();
|
||||
}
|
||||
|
||||
static class Loader1 extends ClassLoader { }
|
||||
static class Loader2 extends ClassLoader { }
|
||||
}
|
109
hotspot/test/runtime/modules/ModuleStress/ModuleSameCLMain.java
Normal file
109
hotspot/test/runtime/modules/ModuleStress/ModuleSameCLMain.java
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import static jdk.test.lib.Asserts.*;
|
||||
|
||||
import java.lang.reflect.Layer;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
//
|
||||
// ClassLoader1 --> defines m1 --> packages p1
|
||||
// ClassLoader1 --> defines m2 --> packages p2
|
||||
//
|
||||
// m1 can read m2
|
||||
// package p2 in m2 is exported to m1
|
||||
//
|
||||
// class p1.c1 defined in m1 tries to access p2.c2 defined in m2
|
||||
// Access allowed since m1 can read m2 and package p2 is exported to m1.
|
||||
//
|
||||
public class ModuleSameCLMain {
|
||||
|
||||
// Create a Layer over the boot layer.
|
||||
// Define modules within this layer to test access between
|
||||
// publically defined classes within packages of those modules.
|
||||
public void createLayerOnBoot() throws Throwable {
|
||||
|
||||
// Define module: m1
|
||||
// Can read: java.base, m2
|
||||
// Packages: p1
|
||||
// Packages exported: p1 is exported to unqualifiedly
|
||||
ModuleDescriptor descriptor_m1 =
|
||||
new ModuleDescriptor.Builder("m1")
|
||||
.requires("java.base")
|
||||
.requires("m2")
|
||||
.exports("p1")
|
||||
.build();
|
||||
|
||||
// Define module: m2
|
||||
// Can read: java.base
|
||||
// Packages: p2
|
||||
// Packages exported: package p2 is exported to m1
|
||||
ModuleDescriptor descriptor_m2 =
|
||||
new ModuleDescriptor.Builder("m2")
|
||||
.requires("java.base")
|
||||
.exports("p2", "m1")
|
||||
.build();
|
||||
|
||||
// Set up a ModuleFinder containing all modules for this layer.
|
||||
ModuleFinder finder = ModuleLibrary.of(descriptor_m1, descriptor_m2);
|
||||
|
||||
// Resolves "m1"
|
||||
Configuration cf = Layer.boot()
|
||||
.configuration()
|
||||
.resolveRequires(finder, ModuleFinder.of(), Set.of("m1"));
|
||||
|
||||
// map each module to the same class loader for this test
|
||||
Map<String, ClassLoader> map = new HashMap<>();
|
||||
Loader1 cl1 = new Loader1();
|
||||
map.put("m1", cl1);
|
||||
map.put("m2", cl1);
|
||||
|
||||
// Create Layer that contains m1 & m2
|
||||
Layer layer = Layer.boot().defineModules(cf, map::get);
|
||||
assertTrue(layer.findLoader("m1") == cl1);
|
||||
assertTrue(layer.findLoader("m2") == cl1);
|
||||
assertTrue(layer.findLoader("java.base") == null);
|
||||
|
||||
// now use the same loader to load class p1.c1
|
||||
Class p1_c1_class = cl1.loadClass("p1.c1");
|
||||
try {
|
||||
p1_c1_class.newInstance();
|
||||
} catch (IllegalAccessError e) {
|
||||
throw new RuntimeException("Test Failed, an IAE should not be thrown since p2 is exported qualifiedly to m1");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Throwable {
|
||||
ModuleSameCLMain test = new ModuleSameCLMain();
|
||||
test.createLayerOnBoot();
|
||||
}
|
||||
|
||||
static class Loader1 extends ClassLoader { }
|
||||
}
|
131
hotspot/test/runtime/modules/ModuleStress/ModuleStress.java
Normal file
131
hotspot/test/runtime/modules/ModuleStress/ModuleStress.java
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8159262
|
||||
* @summary Test differing scenarios where a module's readability list and a package's exportability list should be walked
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /testlibrary /test/lib
|
||||
* @compile ../AccessCheck/ModuleLibrary.java
|
||||
* @compile ModuleSameCLMain.java
|
||||
* @compile ModuleNonBuiltinCLMain.java
|
||||
* @compile CustomSystemClassLoader.java
|
||||
* @build ModuleStress
|
||||
* @run main/othervm ModuleStress
|
||||
*/
|
||||
|
||||
import jdk.test.lib.*;
|
||||
import java.io.File;
|
||||
|
||||
public class ModuleStress {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
// Test #1: java -version
|
||||
// All modules' readability lists and packages' exportability
|
||||
// lists should contain only modules defined to the 3 builtin
|
||||
// loaders (boot, application, platform). Thus there is
|
||||
// not a need to walk those lists at a GC safepoint since
|
||||
// those loaders never die.
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-Xbootclasspath/a:.",
|
||||
"-Xlog:modules=trace",
|
||||
"-version");
|
||||
|
||||
OutputAnalyzer oa = new OutputAnalyzer(pb.start());
|
||||
oa.shouldNotContain("must be walked")
|
||||
.shouldNotContain("being walked")
|
||||
.shouldHaveExitValue(0);
|
||||
|
||||
// Next 2 tests involve the use of class p1.c1 and p2.c2
|
||||
String source1 = "package p1;" +
|
||||
"import p2.c2;" +
|
||||
"public class c1 {" +
|
||||
" public c1() {" +
|
||||
" p2.c2 c2_obj = new p2.c2();" +
|
||||
" c2_obj.method2();" +
|
||||
" }" +
|
||||
"}";
|
||||
|
||||
String source2 = "package p2;" +
|
||||
"public class c2 {" +
|
||||
" public void method2() { }" +
|
||||
"}";
|
||||
|
||||
ClassFileInstaller.writeClassToDisk("p2/c2",
|
||||
InMemoryJavaCompiler.compile("p2.c2", source2), System.getProperty("test.classes"));
|
||||
|
||||
ClassFileInstaller.writeClassToDisk("p1/c1",
|
||||
InMemoryJavaCompiler.compile("p1.c1", source1), System.getProperty("test.classes"));
|
||||
|
||||
// Test #2: Load two modules defined to the same customer class loader.
|
||||
// m1's module readability list and package p2's exportability should
|
||||
// not be walked at a GC safepoint since both modules are defined to
|
||||
// the same loader and thus have the exact same life cycle.
|
||||
pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-Xbootclasspath/a:.",
|
||||
"-Xlog:modules=trace",
|
||||
"ModuleSameCLMain");
|
||||
|
||||
oa = new OutputAnalyzer(pb.start());
|
||||
oa.shouldNotContain("must be walked")
|
||||
.shouldNotContain("being walked")
|
||||
.shouldHaveExitValue(0);
|
||||
|
||||
// Test #3: Load two modules in differing custom class loaders.
|
||||
// m1's module readability list and package p2's exportability list must
|
||||
// be walked at a GC safepoint since both modules are defined to non-builtin
|
||||
// class loaders which could die and thus be unloaded.
|
||||
pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-Xbootclasspath/a:.",
|
||||
"-Xlog:modules=trace",
|
||||
"ModuleNonBuiltinCLMain");
|
||||
|
||||
oa = new OutputAnalyzer(pb.start());
|
||||
oa.shouldContain("module m1 reads list must be walked")
|
||||
.shouldContain("package p2 defined in module m2, exports list must be walked")
|
||||
.shouldNotContain("module m2 reads list must be walked")
|
||||
.shouldHaveExitValue(0);
|
||||
|
||||
// Test #4: Load two modules in differing custom class loaders,
|
||||
// of which one has been designated as the custom system class loader
|
||||
// via -Djava.system.class.loader=CustomSystemClassLoader. Since
|
||||
// m3 is defined to the system class loader, m2's module readability
|
||||
// list does not have to be walked at a GC safepoint, but package p2's
|
||||
// exportability list does.
|
||||
pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-Djava.system.class.loader=CustomSystemClassLoader",
|
||||
"-Xbootclasspath/a:.",
|
||||
"-Xlog:modules=trace",
|
||||
"ModuleNonBuiltinCLMain");
|
||||
|
||||
oa = new OutputAnalyzer(pb.start());
|
||||
oa.shouldContain("package p2 defined in module m2, exports list must be walked")
|
||||
.shouldNotContain("module m2 reads list must be walked")
|
||||
.shouldHaveExitValue(0);
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8159262
|
||||
* @summary layers over the boot layer are repeatedly created, during this iteration, GCs are forced to verify correct walk of module and package lists.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /testlibrary /test/lib
|
||||
* @compile ../CompilerUtils.java
|
||||
* @build ModuleStressGC
|
||||
* @run main/othervm ModuleStressGC
|
||||
*/
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import jdk.test.lib.*;
|
||||
|
||||
public class ModuleStressGC {
|
||||
|
||||
private static final String TEST_SRC = System.getProperty("test.src");
|
||||
private static final String TEST_CLASSES = System.getProperty("test.classes");
|
||||
|
||||
private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
|
||||
private static final Path MODS_DIR = Paths.get(TEST_CLASSES, "mods");
|
||||
|
||||
/**
|
||||
* Compile two module definitions used by the test, jdk.test and jdk.translet.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
boolean compiled;
|
||||
// Compile module jdk.test declaration
|
||||
compiled = CompilerUtils.compile(
|
||||
SRC_DIR.resolve("jdk.test"),
|
||||
MODS_DIR.resolve("jdk.test"));
|
||||
if (!compiled) {
|
||||
throw new RuntimeException("Test failed to compile module jdk.test");
|
||||
}
|
||||
|
||||
// Compile module jdk.translet declaration
|
||||
compiled = CompilerUtils.compile(
|
||||
SRC_DIR.resolve("jdk.translet"),
|
||||
MODS_DIR.resolve("jdk.translet"),
|
||||
"-XaddExports:jdk.test/test=jdk.translet",
|
||||
"-mp", MODS_DIR.toString());
|
||||
if (!compiled) {
|
||||
throw new RuntimeException("Test failed to compile module jdk.translet");
|
||||
}
|
||||
|
||||
// Sanity check that the test, jdk.test/test/MainGC.java,
|
||||
// correctly walks module jdk.test's reads list and package
|
||||
// test's, defined to module jdk.translet, export list at
|
||||
// GC safepoints.
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-Xlog:modules=trace",
|
||||
"-mp", MODS_DIR.toString(),
|
||||
"-m", "jdk.test/test.MainGC");
|
||||
OutputAnalyzer oa = new OutputAnalyzer(pb.start());
|
||||
oa.shouldContain("package test defined in module jdk.test, exports list being walked")
|
||||
.shouldContain("module jdk.test reads list being walked")
|
||||
.shouldHaveExitValue(0);
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
package test;
|
||||
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.reflect.Layer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Module;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class MainGC {
|
||||
|
||||
private static final Path MODS_DIR = Paths.get(System.getProperty("jdk.module.path"));
|
||||
static final String MODULE_NAME = "jdk.translet";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
ModuleFinder finder = ModuleFinder.of(MODS_DIR);
|
||||
Layer layerBoot = Layer.boot();
|
||||
|
||||
Configuration cf = layerBoot
|
||||
.configuration()
|
||||
.resolveRequires(ModuleFinder.of(), finder, Set.of(MODULE_NAME));
|
||||
|
||||
Module testModule = MainGC.class.getModule();
|
||||
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
||||
|
||||
// Create an unique module/class loader in a layer above the boot layer.
|
||||
// Export this module to the jdk.test/test package.
|
||||
// Add a read edge from module jdk.test to this module.
|
||||
Callable<Void> task = new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Layer layer = Layer.boot().defineModulesWithOneLoader(cf, scl);
|
||||
Module transletModule = layer.findModule(MODULE_NAME).get();
|
||||
testModule.addExports("test", transletModule);
|
||||
testModule.addReads(transletModule);
|
||||
Class<?> c = layer.findLoader(MODULE_NAME).loadClass("translet.MainGC");
|
||||
Method method = c.getDeclaredMethod("go");
|
||||
method.invoke(null);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
List<Future<Void>> results = new ArrayList<>();
|
||||
|
||||
// Repeatedly create the layer above stressing the exportation of
|
||||
// package jdk.test/test to several different modules.
|
||||
ExecutorService pool = Executors.newFixedThreadPool(Math.min(100, Runtime.getRuntime().availableProcessors()*10));
|
||||
try {
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
results.add(pool.submit(task));
|
||||
// At specified intervals, force a GC. This provides an
|
||||
// opportunity to verify that both the module jdk.test's reads
|
||||
// and the package test's, which is defined to jdk.test, exports
|
||||
// lists are being walked.
|
||||
if (i == 3000 || i == 6000 || i == 9000) {
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
}
|
||||
|
||||
int passed = 0;
|
||||
int failed = 0;
|
||||
|
||||
// The failed state should be 0, the created modules in layers above the
|
||||
// boot layer should be allowed access to the contents of the jdk.test/test
|
||||
// package since that package was exported to the transletModule above.
|
||||
for (Future<Void> result : results) {
|
||||
try {
|
||||
result.get();
|
||||
passed++;
|
||||
} catch (Throwable x) {
|
||||
x.printStackTrace();
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("passed: " + passed);
|
||||
System.out.println("failed: " + failed);
|
||||
}
|
||||
|
||||
public static void callback() { }
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
package translet;
|
||||
|
||||
public class MainGC {
|
||||
public static void go() {
|
||||
test.MainGC.callback();
|
||||
}
|
||||
}
|
@ -369,3 +369,4 @@ f8899b1884e2c4a000dbcc5b1a80954245fe462e jdk-9+122
|
||||
e04a15153cc293f05fcd60bc98236f50e16af46a jdk-9+124
|
||||
493eb91ec32a6dea7604cfbd86c10045ad9af15b jdk-9+125
|
||||
15722f71281f034bc696d8b96136da2ef34da44f jdk-9+126
|
||||
bdc3c0b737efbf899709eb3121ce760dcfb51151 jdk-9+127
|
||||
|
@ -372,3 +372,4 @@ c42decd28bbfa817347112ed6053b5fbd30517a2 jdk-9+123
|
||||
1600da1665cd2cc127014e8c002b328ec33a9147 jdk-9+124
|
||||
5b0570e3db29f6b8c80a4beac70d51284507b203 jdk-9+125
|
||||
264a44128cd6286e598d5a849ceeb613c06269d0 jdk-9+126
|
||||
06d706c70634775418dc79a2671780ba1c624fd2 jdk-9+127
|
||||
|
@ -169,7 +169,7 @@ class NTLM {
|
||||
|
||||
byte[] readSecurityBuffer(int offset) throws NTLMException {
|
||||
int pos = readInt(offset+4);
|
||||
if (pos == 0) return null;
|
||||
if (pos == 0) return new byte[0];
|
||||
try {
|
||||
return Arrays.copyOfRange(
|
||||
internal, pos, pos + readShort(offset));
|
||||
|
@ -1613,6 +1613,8 @@ public final class Math {
|
||||
* @return (<i>a</i> × <i>b</i> + <i>c</i>)
|
||||
* computed, as if with unlimited range and precision, and rounded
|
||||
* once to the nearest {@code double} value
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
// @HotSpotIntrinsicCandidate
|
||||
public static double fma(double a, double b, double c) {
|
||||
@ -1728,6 +1730,8 @@ public final class Math {
|
||||
* @return (<i>a</i> × <i>b</i> + <i>c</i>)
|
||||
* computed, as if with unlimited range and precision, and rounded
|
||||
* once to the nearest {@code float} value
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
// @HotSpotIntrinsicCandidate
|
||||
public static float fma(float a, float b, float c) {
|
||||
|
@ -1276,6 +1276,8 @@ public final class StrictMath {
|
||||
* @return (<i>a</i> × <i>b</i> + <i>c</i>)
|
||||
* computed, as if with unlimited range and precision, and rounded
|
||||
* once to the nearest {@code double} value
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public static double fma(double a, double b, double c) {
|
||||
return Math.fma(a, b, c);
|
||||
@ -1328,6 +1330,8 @@ public final class StrictMath {
|
||||
* @return (<i>a</i> × <i>b</i> + <i>c</i>)
|
||||
* computed, as if with unlimited range and precision, and rounded
|
||||
* once to the nearest {@code float} value
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public static float fma(float a, float b, float c) {
|
||||
return Math.fma(a, b, c);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2016 Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2016, 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
|
||||
|
@ -3290,8 +3290,8 @@ public final class Files {
|
||||
* a size of {@code 0}. All bytes in the byte array are written to the file.
|
||||
* The method ensures that the file is closed when all bytes have been
|
||||
* written (or an I/O error or other runtime exception is thrown). If an I/O
|
||||
* error occurs then it may do so after the file has created or truncated,
|
||||
* or after some bytes have been written to the file.
|
||||
* error occurs then it may do so after the file has been created or
|
||||
* truncated, or after some bytes have been written to the file.
|
||||
*
|
||||
* <p> <b>Usage example</b>: By default the method creates a new file or
|
||||
* overwrites an existing file. Suppose you instead want to append bytes
|
||||
@ -3360,7 +3360,8 @@ public final class Files {
|
||||
* a size of {@code 0}. The method ensures that the file is closed when all
|
||||
* lines have been written (or an I/O error or other runtime exception is
|
||||
* thrown). If an I/O error occurs then it may do so after the file has
|
||||
* created or truncated, or after some bytes have been written to the file.
|
||||
* been created or truncated, or after some bytes have been written to the
|
||||
* file.
|
||||
*
|
||||
* @param path
|
||||
* the path to the file
|
||||
|
@ -124,7 +124,6 @@ package java.util;
|
||||
* always well-defined for queues with the same elements but different
|
||||
* ordering properties.
|
||||
*
|
||||
*
|
||||
* <p>This interface is a member of the
|
||||
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
|
||||
* Java Collections Framework</a>.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -68,6 +68,7 @@ import java.util.function.ToIntFunction;
|
||||
import java.util.function.ToLongBiFunction;
|
||||
import java.util.function.ToLongFunction;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
/**
|
||||
* A hash table supporting full concurrency of retrievals and
|
||||
@ -747,7 +748,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
/* ---------------- Table element access -------------- */
|
||||
|
||||
/*
|
||||
* Volatile access methods are used for table elements as well as
|
||||
* Atomic access methods are used for table elements as well as
|
||||
* elements of in-progress next table while resizing. All uses of
|
||||
* the tab arguments must be null checked by callers. All callers
|
||||
* also paranoically precheck that tab's length is not zero (or an
|
||||
@ -757,14 +758,12 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
* errors by users, these checks must operate on local variables,
|
||||
* which accounts for some odd-looking inline assignments below.
|
||||
* Note that calls to setTabAt always occur within locked regions,
|
||||
* and so in principle require only release ordering, not
|
||||
* full volatile semantics, but are currently coded as volatile
|
||||
* writes to be conservative.
|
||||
* and so require only release ordering.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
|
||||
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
|
||||
return (Node<K,V>)U.getObjectAcquire(tab, ((long)i << ASHIFT) + ABASE);
|
||||
}
|
||||
|
||||
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
|
||||
@ -773,7 +772,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
}
|
||||
|
||||
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
|
||||
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
|
||||
U.putObjectRelease(tab, ((long)i << ASHIFT) + ABASE, v);
|
||||
}
|
||||
|
||||
/* ---------------- Fields -------------- */
|
||||
@ -3298,7 +3297,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private static final long LOCKSTATE;
|
||||
static {
|
||||
try {
|
||||
@ -6341,7 +6340,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private static final long SIZECTL;
|
||||
private static final long TRANSFERINDEX;
|
||||
private static final long BASECOUNT;
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -292,64 +294,23 @@ public class ConcurrentLinkedDeque<E>
|
||||
volatile Node<E> prev;
|
||||
volatile E item;
|
||||
volatile Node<E> next;
|
||||
}
|
||||
|
||||
Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new node. Uses relaxed write because item can
|
||||
* only be seen after publication via casNext or casPrev.
|
||||
*/
|
||||
Node(E item) {
|
||||
U.putObject(this, ITEM, item);
|
||||
}
|
||||
|
||||
boolean casItem(E cmp, E val) {
|
||||
return U.compareAndSwapObject(this, ITEM, cmp, val);
|
||||
}
|
||||
|
||||
void lazySetNext(Node<E> val) {
|
||||
U.putObjectRelease(this, NEXT, val);
|
||||
}
|
||||
|
||||
boolean casNext(Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(this, NEXT, cmp, val);
|
||||
}
|
||||
|
||||
void lazySetPrev(Node<E> val) {
|
||||
U.putObjectRelease(this, PREV, val);
|
||||
}
|
||||
|
||||
boolean casPrev(Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(this, PREV, cmp, val);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long PREV;
|
||||
private static final long ITEM;
|
||||
private static final long NEXT;
|
||||
|
||||
static {
|
||||
try {
|
||||
PREV = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("prev"));
|
||||
ITEM = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("item"));
|
||||
NEXT = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("next"));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns a new node holding item. Uses relaxed write because item
|
||||
* can only be seen after piggy-backing publication via CAS.
|
||||
*/
|
||||
static <E> Node<E> newNode(E item) {
|
||||
Node<E> node = new Node<E>();
|
||||
ITEM.set(node, item);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Links e as first element.
|
||||
*/
|
||||
private void linkFirst(E e) {
|
||||
final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
|
||||
final Node<E> newNode = newNode(Objects.requireNonNull(e));
|
||||
|
||||
restartFromHead:
|
||||
for (;;)
|
||||
@ -363,13 +324,13 @@ public class ConcurrentLinkedDeque<E>
|
||||
continue restartFromHead;
|
||||
else {
|
||||
// p is first node
|
||||
newNode.lazySetNext(p); // CAS piggyback
|
||||
if (p.casPrev(null, newNode)) {
|
||||
NEXT.set(newNode, p); // CAS piggyback
|
||||
if (PREV.compareAndSet(p, null, newNode)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for e to become an element of this deque,
|
||||
// and for newNode to become "live".
|
||||
if (p != h) // hop two nodes at a time
|
||||
casHead(h, newNode); // Failure is OK.
|
||||
if (p != h) // hop two nodes at a time; failure is OK
|
||||
HEAD.weakCompareAndSetVolatile(this, h, newNode);
|
||||
return;
|
||||
}
|
||||
// Lost CAS race to another thread; re-read prev
|
||||
@ -381,7 +342,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
* Links e as last element.
|
||||
*/
|
||||
private void linkLast(E e) {
|
||||
final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
|
||||
final Node<E> newNode = newNode(Objects.requireNonNull(e));
|
||||
|
||||
restartFromTail:
|
||||
for (;;)
|
||||
@ -395,13 +356,13 @@ public class ConcurrentLinkedDeque<E>
|
||||
continue restartFromTail;
|
||||
else {
|
||||
// p is last node
|
||||
newNode.lazySetPrev(p); // CAS piggyback
|
||||
if (p.casNext(null, newNode)) {
|
||||
PREV.set(newNode, p); // CAS piggyback
|
||||
if (NEXT.compareAndSet(p, null, newNode)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for e to become an element of this deque,
|
||||
// and for newNode to become "live".
|
||||
if (p != t) // hop two nodes at a time
|
||||
casTail(t, newNode); // Failure is OK.
|
||||
if (p != t) // hop two nodes at a time; failure is OK
|
||||
TAIL.weakCompareAndSetVolatile(this, t, newNode);
|
||||
return;
|
||||
}
|
||||
// Lost CAS race to another thread; re-read next
|
||||
@ -516,8 +477,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
updateTail(); // Ensure x is not reachable from tail
|
||||
|
||||
// Finally, actually gc-unlink
|
||||
x.lazySetPrev(isFirst ? prevTerminator() : x);
|
||||
x.lazySetNext(isLast ? nextTerminator() : x);
|
||||
PREV.setRelease(x, isFirst ? prevTerminator() : x);
|
||||
NEXT.setRelease(x, isLast ? nextTerminator() : x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -531,7 +492,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
// assert first.item == null;
|
||||
for (Node<E> o = null, p = next, q;;) {
|
||||
if (p.item != null || (q = p.next) == null) {
|
||||
if (o != null && p.prev != p && first.casNext(next, p)) {
|
||||
if (o != null && p.prev != p &&
|
||||
NEXT.compareAndSet(first, next, p)) {
|
||||
skipDeletedPredecessors(p);
|
||||
if (first.prev == null &&
|
||||
(p.next == null || p.item != null) &&
|
||||
@ -541,8 +503,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
updateTail(); // Ensure o is not reachable from tail
|
||||
|
||||
// Finally, actually gc-unlink
|
||||
o.lazySetNext(o);
|
||||
o.lazySetPrev(prevTerminator());
|
||||
NEXT.setRelease(o, o);
|
||||
PREV.setRelease(o, prevTerminator());
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -565,7 +527,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
// assert last.item == null;
|
||||
for (Node<E> o = null, p = prev, q;;) {
|
||||
if (p.item != null || (q = p.prev) == null) {
|
||||
if (o != null && p.next != p && last.casPrev(prev, p)) {
|
||||
if (o != null && p.next != p &&
|
||||
PREV.compareAndSet(last, prev, p)) {
|
||||
skipDeletedSuccessors(p);
|
||||
if (last.next == null &&
|
||||
(p.prev == null || p.item != null) &&
|
||||
@ -575,8 +538,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
updateTail(); // Ensure o is not reachable from tail
|
||||
|
||||
// Finally, actually gc-unlink
|
||||
o.lazySetPrev(o);
|
||||
o.lazySetNext(nextTerminator());
|
||||
PREV.setRelease(o, o);
|
||||
NEXT.setRelease(o, nextTerminator());
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -607,7 +570,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
(q = (p = q).prev) == null) {
|
||||
// It is possible that p is PREV_TERMINATOR,
|
||||
// but if so, the CAS is guaranteed to fail.
|
||||
if (casHead(h, p))
|
||||
if (HEAD.compareAndSet(this, h, p))
|
||||
return;
|
||||
else
|
||||
continue restartFromHead;
|
||||
@ -637,7 +600,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
(q = (p = q).next) == null) {
|
||||
// It is possible that p is NEXT_TERMINATOR,
|
||||
// but if so, the CAS is guaranteed to fail.
|
||||
if (casTail(t, p))
|
||||
if (TAIL.compareAndSet(this, t, p))
|
||||
return;
|
||||
else
|
||||
continue restartFromTail;
|
||||
@ -675,7 +638,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
}
|
||||
|
||||
// found active CAS target
|
||||
if (prev == p || x.casPrev(prev, p))
|
||||
if (prev == p || PREV.compareAndSet(x, prev, p))
|
||||
return;
|
||||
|
||||
} while (x.item != null || x.next == null);
|
||||
@ -706,7 +669,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
}
|
||||
|
||||
// found active CAS target
|
||||
if (next == p || x.casNext(next, p))
|
||||
if (next == p || NEXT.compareAndSet(x, next, p))
|
||||
return;
|
||||
|
||||
} while (x.item != null || x.prev == null);
|
||||
@ -751,7 +714,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
else if (p == h
|
||||
// It is possible that p is PREV_TERMINATOR,
|
||||
// but if so, the CAS is guaranteed to fail.
|
||||
|| casHead(h, p))
|
||||
|| HEAD.compareAndSet(this, h, p))
|
||||
return p;
|
||||
else
|
||||
continue restartFromHead;
|
||||
@ -776,7 +739,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
else if (p == t
|
||||
// It is possible that p is NEXT_TERMINATOR,
|
||||
// but if so, the CAS is guaranteed to fail.
|
||||
|| casTail(t, p))
|
||||
|| TAIL.compareAndSet(this, t, p))
|
||||
return p;
|
||||
else
|
||||
continue restartFromTail;
|
||||
@ -802,7 +765,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
* Constructs an empty deque.
|
||||
*/
|
||||
public ConcurrentLinkedDeque() {
|
||||
head = tail = new Node<E>(null);
|
||||
head = tail = new Node<E>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -818,12 +781,12 @@ public class ConcurrentLinkedDeque<E>
|
||||
// Copy c into a private chain of Nodes
|
||||
Node<E> h = null, t = null;
|
||||
for (E e : c) {
|
||||
Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
|
||||
Node<E> newNode = newNode(Objects.requireNonNull(e));
|
||||
if (h == null)
|
||||
h = t = newNode;
|
||||
else {
|
||||
t.lazySetNext(newNode);
|
||||
newNode.lazySetPrev(t);
|
||||
NEXT.set(t, newNode);
|
||||
PREV.set(newNode, t);
|
||||
t = newNode;
|
||||
}
|
||||
}
|
||||
@ -836,12 +799,12 @@ public class ConcurrentLinkedDeque<E>
|
||||
private void initHeadTail(Node<E> h, Node<E> t) {
|
||||
if (h == t) {
|
||||
if (h == null)
|
||||
h = t = new Node<E>(null);
|
||||
h = t = new Node<E>();
|
||||
else {
|
||||
// Avoid edge case of a single Node with non-null item.
|
||||
Node<E> newNode = new Node<E>(null);
|
||||
t.lazySetNext(newNode);
|
||||
newNode.lazySetPrev(t);
|
||||
Node<E> newNode = new Node<E>();
|
||||
NEXT.set(t, newNode);
|
||||
PREV.set(newNode, t);
|
||||
t = newNode;
|
||||
}
|
||||
}
|
||||
@ -934,7 +897,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
public E pollFirst() {
|
||||
for (Node<E> p = first(); p != null; p = succ(p)) {
|
||||
E item = p.item;
|
||||
if (item != null && p.casItem(item, null)) {
|
||||
if (item != null && ITEM.compareAndSet(p, item, null)) {
|
||||
unlink(p);
|
||||
return item;
|
||||
}
|
||||
@ -945,7 +908,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
public E pollLast() {
|
||||
for (Node<E> p = last(); p != null; p = pred(p)) {
|
||||
E item = p.item;
|
||||
if (item != null && p.casItem(item, null)) {
|
||||
if (item != null && ITEM.compareAndSet(p, item, null)) {
|
||||
unlink(p);
|
||||
return item;
|
||||
}
|
||||
@ -1031,7 +994,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
Objects.requireNonNull(o);
|
||||
for (Node<E> p = first(); p != null; p = succ(p)) {
|
||||
E item = p.item;
|
||||
if (item != null && o.equals(item) && p.casItem(item, null)) {
|
||||
if (item != null && o.equals(item) &&
|
||||
ITEM.compareAndSet(p, item, null)) {
|
||||
unlink(p);
|
||||
return true;
|
||||
}
|
||||
@ -1055,7 +1019,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
Objects.requireNonNull(o);
|
||||
for (Node<E> p = last(); p != null; p = pred(p)) {
|
||||
E item = p.item;
|
||||
if (item != null && o.equals(item) && p.casItem(item, null)) {
|
||||
if (item != null && o.equals(item) &&
|
||||
ITEM.compareAndSet(p, item, null)) {
|
||||
unlink(p);
|
||||
return true;
|
||||
}
|
||||
@ -1159,12 +1124,12 @@ public class ConcurrentLinkedDeque<E>
|
||||
// Copy c into a private chain of Nodes
|
||||
Node<E> beginningOfTheEnd = null, last = null;
|
||||
for (E e : c) {
|
||||
Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
|
||||
Node<E> newNode = newNode(Objects.requireNonNull(e));
|
||||
if (beginningOfTheEnd == null)
|
||||
beginningOfTheEnd = last = newNode;
|
||||
else {
|
||||
last.lazySetNext(newNode);
|
||||
newNode.lazySetPrev(last);
|
||||
NEXT.set(last, newNode);
|
||||
PREV.set(newNode, last);
|
||||
last = newNode;
|
||||
}
|
||||
}
|
||||
@ -1184,16 +1149,16 @@ public class ConcurrentLinkedDeque<E>
|
||||
continue restartFromTail;
|
||||
else {
|
||||
// p is last node
|
||||
beginningOfTheEnd.lazySetPrev(p); // CAS piggyback
|
||||
if (p.casNext(null, beginningOfTheEnd)) {
|
||||
PREV.set(beginningOfTheEnd, p); // CAS piggyback
|
||||
if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for all elements to be added to this deque.
|
||||
if (!casTail(t, last)) {
|
||||
if (!TAIL.weakCompareAndSetVolatile(this, t, last)) {
|
||||
// Try a little harder to update tail,
|
||||
// since we may be adding many elements.
|
||||
t = tail;
|
||||
if (last.next == null)
|
||||
casTail(t, last);
|
||||
TAIL.weakCompareAndSetVolatile(this, t, last);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1586,41 +1551,38 @@ public class ConcurrentLinkedDeque<E>
|
||||
Node<E> h = null, t = null;
|
||||
for (Object item; (item = s.readObject()) != null; ) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Node<E> newNode = new Node<E>((E) item);
|
||||
Node<E> newNode = newNode((E) item);
|
||||
if (h == null)
|
||||
h = t = newNode;
|
||||
else {
|
||||
t.lazySetNext(newNode);
|
||||
newNode.lazySetPrev(t);
|
||||
NEXT.set(t, newNode);
|
||||
PREV.set(newNode, t);
|
||||
t = newNode;
|
||||
}
|
||||
}
|
||||
initHeadTail(h, t);
|
||||
}
|
||||
|
||||
private boolean casHead(Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(this, HEAD, cmp, val);
|
||||
}
|
||||
|
||||
private boolean casTail(Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(this, TAIL, cmp, val);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long HEAD;
|
||||
private static final long TAIL;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle HEAD;
|
||||
private static final VarHandle TAIL;
|
||||
private static final VarHandle PREV;
|
||||
private static final VarHandle NEXT;
|
||||
private static final VarHandle ITEM;
|
||||
static {
|
||||
PREV_TERMINATOR = new Node<Object>();
|
||||
PREV_TERMINATOR.next = PREV_TERMINATOR;
|
||||
NEXT_TERMINATOR = new Node<Object>();
|
||||
NEXT_TERMINATOR.prev = NEXT_TERMINATOR;
|
||||
try {
|
||||
HEAD = U.objectFieldOffset
|
||||
(ConcurrentLinkedDeque.class.getDeclaredField("head"));
|
||||
TAIL = U.objectFieldOffset
|
||||
(ConcurrentLinkedDeque.class.getDeclaredField("tail"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
HEAD = l.findVarHandle(ConcurrentLinkedDeque.class, "head",
|
||||
Node.class);
|
||||
TAIL = l.findVarHandle(ConcurrentLinkedDeque.class, "tail",
|
||||
Node.class);
|
||||
PREV = l.findVarHandle(Node.class, "prev", Node.class);
|
||||
NEXT = l.findVarHandle(Node.class, "next", Node.class);
|
||||
ITEM = l.findVarHandle(Node.class, "item", Object.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -166,9 +168,8 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
* this is merely an optimization.
|
||||
*
|
||||
* When constructing a Node (before enqueuing it) we avoid paying
|
||||
* for a volatile write to item by using Unsafe.putObject instead
|
||||
* of a normal write. This allows the cost of enqueue to be
|
||||
* "one-and-a-half" CASes.
|
||||
* for a volatile write to item. This allows the cost of enqueue
|
||||
* to be "one-and-a-half" CASes.
|
||||
*
|
||||
* Both head and tail may or may not point to a Node with a
|
||||
* non-null item. If the queue is empty, all items must of course
|
||||
@ -178,33 +179,21 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
* optimization.
|
||||
*/
|
||||
|
||||
private static class Node<E> {
|
||||
static final class Node<E> {
|
||||
volatile E item;
|
||||
volatile Node<E> next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new node holding item. Uses relaxed write because item
|
||||
* can only be seen after piggy-backing publication via casNext.
|
||||
* can only be seen after piggy-backing publication via CAS.
|
||||
*/
|
||||
static <E> Node<E> newNode(E item) {
|
||||
Node<E> node = new Node<E>();
|
||||
U.putObject(node, ITEM, item);
|
||||
ITEM.set(node, item);
|
||||
return node;
|
||||
}
|
||||
|
||||
static <E> boolean casItem(Node<E> node, E cmp, E val) {
|
||||
return U.compareAndSwapObject(node, ITEM, cmp, val);
|
||||
}
|
||||
|
||||
static <E> void lazySetNext(Node<E> node, Node<E> val) {
|
||||
U.putObjectRelease(node, NEXT, val);
|
||||
}
|
||||
|
||||
static <E> boolean casNext(Node<E> node, Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(node, NEXT, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* A node from which the first live (non-deleted) node (if any)
|
||||
* can be reached in O(1) time.
|
||||
@ -256,7 +245,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
if (h == null)
|
||||
h = t = newNode;
|
||||
else {
|
||||
lazySetNext(t, newNode);
|
||||
NEXT.set(t, newNode);
|
||||
t = newNode;
|
||||
}
|
||||
}
|
||||
@ -286,8 +275,8 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
*/
|
||||
final void updateHead(Node<E> h, Node<E> p) {
|
||||
// assert h != null && p != null && (h == p || h.item == null);
|
||||
if (h != p && casHead(h, p))
|
||||
lazySetNext(h, h);
|
||||
if (h != p && HEAD.compareAndSet(this, h, p))
|
||||
NEXT.setRelease(h, h);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -314,12 +303,12 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
Node<E> q = p.next;
|
||||
if (q == null) {
|
||||
// p is last node
|
||||
if (casNext(p, null, newNode)) {
|
||||
if (NEXT.compareAndSet(p, null, newNode)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for e to become an element of this queue,
|
||||
// and for newNode to become "live".
|
||||
if (p != t) // hop two nodes at a time
|
||||
casTail(t, newNode); // Failure is OK.
|
||||
if (p != t) // hop two nodes at a time; failure is OK
|
||||
TAIL.weakCompareAndSetVolatile(this, t, newNode);
|
||||
return true;
|
||||
}
|
||||
// Lost CAS race to another thread; re-read next
|
||||
@ -342,7 +331,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
for (Node<E> h = head, p = h, q;;) {
|
||||
E item = p.item;
|
||||
|
||||
if (item != null && casItem(p, item, null)) {
|
||||
if (item != null && ITEM.compareAndSet(p, item, null)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for item to be removed from this queue.
|
||||
if (p != h) // hop two nodes at a time
|
||||
@ -483,12 +472,12 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
next = succ(p);
|
||||
continue;
|
||||
}
|
||||
removed = casItem(p, item, null);
|
||||
removed = ITEM.compareAndSet(p, item, null);
|
||||
}
|
||||
|
||||
next = succ(p);
|
||||
if (pred != null && next != null) // unlink
|
||||
casNext(pred, p, next);
|
||||
NEXT.weakCompareAndSetVolatile(pred, p, next);
|
||||
if (removed)
|
||||
return true;
|
||||
}
|
||||
@ -520,7 +509,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
if (beginningOfTheEnd == null)
|
||||
beginningOfTheEnd = last = newNode;
|
||||
else {
|
||||
lazySetNext(last, newNode);
|
||||
NEXT.set(last, newNode);
|
||||
last = newNode;
|
||||
}
|
||||
}
|
||||
@ -532,15 +521,15 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
Node<E> q = p.next;
|
||||
if (q == null) {
|
||||
// p is last node
|
||||
if (casNext(p, null, beginningOfTheEnd)) {
|
||||
if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for all elements to be added to this queue.
|
||||
if (!casTail(t, last)) {
|
||||
if (!TAIL.weakCompareAndSetVolatile(this, t, last)) {
|
||||
// Try a little harder to update tail,
|
||||
// since we may be adding many elements.
|
||||
t = tail;
|
||||
if (last.next == null)
|
||||
casTail(t, last);
|
||||
TAIL.weakCompareAndSetVolatile(this, t, last);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -744,7 +733,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
}
|
||||
// unlink deleted nodes
|
||||
if ((q = succ(p)) != null)
|
||||
casNext(pred, p, q);
|
||||
NEXT.compareAndSet(pred, p, q);
|
||||
}
|
||||
}
|
||||
|
||||
@ -801,7 +790,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
if (h == null)
|
||||
h = t = newNode;
|
||||
else {
|
||||
lazySetNext(t, newNode);
|
||||
NEXT.set(t, newNode);
|
||||
t = newNode;
|
||||
}
|
||||
}
|
||||
@ -919,31 +908,20 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
return new CLQSpliterator<E>(this);
|
||||
}
|
||||
|
||||
private boolean casTail(Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(this, TAIL, cmp, val);
|
||||
}
|
||||
|
||||
private boolean casHead(Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(this, HEAD, cmp, val);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long HEAD;
|
||||
private static final long TAIL;
|
||||
private static final long ITEM;
|
||||
private static final long NEXT;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle HEAD;
|
||||
private static final VarHandle TAIL;
|
||||
private static final VarHandle ITEM;
|
||||
private static final VarHandle NEXT;
|
||||
static {
|
||||
try {
|
||||
HEAD = U.objectFieldOffset
|
||||
(ConcurrentLinkedQueue.class.getDeclaredField("head"));
|
||||
TAIL = U.objectFieldOffset
|
||||
(ConcurrentLinkedQueue.class.getDeclaredField("tail"));
|
||||
ITEM = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("item"));
|
||||
NEXT = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("next"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
HEAD = l.findVarHandle(ConcurrentLinkedQueue.class, "head",
|
||||
Node.class);
|
||||
TAIL = l.findVarHandle(ConcurrentLinkedQueue.class, "tail",
|
||||
Node.class);
|
||||
ITEM = l.findVarHandle(Node.class, "item", Object.class);
|
||||
NEXT = l.findVarHandle(Node.class, "next", Node.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.AbstractMap;
|
||||
@ -401,7 +403,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
||||
* compareAndSet head node.
|
||||
*/
|
||||
private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) {
|
||||
return U.compareAndSwapObject(this, HEAD, cmp, val);
|
||||
return HEAD.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/* ---------------- Nodes -------------- */
|
||||
@ -444,14 +446,14 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
||||
* compareAndSet value field.
|
||||
*/
|
||||
boolean casValue(Object cmp, Object val) {
|
||||
return U.compareAndSwapObject(this, VALUE, cmp, val);
|
||||
return VALUE.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* compareAndSet next field.
|
||||
*/
|
||||
boolean casNext(Node<K,V> cmp, Node<K,V> val) {
|
||||
return U.compareAndSwapObject(this, NEXT, cmp, val);
|
||||
return NEXT.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -532,20 +534,16 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
||||
return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long VALUE;
|
||||
private static final long NEXT;
|
||||
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle VALUE;
|
||||
private static final VarHandle NEXT;
|
||||
static {
|
||||
try {
|
||||
VALUE = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("value"));
|
||||
NEXT = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("next"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
VALUE = l.findVarHandle(Node.class, "value", Object.class);
|
||||
NEXT = l.findVarHandle(Node.class, "next", Node.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -577,7 +575,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
||||
* compareAndSet right field.
|
||||
*/
|
||||
final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
|
||||
return U.compareAndSwapObject(this, RIGHT, cmp, val);
|
||||
return RIGHT.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -613,13 +611,12 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
||||
return node.value != null && casRight(succ, succ.right);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long RIGHT;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle RIGHT;
|
||||
static {
|
||||
try {
|
||||
RIGHT = U.objectFieldOffset
|
||||
(Index.class.getDeclaredField("right"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
RIGHT = l.findVarHandle(Index.class, "right", Index.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -3607,13 +3604,13 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long HEAD;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle HEAD;
|
||||
static {
|
||||
try {
|
||||
HEAD = U.objectFieldOffset
|
||||
(ConcurrentSkipListMap.class.getDeclaredField("head"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
HEAD = l.findVarHandle(ConcurrentSkipListMap.class, "head",
|
||||
HeadIndex.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -507,15 +509,16 @@ public class ConcurrentSkipListSet<E>
|
||||
|
||||
// Support for resetting map in clone
|
||||
private void setMap(ConcurrentNavigableMap<E,Object> map) {
|
||||
U.putObjectVolatile(this, MAP, map);
|
||||
MAP.setVolatile(this, map);
|
||||
}
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long MAP;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle MAP;
|
||||
static {
|
||||
try {
|
||||
MAP = U.objectFieldOffset
|
||||
(ConcurrentSkipListSet.class.getDeclaredField("m"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
MAP = l.findVarHandle(ConcurrentSkipListSet.class, "m",
|
||||
ConcurrentNavigableMap.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -34,6 +34,7 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -1541,17 +1542,21 @@ public class CopyOnWriteArrayList<E>
|
||||
}
|
||||
}
|
||||
|
||||
// Support for resetting lock while deserializing
|
||||
/** Initializes the lock; for use when deserializing or cloning. */
|
||||
private void resetLock() {
|
||||
U.putObjectVolatile(this, LOCK, new Object());
|
||||
}
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long LOCK;
|
||||
static {
|
||||
Field lockField = java.security.AccessController.doPrivileged(
|
||||
(java.security.PrivilegedAction<Field>) () -> {
|
||||
try {
|
||||
Field f = CopyOnWriteArrayList.class
|
||||
.getDeclaredField("lock");
|
||||
f.setAccessible(true);
|
||||
return f;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}});
|
||||
try {
|
||||
LOCK = U.objectFieldOffset
|
||||
(CopyOnWriteArrayList.class.getDeclaredField("lock"));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
lockField.set(this, new Object());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,9 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
|
||||
/**
|
||||
* A {@link ForkJoinTask} with a completion action performed when
|
||||
* triggered and there are no remaining pending actions.
|
||||
@ -524,7 +527,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
* @param delta the value to add
|
||||
*/
|
||||
public final void addToPendingCount(int delta) {
|
||||
U.getAndAddInt(this, PENDING, delta);
|
||||
PENDING.getAndAdd(this, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -536,7 +539,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public final boolean compareAndSetPendingCount(int expected, int count) {
|
||||
return U.compareAndSwapInt(this, PENDING, expected, count);
|
||||
return PENDING.compareAndSet(this, expected, count);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -548,7 +551,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
public final int decrementPendingCountUnlessZero() {
|
||||
int c;
|
||||
do {} while ((c = pending) != 0 &&
|
||||
!U.compareAndSwapInt(this, PENDING, c, c - 1));
|
||||
!PENDING.weakCompareAndSetVolatile(this, c, c - 1));
|
||||
return c;
|
||||
}
|
||||
|
||||
@ -581,7 +584,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
|
||||
else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1))
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -604,7 +607,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
|
||||
else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1))
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -649,7 +652,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
for (int c;;) {
|
||||
if ((c = pending) == 0)
|
||||
return this;
|
||||
else if (U.compareAndSwapInt(this, PENDING, c, c - 1))
|
||||
else if (PENDING.weakCompareAndSetVolatile(this, c, c - 1))
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -753,13 +756,13 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
*/
|
||||
protected void setRawResult(T t) { }
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long PENDING;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle PENDING;
|
||||
static {
|
||||
try {
|
||||
PENDING = U.objectFieldOffset
|
||||
(CountedCompleter.class.getDeclaredField("pending"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
PENDING = l.findVarHandle(CountedCompleter.class, "pending", int.class);
|
||||
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -36,6 +36,10 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
/**
|
||||
* A synchronization point at which threads can pair and swap elements
|
||||
* within pairs. Each thread presents some object on entry to the
|
||||
@ -155,9 +159,7 @@ public class Exchanger<V> {
|
||||
* a value that is enough for common platforms. Additionally,
|
||||
* extra care elsewhere is taken to avoid other false/unintended
|
||||
* sharing and to enhance locality, including adding padding (via
|
||||
* @Contended) to Nodes, embedding "bound" as an Exchanger field,
|
||||
* and reworking some park/unpark mechanics compared to
|
||||
* LockSupport versions.
|
||||
* @Contended) to Nodes, embedding "bound" as an Exchanger field.
|
||||
*
|
||||
* The arena starts out with only one used slot. We expand the
|
||||
* effective arena size by tracking collisions; i.e., failed CASes
|
||||
@ -234,12 +236,12 @@ public class Exchanger<V> {
|
||||
* because most of the logic relies on reads of fields that are
|
||||
* maintained as local variables so can't be nicely factored --
|
||||
* mainly, here, bulky spin->yield->block/cancel code), and
|
||||
* heavily dependent on intrinsics (Unsafe) to use inlined
|
||||
* heavily dependent on intrinsics (VarHandles) to use inlined
|
||||
* embedded CAS and related memory access operations (that tend
|
||||
* not to be as readily inlined by dynamic compilers when they are
|
||||
* hidden behind other methods that would more nicely name and
|
||||
* encapsulate the intended effects). This includes the use of
|
||||
* putXRelease to clear fields of the per-thread Nodes between
|
||||
* setRelease to clear fields of the per-thread Nodes between
|
||||
* uses. Note that field Node.item is not declared as volatile
|
||||
* even though it is read by releasing threads, because they only
|
||||
* do so after CAS operations that must precede access, and all
|
||||
@ -252,10 +254,10 @@ public class Exchanger<V> {
|
||||
*/
|
||||
|
||||
/**
|
||||
* The byte distance (as a shift value) between any two used slots
|
||||
* in the arena. 1 << ASHIFT should be at least cacheline size.
|
||||
* The index distance (as a shift value) between any two used slots
|
||||
* in the arena, spacing them out to avoid false sharing.
|
||||
*/
|
||||
private static final int ASHIFT = 7;
|
||||
private static final int ASHIFT = 5;
|
||||
|
||||
/**
|
||||
* The maximum supported arena index. The maximum allocatable
|
||||
@ -356,27 +358,31 @@ public class Exchanger<V> {
|
||||
*/
|
||||
private final Object arenaExchange(Object item, boolean timed, long ns) {
|
||||
Node[] a = arena;
|
||||
int alen = a.length;
|
||||
Node p = participant.get();
|
||||
for (int i = p.index;;) { // access slot at i
|
||||
int b, m, c; long j; // j is raw array offset
|
||||
Node q = (Node)U.getObjectVolatile(a, j = (i << ASHIFT) + ABASE);
|
||||
if (q != null && U.compareAndSwapObject(a, j, q, null)) {
|
||||
int b, m, c;
|
||||
int j = (i << ASHIFT) + ((1 << ASHIFT) - 1);
|
||||
if (j < 0 || j >= alen)
|
||||
j = alen - 1;
|
||||
Node q = (Node)AA.getAcquire(a, j);
|
||||
if (q != null && AA.compareAndSet(a, j, q, null)) {
|
||||
Object v = q.item; // release
|
||||
q.match = item;
|
||||
Thread w = q.parked;
|
||||
if (w != null)
|
||||
U.unpark(w);
|
||||
LockSupport.unpark(w);
|
||||
return v;
|
||||
}
|
||||
else if (i <= (m = (b = bound) & MMASK) && q == null) {
|
||||
p.item = item; // offer
|
||||
if (U.compareAndSwapObject(a, j, null, p)) {
|
||||
if (AA.compareAndSet(a, j, null, p)) {
|
||||
long end = (timed && m == 0) ? System.nanoTime() + ns : 0L;
|
||||
Thread t = Thread.currentThread(); // wait
|
||||
for (int h = p.hash, spins = SPINS;;) {
|
||||
Object v = p.match;
|
||||
if (v != null) {
|
||||
U.putObjectRelease(p, MATCH, null);
|
||||
MATCH.setRelease(p, null);
|
||||
p.item = null; // clear for next use
|
||||
p.hash = h;
|
||||
return v;
|
||||
@ -389,22 +395,24 @@ public class Exchanger<V> {
|
||||
(--spins & ((SPINS >>> 1) - 1)) == 0)
|
||||
Thread.yield(); // two yields per wait
|
||||
}
|
||||
else if (U.getObjectVolatile(a, j) != p)
|
||||
else if (AA.getAcquire(a, j) != p)
|
||||
spins = SPINS; // releaser hasn't set match yet
|
||||
else if (!t.isInterrupted() && m == 0 &&
|
||||
(!timed ||
|
||||
(ns = end - System.nanoTime()) > 0L)) {
|
||||
U.putObject(t, BLOCKER, this); // emulate LockSupport
|
||||
p.parked = t; // minimize window
|
||||
if (U.getObjectVolatile(a, j) == p)
|
||||
U.park(false, ns);
|
||||
if (AA.getAcquire(a, j) == p) {
|
||||
if (ns == 0L)
|
||||
LockSupport.park(this);
|
||||
else
|
||||
LockSupport.parkNanos(this, ns);
|
||||
}
|
||||
p.parked = null;
|
||||
U.putObject(t, BLOCKER, null);
|
||||
}
|
||||
else if (U.getObjectVolatile(a, j) == p &&
|
||||
U.compareAndSwapObject(a, j, p, null)) {
|
||||
else if (AA.getAcquire(a, j) == p &&
|
||||
AA.compareAndSet(a, j, p, null)) {
|
||||
if (m != 0) // try to shrink
|
||||
U.compareAndSwapInt(this, BOUND, b, b + SEQ - 1);
|
||||
BOUND.compareAndSet(this, b, b + SEQ - 1);
|
||||
p.item = null;
|
||||
p.hash = h;
|
||||
i = p.index >>>= 1; // descend
|
||||
@ -426,7 +434,7 @@ public class Exchanger<V> {
|
||||
i = (i != m || m == 0) ? m : m - 1;
|
||||
}
|
||||
else if ((c = p.collides) < m || m == FULL ||
|
||||
!U.compareAndSwapInt(this, BOUND, b, b + SEQ + 1)) {
|
||||
!BOUND.compareAndSet(this, b, b + SEQ + 1)) {
|
||||
p.collides = c + 1;
|
||||
i = (i == 0) ? m : i - 1; // cyclically traverse
|
||||
}
|
||||
@ -455,24 +463,24 @@ public class Exchanger<V> {
|
||||
|
||||
for (Node q;;) {
|
||||
if ((q = slot) != null) {
|
||||
if (U.compareAndSwapObject(this, SLOT, q, null)) {
|
||||
if (SLOT.compareAndSet(this, q, null)) {
|
||||
Object v = q.item;
|
||||
q.match = item;
|
||||
Thread w = q.parked;
|
||||
if (w != null)
|
||||
U.unpark(w);
|
||||
LockSupport.unpark(w);
|
||||
return v;
|
||||
}
|
||||
// create arena on contention, but continue until slot null
|
||||
if (NCPU > 1 && bound == 0 &&
|
||||
U.compareAndSwapInt(this, BOUND, 0, SEQ))
|
||||
BOUND.compareAndSet(this, 0, SEQ))
|
||||
arena = new Node[(FULL + 2) << ASHIFT];
|
||||
}
|
||||
else if (arena != null)
|
||||
return null; // caller must reroute to arenaExchange
|
||||
else {
|
||||
p.item = item;
|
||||
if (U.compareAndSwapObject(this, SLOT, null, p))
|
||||
if (SLOT.compareAndSet(this, null, p))
|
||||
break;
|
||||
p.item = null;
|
||||
}
|
||||
@ -495,19 +503,21 @@ public class Exchanger<V> {
|
||||
spins = SPINS;
|
||||
else if (!t.isInterrupted() && arena == null &&
|
||||
(!timed || (ns = end - System.nanoTime()) > 0L)) {
|
||||
U.putObject(t, BLOCKER, this);
|
||||
p.parked = t;
|
||||
if (slot == p)
|
||||
U.park(false, ns);
|
||||
if (slot == p) {
|
||||
if (ns == 0L)
|
||||
LockSupport.park(this);
|
||||
else
|
||||
LockSupport.parkNanos(this, ns);
|
||||
}
|
||||
p.parked = null;
|
||||
U.putObject(t, BLOCKER, null);
|
||||
}
|
||||
else if (U.compareAndSwapObject(this, SLOT, p, null)) {
|
||||
else if (SLOT.compareAndSet(this, p, null)) {
|
||||
v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
U.putObjectRelease(p, MATCH, null);
|
||||
MATCH.setRelease(p, null);
|
||||
p.item = null;
|
||||
p.hash = h;
|
||||
return v;
|
||||
@ -556,8 +566,9 @@ public class Exchanger<V> {
|
||||
@SuppressWarnings("unchecked")
|
||||
public V exchange(V x) throws InterruptedException {
|
||||
Object v;
|
||||
Node[] a;
|
||||
Object item = (x == null) ? NULL_ITEM : x; // translate null args
|
||||
if ((arena != null ||
|
||||
if (((a = arena) != null ||
|
||||
(v = slotExchange(item, false, 0L)) == null) &&
|
||||
((Thread.interrupted() || // disambiguates null return
|
||||
(v = arenaExchange(item, false, 0L)) == null)))
|
||||
@ -623,31 +634,18 @@ public class Exchanger<V> {
|
||||
return (v == NULL_ITEM) ? null : (V)v;
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long BOUND;
|
||||
private static final long SLOT;
|
||||
private static final long MATCH;
|
||||
private static final long BLOCKER;
|
||||
private static final int ABASE;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle BOUND;
|
||||
private static final VarHandle SLOT;
|
||||
private static final VarHandle MATCH;
|
||||
private static final VarHandle AA;
|
||||
static {
|
||||
try {
|
||||
BOUND = U.objectFieldOffset
|
||||
(Exchanger.class.getDeclaredField("bound"));
|
||||
SLOT = U.objectFieldOffset
|
||||
(Exchanger.class.getDeclaredField("slot"));
|
||||
|
||||
MATCH = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("match"));
|
||||
|
||||
BLOCKER = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("parkBlocker"));
|
||||
|
||||
int scale = U.arrayIndexScale(Node[].class);
|
||||
if ((scale & (scale - 1)) != 0 || scale > (1 << ASHIFT))
|
||||
throw new Error("Unsupported array scale");
|
||||
// ABASE absorbs padding in front of element 0
|
||||
ABASE = U.arrayBaseOffset(Node[].class) + (1 << ASHIFT);
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
BOUND = l.findVarHandle(Exchanger.class, "bound", int.class);
|
||||
SLOT = l.findVarHandle(Exchanger.class, "slot", Node.class);
|
||||
MATCH = l.findVarHandle(Node.class, "match", Object.class);
|
||||
AA = MethodHandles.arrayElementVarHandle(Node[].class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,8 @@
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
@ -92,7 +94,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
* encountering the exception; minimally only the latter.
|
||||
*
|
||||
* <p>It is possible to define and use ForkJoinTasks that may block,
|
||||
* but doing do requires three further considerations: (1) Completion
|
||||
* but doing so requires three further considerations: (1) Completion
|
||||
* of few if any <em>other</em> tasks should be dependent on a task
|
||||
* that blocks on external synchronization or I/O. Event-style async
|
||||
* tasks that are never joined (for example, those subclassing {@link
|
||||
@ -259,7 +261,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
for (int s;;) {
|
||||
if ((s = status) < 0)
|
||||
return s;
|
||||
if (U.compareAndSwapInt(this, STATUS, s, s | completion)) {
|
||||
if (STATUS.compareAndSet(this, s, s | completion)) {
|
||||
if ((s >>> 16) != 0)
|
||||
synchronized (this) { notifyAll(); }
|
||||
return completion;
|
||||
@ -297,7 +299,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
final void internalWait(long timeout) {
|
||||
int s;
|
||||
if ((s = status) >= 0 && // force completer to issue notify
|
||||
U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
STATUS.compareAndSet(this, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0)
|
||||
try { wait(timeout); } catch (InterruptedException ie) { }
|
||||
@ -319,7 +321,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
if (s >= 0 && (s = status) >= 0) {
|
||||
boolean interrupted = false;
|
||||
do {
|
||||
if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
if (STATUS.compareAndSet(this, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0) {
|
||||
try {
|
||||
@ -353,7 +355,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
ForkJoinPool.common.tryExternalUnpush(this) ? doExec() :
|
||||
0)) >= 0) {
|
||||
while ((s = status) >= 0) {
|
||||
if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
if (STATUS.compareAndSet(this, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0)
|
||||
wait(0L);
|
||||
@ -400,22 +402,24 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
// Exception table support
|
||||
|
||||
/**
|
||||
* Table of exceptions thrown by tasks, to enable reporting by
|
||||
* callers. Because exceptions are rare, we don't directly keep
|
||||
* Hash table of exceptions thrown by tasks, to enable reporting
|
||||
* by callers. Because exceptions are rare, we don't directly keep
|
||||
* them with task objects, but instead use a weak ref table. Note
|
||||
* that cancellation exceptions don't appear in the table, but are
|
||||
* instead recorded as status values.
|
||||
*
|
||||
* Note: These statics are initialized below in static block.
|
||||
* The exception table has a fixed capacity.
|
||||
*/
|
||||
private static final ExceptionNode[] exceptionTable;
|
||||
private static final ReentrantLock exceptionTableLock;
|
||||
private static final ReferenceQueue<Object> exceptionTableRefQueue;
|
||||
private static final ExceptionNode[] exceptionTable
|
||||
= new ExceptionNode[32];
|
||||
|
||||
/**
|
||||
* Fixed capacity for exceptionTable.
|
||||
*/
|
||||
private static final int EXCEPTION_MAP_CAPACITY = 32;
|
||||
/** Lock protecting access to exceptionTable. */
|
||||
private static final ReentrantLock exceptionTableLock
|
||||
= new ReentrantLock();
|
||||
|
||||
/** Reference queue of stale exceptionally completed tasks. */
|
||||
private static final ReferenceQueue<ForkJoinTask<?>> exceptionTableRefQueue
|
||||
= new ReferenceQueue<ForkJoinTask<?>>();
|
||||
|
||||
/**
|
||||
* Key-value nodes for exception table. The chained hash table
|
||||
@ -435,7 +439,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
final long thrower; // use id not ref to avoid weak cycles
|
||||
final int hashCode; // store task hashCode before weak ref disappears
|
||||
ExceptionNode(ForkJoinTask<?> task, Throwable ex, ExceptionNode next,
|
||||
ReferenceQueue<Object> exceptionTableRefQueue) {
|
||||
ReferenceQueue<ForkJoinTask<?>> exceptionTableRefQueue) {
|
||||
super(task, exceptionTableRefQueue);
|
||||
this.ex = ex;
|
||||
this.next = next;
|
||||
@ -599,9 +603,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
private static void expungeStaleExceptions() {
|
||||
for (Object x; (x = exceptionTableRefQueue.poll()) != null;) {
|
||||
if (x instanceof ExceptionNode) {
|
||||
int hashCode = ((ExceptionNode)x).hashCode;
|
||||
ExceptionNode[] t = exceptionTable;
|
||||
int i = hashCode & (t.length - 1);
|
||||
int i = ((ExceptionNode)x).hashCode & (t.length - 1);
|
||||
ExceptionNode e = t[i];
|
||||
ExceptionNode pred = null;
|
||||
while (e != null) {
|
||||
@ -1031,7 +1034,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
while ((s = status) >= 0 &&
|
||||
(ns = deadline - System.nanoTime()) > 0L) {
|
||||
if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
|
||||
U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
STATUS.compareAndSet(this, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0)
|
||||
wait(ms); // OK to throw InterruptedException
|
||||
@ -1324,8 +1327,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
*/
|
||||
public final short setForkJoinTaskTag(short newValue) {
|
||||
for (int s;;) {
|
||||
if (U.compareAndSwapInt(this, STATUS, s = status,
|
||||
(s & ~SMASK) | (newValue & SMASK)))
|
||||
if (STATUS.compareAndSet(this, s = status,
|
||||
(s & ~SMASK) | (newValue & SMASK)))
|
||||
return (short)s;
|
||||
}
|
||||
}
|
||||
@ -1348,8 +1351,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
for (int s;;) {
|
||||
if ((short)(s = status) != expect)
|
||||
return false;
|
||||
if (U.compareAndSwapInt(this, STATUS, s,
|
||||
(s & ~SMASK) | (update & SMASK)))
|
||||
if (STATUS.compareAndSet(this, s,
|
||||
(s & ~SMASK) | (update & SMASK)))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1510,17 +1513,12 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
setExceptionalCompletion((Throwable)ex);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long STATUS;
|
||||
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle STATUS;
|
||||
static {
|
||||
exceptionTableLock = new ReentrantLock();
|
||||
exceptionTableRefQueue = new ReferenceQueue<Object>();
|
||||
exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY];
|
||||
try {
|
||||
STATUS = U.objectFieldOffset
|
||||
(ForkJoinTask.class.getDeclaredField("status"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
STATUS = l.findVarHandle(ForkJoinTask.class, "status", int.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -66,8 +66,9 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
* owning thread.
|
||||
*
|
||||
* Support for (non-public) subclass InnocuousForkJoinWorkerThread
|
||||
* requires that we break quite a lot of encapsulation (via Unsafe)
|
||||
* both here and in the subclass to access and set Thread fields.
|
||||
* requires that we break quite a lot of encapsulation (via helper
|
||||
* methods in ThreadLocalRandom) both here and in the subclass to
|
||||
* access and set Thread fields.
|
||||
*/
|
||||
|
||||
final ForkJoinPool pool; // the pool this thread works in
|
||||
@ -92,8 +93,8 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
ForkJoinWorkerThread(ForkJoinPool pool, ThreadGroup threadGroup,
|
||||
AccessControlContext acc) {
|
||||
super(threadGroup, null, "aForkJoinWorkerThread");
|
||||
U.putObjectRelease(this, INHERITEDACCESSCONTROLCONTEXT, acc);
|
||||
eraseThreadLocals(); // clear before registering
|
||||
ThreadLocalRandom.setInheritedAccessControlContext(this, acc);
|
||||
ThreadLocalRandom.eraseThreadLocals(this); // clear before registering
|
||||
this.pool = pool;
|
||||
this.workQueue = pool.registerWorker(this);
|
||||
}
|
||||
@ -170,38 +171,12 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases ThreadLocals by nulling out Thread maps.
|
||||
*/
|
||||
final void eraseThreadLocals() {
|
||||
U.putObject(this, THREADLOCALS, null);
|
||||
U.putObject(this, INHERITABLETHREADLOCALS, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-public hook method for InnocuousForkJoinWorkerThread.
|
||||
*/
|
||||
void afterTopLevelExec() {
|
||||
}
|
||||
|
||||
// Set up to allow setting thread fields in constructor
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long THREADLOCALS;
|
||||
private static final long INHERITABLETHREADLOCALS;
|
||||
private static final long INHERITEDACCESSCONTROLCONTEXT;
|
||||
static {
|
||||
try {
|
||||
THREADLOCALS = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("threadLocals"));
|
||||
INHERITABLETHREADLOCALS = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("inheritableThreadLocals"));
|
||||
INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("inheritedAccessControlContext"));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A worker thread that has no permissions, is not a member of any
|
||||
* user-defined ThreadGroup, and erases all ThreadLocals after
|
||||
@ -210,7 +185,7 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
static final class InnocuousForkJoinWorkerThread extends ForkJoinWorkerThread {
|
||||
/** The ThreadGroup for all InnocuousForkJoinWorkerThreads */
|
||||
private static final ThreadGroup innocuousThreadGroup =
|
||||
createThreadGroup();
|
||||
ThreadLocalRandom.createThreadGroup("InnocuousForkJoinWorkerThreadGroup");
|
||||
|
||||
/** An AccessControlContext supporting no privileges */
|
||||
private static final AccessControlContext INNOCUOUS_ACC =
|
||||
@ -225,7 +200,7 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
|
||||
@Override // to erase ThreadLocals
|
||||
void afterTopLevelExec() {
|
||||
eraseThreadLocals();
|
||||
ThreadLocalRandom.eraseThreadLocals(this);
|
||||
}
|
||||
|
||||
@Override // to always report system loader
|
||||
@ -241,33 +216,5 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
throw new SecurityException("setContextClassLoader");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new group with the system ThreadGroup (the
|
||||
* topmost, parent-less group) as parent. Uses Unsafe to
|
||||
* traverse Thread.group and ThreadGroup.parent fields.
|
||||
*/
|
||||
private static ThreadGroup createThreadGroup() {
|
||||
try {
|
||||
jdk.internal.misc.Unsafe u = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
long tg = u.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("group"));
|
||||
long gp = u.objectFieldOffset
|
||||
(ThreadGroup.class.getDeclaredField("parent"));
|
||||
ThreadGroup group = (ThreadGroup)
|
||||
u.getObject(Thread.currentThread(), tg);
|
||||
while (group != null) {
|
||||
ThreadGroup parent = (ThreadGroup)u.getObject(group, gp);
|
||||
if (parent == null)
|
||||
return new ThreadGroup(group,
|
||||
"InnocuousForkJoinWorkerThreadGroup");
|
||||
group = parent;
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
// fall through if null as cannot-happen safeguard
|
||||
throw new Error("Cannot create ThreadGroup");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
/**
|
||||
@ -69,9 +71,6 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
* cancellation races. Sync control in the current design relies
|
||||
* on a "state" field updated via CAS to track completion, along
|
||||
* with a simple Treiber stack to hold waiting threads.
|
||||
*
|
||||
* Style note: As usual, we bypass overhead of using
|
||||
* AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -163,9 +162,8 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
}
|
||||
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
if (!(state == NEW &&
|
||||
U.compareAndSwapInt(this, STATE, NEW,
|
||||
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
|
||||
if (!(state == NEW && STATE.compareAndSet
|
||||
(this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
|
||||
return false;
|
||||
try { // in case call to interrupt throws exception
|
||||
if (mayInterruptIfRunning) {
|
||||
@ -174,7 +172,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
if (t != null)
|
||||
t.interrupt();
|
||||
} finally { // final state
|
||||
U.putIntRelease(this, STATE, INTERRUPTED);
|
||||
STATE.setRelease(this, INTERRUPTED);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@ -228,9 +226,9 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
* @param v the value
|
||||
*/
|
||||
protected void set(V v) {
|
||||
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
|
||||
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
|
||||
outcome = v;
|
||||
U.putIntRelease(this, STATE, NORMAL); // final state
|
||||
STATE.setRelease(this, NORMAL); // final state
|
||||
finishCompletion();
|
||||
}
|
||||
}
|
||||
@ -246,16 +244,16 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
* @param t the cause of failure
|
||||
*/
|
||||
protected void setException(Throwable t) {
|
||||
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
|
||||
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
|
||||
outcome = t;
|
||||
U.putIntRelease(this, STATE, EXCEPTIONAL); // final state
|
||||
STATE.setRelease(this, EXCEPTIONAL); // final state
|
||||
finishCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if (state != NEW ||
|
||||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
|
||||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
|
||||
return;
|
||||
try {
|
||||
Callable<V> c = callable;
|
||||
@ -296,7 +294,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
*/
|
||||
protected boolean runAndReset() {
|
||||
if (state != NEW ||
|
||||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
|
||||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
|
||||
return false;
|
||||
boolean ran = false;
|
||||
int s = state;
|
||||
@ -363,7 +361,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
private void finishCompletion() {
|
||||
// assert state > COMPLETING;
|
||||
for (WaitNode q; (q = waiters) != null;) {
|
||||
if (U.compareAndSwapObject(this, WAITERS, q, null)) {
|
||||
if (WAITERS.weakCompareAndSetVolatile(this, q, null)) {
|
||||
for (;;) {
|
||||
Thread t = q.thread;
|
||||
if (t != null) {
|
||||
@ -425,8 +423,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
q = new WaitNode();
|
||||
}
|
||||
else if (!queued)
|
||||
queued = U.compareAndSwapObject(this, WAITERS,
|
||||
q.next = waiters, q);
|
||||
queued = WAITERS.weakCompareAndSetVolatile(this, q.next = waiters, q);
|
||||
else if (timed) {
|
||||
final long parkNanos;
|
||||
if (startTime == 0L) { // first time
|
||||
@ -475,7 +472,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
if (pred.thread == null) // check for race
|
||||
continue retry;
|
||||
}
|
||||
else if (!U.compareAndSwapObject(this, WAITERS, q, s))
|
||||
else if (!WAITERS.compareAndSet(this, q, s))
|
||||
continue retry;
|
||||
}
|
||||
break;
|
||||
@ -483,19 +480,16 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long STATE;
|
||||
private static final long RUNNER;
|
||||
private static final long WAITERS;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle STATE;
|
||||
private static final VarHandle RUNNER;
|
||||
private static final VarHandle WAITERS;
|
||||
static {
|
||||
try {
|
||||
STATE = U.objectFieldOffset
|
||||
(FutureTask.class.getDeclaredField("state"));
|
||||
RUNNER = U.objectFieldOffset
|
||||
(FutureTask.class.getDeclaredField("runner"));
|
||||
WAITERS = U.objectFieldOffset
|
||||
(FutureTask.class.getDeclaredField("waiters"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
STATE = l.findVarHandle(FutureTask.class, "state", int.class);
|
||||
RUNNER = l.findVarHandle(FutureTask.class, "runner", Thread.class);
|
||||
WAITERS = l.findVarHandle(FutureTask.class, "waiters", WaitNode.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -444,7 +446,7 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
|
||||
/**
|
||||
* Queue nodes. Uses Object, not E, for items to allow forgetting
|
||||
* them after use. Relies heavily on Unsafe mechanics to minimize
|
||||
* them after use. Relies heavily on VarHandles to minimize
|
||||
* unnecessary ordering constraints: Writes that are intrinsically
|
||||
* ordered wrt other accesses or CASes use simple relaxed forms.
|
||||
*/
|
||||
@ -456,12 +458,12 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
|
||||
// CAS methods for fields
|
||||
final boolean casNext(Node cmp, Node val) {
|
||||
return U.compareAndSwapObject(this, NEXT, cmp, val);
|
||||
return NEXT.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
final boolean casItem(Object cmp, Object val) {
|
||||
// assert cmp == null || cmp.getClass() != Node.class;
|
||||
return U.compareAndSwapObject(this, ITEM, cmp, val);
|
||||
return ITEM.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -469,7 +471,7 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
* only be seen after publication via casNext.
|
||||
*/
|
||||
Node(Object item, boolean isData) {
|
||||
U.putObject(this, ITEM, item); // relaxed write
|
||||
ITEM.set(this, item); // relaxed write
|
||||
this.isData = isData;
|
||||
}
|
||||
|
||||
@ -478,7 +480,7 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
* only after CASing head field, so uses relaxed write.
|
||||
*/
|
||||
final void forgetNext() {
|
||||
U.putObject(this, NEXT, this);
|
||||
NEXT.set(this, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -491,8 +493,8 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
* else we don't care).
|
||||
*/
|
||||
final void forgetContents() {
|
||||
U.putObject(this, ITEM, this);
|
||||
U.putObject(this, WAITER, null);
|
||||
ITEM.set(this, this);
|
||||
WAITER.set(this, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -537,19 +539,16 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
|
||||
private static final long serialVersionUID = -3375979862319811754L;
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long ITEM;
|
||||
private static final long NEXT;
|
||||
private static final long WAITER;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle ITEM;
|
||||
private static final VarHandle NEXT;
|
||||
private static final VarHandle WAITER;
|
||||
static {
|
||||
try {
|
||||
ITEM = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("item"));
|
||||
NEXT = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("next"));
|
||||
WAITER = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("waiter"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
ITEM = l.findVarHandle(Node.class, "item", Object.class);
|
||||
NEXT = l.findVarHandle(Node.class, "next", Node.class);
|
||||
WAITER = l.findVarHandle(Node.class, "waiter", Thread.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -567,15 +566,15 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
|
||||
// CAS methods for fields
|
||||
private boolean casTail(Node cmp, Node val) {
|
||||
return U.compareAndSwapObject(this, TAIL, cmp, val);
|
||||
return TAIL.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
private boolean casHead(Node cmp, Node val) {
|
||||
return U.compareAndSwapObject(this, HEAD, cmp, val);
|
||||
return HEAD.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
private boolean casSweepVotes(int cmp, int val) {
|
||||
return U.compareAndSwapInt(this, SWEEPVOTES, cmp, val);
|
||||
return SWEEPVOTES.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1562,20 +1561,19 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long HEAD;
|
||||
private static final long TAIL;
|
||||
private static final long SWEEPVOTES;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle HEAD;
|
||||
private static final VarHandle TAIL;
|
||||
private static final VarHandle SWEEPVOTES;
|
||||
static {
|
||||
try {
|
||||
HEAD = U.objectFieldOffset
|
||||
(LinkedTransferQueue.class.getDeclaredField("head"));
|
||||
TAIL = U.objectFieldOffset
|
||||
(LinkedTransferQueue.class.getDeclaredField("tail"));
|
||||
SWEEPVOTES = U.objectFieldOffset
|
||||
(LinkedTransferQueue.class.getDeclaredField("sweepVotes"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
HEAD = l.findVarHandle(LinkedTransferQueue.class, "head",
|
||||
Node.class);
|
||||
TAIL = l.findVarHandle(LinkedTransferQueue.class, "tail",
|
||||
Node.class);
|
||||
SWEEPVOTES = l.findVarHandle(LinkedTransferQueue.class, "sweepVotes",
|
||||
int.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
@ -221,7 +223,6 @@ import java.util.concurrent.locks.LockSupport;
|
||||
* phaser.arriveAndDeregister();
|
||||
* }}</pre>
|
||||
*
|
||||
*
|
||||
* <p>To create a set of {@code n} tasks using a tree of phasers, you
|
||||
* could use code of the following form, assuming a Task class with a
|
||||
* constructor accepting a {@code Phaser} that it registers with upon
|
||||
@ -384,7 +385,7 @@ public class Phaser {
|
||||
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
|
||||
if (unarrived <= 0)
|
||||
throw new IllegalStateException(badArrive(s));
|
||||
if (U.compareAndSwapLong(this, STATE, s, s-=adjust)) {
|
||||
if (STATE.compareAndSet(this, s, s-=adjust)) {
|
||||
if (unarrived == 1) {
|
||||
long n = s & PARTIES_MASK; // base of next state
|
||||
int nextUnarrived = (int)n >>> PARTIES_SHIFT;
|
||||
@ -397,12 +398,12 @@ public class Phaser {
|
||||
n |= nextUnarrived;
|
||||
int nextPhase = (phase + 1) & MAX_PHASE;
|
||||
n |= (long)nextPhase << PHASE_SHIFT;
|
||||
U.compareAndSwapLong(this, STATE, s, n);
|
||||
STATE.compareAndSet(this, s, n);
|
||||
releaseWaiters(phase);
|
||||
}
|
||||
else if (nextUnarrived == 0) { // propagate deregistration
|
||||
phase = parent.doArrive(ONE_DEREGISTER);
|
||||
U.compareAndSwapLong(this, STATE, s, s | EMPTY);
|
||||
STATE.compareAndSet(this, s, s | EMPTY);
|
||||
}
|
||||
else
|
||||
phase = parent.doArrive(ONE_ARRIVAL);
|
||||
@ -437,13 +438,13 @@ public class Phaser {
|
||||
if (parent == null || reconcileState() == s) {
|
||||
if (unarrived == 0) // wait out advance
|
||||
root.internalAwaitAdvance(phase, null);
|
||||
else if (U.compareAndSwapLong(this, STATE, s, s + adjust))
|
||||
else if (STATE.compareAndSet(this, s, s + adjust))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (parent == null) { // 1st root registration
|
||||
long next = ((long)phase << PHASE_SHIFT) | adjust;
|
||||
if (U.compareAndSwapLong(this, STATE, s, next))
|
||||
if (STATE.compareAndSet(this, s, next))
|
||||
break;
|
||||
}
|
||||
else {
|
||||
@ -455,8 +456,8 @@ public class Phaser {
|
||||
// finish registration whenever parent registration
|
||||
// succeeded, even when racing with termination,
|
||||
// since these are part of the same "transaction".
|
||||
while (!U.compareAndSwapLong
|
||||
(this, STATE, s,
|
||||
while (!STATE.weakCompareAndSetVolatile
|
||||
(this, s,
|
||||
((long)phase << PHASE_SHIFT) | adjust)) {
|
||||
s = state;
|
||||
phase = (int)(root.state >>> PHASE_SHIFT);
|
||||
@ -487,8 +488,8 @@ public class Phaser {
|
||||
// CAS to root phase with current parties, tripping unarrived
|
||||
while ((phase = (int)(root.state >>> PHASE_SHIFT)) !=
|
||||
(int)(s >>> PHASE_SHIFT) &&
|
||||
!U.compareAndSwapLong
|
||||
(this, STATE, s,
|
||||
!STATE.weakCompareAndSetVolatile
|
||||
(this, s,
|
||||
s = (((long)phase << PHASE_SHIFT) |
|
||||
((phase < 0) ? (s & COUNTS_MASK) :
|
||||
(((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
|
||||
@ -677,7 +678,7 @@ public class Phaser {
|
||||
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
|
||||
if (unarrived <= 0)
|
||||
throw new IllegalStateException(badArrive(s));
|
||||
if (U.compareAndSwapLong(this, STATE, s, s -= ONE_ARRIVAL)) {
|
||||
if (STATE.compareAndSet(this, s, s -= ONE_ARRIVAL)) {
|
||||
if (unarrived > 1)
|
||||
return root.internalAwaitAdvance(phase, null);
|
||||
if (root != this)
|
||||
@ -692,7 +693,7 @@ public class Phaser {
|
||||
n |= nextUnarrived;
|
||||
int nextPhase = (phase + 1) & MAX_PHASE;
|
||||
n |= (long)nextPhase << PHASE_SHIFT;
|
||||
if (!U.compareAndSwapLong(this, STATE, s, n))
|
||||
if (!STATE.compareAndSet(this, s, n))
|
||||
return (int)(state >>> PHASE_SHIFT); // terminated
|
||||
releaseWaiters(phase);
|
||||
return nextPhase;
|
||||
@ -808,7 +809,7 @@ public class Phaser {
|
||||
final Phaser root = this.root;
|
||||
long s;
|
||||
while ((s = root.state) >= 0) {
|
||||
if (U.compareAndSwapLong(root, STATE, s, s | TERMINATION_BIT)) {
|
||||
if (STATE.compareAndSet(root, s, s | TERMINATION_BIT)) {
|
||||
// signal all threads
|
||||
releaseWaiters(0); // Waiters on evenQ
|
||||
releaseWaiters(1); // Waiters on oddQ
|
||||
@ -1043,6 +1044,8 @@ public class Phaser {
|
||||
node = new QNode(this, phase, false, false, 0L);
|
||||
node.wasInterrupted = interrupted;
|
||||
}
|
||||
else
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
else if (node.isReleasable()) // done or aborted
|
||||
break;
|
||||
@ -1131,14 +1134,12 @@ public class Phaser {
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long STATE;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle STATE;
|
||||
static {
|
||||
try {
|
||||
STATE = U.objectFieldOffset
|
||||
(Phaser.class.getDeclaredField("state"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
STATE = l.findVarHandle(Phaser.class, "state", long.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -289,7 +291,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
|
||||
lock.unlock(); // must release and then re-acquire main lock
|
||||
Object[] newArray = null;
|
||||
if (allocationSpinLock == 0 &&
|
||||
U.compareAndSwapInt(this, ALLOCATIONSPINLOCK, 0, 1)) {
|
||||
ALLOCATIONSPINLOCK.compareAndSet(this, 0, 1)) {
|
||||
try {
|
||||
int newCap = oldCap + ((oldCap < 64) ?
|
||||
(oldCap + 2) : // grow faster if small
|
||||
@ -1009,13 +1011,14 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
|
||||
return new PBQSpliterator<E>(this, null, 0, -1);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long ALLOCATIONSPINLOCK;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle ALLOCATIONSPINLOCK;
|
||||
static {
|
||||
try {
|
||||
ALLOCATIONSPINLOCK = U.objectFieldOffset
|
||||
(PriorityBlockingQueue.class.getDeclaredField("allocationSpinLock"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
ALLOCATIONSPINLOCK = l.findVarHandle(PriorityBlockingQueue.class,
|
||||
"allocationSpinLock",
|
||||
int.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
@ -866,7 +868,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
|
||||
/** Subscriber for method consume */
|
||||
private static final class ConsumerSubscriber<T>
|
||||
implements Flow.Subscriber<T> {
|
||||
implements Flow.Subscriber<T> {
|
||||
final CompletableFuture<Void> status;
|
||||
final Consumer<? super T> consumer;
|
||||
Flow.Subscription subscription;
|
||||
@ -906,7 +908,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
static final class ConsumerTask<T> extends ForkJoinTask<Void>
|
||||
implements Runnable {
|
||||
implements Runnable, CompletableFuture.AsynchronousCompletionTask {
|
||||
final BufferedSubscription<T> consumer;
|
||||
ConsumerTask(BufferedSubscription<T> consumer) {
|
||||
this.consumer = consumer;
|
||||
@ -959,11 +961,9 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
* Blocking control relies on the "waiter" field. Producers set
|
||||
* the field before trying to block, but must then recheck (via
|
||||
* offer) before parking. Signalling then just unparks and clears
|
||||
* waiter field. If the producer and consumer are both in the same
|
||||
* ForkJoinPool, or consumers are running in commonPool, the
|
||||
* producer attempts to help run consumer tasks that it forked
|
||||
* before blocking. To avoid potential cycles, only one level of
|
||||
* helping is currently supported.
|
||||
* waiter field. If the producer and/or consumer are using a
|
||||
* ForkJoinPool, the producer attempts to help run consumer tasks
|
||||
* via ForkJoinPool.helpAsyncBlocker before blocking.
|
||||
*
|
||||
* This class uses @Contended and heuristic field declaration
|
||||
* ordering to reduce false-sharing-based memory contention among
|
||||
@ -983,7 +983,6 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
volatile long demand; // # unfilled requests
|
||||
int maxCapacity; // reduced on OOME
|
||||
int putStat; // offer result for ManagedBlocker
|
||||
int helpDepth; // nested helping depth (at most 1)
|
||||
volatile int ctl; // atomic run state flags
|
||||
volatile int head; // next position to take
|
||||
int tail; // next position to put
|
||||
@ -1077,7 +1076,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
alloc = true;
|
||||
}
|
||||
else {
|
||||
U.fullFence(); // recheck
|
||||
VarHandle.fullFence(); // recheck
|
||||
int h = head, t = tail, size = t + 1 - h;
|
||||
if (cap >= size) {
|
||||
a[(cap - 1) & t] = item;
|
||||
@ -1116,10 +1115,10 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
if (a != null && cap > 0) {
|
||||
int mask = cap - 1;
|
||||
for (int j = head; j != t; ++j) {
|
||||
long k = ((long)(j & mask) << ASHIFT) + ABASE;
|
||||
Object x = U.getObjectVolatile(a, k);
|
||||
int k = j & mask;
|
||||
Object x = QA.getAcquire(a, k);
|
||||
if (x != null && // races with consumer
|
||||
U.compareAndSwapObject(a, k, x, null))
|
||||
QA.compareAndSet(a, k, x, null))
|
||||
newArray[j & newMask] = x;
|
||||
}
|
||||
}
|
||||
@ -1136,100 +1135,43 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
* initial offer return 0.
|
||||
*/
|
||||
final int submit(T item) {
|
||||
int stat; Executor e; ForkJoinWorkerThread w;
|
||||
if ((stat = offer(item)) == 0 && helpDepth == 0 &&
|
||||
((e = executor) instanceof ForkJoinPool)) {
|
||||
helpDepth = 1;
|
||||
Thread thread = Thread.currentThread();
|
||||
if ((thread instanceof ForkJoinWorkerThread) &&
|
||||
((w = (ForkJoinWorkerThread)thread)).getPool() == e)
|
||||
stat = internalHelpConsume(w.workQueue, item);
|
||||
else if (e == ForkJoinPool.commonPool())
|
||||
stat = externalHelpConsume
|
||||
(ForkJoinPool.commonSubmitterQueue(), item);
|
||||
helpDepth = 0;
|
||||
}
|
||||
if (stat == 0 && (stat = offer(item)) == 0) {
|
||||
int stat;
|
||||
if ((stat = offer(item)) == 0) {
|
||||
putItem = item;
|
||||
timeout = 0L;
|
||||
try {
|
||||
ForkJoinPool.managedBlock(this);
|
||||
} catch (InterruptedException ie) {
|
||||
timeout = INTERRUPTED;
|
||||
putStat = 0;
|
||||
ForkJoinPool.helpAsyncBlocker(executor, this);
|
||||
if ((stat = putStat) == 0) {
|
||||
try {
|
||||
ForkJoinPool.managedBlock(this);
|
||||
} catch (InterruptedException ie) {
|
||||
timeout = INTERRUPTED;
|
||||
}
|
||||
stat = putStat;
|
||||
}
|
||||
stat = putStat;
|
||||
if (timeout < 0L)
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries helping for FJ submitter.
|
||||
*/
|
||||
private int internalHelpConsume(ForkJoinPool.WorkQueue w, T item) {
|
||||
int stat = 0;
|
||||
if (w != null) {
|
||||
ForkJoinTask<?> t;
|
||||
while ((t = w.peek()) != null && (t instanceof ConsumerTask)) {
|
||||
if ((stat = offer(item)) != 0 || !w.tryUnpush(t))
|
||||
break;
|
||||
((ConsumerTask<?>)t).consumer.consume();
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries helping for non-FJ submitter.
|
||||
*/
|
||||
private int externalHelpConsume(ForkJoinPool.WorkQueue w, T item) {
|
||||
int stat = 0;
|
||||
if (w != null) {
|
||||
ForkJoinTask<?> t;
|
||||
while ((t = w.peek()) != null && (t instanceof ConsumerTask)) {
|
||||
if ((stat = offer(item)) != 0 || !w.trySharedUnpush(t))
|
||||
break;
|
||||
((ConsumerTask<?>)t).consumer.consume();
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timeout version; similar to submit.
|
||||
*/
|
||||
final int timedOffer(T item, long nanos) {
|
||||
int stat; Executor e;
|
||||
if ((stat = offer(item)) == 0 && helpDepth == 0 &&
|
||||
((e = executor) instanceof ForkJoinPool)) {
|
||||
Thread thread = Thread.currentThread();
|
||||
if (((thread instanceof ForkJoinWorkerThread) &&
|
||||
((ForkJoinWorkerThread)thread).getPool() == e) ||
|
||||
e == ForkJoinPool.commonPool()) {
|
||||
helpDepth = 1;
|
||||
ForkJoinTask<?> t;
|
||||
long deadline = System.nanoTime() + nanos;
|
||||
while ((t = ForkJoinTask.peekNextLocalTask()) != null &&
|
||||
(t instanceof ConsumerTask)) {
|
||||
if ((stat = offer(item)) != 0 ||
|
||||
(nanos = deadline - System.nanoTime()) <= 0L ||
|
||||
!t.tryUnfork())
|
||||
break;
|
||||
((ConsumerTask<?>)t).consumer.consume();
|
||||
}
|
||||
helpDepth = 0;
|
||||
}
|
||||
}
|
||||
if (stat == 0 && (stat = offer(item)) == 0 &&
|
||||
(timeout = nanos) > 0L) {
|
||||
int stat;
|
||||
if ((stat = offer(item)) == 0 && (timeout = nanos) > 0L) {
|
||||
putItem = item;
|
||||
try {
|
||||
ForkJoinPool.managedBlock(this);
|
||||
} catch (InterruptedException ie) {
|
||||
timeout = INTERRUPTED;
|
||||
putStat = 0;
|
||||
ForkJoinPool.helpAsyncBlocker(executor, this);
|
||||
if ((stat = putStat) == 0) {
|
||||
try {
|
||||
ForkJoinPool.managedBlock(this);
|
||||
} catch (InterruptedException ie) {
|
||||
timeout = INTERRUPTED;
|
||||
}
|
||||
stat = putStat;
|
||||
}
|
||||
stat = putStat;
|
||||
if (timeout < 0L)
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
@ -1249,22 +1191,20 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
}
|
||||
else if ((c & ACTIVE) != 0) { // ensure keep-alive
|
||||
if ((c & CONSUME) != 0 ||
|
||||
U.compareAndSwapInt(this, CTL, c,
|
||||
c | CONSUME))
|
||||
CTL.compareAndSet(this, c, c | CONSUME))
|
||||
break;
|
||||
}
|
||||
else if (demand == 0L || tail == head)
|
||||
break;
|
||||
else if (U.compareAndSwapInt(this, CTL, c,
|
||||
c | (ACTIVE | CONSUME))) {
|
||||
else if (CTL.compareAndSet(this, c, c | (ACTIVE | CONSUME))) {
|
||||
try {
|
||||
e.execute(new ConsumerTask<T>(this));
|
||||
break;
|
||||
} catch (RuntimeException | Error ex) { // back out
|
||||
do {} while (((c = ctl) & DISABLED) == 0 &&
|
||||
(c & ACTIVE) != 0 &&
|
||||
!U.compareAndSwapInt(this, CTL, c,
|
||||
c & ~ACTIVE));
|
||||
!CTL.weakCompareAndSetVolatile
|
||||
(this, c, c & ~ACTIVE));
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
@ -1300,10 +1240,10 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
break;
|
||||
else if ((c & ACTIVE) != 0) {
|
||||
pendingError = ex;
|
||||
if (U.compareAndSwapInt(this, CTL, c, c | ERROR))
|
||||
if (CTL.compareAndSet(this, c, c | ERROR))
|
||||
break; // cause consumer task to exit
|
||||
}
|
||||
else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
|
||||
else if (CTL.compareAndSet(this, c, DISABLED)) {
|
||||
Flow.Subscriber<? super T> s = subscriber;
|
||||
if (s != null && ex != null) {
|
||||
try {
|
||||
@ -1330,7 +1270,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
for (int c;;) {
|
||||
if ((c = ctl) == DISABLED || (c & ACTIVE) == 0)
|
||||
break;
|
||||
if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) {
|
||||
if (CTL.compareAndSet(this, c, c & ~ACTIVE)) {
|
||||
onError(ex);
|
||||
break;
|
||||
}
|
||||
@ -1343,8 +1283,8 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
for (int c;;) {
|
||||
if ((c = ctl) == DISABLED)
|
||||
break;
|
||||
if (U.compareAndSwapInt(this, CTL, c,
|
||||
c | (ACTIVE | CONSUME | COMPLETE))) {
|
||||
if (CTL.compareAndSet(this, c,
|
||||
c | (ACTIVE | CONSUME | COMPLETE))) {
|
||||
if ((c & ACTIVE) == 0)
|
||||
startOrDisable();
|
||||
break;
|
||||
@ -1356,8 +1296,8 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
for (int c;;) {
|
||||
if ((c = ctl) == DISABLED)
|
||||
break;
|
||||
if (U.compareAndSwapInt(this, CTL, c,
|
||||
c | (ACTIVE | CONSUME | SUBSCRIBE))) {
|
||||
if (CTL.compareAndSet(this, c,
|
||||
c | (ACTIVE | CONSUME | SUBSCRIBE))) {
|
||||
if ((c & ACTIVE) == 0)
|
||||
startOrDisable();
|
||||
break;
|
||||
@ -1375,11 +1315,11 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
if ((c = ctl) == DISABLED)
|
||||
break;
|
||||
else if ((c & ACTIVE) != 0) {
|
||||
if (U.compareAndSwapInt(this, CTL, c,
|
||||
c | (CONSUME | ERROR)))
|
||||
if (CTL.compareAndSet(this, c,
|
||||
c | (CONSUME | ERROR)))
|
||||
break;
|
||||
}
|
||||
else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
|
||||
else if (CTL.compareAndSet(this, c, DISABLED)) {
|
||||
detach();
|
||||
break;
|
||||
}
|
||||
@ -1395,19 +1335,18 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
long prev = demand, d;
|
||||
if ((d = prev + n) < prev) // saturate
|
||||
d = Long.MAX_VALUE;
|
||||
if (U.compareAndSwapLong(this, DEMAND, prev, d)) {
|
||||
if (DEMAND.compareAndSet(this, prev, d)) {
|
||||
for (int c, h;;) {
|
||||
if ((c = ctl) == DISABLED)
|
||||
break;
|
||||
else if ((c & ACTIVE) != 0) {
|
||||
if ((c & CONSUME) != 0 ||
|
||||
U.compareAndSwapInt(this, CTL, c,
|
||||
c | CONSUME))
|
||||
CTL.compareAndSet(this, c, c | CONSUME))
|
||||
break;
|
||||
}
|
||||
else if ((h = head) != tail) {
|
||||
if (U.compareAndSwapInt(this, CTL, c,
|
||||
c | (ACTIVE|CONSUME))) {
|
||||
if (CTL.compareAndSet(this, c,
|
||||
c | (ACTIVE|CONSUME))) {
|
||||
startOrDisable();
|
||||
break;
|
||||
}
|
||||
@ -1476,16 +1415,14 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
if ((s = subscriber) != null) { // else disabled
|
||||
for (;;) {
|
||||
long d = demand;
|
||||
int c; Object[] a; int n; long i; Object x; Thread w;
|
||||
int c; Object[] a; int n, i; Object x; Thread w;
|
||||
if (((c = ctl) & (ERROR | SUBSCRIBE | DISABLED)) != 0) {
|
||||
if (!checkControl(s, c))
|
||||
break;
|
||||
}
|
||||
else if ((a = array) == null || h == tail ||
|
||||
(n = a.length) == 0 ||
|
||||
(x = U.getObjectVolatile
|
||||
(a, (i = ((long)((n - 1) & h) << ASHIFT) + ABASE)))
|
||||
== null) {
|
||||
(x = QA.getAcquire(a, i = (n - 1) & h)) == null) {
|
||||
if (!checkEmpty(s, c))
|
||||
break;
|
||||
}
|
||||
@ -1494,10 +1431,10 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
break;
|
||||
}
|
||||
else if (((c & CONSUME) != 0 ||
|
||||
U.compareAndSwapInt(this, CTL, c, c | CONSUME)) &&
|
||||
U.compareAndSwapObject(a, i, x, null)) {
|
||||
U.putIntRelease(this, HEAD, ++h);
|
||||
U.getAndAddLong(this, DEMAND, -1L);
|
||||
CTL.compareAndSet(this, c, c | CONSUME)) &&
|
||||
QA.compareAndSet(a, i, x, null)) {
|
||||
HEAD.setRelease(this, ++h);
|
||||
DEMAND.getAndAdd(this, -1L);
|
||||
if ((w = waiter) != null)
|
||||
signalWaiter(w);
|
||||
try {
|
||||
@ -1528,7 +1465,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
}
|
||||
}
|
||||
else if ((c & SUBSCRIBE) != 0) {
|
||||
if (U.compareAndSwapInt(this, CTL, c, c & ~SUBSCRIBE)) {
|
||||
if (CTL.compareAndSet(this, c, c & ~SUBSCRIBE)) {
|
||||
try {
|
||||
if (s != null)
|
||||
s.onSubscribe(this);
|
||||
@ -1551,9 +1488,9 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
boolean stat = true;
|
||||
if (head == tail) {
|
||||
if ((c & CONSUME) != 0)
|
||||
U.compareAndSwapInt(this, CTL, c, c & ~CONSUME);
|
||||
CTL.compareAndSet(this, c, c & ~CONSUME);
|
||||
else if ((c & COMPLETE) != 0) {
|
||||
if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
|
||||
if (CTL.compareAndSet(this, c, DISABLED)) {
|
||||
try {
|
||||
if (s != null)
|
||||
s.onComplete();
|
||||
@ -1561,7 +1498,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE))
|
||||
else if (CTL.compareAndSet(this, c, c & ~ACTIVE))
|
||||
stat = false;
|
||||
}
|
||||
return stat;
|
||||
@ -1574,8 +1511,8 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
boolean stat = true;
|
||||
if (demand == 0L) {
|
||||
if ((c & CONSUME) != 0)
|
||||
U.compareAndSwapInt(this, CTL, c, c & ~CONSUME);
|
||||
else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE))
|
||||
CTL.compareAndSet(this, c, c & ~CONSUME);
|
||||
else if (CTL.compareAndSet(this, c, c & ~ACTIVE))
|
||||
stat = false;
|
||||
}
|
||||
return stat;
|
||||
@ -1595,31 +1532,25 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
onError(ex);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long CTL;
|
||||
private static final long TAIL;
|
||||
private static final long HEAD;
|
||||
private static final long DEMAND;
|
||||
private static final int ABASE;
|
||||
private static final int ASHIFT;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle CTL;
|
||||
private static final VarHandle TAIL;
|
||||
private static final VarHandle HEAD;
|
||||
private static final VarHandle DEMAND;
|
||||
private static final VarHandle QA;
|
||||
|
||||
static {
|
||||
try {
|
||||
CTL = U.objectFieldOffset
|
||||
(BufferedSubscription.class.getDeclaredField("ctl"));
|
||||
TAIL = U.objectFieldOffset
|
||||
(BufferedSubscription.class.getDeclaredField("tail"));
|
||||
HEAD = U.objectFieldOffset
|
||||
(BufferedSubscription.class.getDeclaredField("head"));
|
||||
DEMAND = U.objectFieldOffset
|
||||
(BufferedSubscription.class.getDeclaredField("demand"));
|
||||
|
||||
ABASE = U.arrayBaseOffset(Object[].class);
|
||||
int scale = U.arrayIndexScale(Object[].class);
|
||||
if ((scale & (scale - 1)) != 0)
|
||||
throw new Error("data type scale not a power of two");
|
||||
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
CTL = l.findVarHandle(BufferedSubscription.class, "ctl",
|
||||
int.class);
|
||||
TAIL = l.findVarHandle(BufferedSubscription.class, "tail",
|
||||
int.class);
|
||||
HEAD = l.findVarHandle(BufferedSubscription.class, "head",
|
||||
int.class);
|
||||
DEMAND = l.findVarHandle(BufferedSubscription.class, "demand",
|
||||
long.class);
|
||||
QA = MethodHandles.arrayElementVarHandle(Object[].class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -36,6 +36,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -247,7 +249,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
|
||||
boolean casNext(SNode cmp, SNode val) {
|
||||
return cmp == next &&
|
||||
U.compareAndSwapObject(this, NEXT, cmp, val);
|
||||
SNEXT.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -260,7 +262,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
*/
|
||||
boolean tryMatch(SNode s) {
|
||||
if (match == null &&
|
||||
U.compareAndSwapObject(this, MATCH, null, s)) {
|
||||
SMATCH.compareAndSet(this, null, s)) {
|
||||
Thread w = waiter;
|
||||
if (w != null) { // waiters need at most one unpark
|
||||
waiter = null;
|
||||
@ -275,24 +277,21 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
* Tries to cancel a wait by matching node to itself.
|
||||
*/
|
||||
void tryCancel() {
|
||||
U.compareAndSwapObject(this, MATCH, null, this);
|
||||
SMATCH.compareAndSet(this, null, this);
|
||||
}
|
||||
|
||||
boolean isCancelled() {
|
||||
return match == this;
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long MATCH;
|
||||
private static final long NEXT;
|
||||
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle SMATCH;
|
||||
private static final VarHandle SNEXT;
|
||||
static {
|
||||
try {
|
||||
MATCH = U.objectFieldOffset
|
||||
(SNode.class.getDeclaredField("match"));
|
||||
NEXT = U.objectFieldOffset
|
||||
(SNode.class.getDeclaredField("next"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
SMATCH = l.findVarHandle(SNode.class, "match", SNode.class);
|
||||
SNEXT = l.findVarHandle(SNode.class, "next", SNode.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -304,7 +303,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
|
||||
boolean casHead(SNode h, SNode nh) {
|
||||
return h == head &&
|
||||
U.compareAndSwapObject(this, HEAD, h, nh);
|
||||
SHEAD.compareAndSet(this, h, nh);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -451,8 +450,10 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (spins > 0)
|
||||
if (spins > 0) {
|
||||
Thread.onSpinWait();
|
||||
spins = shouldSpin(s) ? (spins - 1) : 0;
|
||||
}
|
||||
else if (s.waiter == null)
|
||||
s.waiter = w; // establish waiter so can park next iter
|
||||
else if (!timed)
|
||||
@ -508,13 +509,12 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long HEAD;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle SHEAD;
|
||||
static {
|
||||
try {
|
||||
HEAD = U.objectFieldOffset
|
||||
(TransferStack.class.getDeclaredField("head"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
SHEAD = l.findVarHandle(TransferStack.class, "head", SNode.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -546,19 +546,19 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
|
||||
boolean casNext(QNode cmp, QNode val) {
|
||||
return next == cmp &&
|
||||
U.compareAndSwapObject(this, NEXT, cmp, val);
|
||||
QNEXT.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
boolean casItem(Object cmp, Object val) {
|
||||
return item == cmp &&
|
||||
U.compareAndSwapObject(this, ITEM, cmp, val);
|
||||
QITEM.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to cancel by CAS'ing ref to this as item.
|
||||
*/
|
||||
void tryCancel(Object cmp) {
|
||||
U.compareAndSwapObject(this, ITEM, cmp, this);
|
||||
QITEM.compareAndSet(this, cmp, this);
|
||||
}
|
||||
|
||||
boolean isCancelled() {
|
||||
@ -574,17 +574,14 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
return next == this;
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long ITEM;
|
||||
private static final long NEXT;
|
||||
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle QITEM;
|
||||
private static final VarHandle QNEXT;
|
||||
static {
|
||||
try {
|
||||
ITEM = U.objectFieldOffset
|
||||
(QNode.class.getDeclaredField("item"));
|
||||
NEXT = U.objectFieldOffset
|
||||
(QNode.class.getDeclaredField("next"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
QITEM = l.findVarHandle(QNode.class, "item", Object.class);
|
||||
QNEXT = l.findVarHandle(QNode.class, "next", QNode.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -614,7 +611,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
*/
|
||||
void advanceHead(QNode h, QNode nh) {
|
||||
if (h == head &&
|
||||
U.compareAndSwapObject(this, HEAD, h, nh))
|
||||
QHEAD.compareAndSet(this, h, nh))
|
||||
h.next = h; // forget old next
|
||||
}
|
||||
|
||||
@ -623,7 +620,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
*/
|
||||
void advanceTail(QNode t, QNode nt) {
|
||||
if (tail == t)
|
||||
U.compareAndSwapObject(this, TAIL, t, nt);
|
||||
QTAIL.compareAndSet(this, t, nt);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -631,7 +628,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
*/
|
||||
boolean casCleanMe(QNode cmp, QNode val) {
|
||||
return cleanMe == cmp &&
|
||||
U.compareAndSwapObject(this, CLEANME, cmp, val);
|
||||
QCLEANME.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -752,8 +749,10 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (spins > 0)
|
||||
if (spins > 0) {
|
||||
--spins;
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
else if (s.waiter == null)
|
||||
s.waiter = w;
|
||||
else if (!timed)
|
||||
@ -817,18 +816,19 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
}
|
||||
}
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long HEAD;
|
||||
private static final long TAIL;
|
||||
private static final long CLEANME;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle QHEAD;
|
||||
private static final VarHandle QTAIL;
|
||||
private static final VarHandle QCLEANME;
|
||||
static {
|
||||
try {
|
||||
HEAD = U.objectFieldOffset
|
||||
(TransferQueue.class.getDeclaredField("head"));
|
||||
TAIL = U.objectFieldOffset
|
||||
(TransferQueue.class.getDeclaredField("tail"));
|
||||
CLEANME = U.objectFieldOffset
|
||||
(TransferQueue.class.getDeclaredField("cleanMe"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
QHEAD = l.findVarHandle(TransferQueue.class, "head",
|
||||
QNode.class);
|
||||
QTAIL = l.findVarHandle(TransferQueue.class, "tail",
|
||||
QNode.class);
|
||||
QCLEANME = l.findVarHandle(TransferQueue.class, "cleanMe",
|
||||
QNode.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.io.ObjectStreamField;
|
||||
import java.security.AccessControlContext;
|
||||
import java.util.Random;
|
||||
import java.util.Spliterator;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@ -47,6 +48,7 @@ import java.util.stream.DoubleStream;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
/**
|
||||
* A random number generator isolated to the current thread. Like the
|
||||
@ -95,7 +97,9 @@ public class ThreadLocalRandom extends Random {
|
||||
* ThreadLocalRandom sequence. The dual use is a marriage of
|
||||
* convenience, but is a simple and efficient way of reducing
|
||||
* application-level overhead and footprint of most concurrent
|
||||
* programs.
|
||||
* programs. Even more opportunistically, we also define here
|
||||
* other package-private utilities that access Thread class
|
||||
* fields.
|
||||
*
|
||||
* Even though this class subclasses java.util.Random, it uses the
|
||||
* same basic algorithm as java.util.SplittableRandom. (See its
|
||||
@ -958,6 +962,49 @@ public class ThreadLocalRandom extends Random {
|
||||
return r;
|
||||
}
|
||||
|
||||
// Support for other package-private ThreadLocal access
|
||||
|
||||
/**
|
||||
* Erases ThreadLocals by nulling out Thread maps.
|
||||
*/
|
||||
static final void eraseThreadLocals(Thread thread) {
|
||||
U.putObject(thread, THREADLOCALS, null);
|
||||
U.putObject(thread, INHERITABLETHREADLOCALS, null);
|
||||
}
|
||||
|
||||
static final void setInheritedAccessControlContext(Thread thread,
|
||||
AccessControlContext acc) {
|
||||
U.putObjectRelease(thread, INHERITEDACCESSCONTROLCONTEXT, acc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new group with the system ThreadGroup (the
|
||||
* topmost, parent-less group) as parent. Uses Unsafe to
|
||||
* traverse Thread.group and ThreadGroup.parent fields.
|
||||
*/
|
||||
static final ThreadGroup createThreadGroup(String name) {
|
||||
if (name == null)
|
||||
throw new NullPointerException();
|
||||
try {
|
||||
long tg = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("group"));
|
||||
long gp = U.objectFieldOffset
|
||||
(ThreadGroup.class.getDeclaredField("parent"));
|
||||
ThreadGroup group = (ThreadGroup)
|
||||
U.getObject(Thread.currentThread(), tg);
|
||||
while (group != null) {
|
||||
ThreadGroup parent = (ThreadGroup)U.getObject(group, gp);
|
||||
if (parent == null)
|
||||
return new ThreadGroup(group, name);
|
||||
group = parent;
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
// fall through if null as cannot-happen safeguard
|
||||
throw new Error("Cannot create ThreadGroup");
|
||||
}
|
||||
|
||||
// Serialization support
|
||||
|
||||
private static final long serialVersionUID = -5851777807851030925L;
|
||||
@ -1022,10 +1069,13 @@ public class ThreadLocalRandom extends Random {
|
||||
static final String BAD_SIZE = "size must be non-negative";
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private static final long SEED;
|
||||
private static final long PROBE;
|
||||
private static final long SECONDARY;
|
||||
private static final long THREADLOCALS;
|
||||
private static final long INHERITABLETHREADLOCALS;
|
||||
private static final long INHERITEDACCESSCONTROLCONTEXT;
|
||||
static {
|
||||
try {
|
||||
SEED = U.objectFieldOffset
|
||||
@ -1034,6 +1084,12 @@ public class ThreadLocalRandom extends Random {
|
||||
(Thread.class.getDeclaredField("threadLocalRandomProbe"));
|
||||
SECONDARY = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
|
||||
THREADLOCALS = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("threadLocals"));
|
||||
INHERITABLETHREADLOCALS = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("inheritableThreadLocals"));
|
||||
INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("inheritedAccessControlContext"));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -35,27 +35,26 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
|
||||
/**
|
||||
* A {@code boolean} value that may be updated atomically. See the
|
||||
* {@link java.util.concurrent.atomic} package specification for
|
||||
* description of the properties of atomic variables. An
|
||||
* {@code AtomicBoolean} is used in applications such as atomically
|
||||
* updated flags, and cannot be used as a replacement for a
|
||||
* {@link java.lang.Boolean}.
|
||||
* {@link VarHandle} specification for descriptions of the properties
|
||||
* of atomic accesses. An {@code AtomicBoolean} is used in
|
||||
* applications such as atomically updated flags, and cannot be used
|
||||
* as a replacement for a {@link java.lang.Boolean}.
|
||||
*
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
*/
|
||||
public class AtomicBoolean implements java.io.Serializable {
|
||||
private static final long serialVersionUID = 4654671469794556979L;
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long VALUE;
|
||||
|
||||
private static final VarHandle VALUE;
|
||||
static {
|
||||
try {
|
||||
VALUE = U.objectFieldOffset
|
||||
(AtomicBoolean.class.getDeclaredField("value"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
VALUE = l.findVarHandle(AtomicBoolean.class, "value", int.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -79,7 +78,8 @@ public class AtomicBoolean implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value.
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* @return the current value
|
||||
*/
|
||||
@ -88,40 +88,39 @@ public class AtomicBoolean implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful. False return indicates that
|
||||
* the actual value was not equal to the expected value.
|
||||
*/
|
||||
public final boolean compareAndSet(boolean expect, boolean update) {
|
||||
return U.compareAndSwapInt(this, VALUE,
|
||||
(expect ? 1 : 0),
|
||||
(update ? 1 : 0));
|
||||
public final boolean compareAndSet(boolean expectedValue, boolean newValue) {
|
||||
return VALUE.compareAndSet(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
|
||||
*
|
||||
* <p><a href="package-summary.html#weakCompareAndSet">May fail
|
||||
* spuriously and does not provide ordering guarantees</a>, so is
|
||||
* only rarely an appropriate alternative to {@code compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public boolean weakCompareAndSet(boolean expect, boolean update) {
|
||||
return U.compareAndSwapInt(this, VALUE,
|
||||
(expect ? 1 : 0),
|
||||
(update ? 1 : 0));
|
||||
public boolean weakCompareAndSet(boolean expectedValue, boolean newValue) {
|
||||
return VALUE.weakCompareAndSet(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setVolatile}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
*/
|
||||
@ -130,17 +129,19 @@ public class AtomicBoolean implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 1.6
|
||||
*/
|
||||
public final void lazySet(boolean newValue) {
|
||||
U.putIntRelease(this, VALUE, (newValue ? 1 : 0));
|
||||
VALUE.setRelease(this, (newValue ? 1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets to the given value and returns the previous value.
|
||||
* Atomically sets the value to {@code newValue} and returns the old value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndSet}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @return the previous value
|
||||
@ -161,4 +162,178 @@ public class AtomicBoolean implements java.io.Serializable {
|
||||
return Boolean.toString(get());
|
||||
}
|
||||
|
||||
// jdk9
|
||||
|
||||
/**
|
||||
* Returns the current value, with memory semantics of reading as
|
||||
* if the variable was declared non-{@code volatile}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean getPlain() {
|
||||
return (int)VALUE.get(this) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue}, with memory semantics
|
||||
* of setting as if the variable was declared non-{@code volatile}
|
||||
* and non-{@code final}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setPlain(boolean newValue) {
|
||||
VALUE.set(this, newValue ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getOpaque}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean getOpaque() {
|
||||
return (int)VALUE.getOpaque(this) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setOpaque}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setOpaque(boolean newValue) {
|
||||
VALUE.setOpaque(this, newValue ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAcquire}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean getAcquire() {
|
||||
return (int)VALUE.getAcquire(this) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setRelease(boolean newValue) {
|
||||
VALUE.setRelease(this, newValue ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchange}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean compareAndExchange(boolean expectedValue, boolean newValue) {
|
||||
return (int)VALUE.compareAndExchange(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean compareAndExchangeAcquire(boolean expectedValue, boolean newValue) {
|
||||
return (int)VALUE.compareAndExchangeAcquire(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean compareAndExchangeRelease(boolean expectedValue, boolean newValue) {
|
||||
return (int)VALUE.compareAndExchangeRelease(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue} if the current
|
||||
* value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetVolatile}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetVolatile(boolean expectedValue, boolean newValue) {
|
||||
return VALUE.weakCompareAndSetVolatile(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue} if the current
|
||||
* value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetAcquire(boolean expectedValue, boolean newValue) {
|
||||
return VALUE.weakCompareAndSetAcquire(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue} if the current
|
||||
* value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetRelease(boolean expectedValue, boolean newValue) {
|
||||
return VALUE.weakCompareAndSetRelease(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,32 +35,30 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.function.IntBinaryOperator;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
|
||||
/**
|
||||
* An {@code int} value that may be updated atomically. See the
|
||||
* {@link java.util.concurrent.atomic} package specification for
|
||||
* description of the properties of atomic variables. An
|
||||
* {@code AtomicInteger} is used in applications such as atomically
|
||||
* incremented counters, and cannot be used as a replacement for an
|
||||
* {@link java.lang.Integer}. However, this class does extend
|
||||
* {@code Number} to allow uniform access by tools and utilities that
|
||||
* deal with numerically-based classes.
|
||||
* {@link VarHandle} specification for descriptions of the properties
|
||||
* of atomic accesses. An {@code AtomicInteger} is used in
|
||||
* applications such as atomically incremented counters, and cannot be
|
||||
* used as a replacement for an {@link java.lang.Integer}. However,
|
||||
* this class does extend {@code Number} to allow uniform access by
|
||||
* tools and utilities that deal with numerically-based classes.
|
||||
*
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
*/
|
||||
public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
private static final long serialVersionUID = 6214790243416807050L;
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long VALUE;
|
||||
|
||||
private static final VarHandle VALUE;
|
||||
static {
|
||||
try {
|
||||
VALUE = U.objectFieldOffset
|
||||
(AtomicInteger.class.getDeclaredField("value"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
VALUE = l.findVarHandle(AtomicInteger.class, "value", int.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -84,7 +82,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value.
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* @return the current value
|
||||
*/
|
||||
@ -93,7 +92,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setVolatile}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
*/
|
||||
@ -102,108 +102,122 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 1.6
|
||||
*/
|
||||
public final void lazySet(int newValue) {
|
||||
U.putIntRelease(this, VALUE, newValue);
|
||||
VALUE.setRelease(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets to the given value and returns the old value.
|
||||
* Atomically sets the value to {@code newValue} and returns the old value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndSet}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @return the previous value
|
||||
*/
|
||||
public final int getAndSet(int newValue) {
|
||||
return U.getAndSetInt(this, VALUE, newValue);
|
||||
return (int)VALUE.getAndSet(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful. False return indicates that
|
||||
* the actual value was not equal to the expected value.
|
||||
*/
|
||||
public final boolean compareAndSet(int expect, int update) {
|
||||
return U.compareAndSwapInt(this, VALUE, expect, update);
|
||||
public final boolean compareAndSet(int expectedValue, int newValue) {
|
||||
return VALUE.compareAndSet(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
|
||||
*
|
||||
* <p><a href="package-summary.html#weakCompareAndSet">May fail
|
||||
* spuriously and does not provide ordering guarantees</a>, so is
|
||||
* only rarely an appropriate alternative to {@code compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public final boolean weakCompareAndSet(int expect, int update) {
|
||||
return U.compareAndSwapInt(this, VALUE, expect, update);
|
||||
public final boolean weakCompareAndSet(int expectedValue, int newValue) {
|
||||
return VALUE.weakCompareAndSet(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the current value.
|
||||
* Atomically increments the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(1)}.
|
||||
*
|
||||
* @return the previous value
|
||||
*/
|
||||
public final int getAndIncrement() {
|
||||
return U.getAndAddInt(this, VALUE, 1);
|
||||
return (int)VALUE.getAndAdd(this, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the current value.
|
||||
* Atomically decrements the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(-1)}.
|
||||
*
|
||||
* @return the previous value
|
||||
*/
|
||||
public final int getAndDecrement() {
|
||||
return U.getAndAddInt(this, VALUE, -1);
|
||||
return (int)VALUE.getAndAdd(this, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the current value.
|
||||
* Atomically adds the given value to the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* @param delta the value to add
|
||||
* @return the previous value
|
||||
*/
|
||||
public final int getAndAdd(int delta) {
|
||||
return U.getAndAddInt(this, VALUE, delta);
|
||||
return (int)VALUE.getAndAdd(this, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the current value.
|
||||
* Atomically increments the current value,
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(1)}.
|
||||
*
|
||||
* @return the updated value
|
||||
*/
|
||||
public final int incrementAndGet() {
|
||||
return U.getAndAddInt(this, VALUE, 1) + 1;
|
||||
return (int)VALUE.addAndGet(this, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the current value.
|
||||
* Atomically decrements the current value,
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(-1)}.
|
||||
*
|
||||
* @return the updated value
|
||||
*/
|
||||
public final int decrementAndGet() {
|
||||
return U.getAndAddInt(this, VALUE, -1) - 1;
|
||||
return (int)VALUE.addAndGet(this, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the current value.
|
||||
* Atomically adds the given value to the current value,
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* @param delta the value to add
|
||||
* @return the updated value
|
||||
*/
|
||||
public final int addAndGet(int delta) {
|
||||
return U.getAndAddInt(this, VALUE, delta) + delta;
|
||||
return (int)VALUE.addAndGet(this, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -217,12 +231,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final int getAndUpdate(IntUnaryOperator updateFunction) {
|
||||
int prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return prev;
|
||||
int prev = get(), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -236,12 +252,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final int updateAndGet(IntUnaryOperator updateFunction) {
|
||||
int prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return next;
|
||||
int prev = get(), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -260,12 +278,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
*/
|
||||
public final int getAndAccumulate(int x,
|
||||
IntBinaryOperator accumulatorFunction) {
|
||||
int prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return prev;
|
||||
int prev = get(), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -284,12 +304,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
*/
|
||||
public final int accumulateAndGet(int x,
|
||||
IntBinaryOperator accumulatorFunction) {
|
||||
int prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return next;
|
||||
int prev = get(), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,7 +323,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicInteger} as an {@code int}.
|
||||
* Returns the current value of this {@code AtomicInteger} as an
|
||||
* {@code int},
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* Equivalent to {@link #get()}.
|
||||
*/
|
||||
public int intValue() {
|
||||
@ -309,8 +334,9 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicInteger} as a {@code long}
|
||||
* after a widening primitive conversion.
|
||||
* Returns the current value of this {@code AtomicInteger} as a
|
||||
* {@code long} after a widening primitive conversion,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* @jls 5.1.2 Widening Primitive Conversions
|
||||
*/
|
||||
public long longValue() {
|
||||
@ -318,8 +344,9 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicInteger} as a {@code float}
|
||||
* after a widening primitive conversion.
|
||||
* Returns the current value of this {@code AtomicInteger} as a
|
||||
* {@code float} after a widening primitive conversion,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* @jls 5.1.2 Widening Primitive Conversions
|
||||
*/
|
||||
public float floatValue() {
|
||||
@ -327,12 +354,175 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicInteger} as a {@code double}
|
||||
* after a widening primitive conversion.
|
||||
* Returns the current value of this {@code AtomicInteger} as a
|
||||
* {@code double} after a widening primitive conversion,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* @jls 5.1.2 Widening Primitive Conversions
|
||||
*/
|
||||
public double doubleValue() {
|
||||
return (double)get();
|
||||
}
|
||||
|
||||
// jdk9
|
||||
|
||||
/**
|
||||
* Returns the current value, with memory semantics of reading as
|
||||
* if the variable was declared non-{@code volatile}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final int getPlain() {
|
||||
return (int)VALUE.get(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue}, with memory semantics
|
||||
* of setting as if the variable was declared non-{@code volatile}
|
||||
* and non-{@code final}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setPlain(int newValue) {
|
||||
VALUE.set(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getOpaque}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final int getOpaque() {
|
||||
return (int)VALUE.getOpaque(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setOpaque}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setOpaque(int newValue) {
|
||||
VALUE.setOpaque(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAcquire}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final int getAcquire() {
|
||||
return (int)VALUE.getAcquire(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setRelease(int newValue) {
|
||||
VALUE.setRelease(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchange}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final int compareAndExchange(int expectedValue, int newValue) {
|
||||
return (int)VALUE.compareAndExchange(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final int compareAndExchangeAcquire(int expectedValue, int newValue) {
|
||||
return (int)VALUE.compareAndExchangeAcquire(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final int compareAndExchangeRelease(int expectedValue, int newValue) {
|
||||
return (int)VALUE.compareAndExchangeRelease(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue} if
|
||||
* the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetVolatile}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetVolatile(int expectedValue, int newValue) {
|
||||
return VALUE.weakCompareAndSetVolatile(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue} if
|
||||
* the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetAcquire(int expectedValue, int newValue) {
|
||||
return VALUE.weakCompareAndSetAcquire(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue} if
|
||||
* the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetRelease(int expectedValue, int newValue) {
|
||||
return VALUE.weakCompareAndSetRelease(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,44 +35,24 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.function.IntBinaryOperator;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
|
||||
/**
|
||||
* An {@code int} array in which elements may be updated atomically.
|
||||
* See the {@link java.util.concurrent.atomic} package
|
||||
* specification for description of the properties of atomic
|
||||
* variables.
|
||||
* See the {@link VarHandle} specification for descriptions of the
|
||||
* properties of atomic accesses.
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
*/
|
||||
public class AtomicIntegerArray implements java.io.Serializable {
|
||||
private static final long serialVersionUID = 2862133569453604235L;
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final int ABASE;
|
||||
private static final int ASHIFT;
|
||||
private static final VarHandle AA
|
||||
= MethodHandles.arrayElementVarHandle(int[].class);
|
||||
private final int[] array;
|
||||
|
||||
static {
|
||||
ABASE = U.arrayBaseOffset(int[].class);
|
||||
int scale = U.arrayIndexScale(int[].class);
|
||||
if ((scale & (scale - 1)) != 0)
|
||||
throw new Error("array index scale not a power of two");
|
||||
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
}
|
||||
|
||||
private long checkedByteOffset(int i) {
|
||||
if (i < 0 || i >= array.length)
|
||||
throw new IndexOutOfBoundsException("index " + i);
|
||||
|
||||
return byteOffset(i);
|
||||
}
|
||||
|
||||
private static long byteOffset(int i) {
|
||||
return ((long) i << ASHIFT) + ABASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new AtomicIntegerArray of the given length, with all
|
||||
* elements initially zero.
|
||||
@ -105,147 +85,155 @@ public class AtomicIntegerArray implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value at position {@code i}.
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the current value
|
||||
*/
|
||||
public final int get(int i) {
|
||||
return getRaw(checkedByteOffset(i));
|
||||
}
|
||||
|
||||
private int getRaw(long offset) {
|
||||
return U.getIntVolatile(array, offset);
|
||||
return (int)AA.getVolatile(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at position {@code i} to the given value.
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
*/
|
||||
public final void set(int i, int newValue) {
|
||||
U.putIntVolatile(array, checkedByteOffset(i), newValue);
|
||||
AA.setVolatile(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually sets the element at position {@code i} to the given value.
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 1.6
|
||||
*/
|
||||
public final void lazySet(int i, int newValue) {
|
||||
U.putIntRelease(array, checkedByteOffset(i), newValue);
|
||||
AA.setRelease(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* value and returns the old value.
|
||||
* Atomically sets the element at index {@code i} to {@code
|
||||
* newValue} and returns the old value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @return the previous value
|
||||
*/
|
||||
public final int getAndSet(int i, int newValue) {
|
||||
return U.getAndSetInt(array, checkedByteOffset(i), newValue);
|
||||
return (int)AA.getAndSet(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* updated value if the current value {@code ==} the expected value.
|
||||
* Atomically sets the element at index {@code i} to {@code
|
||||
* newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#compareAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful. False return indicates that
|
||||
* the actual value was not equal to the expected value.
|
||||
*/
|
||||
public final boolean compareAndSet(int i, int expect, int update) {
|
||||
return compareAndSetRaw(checkedByteOffset(i), expect, update);
|
||||
}
|
||||
|
||||
private boolean compareAndSetRaw(long offset, int expect, int update) {
|
||||
return U.compareAndSwapInt(array, offset, expect, update);
|
||||
public final boolean compareAndSet(int i, int expectedValue, int newValue) {
|
||||
return AA.compareAndSet(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* updated value if the current value {@code ==} the expected value.
|
||||
*
|
||||
* <p><a href="package-summary.html#weakCompareAndSet">May fail
|
||||
* spuriously and does not provide ordering guarantees</a>, so is
|
||||
* only rarely an appropriate alternative to {@code compareAndSet}.
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public final boolean weakCompareAndSet(int i, int expect, int update) {
|
||||
return compareAndSet(i, expect, update);
|
||||
public final boolean weakCompareAndSet(int i, int expectedValue, int newValue) {
|
||||
return AA.weakCompareAndSet(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the element at index {@code i}.
|
||||
* Atomically increments the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(i, 1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the previous value
|
||||
*/
|
||||
public final int getAndIncrement(int i) {
|
||||
return getAndAdd(i, 1);
|
||||
return (int)AA.getAndAdd(array, i, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the element at index {@code i}.
|
||||
* Atomically decrements the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(i, -1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the previous value
|
||||
*/
|
||||
public final int getAndDecrement(int i) {
|
||||
return getAndAdd(i, -1);
|
||||
return (int)AA.getAndAdd(array, i, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the element at index {@code i}.
|
||||
* Atomically adds the given value to the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param delta the value to add
|
||||
* @return the previous value
|
||||
*/
|
||||
public final int getAndAdd(int i, int delta) {
|
||||
return U.getAndAddInt(array, checkedByteOffset(i), delta);
|
||||
return (int)AA.getAndAdd(array, i, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the element at index {@code i}.
|
||||
* Atomically increments the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(i, 1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the updated value
|
||||
*/
|
||||
public final int incrementAndGet(int i) {
|
||||
return getAndAdd(i, 1) + 1;
|
||||
return (int)AA.addAndGet(array, i, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the element at index {@code i}.
|
||||
* Atomically decrements the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(i, -1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the updated value
|
||||
*/
|
||||
public final int decrementAndGet(int i) {
|
||||
return getAndAdd(i, -1) - 1;
|
||||
return (int)AA.addAndGet(array, i, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the element at index {@code i}.
|
||||
* Atomically adds the given value to the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param delta the value to add
|
||||
* @return the updated value
|
||||
*/
|
||||
public final int addAndGet(int i, int delta) {
|
||||
return getAndAdd(i, delta) + delta;
|
||||
return (int)AA.addAndGet(array, i, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -260,13 +248,14 @@ public class AtomicIntegerArray implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final int getAndUpdate(int i, IntUnaryOperator updateFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
int prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return prev;
|
||||
int prev = get(i), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -281,23 +270,25 @@ public class AtomicIntegerArray implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final int updateAndGet(int i, IntUnaryOperator updateFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
int prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return next;
|
||||
int prev = get(i), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically updates the element at index {@code i} with the
|
||||
* results of applying the given function to the current and
|
||||
* given values, returning the previous value. The function should
|
||||
* be side-effect-free, since it may be re-applied when attempted
|
||||
* results of applying the given function to the current and given
|
||||
* values, returning the previous value. The function should be
|
||||
* side-effect-free, since it may be re-applied when attempted
|
||||
* updates fail due to contention among threads. The function is
|
||||
* applied with the current value at index {@code i} as its first
|
||||
* argument, and the given update as the second argument.
|
||||
* applied with the current value of the element at index {@code i}
|
||||
* as its first argument, and the given update as the second
|
||||
* argument.
|
||||
*
|
||||
* @param i the index
|
||||
* @param x the update value
|
||||
@ -307,23 +298,25 @@ public class AtomicIntegerArray implements java.io.Serializable {
|
||||
*/
|
||||
public final int getAndAccumulate(int i, int x,
|
||||
IntBinaryOperator accumulatorFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
int prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return prev;
|
||||
int prev = get(i), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically updates the element at index {@code i} with the
|
||||
* results of applying the given function to the current and
|
||||
* given values, returning the updated value. The function should
|
||||
* be side-effect-free, since it may be re-applied when attempted
|
||||
* results of applying the given function to the current and given
|
||||
* values, returning the updated value. The function should be
|
||||
* side-effect-free, since it may be re-applied when attempted
|
||||
* updates fail due to contention among threads. The function is
|
||||
* applied with the current value at index {@code i} as its first
|
||||
* argument, and the given update as the second argument.
|
||||
* applied with the current value of the element at index {@code i}
|
||||
* as its first argument, and the given update as the second
|
||||
* argument.
|
||||
*
|
||||
* @param i the index
|
||||
* @param x the update value
|
||||
@ -333,13 +326,14 @@ public class AtomicIntegerArray implements java.io.Serializable {
|
||||
*/
|
||||
public final int accumulateAndGet(int i, int x,
|
||||
IntBinaryOperator accumulatorFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
int prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return next;
|
||||
int prev = get(i), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -354,11 +348,190 @@ public class AtomicIntegerArray implements java.io.Serializable {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append('[');
|
||||
for (int i = 0; ; i++) {
|
||||
b.append(getRaw(byteOffset(i)));
|
||||
b.append(get(i));
|
||||
if (i == iMax)
|
||||
return b.append(']').toString();
|
||||
b.append(',').append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
// jdk9
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory semantics of reading as if the variable was declared
|
||||
* non-{@code volatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final int getPlain(int i) {
|
||||
return (int)AA.get(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory semantics of setting as if the variable was
|
||||
* declared non-{@code volatile} and non-{@code final}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setPlain(int i, int newValue) {
|
||||
AA.set(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getOpaque}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final int getOpaque(int i) {
|
||||
return (int)AA.getOpaque(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setOpaque}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setOpaque(int i, int newValue) {
|
||||
AA.setOpaque(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final int getAcquire(int i) {
|
||||
return (int)AA.getAcquire(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setRelease(int i, int newValue) {
|
||||
AA.setRelease(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchange}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final int compareAndExchange(int i, int expectedValue, int newValue) {
|
||||
return (int)AA.compareAndExchange(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final int compareAndExchangeAcquire(int i, int expectedValue, int newValue) {
|
||||
return (int)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final int compareAndExchangeRelease(int i, int expectedValue, int newValue) {
|
||||
return (int)AA.compareAndExchangeRelease(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetVolatile(int i, int expectedValue, int newValue) {
|
||||
return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetAcquire(int i, int expectedValue, int newValue) {
|
||||
return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetRelease(int i, int expectedValue, int newValue) {
|
||||
return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.function.IntBinaryOperator;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
@ -150,8 +151,8 @@ public abstract class AtomicIntegerFieldUpdater<T> {
|
||||
public abstract void lazySet(T obj, int newValue);
|
||||
|
||||
/**
|
||||
* Gets the current value held in the field of the given object managed
|
||||
* by this updater.
|
||||
* Returns the current value held in the field of the given object
|
||||
* managed by this updater.
|
||||
*
|
||||
* @param obj An object whose field to get
|
||||
* @return the current value
|
||||
@ -367,7 +368,7 @@ public abstract class AtomicIntegerFieldUpdater<T> {
|
||||
*/
|
||||
private static final class AtomicIntegerFieldUpdaterImpl<T>
|
||||
extends AtomicIntegerFieldUpdater<T> {
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private final long offset;
|
||||
/**
|
||||
* if field is protected, the subclass constructing updater, else
|
||||
|
@ -35,31 +35,30 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.function.LongBinaryOperator;
|
||||
import java.util.function.LongUnaryOperator;
|
||||
|
||||
/**
|
||||
* A {@code long} value that may be updated atomically. See the
|
||||
* {@link java.util.concurrent.atomic} package specification for
|
||||
* description of the properties of atomic variables. An
|
||||
* {@code AtomicLong} is used in applications such as atomically
|
||||
* incremented sequence numbers, and cannot be used as a replacement
|
||||
* for a {@link java.lang.Long}. However, this class does extend
|
||||
* {@code Number} to allow uniform access by tools and utilities that
|
||||
* deal with numerically-based classes.
|
||||
* {@link VarHandle} specification for descriptions of the properties
|
||||
* of atomic accesses. An {@code AtomicLong} is used in applications
|
||||
* such as atomically incremented sequence numbers, and cannot be used
|
||||
* as a replacement for a {@link java.lang.Long}. However, this class
|
||||
* does extend {@code Number} to allow uniform access by tools and
|
||||
* utilities that deal with numerically-based classes.
|
||||
*
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
*/
|
||||
public class AtomicLong extends Number implements java.io.Serializable {
|
||||
private static final long serialVersionUID = 1927816293512124184L;
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long VALUE;
|
||||
private static final VarHandle VALUE;
|
||||
|
||||
/**
|
||||
* Records whether the underlying JVM supports lockless
|
||||
* compareAndSwap for longs. While the Unsafe.compareAndSwapLong
|
||||
* compareAndSwap for longs. While the intrinsic compareAndSwapLong
|
||||
* method works in either case, some constructions should be
|
||||
* handled at Java level to avoid locking user-visible locks.
|
||||
*/
|
||||
@ -73,8 +72,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
|
||||
static {
|
||||
try {
|
||||
VALUE = U.objectFieldOffset
|
||||
(AtomicLong.class.getDeclaredField("value"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
VALUE = l.findVarHandle(AtomicLong.class, "value", long.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -98,7 +97,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value.
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* @return the current value
|
||||
*/
|
||||
@ -107,119 +107,132 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setVolatile}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
*/
|
||||
public final void set(long newValue) {
|
||||
// Use putLongVolatile instead of ordinary volatile store when
|
||||
// using compareAndSwapLong, for sake of some 32bit systems.
|
||||
U.putLongVolatile(this, VALUE, newValue);
|
||||
VALUE.setVolatile(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 1.6
|
||||
*/
|
||||
public final void lazySet(long newValue) {
|
||||
U.putLongRelease(this, VALUE, newValue);
|
||||
VALUE.setRelease(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets to the given value and returns the old value.
|
||||
* Atomically sets the value to {@code newValue} and returns the old value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndSet}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @return the previous value
|
||||
*/
|
||||
public final long getAndSet(long newValue) {
|
||||
return U.getAndSetLong(this, VALUE, newValue);
|
||||
return (long)VALUE.getAndSet(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful. False return indicates that
|
||||
* the actual value was not equal to the expected value.
|
||||
*/
|
||||
public final boolean compareAndSet(long expect, long update) {
|
||||
return U.compareAndSwapLong(this, VALUE, expect, update);
|
||||
public final boolean compareAndSet(long expectedValue, long newValue) {
|
||||
return VALUE.compareAndSet(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
|
||||
*
|
||||
* <p><a href="package-summary.html#weakCompareAndSet">May fail
|
||||
* spuriously and does not provide ordering guarantees</a>, so is
|
||||
* only rarely an appropriate alternative to {@code compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public final boolean weakCompareAndSet(long expect, long update) {
|
||||
return U.compareAndSwapLong(this, VALUE, expect, update);
|
||||
public final boolean weakCompareAndSet(long expectedValue, long newValue) {
|
||||
return VALUE.weakCompareAndSet(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the current value.
|
||||
* Atomically increments the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(1)}.
|
||||
*
|
||||
* @return the previous value
|
||||
*/
|
||||
public final long getAndIncrement() {
|
||||
return U.getAndAddLong(this, VALUE, 1L);
|
||||
return (long)VALUE.getAndAdd(this, 1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the current value.
|
||||
* Atomically decrements the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(-1)}.
|
||||
*
|
||||
* @return the previous value
|
||||
*/
|
||||
public final long getAndDecrement() {
|
||||
return U.getAndAddLong(this, VALUE, -1L);
|
||||
return (long)VALUE.getAndAdd(this, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the current value.
|
||||
* Atomically adds the given value to the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* @param delta the value to add
|
||||
* @return the previous value
|
||||
*/
|
||||
public final long getAndAdd(long delta) {
|
||||
return U.getAndAddLong(this, VALUE, delta);
|
||||
return (long)VALUE.getAndAdd(this, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the current value.
|
||||
* Atomically increments the current value,
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(1)}.
|
||||
*
|
||||
* @return the updated value
|
||||
*/
|
||||
public final long incrementAndGet() {
|
||||
return U.getAndAddLong(this, VALUE, 1L) + 1L;
|
||||
return (long)VALUE.addAndGet(this, 1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the current value.
|
||||
* Atomically decrements the current value,
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(-1)}.
|
||||
*
|
||||
* @return the updated value
|
||||
*/
|
||||
public final long decrementAndGet() {
|
||||
return U.getAndAddLong(this, VALUE, -1L) - 1L;
|
||||
return (long)VALUE.addAndGet(this, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the current value.
|
||||
* Atomically adds the given value to the current value,
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* @param delta the value to add
|
||||
* @return the updated value
|
||||
*/
|
||||
public final long addAndGet(long delta) {
|
||||
return U.getAndAddLong(this, VALUE, delta) + delta;
|
||||
return (long)VALUE.addAndGet(this, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -233,12 +246,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final long getAndUpdate(LongUnaryOperator updateFunction) {
|
||||
long prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return prev;
|
||||
long prev = get(), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -252,12 +267,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final long updateAndGet(LongUnaryOperator updateFunction) {
|
||||
long prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return next;
|
||||
long prev = get(), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,12 +293,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
*/
|
||||
public final long getAndAccumulate(long x,
|
||||
LongBinaryOperator accumulatorFunction) {
|
||||
long prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return prev;
|
||||
long prev = get(), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -300,12 +319,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
*/
|
||||
public final long accumulateAndGet(long x,
|
||||
LongBinaryOperator accumulatorFunction) {
|
||||
long prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return next;
|
||||
long prev = get(), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -317,8 +338,9 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicLong} as an {@code int}
|
||||
* after a narrowing primitive conversion.
|
||||
* Returns the current value of this {@code AtomicLong} as an {@code int}
|
||||
* after a narrowing primitive conversion,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* @jls 5.1.3 Narrowing Primitive Conversions
|
||||
*/
|
||||
public int intValue() {
|
||||
@ -326,7 +348,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicLong} as a {@code long}.
|
||||
* Returns the current value of this {@code AtomicLong} as a {@code long},
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* Equivalent to {@link #get()}.
|
||||
*/
|
||||
public long longValue() {
|
||||
@ -334,8 +357,9 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicLong} as a {@code float}
|
||||
* after a widening primitive conversion.
|
||||
* Returns the current value of this {@code AtomicLong} as a {@code float}
|
||||
* after a widening primitive conversion,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* @jls 5.1.2 Widening Primitive Conversions
|
||||
*/
|
||||
public float floatValue() {
|
||||
@ -343,12 +367,175 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicLong} as a {@code double}
|
||||
* after a widening primitive conversion.
|
||||
* Returns the current value of this {@code AtomicLong} as a {@code double}
|
||||
* after a widening primitive conversion,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* @jls 5.1.2 Widening Primitive Conversions
|
||||
*/
|
||||
public double doubleValue() {
|
||||
return (double)get();
|
||||
}
|
||||
|
||||
// jdk9
|
||||
|
||||
/**
|
||||
* Returns the current value, with memory semantics of reading as if the
|
||||
* variable was declared non-{@code volatile}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final long getPlain() {
|
||||
return (long)VALUE.get(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue}, with memory semantics
|
||||
* of setting as if the variable was declared non-{@code volatile}
|
||||
* and non-{@code final}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setPlain(long newValue) {
|
||||
VALUE.set(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getOpaque}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final long getOpaque() {
|
||||
return (long)VALUE.getOpaque(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setOpaque}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setOpaque(long newValue) {
|
||||
VALUE.setOpaque(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAcquire}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final long getAcquire() {
|
||||
return (long)VALUE.getAcquire(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setRelease(long newValue) {
|
||||
VALUE.setRelease(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchange}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final long compareAndExchange(long expectedValue, long newValue) {
|
||||
return (long)VALUE.compareAndExchange(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final long compareAndExchangeAcquire(long expectedValue, long newValue) {
|
||||
return (long)VALUE.compareAndExchangeAcquire(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final long compareAndExchangeRelease(long expectedValue, long newValue) {
|
||||
return (long)VALUE.compareAndExchangeRelease(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetVolatile}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetVolatile(long expectedValue, long newValue) {
|
||||
return VALUE.weakCompareAndSetVolatile(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetAcquire(long expectedValue, long newValue) {
|
||||
return VALUE.weakCompareAndSetAcquire(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetRelease(long expectedValue, long newValue) {
|
||||
return VALUE.weakCompareAndSetRelease(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,43 +35,24 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.function.LongBinaryOperator;
|
||||
import java.util.function.LongUnaryOperator;
|
||||
|
||||
/**
|
||||
* A {@code long} array in which elements may be updated atomically.
|
||||
* See the {@link java.util.concurrent.atomic} package specification
|
||||
* for description of the properties of atomic variables.
|
||||
* See the {@link VarHandle} specification for descriptions of the
|
||||
* properties of atomic accesses.
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
*/
|
||||
public class AtomicLongArray implements java.io.Serializable {
|
||||
private static final long serialVersionUID = -2308431214976778248L;
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final int ABASE;
|
||||
private static final int ASHIFT;
|
||||
private static final VarHandle AA
|
||||
= MethodHandles.arrayElementVarHandle(long[].class);
|
||||
private final long[] array;
|
||||
|
||||
static {
|
||||
ABASE = U.arrayBaseOffset(long[].class);
|
||||
int scale = U.arrayIndexScale(long[].class);
|
||||
if ((scale & (scale - 1)) != 0)
|
||||
throw new Error("array index scale not a power of two");
|
||||
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
}
|
||||
|
||||
private long checkedByteOffset(int i) {
|
||||
if (i < 0 || i >= array.length)
|
||||
throw new IndexOutOfBoundsException("index " + i);
|
||||
|
||||
return byteOffset(i);
|
||||
}
|
||||
|
||||
private static long byteOffset(int i) {
|
||||
return ((long) i << ASHIFT) + ABASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new AtomicLongArray of the given length, with all
|
||||
* elements initially zero.
|
||||
@ -104,147 +85,155 @@ public class AtomicLongArray implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value at position {@code i}.
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the current value
|
||||
*/
|
||||
public final long get(int i) {
|
||||
return getRaw(checkedByteOffset(i));
|
||||
}
|
||||
|
||||
private long getRaw(long offset) {
|
||||
return U.getLongVolatile(array, offset);
|
||||
return (long)AA.getVolatile(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at position {@code i} to the given value.
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
*/
|
||||
public final void set(int i, long newValue) {
|
||||
U.putLongVolatile(array, checkedByteOffset(i), newValue);
|
||||
AA.setVolatile(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually sets the element at position {@code i} to the given value.
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 1.6
|
||||
*/
|
||||
public final void lazySet(int i, long newValue) {
|
||||
U.putLongRelease(array, checkedByteOffset(i), newValue);
|
||||
AA.setRelease(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given value
|
||||
* and returns the old value.
|
||||
* Atomically sets the element at index {@code i} to {@code
|
||||
* newValue} and returns the old value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @return the previous value
|
||||
*/
|
||||
public final long getAndSet(int i, long newValue) {
|
||||
return U.getAndSetLong(array, checkedByteOffset(i), newValue);
|
||||
return (long)AA.getAndSet(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* updated value if the current value {@code ==} the expected value.
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#compareAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful. False return indicates that
|
||||
* the actual value was not equal to the expected value.
|
||||
*/
|
||||
public final boolean compareAndSet(int i, long expect, long update) {
|
||||
return compareAndSetRaw(checkedByteOffset(i), expect, update);
|
||||
}
|
||||
|
||||
private boolean compareAndSetRaw(long offset, long expect, long update) {
|
||||
return U.compareAndSwapLong(array, offset, expect, update);
|
||||
public final boolean compareAndSet(int i, long expectedValue, long newValue) {
|
||||
return AA.compareAndSet(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* updated value if the current value {@code ==} the expected value.
|
||||
*
|
||||
* <p><a href="package-summary.html#weakCompareAndSet">May fail
|
||||
* spuriously and does not provide ordering guarantees</a>, so is
|
||||
* only rarely an appropriate alternative to {@code compareAndSet}.
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public final boolean weakCompareAndSet(int i, long expect, long update) {
|
||||
return compareAndSet(i, expect, update);
|
||||
public final boolean weakCompareAndSet(int i, long expectedValue, long newValue) {
|
||||
return AA.weakCompareAndSet(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the element at index {@code i}.
|
||||
* Atomically increments the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(i, 1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the previous value
|
||||
*/
|
||||
public final long getAndIncrement(int i) {
|
||||
return getAndAdd(i, 1);
|
||||
return (long)AA.getAndAdd(array, i, 1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the element at index {@code i}.
|
||||
* Atomically decrements the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(i, -1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the previous value
|
||||
*/
|
||||
public final long getAndDecrement(int i) {
|
||||
return getAndAdd(i, -1);
|
||||
return (long)AA.getAndAdd(array, i, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the element at index {@code i}.
|
||||
* Atomically adds the given value to the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param delta the value to add
|
||||
* @return the previous value
|
||||
*/
|
||||
public final long getAndAdd(int i, long delta) {
|
||||
return U.getAndAddLong(array, checkedByteOffset(i), delta);
|
||||
return (long)AA.getAndAdd(array, i, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the element at index {@code i}.
|
||||
* Atomically increments the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(i, 1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the updated value
|
||||
*/
|
||||
public final long incrementAndGet(int i) {
|
||||
return getAndAdd(i, 1) + 1;
|
||||
return (long)AA.addAndGet(array, i, 1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the element at index {@code i}.
|
||||
* Atomically decrements the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(i, -1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the updated value
|
||||
*/
|
||||
public final long decrementAndGet(int i) {
|
||||
return getAndAdd(i, -1) - 1;
|
||||
return (long)AA.addAndGet(array, i, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the element at index {@code i}.
|
||||
* Atomically adds the given value to the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param delta the value to add
|
||||
* @return the updated value
|
||||
*/
|
||||
public long addAndGet(int i, long delta) {
|
||||
return getAndAdd(i, delta) + delta;
|
||||
return (long)AA.addAndGet(array, i, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -259,13 +248,14 @@ public class AtomicLongArray implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final long getAndUpdate(int i, LongUnaryOperator updateFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
long prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return prev;
|
||||
long prev = get(i), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,23 +270,25 @@ public class AtomicLongArray implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final long updateAndGet(int i, LongUnaryOperator updateFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
long prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return next;
|
||||
long prev = get(i), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically updates the element at index {@code i} with the
|
||||
* results of applying the given function to the current and
|
||||
* given values, returning the previous value. The function should
|
||||
* be side-effect-free, since it may be re-applied when attempted
|
||||
* results of applying the given function to the current and given
|
||||
* values, returning the previous value. The function should be
|
||||
* side-effect-free, since it may be re-applied when attempted
|
||||
* updates fail due to contention among threads. The function is
|
||||
* applied with the current value at index {@code i} as its first
|
||||
* argument, and the given update as the second argument.
|
||||
* applied with the current value of the element at index {@code i}
|
||||
* as its first argument, and the given update as the second
|
||||
* argument.
|
||||
*
|
||||
* @param i the index
|
||||
* @param x the update value
|
||||
@ -306,23 +298,25 @@ public class AtomicLongArray implements java.io.Serializable {
|
||||
*/
|
||||
public final long getAndAccumulate(int i, long x,
|
||||
LongBinaryOperator accumulatorFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
long prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return prev;
|
||||
long prev = get(i), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically updates the element at index {@code i} with the
|
||||
* results of applying the given function to the current and
|
||||
* given values, returning the updated value. The function should
|
||||
* be side-effect-free, since it may be re-applied when attempted
|
||||
* results of applying the given function to the current and given
|
||||
* values, returning the updated value. The function should be
|
||||
* side-effect-free, since it may be re-applied when attempted
|
||||
* updates fail due to contention among threads. The function is
|
||||
* applied with the current value at index {@code i} as its first
|
||||
* argument, and the given update as the second argument.
|
||||
* applied with the current value of the element at index {@code i}
|
||||
* as its first argument, and the given update as the second
|
||||
* argument.
|
||||
*
|
||||
* @param i the index
|
||||
* @param x the update value
|
||||
@ -332,13 +326,14 @@ public class AtomicLongArray implements java.io.Serializable {
|
||||
*/
|
||||
public final long accumulateAndGet(int i, long x,
|
||||
LongBinaryOperator accumulatorFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
long prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return next;
|
||||
long prev = get(i), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -353,11 +348,189 @@ public class AtomicLongArray implements java.io.Serializable {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append('[');
|
||||
for (int i = 0; ; i++) {
|
||||
b.append(getRaw(byteOffset(i)));
|
||||
b.append(get(i));
|
||||
if (i == iMax)
|
||||
return b.append(']').toString();
|
||||
b.append(',').append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
// jdk9
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory semantics of reading as if the variable was declared
|
||||
* non-{@code volatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final long getPlain(int i) {
|
||||
return (long)AA.get(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory semantics of setting as if the variable was
|
||||
* declared non-{@code volatile} and non-{@code final}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setPlain(int i, long newValue) {
|
||||
AA.set(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getOpaque}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final long getOpaque(int i) {
|
||||
return (long)AA.getOpaque(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setOpaque}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setOpaque(int i, long newValue) {
|
||||
AA.setOpaque(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final long getAcquire(int i) {
|
||||
return (long)AA.getAcquire(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setRelease(int i, long newValue) {
|
||||
AA.setRelease(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchange}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final long compareAndExchange(int i, long expectedValue, long newValue) {
|
||||
return (long)AA.compareAndExchange(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final long compareAndExchangeAcquire(int i, long expectedValue, long newValue) {
|
||||
return (long)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final long compareAndExchangeRelease(int i, long expectedValue, long newValue) {
|
||||
return (long)AA.compareAndExchangeRelease(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetVolatile(int i, long expectedValue, long newValue) {
|
||||
return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetAcquire(int i, long expectedValue, long newValue) {
|
||||
return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetRelease(int i, long expectedValue, long newValue) {
|
||||
return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.function.LongBinaryOperator;
|
||||
import java.util.function.LongUnaryOperator;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
@ -153,8 +154,8 @@ public abstract class AtomicLongFieldUpdater<T> {
|
||||
public abstract void lazySet(T obj, long newValue);
|
||||
|
||||
/**
|
||||
* Gets the current value held in the field of the given object managed
|
||||
* by this updater.
|
||||
* Returns the current value held in the field of the given object
|
||||
* managed by this updater.
|
||||
*
|
||||
* @param obj An object whose field to get
|
||||
* @return the current value
|
||||
@ -366,7 +367,7 @@ public abstract class AtomicLongFieldUpdater<T> {
|
||||
}
|
||||
|
||||
private static final class CASUpdater<T> extends AtomicLongFieldUpdater<T> {
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private final long offset;
|
||||
/**
|
||||
* if field is protected, the subclass constructing updater, else
|
||||
@ -497,7 +498,7 @@ public abstract class AtomicLongFieldUpdater<T> {
|
||||
}
|
||||
|
||||
private static final class LockedUpdater<T> extends AtomicLongFieldUpdater<T> {
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private final long offset;
|
||||
/**
|
||||
* if field is protected, the subclass constructing updater, else
|
||||
|
@ -35,6 +35,9 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
|
||||
/**
|
||||
* An {@code AtomicMarkableReference} maintains an object reference
|
||||
* along with a mark bit, that can be updated atomically.
|
||||
@ -188,20 +191,19 @@ public class AtomicMarkableReference<V> {
|
||||
casPair(current, Pair.of(expectedReference, newMark)));
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long PAIR;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle PAIR;
|
||||
static {
|
||||
try {
|
||||
PAIR = U.objectFieldOffset
|
||||
(AtomicMarkableReference.class.getDeclaredField("pair"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
PAIR = l.findVarHandle(AtomicMarkableReference.class, "pair",
|
||||
Pair.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean casPair(Pair<V> cmp, Pair<V> val) {
|
||||
return U.compareAndSwapObject(this, PAIR, cmp, val);
|
||||
return PAIR.compareAndSet(this, cmp, val);
|
||||
}
|
||||
}
|
||||
|
@ -35,33 +35,32 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* An object reference that may be updated atomically. See the {@link
|
||||
* java.util.concurrent.atomic} package specification for description
|
||||
* of the properties of atomic variables.
|
||||
* An object reference that may be updated atomically. See the {@link
|
||||
* VarHandle} specification for descriptions of the properties of
|
||||
* atomic accesses.
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
* @param <V> The type of object referred to by this reference
|
||||
*/
|
||||
public class AtomicReference<V> implements java.io.Serializable {
|
||||
private static final long serialVersionUID = -1848883965231344442L;
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long VALUE;
|
||||
|
||||
private static final VarHandle VALUE;
|
||||
static {
|
||||
try {
|
||||
VALUE = U.objectFieldOffset
|
||||
(AtomicReference.class.getDeclaredField("value"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
VALUE = l.findVarHandle(AtomicReference.class, "value", Object.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private volatile V value;
|
||||
private volatile Object value;
|
||||
|
||||
/**
|
||||
* Creates a new AtomicReference with the given initial value.
|
||||
@ -79,16 +78,19 @@ public class AtomicReference<V> implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value.
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* @return the current value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final V get() {
|
||||
return value;
|
||||
return (V)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setVolatile}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
*/
|
||||
@ -97,52 +99,53 @@ public class AtomicReference<V> implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 1.6
|
||||
*/
|
||||
public final void lazySet(V newValue) {
|
||||
U.putObjectRelease(this, VALUE, newValue);
|
||||
VALUE.setRelease(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* Atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#compareAndSet}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful. False return indicates that
|
||||
* the actual value was not equal to the expected value.
|
||||
*/
|
||||
public final boolean compareAndSet(V expect, V update) {
|
||||
return U.compareAndSwapObject(this, VALUE, expect, update);
|
||||
public final boolean compareAndSet(V expectedValue, V newValue) {
|
||||
return VALUE.compareAndSet(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
|
||||
*
|
||||
* <p><a href="package-summary.html#weakCompareAndSet">May fail
|
||||
* spuriously and does not provide ordering guarantees</a>, so is
|
||||
* only rarely an appropriate alternative to {@code compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public final boolean weakCompareAndSet(V expect, V update) {
|
||||
return U.compareAndSwapObject(this, VALUE, expect, update);
|
||||
public final boolean weakCompareAndSet(V expectedValue, V newValue) {
|
||||
return VALUE.weakCompareAndSet(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets to the given value and returns the old value.
|
||||
* Atomically sets the value to {@code newValue} and returns the old value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndSet}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @return the previous value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final V getAndSet(V newValue) {
|
||||
return (V)U.getAndSetObject(this, VALUE, newValue);
|
||||
return (V)VALUE.getAndSet(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,12 +159,14 @@ public class AtomicReference<V> implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final V getAndUpdate(UnaryOperator<V> updateFunction) {
|
||||
V prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = updateFunction.apply(prev);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return prev;
|
||||
V prev = get(), next = null;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.apply(prev);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,12 +180,14 @@ public class AtomicReference<V> implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final V updateAndGet(UnaryOperator<V> updateFunction) {
|
||||
V prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = updateFunction.apply(prev);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return next;
|
||||
V prev = get(), next = null;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.apply(prev);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -199,12 +206,14 @@ public class AtomicReference<V> implements java.io.Serializable {
|
||||
*/
|
||||
public final V getAndAccumulate(V x,
|
||||
BinaryOperator<V> accumulatorFunction) {
|
||||
V prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = accumulatorFunction.apply(prev, x);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return prev;
|
||||
V prev = get(), next = null;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.apply(prev, x);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -223,12 +232,14 @@ public class AtomicReference<V> implements java.io.Serializable {
|
||||
*/
|
||||
public final V accumulateAndGet(V x,
|
||||
BinaryOperator<V> accumulatorFunction) {
|
||||
V prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = accumulatorFunction.apply(prev, x);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return next;
|
||||
V prev = get(), next = null;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.apply(prev, x);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -239,4 +250,166 @@ public class AtomicReference<V> implements java.io.Serializable {
|
||||
return String.valueOf(get());
|
||||
}
|
||||
|
||||
// jdk9
|
||||
|
||||
/**
|
||||
* Returns the current value, with memory semantics of reading as
|
||||
* if the variable was declared non-{@code volatile}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final V getPlain() {
|
||||
return (V)VALUE.get(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue}, with memory semantics
|
||||
* of setting as if the variable was declared non-{@code volatile}
|
||||
* and non-{@code final}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setPlain(V newValue) {
|
||||
VALUE.set(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getOpaque}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final V getOpaque() {
|
||||
return (V)VALUE.getOpaque(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setOpaque}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setOpaque(V newValue) {
|
||||
VALUE.setOpaque(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAcquire}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final V getAcquire() {
|
||||
return (V)VALUE.getAcquire(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setRelease(V newValue) {
|
||||
VALUE.setRelease(this, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchange}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final V compareAndExchange(V expectedValue, V newValue) {
|
||||
return (V)VALUE.compareAndExchange(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final V compareAndExchangeAcquire(V expectedValue, V newValue) {
|
||||
return (V)VALUE.compareAndExchangeAcquire(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final V compareAndExchangeRelease(V expectedValue, V newValue) {
|
||||
return (V)VALUE.compareAndExchangeRelease(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetVolatile}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetVolatile(V expectedValue, V newValue) {
|
||||
return VALUE.weakCompareAndSetVolatile(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetAcquire(V expectedValue, V newValue) {
|
||||
return VALUE.weakCompareAndSetAcquire(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetRelease(V expectedValue, V newValue) {
|
||||
return VALUE.weakCompareAndSetRelease(this, expectedValue, newValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,54 +35,28 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* An array of object references in which elements may be updated
|
||||
* atomically. See the {@link java.util.concurrent.atomic} package
|
||||
* specification for description of the properties of atomic
|
||||
* variables.
|
||||
* atomically. See the {@link VarHandle} specification for
|
||||
* descriptions of the properties of atomic accesses.
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
* @param <E> The base class of elements held in this array
|
||||
*/
|
||||
public class AtomicReferenceArray<E> implements java.io.Serializable {
|
||||
private static final long serialVersionUID = -6209656149925076980L;
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long ARRAY;
|
||||
private static final int ABASE;
|
||||
private static final int ASHIFT;
|
||||
private static final VarHandle AA
|
||||
= MethodHandles.arrayElementVarHandle(Object[].class);
|
||||
private final Object[] array; // must have exact type Object[]
|
||||
|
||||
static {
|
||||
try {
|
||||
ARRAY = U.objectFieldOffset
|
||||
(AtomicReferenceArray.class.getDeclaredField("array"));
|
||||
ABASE = U.arrayBaseOffset(Object[].class);
|
||||
int scale = U.arrayIndexScale(Object[].class);
|
||||
if ((scale & (scale - 1)) != 0)
|
||||
throw new Error("array index scale not a power of two");
|
||||
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private long checkedByteOffset(int i) {
|
||||
if (i < 0 || i >= array.length)
|
||||
throw new IndexOutOfBoundsException("index " + i);
|
||||
|
||||
return byteOffset(i);
|
||||
}
|
||||
|
||||
private static long byteOffset(int i) {
|
||||
return ((long) i << ASHIFT) + ABASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new AtomicReferenceArray of the given length, with all
|
||||
* elements initially null.
|
||||
@ -115,44 +89,44 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value at position {@code i}.
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the current value
|
||||
*/
|
||||
public final E get(int i) {
|
||||
return getRaw(checkedByteOffset(i));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private E getRaw(long offset) {
|
||||
return (E) U.getObjectVolatile(array, offset);
|
||||
public final E get(int i) {
|
||||
return (E)AA.getVolatile(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at position {@code i} to the given value.
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
*/
|
||||
public final void set(int i, E newValue) {
|
||||
U.putObjectVolatile(array, checkedByteOffset(i), newValue);
|
||||
AA.setVolatile(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually sets the element at position {@code i} to the given value.
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 1.6
|
||||
*/
|
||||
public final void lazySet(int i, E newValue) {
|
||||
U.putObjectRelease(array, checkedByteOffset(i), newValue);
|
||||
AA.setRelease(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* value and returns the old value.
|
||||
* Atomically sets the element at index {@code i} to {@code
|
||||
* newValue} and returns the old value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
@ -160,42 +134,36 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final E getAndSet(int i, E newValue) {
|
||||
return (E)U.getAndSetObject(array, checkedByteOffset(i), newValue);
|
||||
return (E)AA.getAndSet(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* updated value if the current value {@code ==} the expected value.
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#compareAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful. False return indicates that
|
||||
* the actual value was not equal to the expected value.
|
||||
*/
|
||||
public final boolean compareAndSet(int i, E expect, E update) {
|
||||
return compareAndSetRaw(checkedByteOffset(i), expect, update);
|
||||
}
|
||||
|
||||
private boolean compareAndSetRaw(long offset, E expect, E update) {
|
||||
return U.compareAndSwapObject(array, offset, expect, update);
|
||||
public final boolean compareAndSet(int i, E expectedValue, E newValue) {
|
||||
return AA.compareAndSet(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* updated value if the current value {@code ==} the expected value.
|
||||
*
|
||||
* <p><a href="package-summary.html#weakCompareAndSet">May fail
|
||||
* spuriously and does not provide ordering guarantees</a>, so is
|
||||
* only rarely an appropriate alternative to {@code compareAndSet}.
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public final boolean weakCompareAndSet(int i, E expect, E update) {
|
||||
return compareAndSet(i, expect, update);
|
||||
public final boolean weakCompareAndSet(int i, E expectedValue, E newValue) {
|
||||
return AA.weakCompareAndSet(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -210,13 +178,14 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final E getAndUpdate(int i, UnaryOperator<E> updateFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
E prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = updateFunction.apply(prev);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return prev;
|
||||
E prev = get(i), next = null;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.apply(prev);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -231,23 +200,25 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final E updateAndGet(int i, UnaryOperator<E> updateFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
E prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = updateFunction.apply(prev);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return next;
|
||||
E prev = get(i), next = null;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.apply(prev);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically updates the element at index {@code i} with the
|
||||
* results of applying the given function to the current and
|
||||
* given values, returning the previous value. The function should
|
||||
* be side-effect-free, since it may be re-applied when attempted
|
||||
* results of applying the given function to the current and given
|
||||
* values, returning the previous value. The function should be
|
||||
* side-effect-free, since it may be re-applied when attempted
|
||||
* updates fail due to contention among threads. The function is
|
||||
* applied with the current value at index {@code i} as its first
|
||||
* argument, and the given update as the second argument.
|
||||
* applied with the current value of the element at index {@code i}
|
||||
* as its first argument, and the given update as the second
|
||||
* argument.
|
||||
*
|
||||
* @param i the index
|
||||
* @param x the update value
|
||||
@ -257,23 +228,25 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
|
||||
*/
|
||||
public final E getAndAccumulate(int i, E x,
|
||||
BinaryOperator<E> accumulatorFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
E prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = accumulatorFunction.apply(prev, x);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return prev;
|
||||
E prev = get(i), next = null;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.apply(prev, x);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically updates the element at index {@code i} with the
|
||||
* results of applying the given function to the current and
|
||||
* given values, returning the updated value. The function should
|
||||
* be side-effect-free, since it may be re-applied when attempted
|
||||
* results of applying the given function to the current and given
|
||||
* values, returning the updated value. The function should be
|
||||
* side-effect-free, since it may be re-applied when attempted
|
||||
* updates fail due to contention among threads. The function is
|
||||
* applied with the current value at index {@code i} as its first
|
||||
* argument, and the given update as the second argument.
|
||||
* applied with the current value of the element at index {@code i}
|
||||
* as its first argument, and the given update as the second
|
||||
* argument.
|
||||
*
|
||||
* @param i the index
|
||||
* @param x the update value
|
||||
@ -283,13 +256,14 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
|
||||
*/
|
||||
public final E accumulateAndGet(int i, E x,
|
||||
BinaryOperator<E> accumulatorFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
E prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = accumulatorFunction.apply(prev, x);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return next;
|
||||
E prev = get(i), next = null;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.apply(prev, x);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -304,7 +278,7 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append('[');
|
||||
for (int i = 0; ; i++) {
|
||||
b.append(getRaw(byteOffset(i)));
|
||||
b.append(get(i));
|
||||
if (i == iMax)
|
||||
return b.append(']').toString();
|
||||
b.append(',').append(' ');
|
||||
@ -326,7 +300,199 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
|
||||
throw new java.io.InvalidObjectException("Not array type");
|
||||
if (a.getClass() != Object[].class)
|
||||
a = Arrays.copyOf((Object[])a, Array.getLength(a), Object[].class);
|
||||
U.putObjectVolatile(this, ARRAY, a);
|
||||
Field arrayField = java.security.AccessController.doPrivileged(
|
||||
(java.security.PrivilegedAction<Field>) () -> {
|
||||
try {
|
||||
Field f = AtomicReferenceArray.class
|
||||
.getDeclaredField("array");
|
||||
f.setAccessible(true);
|
||||
return f;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}});
|
||||
try {
|
||||
arrayField.set(this, a);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
// jdk9
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory semantics of reading as if the variable was declared
|
||||
* non-{@code volatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final E getPlain(int i) {
|
||||
return (E)AA.get(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory semantics of setting as if the variable was
|
||||
* declared non-{@code volatile} and non-{@code final}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setPlain(int i, E newValue) {
|
||||
AA.set(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getOpaque}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final E getOpaque(int i) {
|
||||
return (E)AA.getOpaque(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setOpaque}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setOpaque(int i, E newValue) {
|
||||
AA.setOpaque(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final E getAcquire(int i) {
|
||||
return (E)AA.getAcquire(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setRelease(int i, E newValue) {
|
||||
AA.setRelease(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchange}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final E compareAndExchange(int i, E expectedValue, E newValue) {
|
||||
return (E)AA.compareAndExchange(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final E compareAndExchangeAcquire(int i, E expectedValue, E newValue) {
|
||||
return (E)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final E compareAndExchangeRelease(int i, E expectedValue, E newValue) {
|
||||
return (E)AA.compareAndExchangeRelease(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetVolatile(int i, E expectedValue, E newValue) {
|
||||
return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetAcquire(int i, E expectedValue, E newValue) {
|
||||
return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetRelease(int i, E expectedValue, E newValue) {
|
||||
return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.UnaryOperator;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
@ -168,8 +169,8 @@ public abstract class AtomicReferenceFieldUpdater<T,V> {
|
||||
public abstract void lazySet(T obj, V newValue);
|
||||
|
||||
/**
|
||||
* Gets the current value held in the field of the given object managed
|
||||
* by this updater.
|
||||
* Returns the current value held in the field of the given object
|
||||
* managed by this updater.
|
||||
*
|
||||
* @param obj An object whose field to get
|
||||
* @return the current value
|
||||
@ -284,7 +285,7 @@ public abstract class AtomicReferenceFieldUpdater<T,V> {
|
||||
|
||||
private static final class AtomicReferenceFieldUpdaterImpl<T,V>
|
||||
extends AtomicReferenceFieldUpdater<T,V> {
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private final long offset;
|
||||
/**
|
||||
* if field is protected, the subclass constructing updater, else
|
||||
|
@ -35,6 +35,9 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
|
||||
/**
|
||||
* An {@code AtomicStampedReference} maintains an object reference
|
||||
* along with an integer "stamp", that can be updated atomically.
|
||||
@ -188,20 +191,19 @@ public class AtomicStampedReference<V> {
|
||||
casPair(current, Pair.of(expectedReference, newStamp)));
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long PAIR;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle PAIR;
|
||||
static {
|
||||
try {
|
||||
PAIR = U.objectFieldOffset
|
||||
(AtomicStampedReference.class.getDeclaredField("pair"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
PAIR = l.findVarHandle(AtomicStampedReference.class, "pair",
|
||||
Pair.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean casPair(Pair<V> cmp, Pair<V> val) {
|
||||
return U.compareAndSwapObject(this, PAIR, cmp, val);
|
||||
return PAIR.compareAndSet(this, cmp, val);
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ import java.util.function.LongBinaryOperator;
|
||||
* <p>Class {@link LongAdder} provides analogs of the functionality of
|
||||
* this class for the common special case of maintaining counts and
|
||||
* sums. The call {@code new LongAdder()} is equivalent to {@code new
|
||||
* LongAccumulator((x, y) -> x + y, 0L}.
|
||||
* LongAccumulator((x, y) -> x + y, 0L)}.
|
||||
*
|
||||
* <p>This class extends {@link Number}, but does <em>not</em> define
|
||||
* methods such as {@code equals}, {@code hashCode} and {@code
|
||||
|
@ -35,10 +35,13 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.function.DoubleBinaryOperator;
|
||||
import java.util.function.LongBinaryOperator;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
/**
|
||||
* A package-local class holding common representation and mechanics
|
||||
@ -123,22 +126,21 @@ abstract class Striped64 extends Number {
|
||||
volatile long value;
|
||||
Cell(long x) { value = x; }
|
||||
final boolean cas(long cmp, long val) {
|
||||
return U.compareAndSwapLong(this, VALUE, cmp, val);
|
||||
return VALUE.compareAndSet(this, cmp, val);
|
||||
}
|
||||
final void reset() {
|
||||
U.putLongVolatile(this, VALUE, 0L);
|
||||
VALUE.setVolatile(this, 0L);
|
||||
}
|
||||
final void reset(long identity) {
|
||||
U.putLongVolatile(this, VALUE, identity);
|
||||
VALUE.setVolatile(this, identity);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long VALUE;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle VALUE;
|
||||
static {
|
||||
try {
|
||||
VALUE = U.objectFieldOffset
|
||||
(Cell.class.getDeclaredField("value"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
VALUE = l.findVarHandle(Cell.class, "value", long.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -174,14 +176,14 @@ abstract class Striped64 extends Number {
|
||||
* CASes the base field.
|
||||
*/
|
||||
final boolean casBase(long cmp, long val) {
|
||||
return U.compareAndSwapLong(this, BASE, cmp, val);
|
||||
return BASE.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* CASes the cellsBusy field from 0 to 1 to acquire lock.
|
||||
*/
|
||||
final boolean casCellsBusy() {
|
||||
return U.compareAndSwapInt(this, CELLSBUSY, 0, 1);
|
||||
return CELLSBUSY.compareAndSet(this, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -371,18 +373,16 @@ abstract class Striped64 extends Number {
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long BASE;
|
||||
private static final long CELLSBUSY;
|
||||
// Unsafe and VarHandle mechanics
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private static final VarHandle BASE;
|
||||
private static final VarHandle CELLSBUSY;
|
||||
private static final long PROBE;
|
||||
static {
|
||||
try {
|
||||
BASE = U.objectFieldOffset
|
||||
(Striped64.class.getDeclaredField("base"));
|
||||
CELLSBUSY = U.objectFieldOffset
|
||||
(Striped64.class.getDeclaredField("cellsBusy"));
|
||||
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
BASE = l.findVarHandle(Striped64.class, "base", long.class);
|
||||
CELLSBUSY = l.findVarHandle(Striped64.class, "cellsBusy", int.class);
|
||||
PROBE = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("threadLocalRandomProbe"));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
|
@ -35,26 +35,10 @@
|
||||
|
||||
/**
|
||||
* A small toolkit of classes that support lock-free thread-safe
|
||||
* programming on single variables. In essence, the classes in this
|
||||
* package extend the notion of {@code volatile} values, fields, and
|
||||
* array elements to those that also provide an atomic conditional update
|
||||
* operation of the form:
|
||||
*
|
||||
* <pre> {@code boolean compareAndSet(expectedValue, updateValue);}</pre>
|
||||
*
|
||||
* <p>This method (which varies in argument types across different
|
||||
* classes) atomically sets a variable to the {@code updateValue} if it
|
||||
* currently holds the {@code expectedValue}, reporting {@code true} on
|
||||
* success. The classes in this package also contain methods to get and
|
||||
* unconditionally set values, as well as a weaker conditional atomic
|
||||
* update operation {@code weakCompareAndSet} described below.
|
||||
*
|
||||
* <p>The specifications of these methods enable implementations to
|
||||
* employ efficient machine-level atomic instructions that are available
|
||||
* on contemporary processors. However on some platforms, support may
|
||||
* entail some form of internal locking. Thus the methods are not
|
||||
* strictly guaranteed to be non-blocking --
|
||||
* a thread may block transiently before performing the operation.
|
||||
* programming on single variables. Instances of Atomic classes
|
||||
* maintain values that are accessed and updated using methods
|
||||
* otherwise available for fields using associated atomic {@link
|
||||
* java.lang.invoke.VarHandle} operations.
|
||||
*
|
||||
* <p>Instances of classes
|
||||
* {@link java.util.concurrent.atomic.AtomicBoolean},
|
||||
@ -92,45 +76,26 @@
|
||||
* return prev; // return next; for transformAndGet
|
||||
* }}</pre>
|
||||
*
|
||||
* <p>The memory effects for accesses and updates of atomics generally
|
||||
* follow the rules for volatiles, as stated in
|
||||
* <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4">
|
||||
* Chapter 17 of
|
||||
* <cite>The Java™ Language Specification</cite></a>:
|
||||
* <p>These classes are not general purpose replacements for {@code
|
||||
* java.lang.Integer} and related classes. They do <em>not</em>
|
||||
* define methods such as {@code equals}, {@code hashCode} and {@code
|
||||
* compareTo}. Because atomic variables are expected to be mutated,
|
||||
* they are poor choices for hash table keys.
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li>{@code get} has the memory effects of reading a
|
||||
* {@code volatile} variable.
|
||||
*
|
||||
* <li>{@code set} has the memory effects of writing (assigning) a
|
||||
* {@code volatile} variable.
|
||||
*
|
||||
* <li>{@code lazySet} has the memory effects of writing (assigning)
|
||||
* a {@code volatile} variable except that it permits reorderings with
|
||||
* subsequent (but not previous) memory actions that do not themselves
|
||||
* impose reordering constraints with ordinary non-{@code volatile}
|
||||
* writes. Among other usage contexts, {@code lazySet} may apply when
|
||||
* nulling out, for the sake of garbage collection, a reference that is
|
||||
* never accessed again.
|
||||
*
|
||||
* <li>{@code weakCompareAndSet} atomically reads and conditionally
|
||||
* writes a variable but does <em>not</em>
|
||||
* create any happens-before orderings, so provides no guarantees
|
||||
* with respect to previous or subsequent reads and writes of any
|
||||
* variables other than the target of the {@code weakCompareAndSet}.
|
||||
*
|
||||
* <li>{@code compareAndSet}
|
||||
* and all other read-and-update operations such as {@code getAndIncrement}
|
||||
* have the memory effects of both reading and
|
||||
* writing {@code volatile} variables.
|
||||
* </ul>
|
||||
*
|
||||
* <p>In addition to classes representing single values, this package
|
||||
* contains <em>Updater</em> classes that can be used to obtain
|
||||
* {@code compareAndSet} operations on any selected {@code volatile}
|
||||
* field of any selected class.
|
||||
* <p>The
|
||||
* {@link java.util.concurrent.atomic.AtomicIntegerArray},
|
||||
* {@link java.util.concurrent.atomic.AtomicLongArray}, and
|
||||
* {@link java.util.concurrent.atomic.AtomicReferenceArray} classes
|
||||
* further extend atomic operation support to arrays of these types.
|
||||
* These classes are also notable in providing {@code volatile} access
|
||||
* semantics for their array elements.
|
||||
*
|
||||
* <p>In addition to classes representing single values and arrays,
|
||||
* this package contains <em>Updater</em> classes that can be used to
|
||||
* obtain {@code compareAndSet} and related operations on any selected
|
||||
* {@code volatile} field of any selected class. These classes
|
||||
* predate the introduction of {@link
|
||||
* java.lang.invoke.VarHandle}, and are of more limited use.
|
||||
* {@link java.util.concurrent.atomic.AtomicReferenceFieldUpdater},
|
||||
* {@link java.util.concurrent.atomic.AtomicIntegerFieldUpdater}, and
|
||||
* {@link java.util.concurrent.atomic.AtomicLongFieldUpdater} are
|
||||
@ -143,38 +108,6 @@
|
||||
* reflection-based setup, less convenient usage, and weaker
|
||||
* guarantees.
|
||||
*
|
||||
* <p>The
|
||||
* {@link java.util.concurrent.atomic.AtomicIntegerArray},
|
||||
* {@link java.util.concurrent.atomic.AtomicLongArray}, and
|
||||
* {@link java.util.concurrent.atomic.AtomicReferenceArray} classes
|
||||
* further extend atomic operation support to arrays of these types.
|
||||
* These classes are also notable in providing {@code volatile} access
|
||||
* semantics for their array elements, which is not supported for
|
||||
* ordinary arrays.
|
||||
*
|
||||
* <p id="weakCompareAndSet">The atomic classes also support method
|
||||
* {@code weakCompareAndSet}, which has limited applicability. On some
|
||||
* platforms, the weak version may be more efficient than {@code
|
||||
* compareAndSet} in the normal case, but differs in that any given
|
||||
* invocation of the {@code weakCompareAndSet} method may return {@code
|
||||
* false} <em>spuriously</em> (that is, for no apparent reason). A
|
||||
* {@code false} return means only that the operation may be retried if
|
||||
* desired, relying on the guarantee that repeated invocation when the
|
||||
* variable holds {@code expectedValue} and no other thread is also
|
||||
* attempting to set the variable will eventually succeed. (Such
|
||||
* spurious failures may for example be due to memory contention effects
|
||||
* that are unrelated to whether the expected and current values are
|
||||
* equal.) Additionally {@code weakCompareAndSet} does not provide
|
||||
* ordering guarantees that are usually needed for synchronization
|
||||
* control. However, the method may be useful for updating counters and
|
||||
* statistics when such updates are unrelated to the other
|
||||
* happens-before orderings of a program. When a thread sees an update
|
||||
* to an atomic variable caused by a {@code weakCompareAndSet}, it does
|
||||
* not necessarily see updates to any <em>other</em> variables that
|
||||
* occurred before the {@code weakCompareAndSet}. This may be
|
||||
* acceptable when, for example, updating performance statistics, but
|
||||
* rarely otherwise.
|
||||
*
|
||||
* <p>The {@link java.util.concurrent.atomic.AtomicMarkableReference}
|
||||
* class associates a single boolean with a reference. For example, this
|
||||
* bit might be used inside a data structure to mean that the object
|
||||
@ -185,29 +118,6 @@
|
||||
* used for example, to represent version numbers corresponding to
|
||||
* series of updates.
|
||||
*
|
||||
* <p>Atomic classes are designed primarily as building blocks for
|
||||
* implementing non-blocking data structures and related infrastructure
|
||||
* classes. The {@code compareAndSet} method is not a general
|
||||
* replacement for locking. It applies only when critical updates for an
|
||||
* object are confined to a <em>single</em> variable.
|
||||
*
|
||||
* <p>Atomic classes are not general purpose replacements for
|
||||
* {@code java.lang.Integer} and related classes. They do <em>not</em>
|
||||
* define methods such as {@code equals}, {@code hashCode} and
|
||||
* {@code compareTo}. (Because atomic variables are expected to be
|
||||
* mutated, they are poor choices for hash table keys.) Additionally,
|
||||
* classes are provided only for those types that are commonly useful in
|
||||
* intended applications. For example, there is no atomic class for
|
||||
* representing {@code byte}. In those infrequent cases where you would
|
||||
* like to do so, you can use an {@code AtomicInteger} to hold
|
||||
* {@code byte} values, and cast appropriately.
|
||||
*
|
||||
* You can also hold floats using
|
||||
* {@link java.lang.Float#floatToRawIntBits} and
|
||||
* {@link java.lang.Float#intBitsToFloat} conversions, and doubles using
|
||||
* {@link java.lang.Double#doubleToRawLongBits} and
|
||||
* {@link java.lang.Double#longBitsToDouble} conversions.
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
package java.util.concurrent.atomic;
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent.locks;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
@ -113,7 +115,7 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
protected final void setState(long newState) {
|
||||
// Use putLongVolatile instead of ordinary volatile store when
|
||||
// using compareAndSwapLong, for sake of some 32bit systems.
|
||||
U.putLongVolatile(this, STATE, newState);
|
||||
STATE.setVolatile(this, newState);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,7 +130,7 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
* value was not equal to the expected value.
|
||||
*/
|
||||
protected final boolean compareAndSetState(long expect, long update) {
|
||||
return U.compareAndSwapLong(this, STATE, expect, update);
|
||||
return STATE.compareAndSet(this, expect, update);
|
||||
}
|
||||
|
||||
// Queuing utilities
|
||||
@ -149,7 +151,7 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
for (;;) {
|
||||
Node oldTail = tail;
|
||||
if (oldTail != null) {
|
||||
U.putObject(node, Node.PREV, oldTail);
|
||||
node.setPrevRelaxed(oldTail);
|
||||
if (compareAndSetTail(oldTail, node)) {
|
||||
oldTail.next = node;
|
||||
return oldTail;
|
||||
@ -172,7 +174,7 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
for (;;) {
|
||||
Node oldTail = tail;
|
||||
if (oldTail != null) {
|
||||
U.putObject(node, Node.PREV, oldTail);
|
||||
node.setPrevRelaxed(oldTail);
|
||||
if (compareAndSetTail(oldTail, node)) {
|
||||
oldTail.next = node;
|
||||
return node;
|
||||
@ -1810,28 +1812,17 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup to support compareAndSet. We need to natively implement
|
||||
* this here: For the sake of permitting future enhancements, we
|
||||
* cannot explicitly subclass AtomicLong, which would be
|
||||
* efficient and useful otherwise. So, as the lesser of evils, we
|
||||
* natively implement using hotspot intrinsics API. And while we
|
||||
* are at it, we do the same for other CASable fields (which could
|
||||
* otherwise be done with atomic field updaters).
|
||||
*/
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long STATE;
|
||||
private static final long HEAD;
|
||||
private static final long TAIL;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle STATE;
|
||||
private static final VarHandle HEAD;
|
||||
private static final VarHandle TAIL;
|
||||
|
||||
static {
|
||||
try {
|
||||
STATE = U.objectFieldOffset
|
||||
(AbstractQueuedLongSynchronizer.class.getDeclaredField("state"));
|
||||
HEAD = U.objectFieldOffset
|
||||
(AbstractQueuedLongSynchronizer.class.getDeclaredField("head"));
|
||||
TAIL = U.objectFieldOffset
|
||||
(AbstractQueuedLongSynchronizer.class.getDeclaredField("tail"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
STATE = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "state", long.class);
|
||||
HEAD = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "head", Node.class);
|
||||
TAIL = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "tail", Node.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -1846,7 +1837,7 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
*/
|
||||
private final void initializeSyncQueue() {
|
||||
Node h;
|
||||
if (U.compareAndSwapObject(this, HEAD, null, (h = new Node())))
|
||||
if (HEAD.compareAndSet(this, null, (h = new Node())))
|
||||
tail = h;
|
||||
}
|
||||
|
||||
@ -1854,6 +1845,6 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
* CASes tail field.
|
||||
*/
|
||||
private final boolean compareAndSetTail(Node expect, Node update) {
|
||||
return U.compareAndSwapObject(this, TAIL, expect, update);
|
||||
return TAIL.compareAndSet(this, expect, update);
|
||||
}
|
||||
}
|
||||
|
@ -35,11 +35,12 @@
|
||||
|
||||
package java.util.concurrent.locks;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jdk.internal.vm.annotation.ReservedStackAccess;
|
||||
|
||||
/**
|
||||
* Provides a framework for implementing blocking locks and related
|
||||
@ -506,40 +507,41 @@ public abstract class AbstractQueuedSynchronizer
|
||||
/** Constructor used by addWaiter. */
|
||||
Node(Node nextWaiter) {
|
||||
this.nextWaiter = nextWaiter;
|
||||
U.putObject(this, THREAD, Thread.currentThread());
|
||||
THREAD.set(this, Thread.currentThread());
|
||||
}
|
||||
|
||||
/** Constructor used by addConditionWaiter. */
|
||||
Node(int waitStatus) {
|
||||
U.putInt(this, WAITSTATUS, waitStatus);
|
||||
U.putObject(this, THREAD, Thread.currentThread());
|
||||
WAITSTATUS.set(this, waitStatus);
|
||||
THREAD.set(this, Thread.currentThread());
|
||||
}
|
||||
|
||||
/** CASes waitStatus field. */
|
||||
final boolean compareAndSetWaitStatus(int expect, int update) {
|
||||
return U.compareAndSwapInt(this, WAITSTATUS, expect, update);
|
||||
return WAITSTATUS.compareAndSet(this, expect, update);
|
||||
}
|
||||
|
||||
/** CASes next field. */
|
||||
final boolean compareAndSetNext(Node expect, Node update) {
|
||||
return U.compareAndSwapObject(this, NEXT, expect, update);
|
||||
return NEXT.compareAndSet(this, expect, update);
|
||||
}
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long NEXT;
|
||||
static final long PREV;
|
||||
private static final long THREAD;
|
||||
private static final long WAITSTATUS;
|
||||
final void setPrevRelaxed(Node p) {
|
||||
PREV.set(this, p);
|
||||
}
|
||||
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle NEXT;
|
||||
private static final VarHandle PREV;
|
||||
private static final VarHandle THREAD;
|
||||
private static final VarHandle WAITSTATUS;
|
||||
static {
|
||||
try {
|
||||
NEXT = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("next"));
|
||||
PREV = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("prev"));
|
||||
THREAD = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("thread"));
|
||||
WAITSTATUS = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("waitStatus"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
NEXT = l.findVarHandle(Node.class, "next", Node.class);
|
||||
PREV = l.findVarHandle(Node.class, "prev", Node.class);
|
||||
THREAD = l.findVarHandle(Node.class, "thread", Thread.class);
|
||||
WAITSTATUS = l.findVarHandle(Node.class, "waitStatus", int.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -595,7 +597,7 @@ public abstract class AbstractQueuedSynchronizer
|
||||
* value was not equal to the expected value.
|
||||
*/
|
||||
protected final boolean compareAndSetState(int expect, int update) {
|
||||
return U.compareAndSwapInt(this, STATE, expect, update);
|
||||
return STATE.compareAndSet(this, expect, update);
|
||||
}
|
||||
|
||||
// Queuing utilities
|
||||
@ -616,7 +618,7 @@ public abstract class AbstractQueuedSynchronizer
|
||||
for (;;) {
|
||||
Node oldTail = tail;
|
||||
if (oldTail != null) {
|
||||
U.putObject(node, Node.PREV, oldTail);
|
||||
node.setPrevRelaxed(oldTail);
|
||||
if (compareAndSetTail(oldTail, node)) {
|
||||
oldTail.next = node;
|
||||
return oldTail;
|
||||
@ -639,7 +641,7 @@ public abstract class AbstractQueuedSynchronizer
|
||||
for (;;) {
|
||||
Node oldTail = tail;
|
||||
if (oldTail != null) {
|
||||
U.putObject(node, Node.PREV, oldTail);
|
||||
node.setPrevRelaxed(oldTail);
|
||||
if (compareAndSetTail(oldTail, node)) {
|
||||
oldTail.next = node;
|
||||
return node;
|
||||
@ -887,7 +889,6 @@ public abstract class AbstractQueuedSynchronizer
|
||||
* @param arg the acquire argument
|
||||
* @return {@code true} if interrupted while waiting
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
final boolean acquireQueued(final Node node, int arg) {
|
||||
try {
|
||||
boolean interrupted = false;
|
||||
@ -1220,7 +1221,6 @@ public abstract class AbstractQueuedSynchronizer
|
||||
* {@link #tryAcquire} but is otherwise uninterpreted and
|
||||
* can represent anything you like.
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public final void acquire(int arg) {
|
||||
if (!tryAcquire(arg) &&
|
||||
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
|
||||
@ -1284,7 +1284,6 @@ public abstract class AbstractQueuedSynchronizer
|
||||
* can represent anything you like.
|
||||
* @return the value returned from {@link #tryRelease}
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public final boolean release(int arg) {
|
||||
if (tryRelease(arg)) {
|
||||
Node h = head;
|
||||
@ -1365,7 +1364,6 @@ public abstract class AbstractQueuedSynchronizer
|
||||
* and can represent anything you like.
|
||||
* @return the value returned from {@link #tryReleaseShared}
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public final boolean releaseShared(int arg) {
|
||||
if (tryReleaseShared(arg)) {
|
||||
doReleaseShared();
|
||||
@ -2279,28 +2277,17 @@ public abstract class AbstractQueuedSynchronizer
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup to support compareAndSet. We need to natively implement
|
||||
* this here: For the sake of permitting future enhancements, we
|
||||
* cannot explicitly subclass AtomicInteger, which would be
|
||||
* efficient and useful otherwise. So, as the lesser of evils, we
|
||||
* natively implement using hotspot intrinsics API. And while we
|
||||
* are at it, we do the same for other CASable fields (which could
|
||||
* otherwise be done with atomic field updaters).
|
||||
*/
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long STATE;
|
||||
private static final long HEAD;
|
||||
private static final long TAIL;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle STATE;
|
||||
private static final VarHandle HEAD;
|
||||
private static final VarHandle TAIL;
|
||||
|
||||
static {
|
||||
try {
|
||||
STATE = U.objectFieldOffset
|
||||
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
|
||||
HEAD = U.objectFieldOffset
|
||||
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
|
||||
TAIL = U.objectFieldOffset
|
||||
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
STATE = l.findVarHandle(AbstractQueuedSynchronizer.class, "state", int.class);
|
||||
HEAD = l.findVarHandle(AbstractQueuedSynchronizer.class, "head", Node.class);
|
||||
TAIL = l.findVarHandle(AbstractQueuedSynchronizer.class, "tail", Node.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -2315,7 +2302,7 @@ public abstract class AbstractQueuedSynchronizer
|
||||
*/
|
||||
private final void initializeSyncQueue() {
|
||||
Node h;
|
||||
if (U.compareAndSwapObject(this, HEAD, null, (h = new Node())))
|
||||
if (HEAD.compareAndSet(this, null, (h = new Node())))
|
||||
tail = h;
|
||||
}
|
||||
|
||||
@ -2323,6 +2310,6 @@ public abstract class AbstractQueuedSynchronizer
|
||||
* CASes tail field.
|
||||
*/
|
||||
private final boolean compareAndSetTail(Node expect, Node update) {
|
||||
return U.compareAndSwapObject(this, TAIL, expect, update);
|
||||
return TAIL.compareAndSet(this, expect, update);
|
||||
}
|
||||
}
|
||||
|
@ -396,7 +396,6 @@ public interface Condition {
|
||||
* re-acquire the lock associated with this condition. When the
|
||||
* thread returns it is <em>guaranteed</em> to hold this lock.
|
||||
*
|
||||
*
|
||||
* <p>If the current thread:
|
||||
* <ul>
|
||||
* <li>has its interrupted status set on entry to this method; or
|
||||
@ -408,7 +407,6 @@ public interface Condition {
|
||||
* case, whether or not the test for interruption occurs before the lock
|
||||
* is released.
|
||||
*
|
||||
*
|
||||
* <p>The return value indicates whether the deadline has elapsed,
|
||||
* which can be used as follows:
|
||||
* <pre> {@code
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent.locks;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
/**
|
||||
* Basic thread blocking primitives for creating locks and other
|
||||
* synchronization classes.
|
||||
@ -405,16 +407,30 @@ public class LockSupport {
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the thread id for the given thread. We must access
|
||||
* this directly rather than via method Thread.getId() because
|
||||
* getId() is not final, and has been known to be overridden in
|
||||
* ways that do not preserve unique mappings.
|
||||
*/
|
||||
static final long getThreadId(Thread thread) {
|
||||
return U.getLongVolatile(thread, TID);
|
||||
}
|
||||
|
||||
// Hotspot implementation via intrinsics API
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private static final long PARKBLOCKER;
|
||||
private static final long SECONDARY;
|
||||
private static final long TID;
|
||||
static {
|
||||
try {
|
||||
PARKBLOCKER = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("parkBlocker"));
|
||||
SECONDARY = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
|
||||
TID = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("tid"));
|
||||
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -79,7 +79,6 @@ package java.util.concurrent.locks;
|
||||
* and measurement will establish whether the use of a read-write lock is
|
||||
* suitable for your application.
|
||||
*
|
||||
*
|
||||
* <p>Although the basic operation of a read-write lock is straight-forward,
|
||||
* there are many policy decisions that an implementation must make, which
|
||||
* may affect the effectiveness of the read-write lock in a given application.
|
||||
|
@ -118,12 +118,6 @@ public class ReentrantLock implements Lock, java.io.Serializable {
|
||||
abstract static class Sync extends AbstractQueuedSynchronizer {
|
||||
private static final long serialVersionUID = -5179523762034025860L;
|
||||
|
||||
/**
|
||||
* Performs {@link Lock#lock}. The main reason for subclassing
|
||||
* is to allow fast path for nonfair version.
|
||||
*/
|
||||
abstract void lock();
|
||||
|
||||
/**
|
||||
* Performs non-fair tryLock. tryAcquire is implemented in
|
||||
* subclasses, but both need nonfair try for trylock method.
|
||||
@ -201,19 +195,6 @@ public class ReentrantLock implements Lock, java.io.Serializable {
|
||||
*/
|
||||
static final class NonfairSync extends Sync {
|
||||
private static final long serialVersionUID = 7316153563782823691L;
|
||||
|
||||
/**
|
||||
* Performs lock. Try immediate barge, backing up to normal
|
||||
* acquire on failure.
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
final void lock() {
|
||||
if (compareAndSetState(0, 1))
|
||||
setExclusiveOwnerThread(Thread.currentThread());
|
||||
else
|
||||
acquire(1);
|
||||
}
|
||||
|
||||
protected final boolean tryAcquire(int acquires) {
|
||||
return nonfairTryAcquire(acquires);
|
||||
}
|
||||
@ -224,11 +205,6 @@ public class ReentrantLock implements Lock, java.io.Serializable {
|
||||
*/
|
||||
static final class FairSync extends Sync {
|
||||
private static final long serialVersionUID = -3000897897090466540L;
|
||||
|
||||
final void lock() {
|
||||
acquire(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fair version of tryAcquire. Don't grant access unless
|
||||
* recursive call or no waiters or is first.
|
||||
@ -288,7 +264,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
|
||||
* at which time the lock hold count is set to one.
|
||||
*/
|
||||
public void lock() {
|
||||
sync.lock();
|
||||
sync.acquire(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,6 +37,7 @@ package java.util.concurrent.locks;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jdk.internal.vm.annotation.ReservedStackAccess;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ReadWriteLock} supporting similar
|
||||
@ -278,7 +279,7 @@ public class ReentrantReadWriteLock
|
||||
static final class HoldCounter {
|
||||
int count; // initially 0
|
||||
// Use id, not reference, to avoid garbage retention
|
||||
final long tid = getThreadId(Thread.currentThread());
|
||||
final long tid = LockSupport.getThreadId(Thread.currentThread());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -367,7 +368,7 @@ public class ReentrantReadWriteLock
|
||||
* both read and write holds that are all released during a
|
||||
* condition wait and re-established in tryAcquire.
|
||||
*/
|
||||
|
||||
@ReservedStackAccess
|
||||
protected final boolean tryRelease(int releases) {
|
||||
if (!isHeldExclusively())
|
||||
throw new IllegalMonitorStateException();
|
||||
@ -379,6 +380,7 @@ public class ReentrantReadWriteLock
|
||||
return free;
|
||||
}
|
||||
|
||||
@ReservedStackAccess
|
||||
protected final boolean tryAcquire(int acquires) {
|
||||
/*
|
||||
* Walkthrough:
|
||||
@ -411,6 +413,7 @@ public class ReentrantReadWriteLock
|
||||
return true;
|
||||
}
|
||||
|
||||
@ReservedStackAccess
|
||||
protected final boolean tryReleaseShared(int unused) {
|
||||
Thread current = Thread.currentThread();
|
||||
if (firstReader == current) {
|
||||
@ -421,7 +424,8 @@ public class ReentrantReadWriteLock
|
||||
firstReaderHoldCount--;
|
||||
} else {
|
||||
HoldCounter rh = cachedHoldCounter;
|
||||
if (rh == null || rh.tid != getThreadId(current))
|
||||
if (rh == null ||
|
||||
rh.tid != LockSupport.getThreadId(current))
|
||||
rh = readHolds.get();
|
||||
int count = rh.count;
|
||||
if (count <= 1) {
|
||||
@ -447,6 +451,7 @@ public class ReentrantReadWriteLock
|
||||
"attempt to unlock read lock, not locked by current thread");
|
||||
}
|
||||
|
||||
@ReservedStackAccess
|
||||
protected final int tryAcquireShared(int unused) {
|
||||
/*
|
||||
* Walkthrough:
|
||||
@ -479,7 +484,8 @@ public class ReentrantReadWriteLock
|
||||
firstReaderHoldCount++;
|
||||
} else {
|
||||
HoldCounter rh = cachedHoldCounter;
|
||||
if (rh == null || rh.tid != getThreadId(current))
|
||||
if (rh == null ||
|
||||
rh.tid != LockSupport.getThreadId(current))
|
||||
cachedHoldCounter = rh = readHolds.get();
|
||||
else if (rh.count == 0)
|
||||
readHolds.set(rh);
|
||||
@ -516,7 +522,8 @@ public class ReentrantReadWriteLock
|
||||
} else {
|
||||
if (rh == null) {
|
||||
rh = cachedHoldCounter;
|
||||
if (rh == null || rh.tid != getThreadId(current)) {
|
||||
if (rh == null ||
|
||||
rh.tid != LockSupport.getThreadId(current)) {
|
||||
rh = readHolds.get();
|
||||
if (rh.count == 0)
|
||||
readHolds.remove();
|
||||
@ -537,7 +544,8 @@ public class ReentrantReadWriteLock
|
||||
} else {
|
||||
if (rh == null)
|
||||
rh = cachedHoldCounter;
|
||||
if (rh == null || rh.tid != getThreadId(current))
|
||||
if (rh == null ||
|
||||
rh.tid != LockSupport.getThreadId(current))
|
||||
rh = readHolds.get();
|
||||
else if (rh.count == 0)
|
||||
readHolds.set(rh);
|
||||
@ -554,6 +562,7 @@ public class ReentrantReadWriteLock
|
||||
* This is identical in effect to tryAcquire except for lack
|
||||
* of calls to writerShouldBlock.
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
final boolean tryWriteLock() {
|
||||
Thread current = Thread.currentThread();
|
||||
int c = getState();
|
||||
@ -575,6 +584,7 @@ public class ReentrantReadWriteLock
|
||||
* This is identical in effect to tryAcquireShared except for
|
||||
* lack of calls to readerShouldBlock.
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
final boolean tryReadLock() {
|
||||
Thread current = Thread.currentThread();
|
||||
for (;;) {
|
||||
@ -593,7 +603,8 @@ public class ReentrantReadWriteLock
|
||||
firstReaderHoldCount++;
|
||||
} else {
|
||||
HoldCounter rh = cachedHoldCounter;
|
||||
if (rh == null || rh.tid != getThreadId(current))
|
||||
if (rh == null ||
|
||||
rh.tid != LockSupport.getThreadId(current))
|
||||
cachedHoldCounter = rh = readHolds.get();
|
||||
else if (rh.count == 0)
|
||||
readHolds.set(rh);
|
||||
@ -644,7 +655,7 @@ public class ReentrantReadWriteLock
|
||||
return firstReaderHoldCount;
|
||||
|
||||
HoldCounter rh = cachedHoldCounter;
|
||||
if (rh != null && rh.tid == getThreadId(current))
|
||||
if (rh != null && rh.tid == LockSupport.getThreadId(current))
|
||||
return rh.count;
|
||||
|
||||
int count = readHolds.get().count;
|
||||
@ -1490,26 +1501,4 @@ public class ReentrantReadWriteLock
|
||||
"[Write locks = " + w + ", Read locks = " + r + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the thread id for the given thread. We must access
|
||||
* this directly rather than via method Thread.getId() because
|
||||
* getId() is not final, and has been known to be overridden in
|
||||
* ways that do not preserve unique mappings.
|
||||
*/
|
||||
static final long getThreadId(Thread thread) {
|
||||
return U.getLongVolatile(thread, TID);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long TID;
|
||||
static {
|
||||
try {
|
||||
TID = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("tid"));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,7 +35,10 @@
|
||||
|
||||
package java.util.concurrent.locks;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jdk.internal.vm.annotation.ReservedStackAccess;
|
||||
|
||||
/**
|
||||
* A capability-based lock with three modes for controlling read/write
|
||||
@ -108,6 +111,10 @@ import java.util.concurrent.TimeUnit;
|
||||
* into initial unlocked state, so they are not useful for remote
|
||||
* locking.
|
||||
*
|
||||
* <p>Like {@link java.util.concurrent.Semaphore Semaphore}, but unlike most
|
||||
* {@link Lock} implementations, StampedLocks have no notion of ownership.
|
||||
* Locks acquired in one thread can be released or converted in another.
|
||||
*
|
||||
* <p>The scheduling policy of StampedLock does not consistently
|
||||
* prefer readers over writers or vice versa. All "try" methods are
|
||||
* best-effort and do not necessarily conform to any scheduling or
|
||||
@ -126,7 +133,7 @@ import java.util.concurrent.TimeUnit;
|
||||
* in a class that maintains simple two-dimensional points. The sample
|
||||
* code illustrates some try/catch conventions even though they are
|
||||
* not strictly needed here because no exceptions can occur in their
|
||||
* bodies.<br>
|
||||
* bodies.
|
||||
*
|
||||
* <pre> {@code
|
||||
* class Point {
|
||||
@ -234,9 +241,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
* used in the acquire methods to reduce (increasingly expensive)
|
||||
* context switching while also avoiding sustained memory
|
||||
* thrashing among many threads. We limit spins to the head of
|
||||
* queue. A thread spin-waits up to SPINS times (where each
|
||||
* iteration decreases spin count with 50% probability) before
|
||||
* blocking. If, upon wakening it fails to obtain lock, and is
|
||||
* queue. If, upon wakening, a thread fails to obtain lock, and is
|
||||
* still (or becomes) the first waiting thread (which indicates
|
||||
* that some other thread barged and obtained lock), it escalates
|
||||
* spins (up to MAX_HEAD_SPINS) to reduce the likelihood of
|
||||
@ -252,7 +257,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
* to normal volatile reads (of "state"). To force orderings of
|
||||
* reads before a validation and the validation itself in those
|
||||
* cases where this is not already forced, we use
|
||||
* Unsafe.loadFence.
|
||||
* VarHandle.acquireFence.
|
||||
*
|
||||
* The memory layout keeps lock state and queue pointers together
|
||||
* (normally on the same cache line). This usually works well for
|
||||
@ -290,7 +295,20 @@ public class StampedLock implements java.io.Serializable {
|
||||
private static final long ABITS = RBITS | WBIT;
|
||||
private static final long SBITS = ~RBITS; // note overlap with ABITS
|
||||
|
||||
// Initial value for lock state; avoid failure value zero
|
||||
/*
|
||||
* 3 stamp modes can be distinguished by examining (m = stamp & ABITS):
|
||||
* write mode: m == WBIT
|
||||
* optimistic read mode: m == 0L (even when read lock is held)
|
||||
* read mode: m > 0L && m <= RFULL (the stamp is a copy of state, but the
|
||||
* read hold count in the stamp is unused other than to determine mode)
|
||||
*
|
||||
* This differs slightly from the encoding of state:
|
||||
* (state & ABITS) == 0L indicates the lock is currently unlocked.
|
||||
* (state & ABITS) == RBITS is a special transient value
|
||||
* indicating spin-locked to manipulate reader bits overflow.
|
||||
*/
|
||||
|
||||
/** Initial value for lock state; avoids failure value zero. */
|
||||
private static final long ORIGIN = WBIT << 1;
|
||||
|
||||
// Special value from cancelled acquire methods so caller can throw IE
|
||||
@ -341,25 +359,27 @@ public class StampedLock implements java.io.Serializable {
|
||||
* Exclusively acquires the lock, blocking if necessary
|
||||
* until available.
|
||||
*
|
||||
* @return a stamp that can be used to unlock or convert mode
|
||||
* @return a write stamp that can be used to unlock or convert mode
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public long writeLock() {
|
||||
long s, next; // bypass acquireWrite in fully unlocked case only
|
||||
return ((((s = state) & ABITS) == 0L &&
|
||||
U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
|
||||
STATE.compareAndSet(this, s, next = s + WBIT)) ?
|
||||
next : acquireWrite(false, 0L));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclusively acquires the lock if it is immediately available.
|
||||
*
|
||||
* @return a stamp that can be used to unlock or convert mode,
|
||||
* @return a write stamp that can be used to unlock or convert mode,
|
||||
* or zero if the lock is not available
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public long tryWriteLock() {
|
||||
long s, next;
|
||||
return ((((s = state) & ABITS) == 0L &&
|
||||
U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
|
||||
STATE.compareAndSet(this, s, next = s + WBIT)) ?
|
||||
next : 0L);
|
||||
}
|
||||
|
||||
@ -371,7 +391,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
*
|
||||
* @param time the maximum time to wait for the lock
|
||||
* @param unit the time unit of the {@code time} argument
|
||||
* @return a stamp that can be used to unlock or convert mode,
|
||||
* @return a write stamp that can be used to unlock or convert mode,
|
||||
* or zero if the lock is not available
|
||||
* @throws InterruptedException if the current thread is interrupted
|
||||
* before acquiring the lock
|
||||
@ -399,10 +419,11 @@ public class StampedLock implements java.io.Serializable {
|
||||
* Behavior under interruption matches that specified
|
||||
* for method {@link Lock#lockInterruptibly()}.
|
||||
*
|
||||
* @return a stamp that can be used to unlock or convert mode
|
||||
* @return a write stamp that can be used to unlock or convert mode
|
||||
* @throws InterruptedException if the current thread is interrupted
|
||||
* before acquiring the lock
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public long writeLockInterruptibly() throws InterruptedException {
|
||||
long next;
|
||||
if (!Thread.interrupted() &&
|
||||
@ -415,33 +436,34 @@ public class StampedLock implements java.io.Serializable {
|
||||
* Non-exclusively acquires the lock, blocking if necessary
|
||||
* until available.
|
||||
*
|
||||
* @return a stamp that can be used to unlock or convert mode
|
||||
* @return a read stamp that can be used to unlock or convert mode
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public long readLock() {
|
||||
long s = state, next; // bypass acquireRead on common uncontended case
|
||||
return ((whead == wtail && (s & ABITS) < RFULL &&
|
||||
U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
|
||||
STATE.compareAndSet(this, s, next = s + RUNIT)) ?
|
||||
next : acquireRead(false, 0L));
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-exclusively acquires the lock if it is immediately available.
|
||||
*
|
||||
* @return a stamp that can be used to unlock or convert mode,
|
||||
* @return a read stamp that can be used to unlock or convert mode,
|
||||
* or zero if the lock is not available
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public long tryReadLock() {
|
||||
for (;;) {
|
||||
long s, m, next;
|
||||
if ((m = (s = state) & ABITS) == WBIT)
|
||||
return 0L;
|
||||
else if (m < RFULL) {
|
||||
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
|
||||
long s, m, next;
|
||||
while ((m = (s = state) & ABITS) != WBIT) {
|
||||
if (m < RFULL) {
|
||||
if (STATE.compareAndSet(this, s, next = s + RUNIT))
|
||||
return next;
|
||||
}
|
||||
else if ((next = tryIncReaderOverflow(s)) != 0L)
|
||||
return next;
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -452,11 +474,12 @@ public class StampedLock implements java.io.Serializable {
|
||||
*
|
||||
* @param time the maximum time to wait for the lock
|
||||
* @param unit the time unit of the {@code time} argument
|
||||
* @return a stamp that can be used to unlock or convert mode,
|
||||
* @return a read stamp that can be used to unlock or convert mode,
|
||||
* or zero if the lock is not available
|
||||
* @throws InterruptedException if the current thread is interrupted
|
||||
* before acquiring the lock
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public long tryReadLock(long time, TimeUnit unit)
|
||||
throws InterruptedException {
|
||||
long s, m, next, deadline;
|
||||
@ -464,7 +487,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
if (!Thread.interrupted()) {
|
||||
if ((m = (s = state) & ABITS) != WBIT) {
|
||||
if (m < RFULL) {
|
||||
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
|
||||
if (STATE.compareAndSet(this, s, next = s + RUNIT))
|
||||
return next;
|
||||
}
|
||||
else if ((next = tryIncReaderOverflow(s)) != 0L)
|
||||
@ -486,10 +509,11 @@ public class StampedLock implements java.io.Serializable {
|
||||
* Behavior under interruption matches that specified
|
||||
* for method {@link Lock#lockInterruptibly()}.
|
||||
*
|
||||
* @return a stamp that can be used to unlock or convert mode
|
||||
* @return a read stamp that can be used to unlock or convert mode
|
||||
* @throws InterruptedException if the current thread is interrupted
|
||||
* before acquiring the lock
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public long readLockInterruptibly() throws InterruptedException {
|
||||
long next;
|
||||
if (!Thread.interrupted() &&
|
||||
@ -502,7 +526,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
* Returns a stamp that can later be validated, or zero
|
||||
* if exclusively locked.
|
||||
*
|
||||
* @return a stamp, or zero if exclusively locked
|
||||
* @return a valid optimistic read stamp, or zero if exclusively locked
|
||||
*/
|
||||
public long tryOptimisticRead() {
|
||||
long s;
|
||||
@ -522,10 +546,28 @@ public class StampedLock implements java.io.Serializable {
|
||||
* since issuance of the given stamp; else false
|
||||
*/
|
||||
public boolean validate(long stamp) {
|
||||
U.loadFence();
|
||||
VarHandle.acquireFence();
|
||||
return (stamp & SBITS) == (state & SBITS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unlocked state, incrementing the version and
|
||||
* avoiding special failure value 0L.
|
||||
*
|
||||
* @param s a write-locked state (or stamp)
|
||||
*/
|
||||
private static long unlockWriteState(long s) {
|
||||
return ((s += WBIT) == 0L) ? ORIGIN : s;
|
||||
}
|
||||
|
||||
private long unlockWriteInternal(long s) {
|
||||
long next; WNode h;
|
||||
STATE.setVolatile(this, next = unlockWriteState(s));
|
||||
if ((h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the lock state matches the given stamp, releases the
|
||||
* exclusive lock.
|
||||
@ -534,13 +576,11 @@ public class StampedLock implements java.io.Serializable {
|
||||
* @throws IllegalMonitorStateException if the stamp does
|
||||
* not match the current state of this lock
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public void unlockWrite(long stamp) {
|
||||
WNode h;
|
||||
if (state != stamp || (stamp & WBIT) == 0L)
|
||||
throw new IllegalMonitorStateException();
|
||||
U.putLongVolatile(this, STATE, (stamp += WBIT) == 0L ? ORIGIN : stamp);
|
||||
if ((h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
unlockWriteInternal(stamp);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -551,22 +591,23 @@ public class StampedLock implements java.io.Serializable {
|
||||
* @throws IllegalMonitorStateException if the stamp does
|
||||
* not match the current state of this lock
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public void unlockRead(long stamp) {
|
||||
long s, m; WNode h;
|
||||
for (;;) {
|
||||
if (((s = state) & SBITS) != (stamp & SBITS) ||
|
||||
(stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
|
||||
throw new IllegalMonitorStateException();
|
||||
while (((s = state) & SBITS) == (stamp & SBITS)
|
||||
&& (stamp & RBITS) > 0L
|
||||
&& ((m = s & RBITS) > 0L)) {
|
||||
if (m < RFULL) {
|
||||
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
|
||||
if (STATE.compareAndSet(this, s, s - RUNIT)) {
|
||||
if (m == RUNIT && (h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (tryDecReaderOverflow(s) != 0L)
|
||||
break;
|
||||
return;
|
||||
}
|
||||
throw new IllegalMonitorStateException();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -577,32 +618,12 @@ public class StampedLock implements java.io.Serializable {
|
||||
* @throws IllegalMonitorStateException if the stamp does
|
||||
* not match the current state of this lock
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public void unlock(long stamp) {
|
||||
long a = stamp & ABITS, m, s; WNode h;
|
||||
while (((s = state) & SBITS) == (stamp & SBITS)) {
|
||||
if ((m = s & ABITS) == 0L)
|
||||
break;
|
||||
else if (m == WBIT) {
|
||||
if (a != m)
|
||||
break;
|
||||
U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
|
||||
if ((h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
return;
|
||||
}
|
||||
else if (a == 0L || a >= WBIT)
|
||||
break;
|
||||
else if (m < RFULL) {
|
||||
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
|
||||
if (m == RUNIT && (h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (tryDecReaderOverflow(s) != 0L)
|
||||
return;
|
||||
}
|
||||
throw new IllegalMonitorStateException();
|
||||
if ((stamp & WBIT) != 0)
|
||||
unlockWrite(stamp);
|
||||
else
|
||||
unlockRead(stamp);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -623,7 +644,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
if ((m = s & ABITS) == 0L) {
|
||||
if (a != 0L)
|
||||
break;
|
||||
if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
|
||||
if (STATE.compareAndSet(this, s, next = s + WBIT))
|
||||
return next;
|
||||
}
|
||||
else if (m == WBIT) {
|
||||
@ -632,8 +653,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
return stamp;
|
||||
}
|
||||
else if (m == RUNIT && a != 0L) {
|
||||
if (U.compareAndSwapLong(this, STATE, s,
|
||||
next = s - RUNIT + WBIT))
|
||||
if (STATE.compareAndSet(this, s, next = s - RUNIT + WBIT))
|
||||
return next;
|
||||
}
|
||||
else
|
||||
@ -654,30 +674,32 @@ public class StampedLock implements java.io.Serializable {
|
||||
* @return a valid read stamp, or zero on failure
|
||||
*/
|
||||
public long tryConvertToReadLock(long stamp) {
|
||||
long a = stamp & ABITS, m, s, next; WNode h;
|
||||
long a, s, next; WNode h;
|
||||
while (((s = state) & SBITS) == (stamp & SBITS)) {
|
||||
if ((m = s & ABITS) == 0L) {
|
||||
if (a != 0L)
|
||||
if ((a = stamp & ABITS) >= WBIT) {
|
||||
// write stamp
|
||||
if (s != stamp)
|
||||
break;
|
||||
else if (m < RFULL) {
|
||||
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
|
||||
STATE.setVolatile(this, next = unlockWriteState(s) + RUNIT);
|
||||
if ((h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
return next;
|
||||
}
|
||||
else if (a == 0L) {
|
||||
// optimistic read stamp
|
||||
if ((s & ABITS) < RFULL) {
|
||||
if (STATE.compareAndSet(this, s, next = s + RUNIT))
|
||||
return next;
|
||||
}
|
||||
else if ((next = tryIncReaderOverflow(s)) != 0L)
|
||||
return next;
|
||||
}
|
||||
else if (m == WBIT) {
|
||||
if (a != m)
|
||||
else {
|
||||
// already a read stamp
|
||||
if ((s & ABITS) == 0L)
|
||||
break;
|
||||
U.putLongVolatile(this, STATE, next = s + (WBIT + RUNIT));
|
||||
if ((h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
return next;
|
||||
}
|
||||
else if (a != 0L && a < WBIT)
|
||||
return stamp;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
@ -693,29 +715,22 @@ public class StampedLock implements java.io.Serializable {
|
||||
* @return a valid optimistic read stamp, or zero on failure
|
||||
*/
|
||||
public long tryConvertToOptimisticRead(long stamp) {
|
||||
long a = stamp & ABITS, m, s, next; WNode h;
|
||||
U.loadFence();
|
||||
for (;;) {
|
||||
if (((s = state) & SBITS) != (stamp & SBITS))
|
||||
break;
|
||||
if ((m = s & ABITS) == 0L) {
|
||||
if (a != 0L)
|
||||
long a, m, s, next; WNode h;
|
||||
VarHandle.acquireFence();
|
||||
while (((s = state) & SBITS) == (stamp & SBITS)) {
|
||||
if ((a = stamp & ABITS) >= WBIT) {
|
||||
// write stamp
|
||||
if (s != stamp)
|
||||
break;
|
||||
return s;
|
||||
return unlockWriteInternal(s);
|
||||
}
|
||||
else if (m == WBIT) {
|
||||
if (a != m)
|
||||
break;
|
||||
U.putLongVolatile(this, STATE,
|
||||
next = (s += WBIT) == 0L ? ORIGIN : s);
|
||||
if ((h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
return next;
|
||||
}
|
||||
else if (a == 0L || a >= WBIT)
|
||||
else if (a == 0L)
|
||||
// already an optimistic read stamp
|
||||
return stamp;
|
||||
else if ((m = s & ABITS) == 0L) // invalid read stamp
|
||||
break;
|
||||
else if (m < RFULL) {
|
||||
if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) {
|
||||
if (STATE.compareAndSet(this, s, next = s - RUNIT)) {
|
||||
if (m == RUNIT && (h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
return next & SBITS;
|
||||
@ -734,12 +749,11 @@ public class StampedLock implements java.io.Serializable {
|
||||
*
|
||||
* @return {@code true} if the lock was held, else false
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public boolean tryUnlockWrite() {
|
||||
long s; WNode h;
|
||||
long s;
|
||||
if (((s = state) & WBIT) != 0L) {
|
||||
U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
|
||||
if ((h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
unlockWriteInternal(s);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -752,11 +766,12 @@ public class StampedLock implements java.io.Serializable {
|
||||
*
|
||||
* @return {@code true} if the read lock was held, else false
|
||||
*/
|
||||
@ReservedStackAccess
|
||||
public boolean tryUnlockRead() {
|
||||
long s, m; WNode h;
|
||||
while ((m = (s = state) & ABITS) != 0L && m < WBIT) {
|
||||
if (m < RFULL) {
|
||||
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
|
||||
if (STATE.compareAndSet(this, s, s - RUNIT)) {
|
||||
if (m == RUNIT && (h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
return true;
|
||||
@ -832,32 +847,30 @@ public class StampedLock implements java.io.Serializable {
|
||||
* Returns a plain {@link Lock} view of this StampedLock in which
|
||||
* the {@link Lock#lock} method is mapped to {@link #readLock},
|
||||
* and similarly for other methods. The returned Lock does not
|
||||
* support a {@link Condition}; method {@link
|
||||
* Lock#newCondition()} throws {@code
|
||||
* UnsupportedOperationException}.
|
||||
* support a {@link Condition}; method {@link Lock#newCondition()}
|
||||
* throws {@code UnsupportedOperationException}.
|
||||
*
|
||||
* @return the lock
|
||||
*/
|
||||
public Lock asReadLock() {
|
||||
ReadLockView v;
|
||||
return ((v = readLockView) != null ? v :
|
||||
(readLockView = new ReadLockView()));
|
||||
if ((v = readLockView) != null) return v;
|
||||
return readLockView = new ReadLockView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plain {@link Lock} view of this StampedLock in which
|
||||
* the {@link Lock#lock} method is mapped to {@link #writeLock},
|
||||
* and similarly for other methods. The returned Lock does not
|
||||
* support a {@link Condition}; method {@link
|
||||
* Lock#newCondition()} throws {@code
|
||||
* UnsupportedOperationException}.
|
||||
* support a {@link Condition}; method {@link Lock#newCondition()}
|
||||
* throws {@code UnsupportedOperationException}.
|
||||
*
|
||||
* @return the lock
|
||||
*/
|
||||
public Lock asWriteLock() {
|
||||
WriteLockView v;
|
||||
return ((v = writeLockView) != null ? v :
|
||||
(writeLockView = new WriteLockView()));
|
||||
if ((v = writeLockView) != null) return v;
|
||||
return writeLockView = new WriteLockView();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -870,8 +883,8 @@ public class StampedLock implements java.io.Serializable {
|
||||
*/
|
||||
public ReadWriteLock asReadWriteLock() {
|
||||
ReadWriteLockView v;
|
||||
return ((v = readWriteLockView) != null ? v :
|
||||
(readWriteLockView = new ReadWriteLockView()));
|
||||
if ((v = readWriteLockView) != null) return v;
|
||||
return readWriteLockView = new ReadWriteLockView();
|
||||
}
|
||||
|
||||
// view classes
|
||||
@ -917,35 +930,32 @@ public class StampedLock implements java.io.Serializable {
|
||||
// Needed because view-class lock methods throw away stamps.
|
||||
|
||||
final void unstampedUnlockWrite() {
|
||||
WNode h; long s;
|
||||
long s;
|
||||
if (((s = state) & WBIT) == 0L)
|
||||
throw new IllegalMonitorStateException();
|
||||
U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
|
||||
if ((h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
unlockWriteInternal(s);
|
||||
}
|
||||
|
||||
final void unstampedUnlockRead() {
|
||||
for (;;) {
|
||||
long s, m; WNode h;
|
||||
if ((m = (s = state) & ABITS) == 0L || m >= WBIT)
|
||||
throw new IllegalMonitorStateException();
|
||||
else if (m < RFULL) {
|
||||
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
|
||||
long s, m; WNode h;
|
||||
while ((m = (s = state) & RBITS) > 0L) {
|
||||
if (m < RFULL) {
|
||||
if (STATE.compareAndSet(this, s, s - RUNIT)) {
|
||||
if (m == RUNIT && (h = whead) != null && h.status != 0)
|
||||
release(h);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (tryDecReaderOverflow(s) != 0L)
|
||||
break;
|
||||
return;
|
||||
}
|
||||
throw new IllegalMonitorStateException();
|
||||
}
|
||||
|
||||
private void readObject(java.io.ObjectInputStream s)
|
||||
throws java.io.IOException, ClassNotFoundException {
|
||||
s.defaultReadObject();
|
||||
U.putLongVolatile(this, STATE, ORIGIN); // reset to unlocked state
|
||||
STATE.setVolatile(this, ORIGIN); // reset to unlocked state
|
||||
}
|
||||
|
||||
// internals
|
||||
@ -961,15 +971,16 @@ public class StampedLock implements java.io.Serializable {
|
||||
private long tryIncReaderOverflow(long s) {
|
||||
// assert (s & ABITS) >= RFULL;
|
||||
if ((s & ABITS) == RFULL) {
|
||||
if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
|
||||
if (STATE.compareAndSet(this, s, s | RBITS)) {
|
||||
++readerOverflow;
|
||||
U.putLongVolatile(this, STATE, s);
|
||||
STATE.setVolatile(this, s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
else if ((LockSupport.nextSecondarySeed() &
|
||||
OVERFLOW_YIELD_RATE) == 0)
|
||||
else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
|
||||
Thread.yield();
|
||||
else
|
||||
Thread.onSpinWait();
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@ -982,7 +993,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
private long tryDecReaderOverflow(long s) {
|
||||
// assert (s & ABITS) >= RFULL;
|
||||
if ((s & ABITS) == RFULL) {
|
||||
if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
|
||||
if (STATE.compareAndSet(this, s, s | RBITS)) {
|
||||
int r; long next;
|
||||
if ((r = readerOverflow) > 0) {
|
||||
readerOverflow = r - 1;
|
||||
@ -990,13 +1001,14 @@ public class StampedLock implements java.io.Serializable {
|
||||
}
|
||||
else
|
||||
next = s - RUNIT;
|
||||
U.putLongVolatile(this, STATE, next);
|
||||
STATE.setVolatile(this, next);
|
||||
return next;
|
||||
}
|
||||
}
|
||||
else if ((LockSupport.nextSecondarySeed() &
|
||||
OVERFLOW_YIELD_RATE) == 0)
|
||||
else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
|
||||
Thread.yield();
|
||||
else
|
||||
Thread.onSpinWait();
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@ -1010,14 +1022,14 @@ public class StampedLock implements java.io.Serializable {
|
||||
private void release(WNode h) {
|
||||
if (h != null) {
|
||||
WNode q; Thread w;
|
||||
U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
|
||||
WSTATUS.compareAndSet(h, WAITING, 0);
|
||||
if ((q = h.next) == null || q.status == CANCELLED) {
|
||||
for (WNode t = wtail; t != null && t != h; t = t.prev)
|
||||
if (t.status <= 0)
|
||||
q = t;
|
||||
}
|
||||
if (q != null && (w = q.thread) != null)
|
||||
U.unpark(w);
|
||||
LockSupport.unpark(w);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1035,25 +1047,25 @@ public class StampedLock implements java.io.Serializable {
|
||||
for (int spins = -1;;) { // spin while enqueuing
|
||||
long m, s, ns;
|
||||
if ((m = (s = state) & ABITS) == 0L) {
|
||||
if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
|
||||
if (STATE.compareAndSet(this, s, ns = s + WBIT))
|
||||
return ns;
|
||||
}
|
||||
else if (spins < 0)
|
||||
spins = (m == WBIT && wtail == whead) ? SPINS : 0;
|
||||
else if (spins > 0) {
|
||||
if (LockSupport.nextSecondarySeed() >= 0)
|
||||
--spins;
|
||||
--spins;
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
else if ((p = wtail) == null) { // initialize queue
|
||||
WNode hd = new WNode(WMODE, null);
|
||||
if (U.compareAndSwapObject(this, WHEAD, null, hd))
|
||||
if (WHEAD.weakCompareAndSetVolatile(this, null, hd))
|
||||
wtail = hd;
|
||||
}
|
||||
else if (node == null)
|
||||
node = new WNode(WMODE, p);
|
||||
else if (node.prev != p)
|
||||
node.prev = p;
|
||||
else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
|
||||
else if (WTAIL.weakCompareAndSetVolatile(this, p, node)) {
|
||||
p.next = node;
|
||||
break;
|
||||
}
|
||||
@ -1067,11 +1079,10 @@ public class StampedLock implements java.io.Serializable {
|
||||
spins = HEAD_SPINS;
|
||||
else if (spins < MAX_HEAD_SPINS)
|
||||
spins <<= 1;
|
||||
for (int k = spins;;) { // spin at head
|
||||
for (int k = spins; k > 0; --k) { // spin at head
|
||||
long s, ns;
|
||||
if (((s = state) & ABITS) == 0L) {
|
||||
if (U.compareAndSwapLong(this, STATE, s,
|
||||
ns = s + WBIT)) {
|
||||
if (STATE.compareAndSet(this, s, ns = s + WBIT)) {
|
||||
whead = node;
|
||||
node.prev = null;
|
||||
if (wasInterrupted)
|
||||
@ -1079,17 +1090,16 @@ public class StampedLock implements java.io.Serializable {
|
||||
return ns;
|
||||
}
|
||||
}
|
||||
else if (LockSupport.nextSecondarySeed() >= 0 &&
|
||||
--k <= 0)
|
||||
break;
|
||||
else
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
}
|
||||
else if (h != null) { // help release stale waiters
|
||||
WNode c; Thread w;
|
||||
while ((c = h.cowait) != null) {
|
||||
if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
|
||||
if (WCOWAIT.weakCompareAndSetVolatile(h, c, c.cowait) &&
|
||||
(w = c.thread) != null)
|
||||
U.unpark(w);
|
||||
LockSupport.unpark(w);
|
||||
}
|
||||
}
|
||||
if (whead == h) {
|
||||
@ -1098,7 +1108,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
(p = np).next = node; // stale
|
||||
}
|
||||
else if ((ps = p.status) == 0)
|
||||
U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
|
||||
WSTATUS.compareAndSet(p, 0, WAITING);
|
||||
else if (ps == CANCELLED) {
|
||||
if ((pp = p.prev) != null) {
|
||||
node.prev = pp;
|
||||
@ -1112,13 +1122,15 @@ public class StampedLock implements java.io.Serializable {
|
||||
else if ((time = deadline - System.nanoTime()) <= 0L)
|
||||
return cancelWaiter(node, node, false);
|
||||
Thread wt = Thread.currentThread();
|
||||
U.putObject(wt, PARKBLOCKER, this);
|
||||
node.thread = wt;
|
||||
if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
|
||||
whead == h && node.prev == p)
|
||||
U.park(false, time); // emulate LockSupport.park
|
||||
whead == h && node.prev == p) {
|
||||
if (time == 0L)
|
||||
LockSupport.park(this);
|
||||
else
|
||||
LockSupport.parkNanos(this, time);
|
||||
}
|
||||
node.thread = null;
|
||||
U.putObject(wt, PARKBLOCKER, null);
|
||||
if (Thread.interrupted()) {
|
||||
if (interruptible)
|
||||
return cancelWaiter(node, node, true);
|
||||
@ -1146,7 +1158,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
if ((h = whead) == (p = wtail)) {
|
||||
for (long m, s, ns;;) {
|
||||
if ((m = (s = state) & ABITS) < RFULL ?
|
||||
U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
|
||||
STATE.compareAndSet(this, s, ns = s + RUNIT) :
|
||||
(m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
|
||||
if (wasInterrupted)
|
||||
Thread.currentThread().interrupt();
|
||||
@ -1154,8 +1166,8 @@ public class StampedLock implements java.io.Serializable {
|
||||
}
|
||||
else if (m >= WBIT) {
|
||||
if (spins > 0) {
|
||||
if (LockSupport.nextSecondarySeed() >= 0)
|
||||
--spins;
|
||||
--spins;
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
else {
|
||||
if (spins == 0) {
|
||||
@ -1170,7 +1182,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
}
|
||||
if (p == null) { // initialize queue
|
||||
WNode hd = new WNode(WMODE, null);
|
||||
if (U.compareAndSwapObject(this, WHEAD, null, hd))
|
||||
if (WHEAD.weakCompareAndSetVolatile(this, null, hd))
|
||||
wtail = hd;
|
||||
}
|
||||
else if (node == null)
|
||||
@ -1178,27 +1190,25 @@ public class StampedLock implements java.io.Serializable {
|
||||
else if (h == p || p.mode != RMODE) {
|
||||
if (node.prev != p)
|
||||
node.prev = p;
|
||||
else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
|
||||
else if (WTAIL.weakCompareAndSetVolatile(this, p, node)) {
|
||||
p.next = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!U.compareAndSwapObject(p, WCOWAIT,
|
||||
node.cowait = p.cowait, node))
|
||||
else if (!WCOWAIT.compareAndSet(p, node.cowait = p.cowait, node))
|
||||
node.cowait = null;
|
||||
else {
|
||||
for (;;) {
|
||||
WNode pp, c; Thread w;
|
||||
if ((h = whead) != null && (c = h.cowait) != null &&
|
||||
U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
|
||||
WCOWAIT.compareAndSet(h, c, c.cowait) &&
|
||||
(w = c.thread) != null) // help release
|
||||
U.unpark(w);
|
||||
LockSupport.unpark(w);
|
||||
if (h == (pp = p.prev) || h == p || pp == null) {
|
||||
long m, s, ns;
|
||||
do {
|
||||
if ((m = (s = state) & ABITS) < RFULL ?
|
||||
U.compareAndSwapLong(this, STATE, s,
|
||||
ns = s + RUNIT) :
|
||||
STATE.compareAndSet(this, s, ns = s + RUNIT) :
|
||||
(m < WBIT &&
|
||||
(ns = tryIncReaderOverflow(s)) != 0L)) {
|
||||
if (wasInterrupted)
|
||||
@ -1221,13 +1231,15 @@ public class StampedLock implements java.io.Serializable {
|
||||
return cancelWaiter(node, p, false);
|
||||
}
|
||||
Thread wt = Thread.currentThread();
|
||||
U.putObject(wt, PARKBLOCKER, this);
|
||||
node.thread = wt;
|
||||
if ((h != pp || (state & ABITS) == WBIT) &&
|
||||
whead == h && p.prev == pp)
|
||||
U.park(false, time);
|
||||
whead == h && p.prev == pp) {
|
||||
if (time == 0L)
|
||||
LockSupport.park(this);
|
||||
else
|
||||
LockSupport.parkNanos(this, time);
|
||||
}
|
||||
node.thread = null;
|
||||
U.putObject(wt, PARKBLOCKER, null);
|
||||
if (Thread.interrupted()) {
|
||||
if (interruptible)
|
||||
return cancelWaiter(node, p, true);
|
||||
@ -1248,32 +1260,32 @@ public class StampedLock implements java.io.Serializable {
|
||||
for (int k = spins;;) { // spin at head
|
||||
long m, s, ns;
|
||||
if ((m = (s = state) & ABITS) < RFULL ?
|
||||
U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
|
||||
STATE.compareAndSet(this, s, ns = s + RUNIT) :
|
||||
(m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
|
||||
WNode c; Thread w;
|
||||
whead = node;
|
||||
node.prev = null;
|
||||
while ((c = node.cowait) != null) {
|
||||
if (U.compareAndSwapObject(node, WCOWAIT,
|
||||
c, c.cowait) &&
|
||||
if (WCOWAIT.compareAndSet(node, c, c.cowait) &&
|
||||
(w = c.thread) != null)
|
||||
U.unpark(w);
|
||||
LockSupport.unpark(w);
|
||||
}
|
||||
if (wasInterrupted)
|
||||
Thread.currentThread().interrupt();
|
||||
return ns;
|
||||
}
|
||||
else if (m >= WBIT &&
|
||||
LockSupport.nextSecondarySeed() >= 0 && --k <= 0)
|
||||
else if (m >= WBIT && --k <= 0)
|
||||
break;
|
||||
else
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
}
|
||||
else if (h != null) {
|
||||
WNode c; Thread w;
|
||||
while ((c = h.cowait) != null) {
|
||||
if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
|
||||
if (WCOWAIT.compareAndSet(h, c, c.cowait) &&
|
||||
(w = c.thread) != null)
|
||||
U.unpark(w);
|
||||
LockSupport.unpark(w);
|
||||
}
|
||||
}
|
||||
if (whead == h) {
|
||||
@ -1282,7 +1294,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
(p = np).next = node; // stale
|
||||
}
|
||||
else if ((ps = p.status) == 0)
|
||||
U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
|
||||
WSTATUS.compareAndSet(p, 0, WAITING);
|
||||
else if (ps == CANCELLED) {
|
||||
if ((pp = p.prev) != null) {
|
||||
node.prev = pp;
|
||||
@ -1296,14 +1308,16 @@ public class StampedLock implements java.io.Serializable {
|
||||
else if ((time = deadline - System.nanoTime()) <= 0L)
|
||||
return cancelWaiter(node, node, false);
|
||||
Thread wt = Thread.currentThread();
|
||||
U.putObject(wt, PARKBLOCKER, this);
|
||||
node.thread = wt;
|
||||
if (p.status < 0 &&
|
||||
(p != h || (state & ABITS) == WBIT) &&
|
||||
whead == h && node.prev == p)
|
||||
U.park(false, time);
|
||||
whead == h && node.prev == p) {
|
||||
if (time == 0L)
|
||||
LockSupport.park(this);
|
||||
else
|
||||
LockSupport.parkNanos(this, time);
|
||||
}
|
||||
node.thread = null;
|
||||
U.putObject(wt, PARKBLOCKER, null);
|
||||
if (Thread.interrupted()) {
|
||||
if (interruptible)
|
||||
return cancelWaiter(node, node, true);
|
||||
@ -1325,7 +1339,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
* AbstractQueuedSynchronizer (see its detailed explanation in AQS
|
||||
* internal documentation).
|
||||
*
|
||||
* @param node if nonnull, the waiter
|
||||
* @param node if non-null, the waiter
|
||||
* @param group either node or the group node is cowaiting with
|
||||
* @param interrupted if already interrupted
|
||||
* @return INTERRUPTED if interrupted or Thread.interrupted, else zero
|
||||
@ -1337,7 +1351,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
// unsplice cancelled nodes from group
|
||||
for (WNode p = group, q; (q = p.cowait) != null;) {
|
||||
if (q.status == CANCELLED) {
|
||||
U.compareAndSwapObject(p, WCOWAIT, q, q.cowait);
|
||||
WCOWAIT.compareAndSet(p, q, q.cowait);
|
||||
p = group; // restart
|
||||
}
|
||||
else
|
||||
@ -1346,7 +1360,7 @@ public class StampedLock implements java.io.Serializable {
|
||||
if (group == node) {
|
||||
for (WNode r = group.cowait; r != null; r = r.cowait) {
|
||||
if ((w = r.thread) != null)
|
||||
U.unpark(w); // wake up uncancelled co-waiters
|
||||
LockSupport.unpark(w); // wake up uncancelled co-waiters
|
||||
}
|
||||
for (WNode pred = node.prev; pred != null; ) { // unsplice
|
||||
WNode succ, pp; // find valid successor
|
||||
@ -1357,23 +1371,23 @@ public class StampedLock implements java.io.Serializable {
|
||||
if (t.status != CANCELLED)
|
||||
q = t; // don't link if succ cancelled
|
||||
if (succ == q || // ensure accurate successor
|
||||
U.compareAndSwapObject(node, WNEXT,
|
||||
succ, succ = q)) {
|
||||
WNEXT.compareAndSet(node, succ, succ = q)) {
|
||||
if (succ == null && node == wtail)
|
||||
U.compareAndSwapObject(this, WTAIL, node, pred);
|
||||
WTAIL.compareAndSet(this, node, pred);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pred.next == node) // unsplice pred link
|
||||
U.compareAndSwapObject(pred, WNEXT, node, succ);
|
||||
WNEXT.compareAndSet(pred, node, succ);
|
||||
if (succ != null && (w = succ.thread) != null) {
|
||||
// wake up succ to observe new pred
|
||||
succ.thread = null;
|
||||
U.unpark(w); // wake up succ to observe new pred
|
||||
LockSupport.unpark(w);
|
||||
}
|
||||
if (pred.status != CANCELLED || (pp = pred.prev) == null)
|
||||
break;
|
||||
node.prev = pp; // repeat if new pred wrong/cancelled
|
||||
U.compareAndSwapObject(pp, WNEXT, pred, succ);
|
||||
WNEXT.compareAndSet(pp, pred, succ);
|
||||
pred = pp;
|
||||
}
|
||||
}
|
||||
@ -1397,34 +1411,22 @@ public class StampedLock implements java.io.Serializable {
|
||||
return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long STATE;
|
||||
private static final long WHEAD;
|
||||
private static final long WTAIL;
|
||||
private static final long WNEXT;
|
||||
private static final long WSTATUS;
|
||||
private static final long WCOWAIT;
|
||||
private static final long PARKBLOCKER;
|
||||
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle STATE;
|
||||
private static final VarHandle WHEAD;
|
||||
private static final VarHandle WTAIL;
|
||||
private static final VarHandle WNEXT;
|
||||
private static final VarHandle WSTATUS;
|
||||
private static final VarHandle WCOWAIT;
|
||||
static {
|
||||
try {
|
||||
STATE = U.objectFieldOffset
|
||||
(StampedLock.class.getDeclaredField("state"));
|
||||
WHEAD = U.objectFieldOffset
|
||||
(StampedLock.class.getDeclaredField("whead"));
|
||||
WTAIL = U.objectFieldOffset
|
||||
(StampedLock.class.getDeclaredField("wtail"));
|
||||
|
||||
WSTATUS = U.objectFieldOffset
|
||||
(WNode.class.getDeclaredField("status"));
|
||||
WNEXT = U.objectFieldOffset
|
||||
(WNode.class.getDeclaredField("next"));
|
||||
WCOWAIT = U.objectFieldOffset
|
||||
(WNode.class.getDeclaredField("cowait"));
|
||||
|
||||
PARKBLOCKER = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("parkBlocker"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
STATE = l.findVarHandle(StampedLock.class, "state", long.class);
|
||||
WHEAD = l.findVarHandle(StampedLock.class, "whead", WNode.class);
|
||||
WTAIL = l.findVarHandle(StampedLock.class, "wtail", WNode.class);
|
||||
WSTATUS = l.findVarHandle(WNode.class, "status", int.class);
|
||||
WNEXT = l.findVarHandle(WNode.class, "next", WNode.class);
|
||||
WCOWAIT = l.findVarHandle(WNode.class, "cowait", WNode.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -262,7 +262,6 @@
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* The methods of all classes in {@code java.util.concurrent} and its
|
||||
* subpackages extend these guarantees to higher-level
|
||||
* synchronization. In particular:
|
||||
|
@ -20,7 +20,9 @@
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package java.net.http;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -20,6 +20,7 @@
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package java.net.http;
|
||||
|
@ -20,7 +20,9 @@
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package java.net.http;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -20,7 +20,9 @@
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package java.net.http;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
@ -20,7 +20,9 @@
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package java.net.http;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -20,6 +20,7 @@
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package java.net.http;
|
||||
|
@ -20,6 +20,7 @@
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package java.net.http;
|
||||
|
@ -20,6 +20,7 @@
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package java.net.http;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, 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
|
||||
@ -20,7 +20,9 @@
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package java.net.http;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -20,7 +20,9 @@
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package java.net.http;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
@ -20,6 +20,7 @@
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package java.net.http;
|
||||
|
@ -20,6 +20,7 @@
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package java.net.http;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user