Skip to content

Commit

Permalink
Remove Unsafe-based implementation from ExecutionListBenchmark.
Browse files Browse the repository at this point in the history
It's nice to have, but it's not worth going to any trouble for in the face of the ongoing work to remove the `Unsafe` APIs.

RELNOTES=n/a
PiperOrigin-RevId: 714178045
  • Loading branch information
cpovirk authored and Google Java Core Libraries committed Jan 10, 2025
1 parent b3bb29a commit 80aab00
Show file tree
Hide file tree
Showing 2 changed files with 0 additions and 302 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.AbstractFutureBenchmarks.OldAbstractFuture;
import com.google.errorprone.annotations.concurrent.GuardedBy;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
Expand All @@ -44,7 +40,6 @@
import java.util.logging.Logger;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import sun.misc.Unsafe;

/** Benchmarks for {@link ExecutionList}. */
@VmOptions({"-Xms8g", "-Xmx8g"})
Expand Down Expand Up @@ -85,29 +80,6 @@ public Object getImpl() {
};
}
},
NEW_WITH_CAS {
@Override
ExecutionListWrapper newExecutionList() {
return new ExecutionListWrapper() {
final ExecutionListUsingCompareAndSwap list = new ExecutionListUsingCompareAndSwap();

@Override
public void add(Runnable runnable, Executor executor) {
list.add(runnable, executor);
}

@Override
public void execute() {
list.execute();
}

@Override
public Object getImpl() {
return list;
}
};
}
},
NEW_WITH_QUEUE {
@Override
ExecutionListWrapper newExecutionList() {
Expand Down Expand Up @@ -578,127 +550,4 @@ private static final class RunnableExecutorPair {
}
}
}

// A version of the list that uses compare and swap to manage the stack without locks.
@SuppressWarnings({"SunApi", "removal"}) // b/345822163
private static final class ExecutionListUsingCompareAndSwap {
static final Logger log = Logger.getLogger(ExecutionListUsingCompareAndSwap.class.getName());

private static final Unsafe UNSAFE;
private static final long HEAD_OFFSET;

/**
* A special instance of {@link RunnableExecutorPair} that is used as a sentinel value for the
* bottom of the stack.
*/
private static final RunnableExecutorPair NULL_PAIR = new RunnableExecutorPair(null, null);

static {
try {
UNSAFE = getUnsafe();
HEAD_OFFSET =
UNSAFE.objectFieldOffset(
ExecutionListUsingCompareAndSwap.class.getDeclaredField("head"));
} catch (Exception ex) {
throw new Error(ex);
}
}

/** TODO(lukes): This was copied verbatim from Striped64.java... standardize this? */
private static Unsafe getUnsafe() {
try {
return Unsafe.getUnsafe();
} catch (SecurityException tryReflectionInstead) {
}
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<Unsafe>() {
@Override
public Unsafe run() throws Exception {
Class<Unsafe> k = Unsafe.class;
for (Field f : k.getDeclaredFields()) {
f.setAccessible(true);
Object x = f.get(null);
if (k.isInstance(x)) return k.cast(x);
}
throw new NoSuchFieldError("the Unsafe");
}
});
} catch (PrivilegedActionException e) {
throw new RuntimeException("Could not initialize intrinsics", e.getCause());
}
}

private volatile RunnableExecutorPair head = NULL_PAIR;

public void add(Runnable runnable, Executor executor) {
Preconditions.checkNotNull(runnable, "Runnable was null.");
Preconditions.checkNotNull(executor, "Executor was null.");

RunnableExecutorPair newHead = new RunnableExecutorPair(runnable, executor);
RunnableExecutorPair oldHead;
do {
oldHead = head;
if (oldHead == null) {
// If runnables == null then execute() has been called so we should just execute our
// listener immediately.
newHead.execute();
return;
}
// Try to make newHead the new head of the stack at runnables.
newHead.next = oldHead;
} while (!UNSAFE.compareAndSwapObject(this, HEAD_OFFSET, oldHead, newHead));
}

public void execute() {
RunnableExecutorPair stack;
do {
stack = head;
if (stack == null) {
// If head == null then execute() has been called so we should just return
return;
}
// try to swap null into head.
} while (!UNSAFE.compareAndSwapObject(this, HEAD_OFFSET, stack, null));

RunnableExecutorPair reversedStack = null;
while (stack != NULL_PAIR) {
RunnableExecutorPair head = stack;
stack = stack.next;
head.next = reversedStack;
reversedStack = head;
}
stack = reversedStack;
while (stack != null) {
stack.execute();
stack = stack.next;
}
}

private static class RunnableExecutorPair {
final Runnable runnable;
final Executor executor;
// Volatile because this is written on one thread and read on another with no synchronization.
@Nullable volatile RunnableExecutorPair next;

RunnableExecutorPair(@Nullable Runnable runnable, @Nullable Executor executor) {
this.runnable = runnable;
this.executor = executor;
}

void execute() {
try {
executor.execute(runnable);
} catch (RuntimeException e) {
log.log(
Level.SEVERE,
"RuntimeException while executing runnable "
+ runnable
+ " with executor "
+ executor,
e);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.AbstractFutureBenchmarks.OldAbstractFuture;
import com.google.errorprone.annotations.concurrent.GuardedBy;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
Expand All @@ -44,7 +40,6 @@
import java.util.logging.Logger;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import sun.misc.Unsafe;

/** Benchmarks for {@link ExecutionList}. */
@VmOptions({"-Xms8g", "-Xmx8g"})
Expand Down Expand Up @@ -85,29 +80,6 @@ public Object getImpl() {
};
}
},
NEW_WITH_CAS {
@Override
ExecutionListWrapper newExecutionList() {
return new ExecutionListWrapper() {
final ExecutionListUsingCompareAndSwap list = new ExecutionListUsingCompareAndSwap();

@Override
public void add(Runnable runnable, Executor executor) {
list.add(runnable, executor);
}

@Override
public void execute() {
list.execute();
}

@Override
public Object getImpl() {
return list;
}
};
}
},
NEW_WITH_QUEUE {
@Override
ExecutionListWrapper newExecutionList() {
Expand Down Expand Up @@ -578,127 +550,4 @@ private static final class RunnableExecutorPair {
}
}
}

// A version of the list that uses compare and swap to manage the stack without locks.
@SuppressWarnings({"SunApi", "removal"}) // b/345822163
private static final class ExecutionListUsingCompareAndSwap {
static final Logger log = Logger.getLogger(ExecutionListUsingCompareAndSwap.class.getName());

private static final Unsafe UNSAFE;
private static final long HEAD_OFFSET;

/**
* A special instance of {@link RunnableExecutorPair} that is used as a sentinel value for the
* bottom of the stack.
*/
private static final RunnableExecutorPair NULL_PAIR = new RunnableExecutorPair(null, null);

static {
try {
UNSAFE = getUnsafe();
HEAD_OFFSET =
UNSAFE.objectFieldOffset(
ExecutionListUsingCompareAndSwap.class.getDeclaredField("head"));
} catch (Exception ex) {
throw new Error(ex);
}
}

/** TODO(lukes): This was copied verbatim from Striped64.java... standardize this? */
private static Unsafe getUnsafe() {
try {
return Unsafe.getUnsafe();
} catch (SecurityException tryReflectionInstead) {
}
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<Unsafe>() {
@Override
public Unsafe run() throws Exception {
Class<Unsafe> k = Unsafe.class;
for (Field f : k.getDeclaredFields()) {
f.setAccessible(true);
Object x = f.get(null);
if (k.isInstance(x)) return k.cast(x);
}
throw new NoSuchFieldError("the Unsafe");
}
});
} catch (PrivilegedActionException e) {
throw new RuntimeException("Could not initialize intrinsics", e.getCause());
}
}

private volatile RunnableExecutorPair head = NULL_PAIR;

public void add(Runnable runnable, Executor executor) {
Preconditions.checkNotNull(runnable, "Runnable was null.");
Preconditions.checkNotNull(executor, "Executor was null.");

RunnableExecutorPair newHead = new RunnableExecutorPair(runnable, executor);
RunnableExecutorPair oldHead;
do {
oldHead = head;
if (oldHead == null) {
// If runnables == null then execute() has been called so we should just execute our
// listener immediately.
newHead.execute();
return;
}
// Try to make newHead the new head of the stack at runnables.
newHead.next = oldHead;
} while (!UNSAFE.compareAndSwapObject(this, HEAD_OFFSET, oldHead, newHead));
}

public void execute() {
RunnableExecutorPair stack;
do {
stack = head;
if (stack == null) {
// If head == null then execute() has been called so we should just return
return;
}
// try to swap null into head.
} while (!UNSAFE.compareAndSwapObject(this, HEAD_OFFSET, stack, null));

RunnableExecutorPair reversedStack = null;
while (stack != NULL_PAIR) {
RunnableExecutorPair head = stack;
stack = stack.next;
head.next = reversedStack;
reversedStack = head;
}
stack = reversedStack;
while (stack != null) {
stack.execute();
stack = stack.next;
}
}

private static class RunnableExecutorPair {
final Runnable runnable;
final Executor executor;
// Volatile because this is written on one thread and read on another with no synchronization.
@Nullable volatile RunnableExecutorPair next;

RunnableExecutorPair(@Nullable Runnable runnable, @Nullable Executor executor) {
this.runnable = runnable;
this.executor = executor;
}

void execute() {
try {
executor.execute(runnable);
} catch (RuntimeException e) {
log.log(
Level.SEVERE,
"RuntimeException while executing runnable "
+ runnable
+ " with executor "
+ executor,
e);
}
}
}
}
}

0 comments on commit 80aab00

Please sign in to comment.