146 lines
4.2 KiB
Groovy
146 lines
4.2 KiB
Groovy
/*
|
|
* Copyright The OpenTelemetry Authors
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
package io.opentelemetry.auto.tooling
|
|
|
|
import io.opentelemetry.auto.util.gc.GCUtils
|
|
import io.opentelemetry.auto.util.test.AgentSpecification
|
|
import io.opentelemetry.instrumentation.auto.api.WeakMap
|
|
import spock.lang.Shared
|
|
|
|
import java.lang.ref.WeakReference
|
|
import java.util.concurrent.TimeUnit
|
|
|
|
class WeakConcurrentSupplierTest extends AgentSpecification {
|
|
@Shared
|
|
def weakConcurrentSupplier = new WeakMapSuppliers.WeakConcurrent()
|
|
@Shared
|
|
def weakInlineSupplier = new WeakMapSuppliers.WeakConcurrent.Inline()
|
|
@Shared
|
|
def guavaSupplier = new WeakMapSuppliers.Guava()
|
|
|
|
def "Calling newWeakMap on #name creates independent maps"() {
|
|
setup:
|
|
WeakMap.Provider.provider.set(supplier)
|
|
def key = new Object()
|
|
def map1 = WeakMap.Provider.newWeakMap()
|
|
def map2 = WeakMap.Provider.newWeakMap()
|
|
|
|
when:
|
|
map1.put(key, "value1")
|
|
map2.put(key, "value2")
|
|
|
|
then:
|
|
map1.get(key) == "value1"
|
|
map2.get(key) == "value2"
|
|
|
|
where:
|
|
name | supplier
|
|
"WeakConcurrent" | weakConcurrentSupplier
|
|
"WeakInline" | weakInlineSupplier
|
|
"Guava" | guavaSupplier
|
|
}
|
|
|
|
def "Unreferenced supplier gets cleaned up on #name"() {
|
|
setup:
|
|
// Note: we use 'double supplier' here because Groovy keeps reference to test data preventing it from being GCed
|
|
def supplier = supplierSupplier()
|
|
def ref = new WeakReference(supplier)
|
|
|
|
when:
|
|
def supplierRef = new WeakReference(supplier)
|
|
supplier = null
|
|
GCUtils.awaitGC(supplierRef)
|
|
|
|
then:
|
|
ref.get() == null
|
|
|
|
where:
|
|
name | supplierSupplier
|
|
"WeakConcurrent" | { -> new WeakMapSuppliers.WeakConcurrent() }
|
|
"WeakInline" | { -> new WeakMapSuppliers.WeakConcurrent.Inline() }
|
|
"Guava" | { -> new WeakMapSuppliers.Guava() }
|
|
}
|
|
|
|
def "Unreferenced map gets cleaned up on #name"() {
|
|
setup:
|
|
WeakMap.Provider.provider.set(supplier)
|
|
def map = WeakMap.Provider.newWeakMap()
|
|
def ref = new WeakReference(map)
|
|
|
|
when:
|
|
def mapRef = new WeakReference(map)
|
|
map = null
|
|
GCUtils.awaitGC(mapRef)
|
|
|
|
then:
|
|
ref.get() == null
|
|
|
|
where:
|
|
name | supplier
|
|
"WeakConcurrent" | weakConcurrentSupplier
|
|
"WeakInline" | weakInlineSupplier
|
|
"Guava" | guavaSupplier
|
|
}
|
|
|
|
def "Unreferenced keys get cleaned up on #name"() {
|
|
setup:
|
|
def key = new Object()
|
|
map.put(key, "value")
|
|
GCUtils.awaitGC()
|
|
|
|
expect:
|
|
map.size() == 1
|
|
|
|
when:
|
|
def keyRef = new WeakReference(key)
|
|
key = null
|
|
GCUtils.awaitGC(keyRef)
|
|
|
|
if (name == "WeakConcurrent") {
|
|
// Sleep enough time for cleanup thread to get scheduled.
|
|
// But on a very slow box (or high load) scheduling may not be exactly predictable
|
|
// so we try a few times.
|
|
int count = 0
|
|
while (map.size() != 0 && count < 10) {
|
|
Thread.sleep(TimeUnit.SECONDS.toMillis(WeakMapSuppliers.WeakConcurrent.CLEAN_FREQUENCY_SECONDS))
|
|
count++
|
|
}
|
|
}
|
|
|
|
// Hit map a few times to trigger unreferenced entries cleanup.
|
|
// Exact number of times that we need to hit map is implementation dependent.
|
|
// For Guava it is specified in
|
|
// com.google.common.collect.MapMakerInternalMap.DRAIN_THRESHOLD = 0x3F
|
|
if (name == "Guava" || name == "WeakInline") {
|
|
for (int i = 0; i <= 0x3F; i++) {
|
|
map.get("test")
|
|
}
|
|
}
|
|
|
|
then:
|
|
map.size() == 0
|
|
|
|
where:
|
|
name | map
|
|
"WeakConcurrent" | weakConcurrentSupplier.get()
|
|
"WeakInline" | weakInlineSupplier.get()
|
|
// Guava's cleanup process depends on concurrency level,
|
|
// and in order to be able to test it we need to set concurrency to 1
|
|
"Guava" | guavaSupplier.get(1)
|
|
}
|
|
}
|