Recycler.WeakOrderQueue drop Object hasBeenRecycled (#11402)

Motivation:

WeakOrderQueue would drop object that has been recycled, even when it has space for it.
WeakOrderQueue#add should check DefaultHandler.hasBeenRecycler field  first

Modifications:

WeakOrderQueue test the DefaultHandler.hasBeenRecycler first

Result:

WeakOrderQueue would not drop object that has been recycled when there is space


Co-authored-by: Norman Maurer <norman_maurer@apple.com>
Co-authored-by: Trustin Lee <t@motd.kr>
This commit is contained in:
ping 2021-06-23 17:32:44 +08:00 committed by GitHub
parent 956e1da2ad
commit 98a3a0c0cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 5 deletions

View File

@ -399,12 +399,14 @@ public abstract class Recycler<T> {
// While we also enforce the recycling ratio when we transfer objects from the WeakOrderQueue to the Stack // While we also enforce the recycling ratio when we transfer objects from the WeakOrderQueue to the Stack
// we better should enforce it as well early. Missing to do so may let the WeakOrderQueue grow very fast // we better should enforce it as well early. Missing to do so may let the WeakOrderQueue grow very fast
// without control // without control
if (!handle.hasBeenRecycled) {
if (handleRecycleCount < interval) { if (handleRecycleCount < interval) {
handleRecycleCount++; handleRecycleCount++;
// Drop the item to prevent recycling to aggressive. // Drop the item to prevent from recycling too aggressively.
return; return;
} }
handleRecycleCount = 0; handleRecycleCount = 0;
}
Link tail = this.tail; Link tail = this.tail;
int writeIndex; int writeIndex;

View File

@ -21,6 +21,8 @@ import org.junit.jupiter.api.function.Executable;
import java.util.Random; import java.util.Random;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -345,6 +347,43 @@ public class RecyclerTest {
assertNotSame(recycler.get(), o2); assertNotSame(recycler.get(), o2);
} }
@Test
public void testRecycleAtTwoThreadsMulti() throws Exception {
final Recycler<HandledObject> recycler = newRecycler(256);
final HandledObject o = recycler.get();
ExecutorService single = Executors.newSingleThreadExecutor();
final CountDownLatch latch1 = new CountDownLatch(1);
single.execute(new Runnable() {
@Override
public void run() {
o.recycle();
latch1.countDown();
}
});
assertTrue(latch1.await(100, TimeUnit.MILLISECONDS));
final HandledObject o2 = recycler.get();
// Always recycler the first object, that is Ok
assertSame(o2, o);
final CountDownLatch latch2 = new CountDownLatch(1);
single.execute(new Runnable() {
@Override
public void run() {
//The object should be recycled
o2.recycle();
latch2.countDown();
}
});
assertTrue(latch2.await(100, TimeUnit.MILLISECONDS));
// It should be the same object, right?
final HandledObject o3 = recycler.get();
assertSame(o3, o);
single.shutdown();
}
@Test @Test
public void testMaxCapacityWithRecycleAtDifferentThread() throws Exception { public void testMaxCapacityWithRecycleAtDifferentThread() throws Exception {
final int maxCapacity = 4; // Choose the number smaller than WeakOrderQueue.LINK_CAPACITY final int maxCapacity = 4; // Choose the number smaller than WeakOrderQueue.LINK_CAPACITY