This commit is contained in:
MrHua269
2026-07-09 17:56:26 +08:00
parent 717756b5b0
commit d07264f1cd
8 changed files with 761 additions and 0 deletions
@@ -0,0 +1,50 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021-2026 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.c2me.base.common.scheduler;
import com.ishland.flowsched.executor.Task;
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
import java.util.Objects;
public abstract class AbstractPosAwarePrioritizedTask extends Task {
protected final ReferenceArrayList<Runnable> postExec = new ReferenceArrayList<>(4);
private final long pos;
public AbstractPosAwarePrioritizedTask(long pos) {
this.pos = pos;
}
public long getPos() {
return this.pos;
}
public void addPostExec(Runnable runnable) {
synchronized (this.postExec) {
postExec.add(Objects.requireNonNull(runnable));
}
}
}
@@ -0,0 +1,90 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021-2026 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.c2me.base.common.scheduler;
import com.ishland.flowsched.executor.LockToken;
import java.util.Objects;
public final class LockTokenImpl implements LockToken {
private final int ownerTag;
private final long pos;
private final Usage usage;
public LockTokenImpl(int ownerTag, long pos, Usage usage) {
this.ownerTag = ownerTag;
this.pos = pos;
this.usage = usage;
}
public int ownerTag() {
return ownerTag;
}
public long pos() {
return pos;
}
public Usage usage() {
return usage;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (LockTokenImpl) obj;
return this.ownerTag == that.ownerTag &&
this.pos == that.pos &&
Objects.equals(this.usage, that.usage);
}
@Override
public int hashCode() {
// inlined Objects.hash(ownerTag, usage, pos)
int result = 1;
result = 31 * result + Integer.hashCode(ownerTag);
result = 31 * result + usage.hashCode();
result = 31 * result + Long.hashCode(pos);
return result;
}
@Override
public String toString() {
return "LockTokenImpl[" +
"ownerTag=" + ownerTag + ", " +
"pos=" + pos + ", " +
"usage=" + usage + ']';
}
public enum Usage {
WORLDGEN,
LIGHTING,
}
}
@@ -0,0 +1,76 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021-2026 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.c2me.base.common.scheduler;
import com.ishland.flowsched.executor.LockToken;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
public class ScheduledTask<T> extends AbstractPosAwarePrioritizedTask {
private final Supplier<CompletableFuture<T>> action;
private final LockToken[] lockTokens;
private final CompletableFuture<T> future = new CompletableFuture<>();
public ScheduledTask(long pos, Supplier<CompletableFuture<T>> action, LockToken[] lockTokens) {
super(pos);
this.action = action;
this.lockTokens = lockTokens;
}
@Override
public void run(Runnable releaseLocks) {
action.get().whenComplete((t, throwable) -> {
releaseLocks.run();
if (throwable != null) {
future.completeExceptionally(throwable);
} else {
future.complete(t);
}
for (Runnable runnable : this.postExec) {
try {
runnable.run();
} catch (Throwable t1) {
t1.printStackTrace();
}
}
});
}
@Override
public void propagateException(Throwable t) {
future.completeExceptionally(t);
}
@Override
public LockToken[] lockTokens() {
return this.lockTokens;
}
public CompletableFuture<T> getFuture() {
return this.future;
}
}
@@ -0,0 +1,266 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021-2026 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.c2me.base.common.scheduler;
import com.ishland.c2me.base.common.GlobalExecutors;
import com.ishland.flowsched.executor.ExecutorManager;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArraySet;
import net.minecraft.server.level.ChunkLevel;
import net.minecraft.world.level.ChunkPos;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.StampedLock;
public class SchedulingManager {
private static final AtomicInteger COUNTER = new AtomicInteger(0);
public static final int MAX_LEVEL = ChunkLevel.MAX_LEVEL + 1;
private final ConcurrentMap<Long, FreeableTaskList> pos2Tasks = new ConcurrentHashMap<>();
private final Long2IntOpenHashMap prioritiesFromLevel = new Long2IntOpenHashMap() {
@Override
protected void rehash(int newN) {
if (n < newN) {
super.rehash(newN);
}
}
};
private final StampedLock prioritiesLock = new StampedLock();
private final int id = COUNTER.getAndIncrement();
private volatile ChunkPos currentSyncLoad = null;
private boolean consolidatingLevelUpdates = false;
private Queue<Runnable> consolidatedLevelUpdates = new ArrayDeque<>();
private final Executor executor;
{
prioritiesFromLevel.defaultReturnValue(MAX_LEVEL);
}
private final ExecutorManager worker;
public SchedulingManager(Executor managementExecutor, ExecutorManager worker) {
this.executor = managementExecutor;
this.worker = worker;
}
public void enqueue(AbstractPosAwarePrioritizedTask task) {
retry:
while (true) {
final long pos = task.getPos();
final FreeableTaskList locks = this.pos2Tasks.computeIfAbsent(pos, unused -> new FreeableTaskList());
synchronized (locks) {
if (locks.freed) continue retry;
locks.add(task);
}
int priority = this.getPriority(pos);
task.addPostExec(() -> {
final FreeableTaskList tasks = this.pos2Tasks.get(task.getPos());
if (tasks != null) {
synchronized (tasks) {
if (tasks.freed) return;
tasks.remove(task);
if (tasks.isEmpty()) {
tasks.freed = true;
}
}
if (tasks.freed) {
this.pos2Tasks.remove(task.getPos());
}
}
});
this.worker.schedule(task, priority);
return;
}
}
public void enqueue(long pos, Runnable command) {
this.enqueue(new WrappingTask(pos, command));
}
public Executor positionedExecutor(long pos) {
return command -> this.enqueue(pos, command);
}
public void updatePriorityFromLevel(long pos, int level) {
this.executor.execute(() -> {
updatePriorityFromLevel0(pos, level);
});
}
private void updatePriorityFromLevel0(long pos, int level) {
if (this.getPriorityFromMap(pos) == level) return;
final long stamp = this.prioritiesLock.writeLock();
try {
if (level < MAX_LEVEL) {
this.prioritiesFromLevel.put(pos, level);
} else {
this.prioritiesFromLevel.remove(pos);
}
} finally {
this.prioritiesLock.unlockWrite(stamp);
}
updatePriorityInternal(pos);
}
public void updatePriorityFromLevelOnMain(long pos, int level) {
if (this.consolidatingLevelUpdates) {
this.consolidatedLevelUpdates.add(() -> updatePriorityFromLevel0(pos, level));
} else {
updatePriorityFromLevel(pos, level);
}
}
public void setConsolidatingLevelUpdates(boolean value) {
this.consolidatingLevelUpdates = value;
if (!value) {
if (!this.consolidatedLevelUpdates.isEmpty()) {
Queue<Runnable> runnables = this.consolidatedLevelUpdates;
this.consolidatedLevelUpdates = new ArrayDeque<>();
this.executor.execute(() -> {
for (Runnable runnable : runnables) {
try {
runnable.run();
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
}
}
private void updatePriorityInternal(long pos) {
final int priority = getPriority(pos);
final FreeableTaskList locks = this.pos2Tasks.get(pos);
if (locks != null) {
synchronized (locks) {
if (locks.freed) return;
for (AbstractPosAwarePrioritizedTask lock : locks) {
this.worker.changePriority(lock, priority);
}
}
}
}
public ExecutorManager getWorker() {
return this.worker;
}
private int getPriority(long pos) {
final int fromLevel = getPriorityFromMap(pos);
int fromSyncLoad;
ChunkPos currentSyncLoad1 = currentSyncLoad;
if (currentSyncLoad1 != null) {
final int chebyshevDistance = chebyshev(ChunkPos.unpack(pos), currentSyncLoad1);
if (chebyshevDistance <= 8) {
fromSyncLoad = chebyshevDistance;
// System.out.println("dist for chunk [%d,%d] is %d".formatted(currentSyncLoad.x, currentSyncLoad.z, chebyshevDistance));
} else {
fromSyncLoad = MAX_LEVEL;
}
} else {
fromSyncLoad = MAX_LEVEL;
}
return Math.min(fromLevel, fromSyncLoad);
}
private int getPriorityFromMap(long pos) {
int fromLevel = MAX_LEVEL;
long stamp = this.prioritiesLock.tryOptimisticRead();
try {
fromLevel = this.prioritiesFromLevel.get(pos);
} catch (Throwable t) {
}
if (!this.prioritiesLock.validate(stamp)) {
stamp = this.prioritiesLock.readLock();
try {
fromLevel = this.prioritiesFromLevel.get(pos);
} finally {
this.prioritiesLock.unlockRead(stamp);
}
}
return fromLevel;
}
public void setCurrentSyncLoad(ChunkPos pos) {
executor.execute(() -> {
if (this.currentSyncLoad != null) {
final ChunkPos lastSyncLoad = this.currentSyncLoad;
this.currentSyncLoad = null;
updateSyncLoadInternal(lastSyncLoad);
}
if (pos != null) {
this.currentSyncLoad = pos;
updateSyncLoadInternal(pos);
}
});
}
public int getId() {
return this.id;
}
private void updateSyncLoadInternal(ChunkPos pos) {
long startTime = System.nanoTime();
for (int xOff = -8; xOff <= 8; xOff++) {
for (int zOff = -8; zOff <= 8; zOff++) {
updatePriorityInternal(ChunkPos.pack(pos.x() + xOff, pos.z() + zOff));
}
}
long endTime = System.nanoTime();
}
private static int chebyshev(ChunkPos a, ChunkPos b) {
return Math.max(Math.abs(a.x() - b.x()), Math.abs(a.z() - b.z()));
}
private static int chebyshev(long a, long b) {
return Math.max(Math.abs(ChunkPos.getX(a) - ChunkPos.getX(b)), Math.abs(ChunkPos.getZ(a) - ChunkPos.getZ(b)));
}
private static class FreeableTaskList extends ObjectArraySet<AbstractPosAwarePrioritizedTask> {
private boolean freed = false;
@Override
public boolean equals(Object o) {
return this == o;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
}
}
@@ -0,0 +1,60 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021-2026 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.c2me.base.common.scheduler;
import com.ishland.flowsched.executor.LockToken;
import com.ishland.flowsched.executor.Task;
import java.util.Objects;
public class SimplePrioritizedTask extends Task {
private final Runnable task;
private final LockToken[] lockTokens;
public SimplePrioritizedTask(Runnable task, LockToken[] lockTokens) {
this.task = Objects.requireNonNull(task, "task");
this.lockTokens = Objects.requireNonNull(lockTokens, "lockTokens");
}
@Override
public void run(Runnable releaseLocks) {
try {
this.task.run();
} finally {
releaseLocks.run();
}
}
@Override
public void propagateException(Throwable t) {
t.printStackTrace();
}
@Override
public LockToken[] lockTokens() {
return this.lockTokens;
}
}
@@ -0,0 +1,107 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021-2026 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.c2me.base.common.scheduler;
import com.google.common.base.Preconditions;
import io.netty.util.internal.PlatformDependent;
import org.jetbrains.annotations.NotNull;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
public class SingleThreadExecutor extends Thread implements Executor {
private final AtomicInteger size = new AtomicInteger();
public final Queue<Runnable> queue = PlatformDependent.newMpscQueue();
private final AtomicBoolean shutdown = new AtomicBoolean(false);
private final Object sync = new Object();
@Override
public void run() {
main_loop:
while (true) {
if (pollTasks()) {
continue;
}
if (this.shutdown.get()) {
return;
}
// // attempt to spin-wait before sleeping
// if (!pollTasks()) {
// Thread.interrupted(); // clear interrupt flag
// for (int i = 0; i < 5000; i ++) {
// if (pollTasks()) continue main_loop;
// LockSupport.parkNanos("Spin-waiting for tasks", 10_000); // 100us
// }
// }
synchronized (sync) {
if (this.size.get() != 0 || this.shutdown.get()) continue main_loop;
try {
sync.wait();
} catch (InterruptedException ignored) {
}
}
}
}
private boolean pollTasks() {
boolean hasWork = false;
Runnable task;
while ((task = queue.poll()) != null) {
this.size.decrementAndGet();
try {
task.run();
} catch (Throwable t) {
t.printStackTrace();
}
hasWork = true;
}
return hasWork;
}
@Override
public void execute(@NotNull Runnable command) {
Preconditions.checkNotNull(command, "command");
final boolean wasEmpty = this.size.getAndIncrement() == 0;
this.queue.add(command);
if (wasEmpty) {
synchronized (sync) {
sync.notify();
}
}
}
public void shutdown() {
this.shutdown.set(true);
synchronized (sync) {
sync.notify();
}
}
}
@@ -0,0 +1,45 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021-2026 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.c2me.base.common.scheduler;
import net.minecraft.server.world.ChunkHolder;
public class ThreadLocalWorldGenSchedulingState {
private static final ThreadLocal<ChunkHolder> chunkHolder = new ThreadLocal<>();
public static ChunkHolder getChunkHolder() {
return chunkHolder.get();
}
public static void setChunkHolder(ChunkHolder holder) {
chunkHolder.set(holder);
}
public static void clearChunkHolder() {
chunkHolder.remove();
}
}
@@ -0,0 +1,67 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021-2026 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.c2me.base.common.scheduler;
import com.ishland.flowsched.executor.LockToken;
import java.util.Objects;
public class WrappingTask extends AbstractPosAwarePrioritizedTask {
private static final LockToken[] EMPTY_LOCK_TOKENS = new LockToken[0];
private final Runnable wrapped;
public WrappingTask(long pos, Runnable wrapped) {
super(pos);
this.wrapped = Objects.requireNonNull(wrapped);
}
@Override
public void run(Runnable releaseLocks) {
try {
wrapped.run();
} finally {
releaseLocks.run();
for (Runnable runnable : this.postExec) {
try {
runnable.run();
} catch (Throwable t1) {
t1.printStackTrace();
}
}
}
}
@Override
public void propagateException(Throwable t) {
t.printStackTrace();
}
@Override
public LockToken[] lockTokens() {
return EMPTY_LOCK_TOKENS;
}
}