/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "lambda/box_table.h" #include "base/mutex.h" #include "common_throws.h" #include "gc_root-inl.h" #include "mirror/method.h" #include "mirror/object-inl.h" #include "thread.h" #include namespace art { namespace lambda { BoxTable::BoxTable() : allow_new_weaks_(true), new_weaks_condition_("lambda box table allowed weaks", *Locks::lambda_table_lock_) {} mirror::Object* BoxTable::BoxLambda(const ClosureType& closure) { Thread* self = Thread::Current(); { // TODO: Switch to ReaderMutexLock if ConditionVariable ever supports RW Mutexes /*Reader*/MutexLock mu(self, *Locks::lambda_table_lock_); BlockUntilWeaksAllowed(); // Attempt to look up this object, it's possible it was already boxed previously. // If this is the case we *must* return the same object as before to maintain // referential equality. // // In managed code: // Functional f = () -> 5; // vF = create-lambda // Object a = f; // vA = box-lambda vA // Object b = f; // vB = box-lambda vB // assert(a == f) ValueType value = FindBoxedLambda(closure); if (!value.IsNull()) { return value.Read(); } // Otherwise we need to box ourselves and insert it into the hash map } // Release the lambda table lock here, so that thread suspension is allowed. // Convert the ArtMethod into a java.lang.reflect.Method which will serve // as the temporary 'boxed' version of the lambda. This is good enough // to check all the basic object identities that a boxed lambda must retain. // TODO: Boxing an innate lambda (i.e. made with create-lambda) should make a proxy class // TODO: Boxing a learned lambda (i.e. made with unbox-lambda) should return the original object mirror::Method* method_as_object = mirror::Method::CreateFromArtMethod(self, closure); // There are no thread suspension points after this, so we don't need to put it into a handle. if (UNLIKELY(method_as_object == nullptr)) { // Most likely an OOM has occurred. CHECK(self->IsExceptionPending()); return nullptr; } // The method has been successfully boxed into an object, now insert it into the hash map. { MutexLock mu(self, *Locks::lambda_table_lock_); BlockUntilWeaksAllowed(); // Lookup the object again, it's possible another thread already boxed it while // we were allocating the object before. ValueType value = FindBoxedLambda(closure); if (UNLIKELY(!value.IsNull())) { // Let the GC clean up method_as_object at a later time. return value.Read(); } // Otherwise we should insert it into the hash map in this thread. map_.Insert(std::make_pair(closure, ValueType(method_as_object))); } return method_as_object; } bool BoxTable::UnboxLambda(mirror::Object* object, ClosureType* out_closure) { DCHECK(object != nullptr); *out_closure = nullptr; // Note that we do not need to access lambda_table_lock_ here // since we don't need to look at the map. mirror::Object* boxed_closure_object = object; // Raise ClassCastException if object is not instanceof java.lang.reflect.Method if (UNLIKELY(!boxed_closure_object->InstanceOf(mirror::Method::StaticClass()))) { ThrowClassCastException(mirror::Method::StaticClass(), boxed_closure_object->GetClass()); return false; } // TODO(iam): We must check that the closure object extends/implements the type // specified in [type id]. This is not currently implemented since it's always a Method. // If we got this far, the inputs are valid. // Write out the java.lang.reflect.Method's embedded ArtMethod* into the vreg target. mirror::AbstractMethod* boxed_closure_as_method = down_cast(boxed_closure_object); ArtMethod* unboxed_closure = boxed_closure_as_method->GetArtMethod(); DCHECK(unboxed_closure != nullptr); *out_closure = unboxed_closure; return true; } BoxTable::ValueType BoxTable::FindBoxedLambda(const ClosureType& closure) const { auto map_iterator = map_.Find(closure); if (map_iterator != map_.end()) { const std::pair& key_value_pair = *map_iterator; const ValueType& value = key_value_pair.second; DCHECK(!value.IsNull()); // Never store null boxes. return value; } return ValueType(nullptr); } void BoxTable::BlockUntilWeaksAllowed() { Thread* self = Thread::Current(); while (UNLIKELY((!kUseReadBarrier && !allow_new_weaks_) || (kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) { new_weaks_condition_.WaitHoldingLocks(self); // wait while holding mutator lock } } void BoxTable::SweepWeakBoxedLambdas(IsMarkedVisitor* visitor) { DCHECK(visitor != nullptr); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::lambda_table_lock_); /* * Visit every weak root in our lambda box table. * Remove unmarked objects, update marked objects to new address. */ std::vector remove_list; for (auto map_iterator = map_.begin(); map_iterator != map_.end(); ) { std::pair& key_value_pair = *map_iterator; const ValueType& old_value = key_value_pair.second; // This does not need a read barrier because this is called by GC. mirror::Object* old_value_raw = old_value.Read(); mirror::Object* new_value = visitor->IsMarked(old_value_raw); if (new_value == nullptr) { const ClosureType& closure = key_value_pair.first; // The object has been swept away. // Delete the entry from the map. map_iterator = map_.Erase(map_.Find(closure)); } else { // The object has been moved. // Update the map. key_value_pair.second = ValueType(new_value); ++map_iterator; } } // Occasionally shrink the map to avoid growing very large. if (map_.CalculateLoadFactor() < kMinimumLoadFactor) { map_.ShrinkToMaximumLoad(); } } void BoxTable::DisallowNewWeakBoxedLambdas() { CHECK(!kUseReadBarrier); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::lambda_table_lock_); allow_new_weaks_ = false; } void BoxTable::AllowNewWeakBoxedLambdas() { CHECK(!kUseReadBarrier); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::lambda_table_lock_); allow_new_weaks_ = true; new_weaks_condition_.Broadcast(self); } void BoxTable::BroadcastForNewWeakBoxedLambdas() { CHECK(kUseReadBarrier); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::lambda_table_lock_); new_weaks_condition_.Broadcast(self); } bool BoxTable::EqualsFn::operator()(const ClosureType& lhs, const ClosureType& rhs) const { // Nothing needs this right now, but leave this assertion for later when // we need to look at the references inside of the closure. if (kIsDebugBuild) { Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); } // TODO: Need rework to use read barriers once closures have references inside of them that can // move. Until then, it's safe to just compare the data inside of it directly. return lhs == rhs; } } // namespace lambda } // namespace art