Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Unsafe-based implementation from ExecutionListBenchmark. #7612

Merged
merged 1 commit into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}
}
}
}
Loading