From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Martijn Muijsers Date: Fri, 2 Dec 2022 10:31:49 +0100 Subject: [PATCH] Mutex utility License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/org/galemc/gale/concurrent/Mutex.java b/src/main/java/org/galemc/gale/concurrent/Mutex.java new file mode 100644 index 0000000000000000000000000000000000000000..649d2cfc1d73699302b4e5e64e9110e7681ae09c --- /dev/null +++ b/src/main/java/org/galemc/gale/concurrent/Mutex.java @@ -0,0 +1,127 @@ +// Gale - mutex utility + +package org.galemc.gale.concurrent; + +import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +/** + * A mutex, intended to be a more performant alternative to {@link java.util.concurrent.locks.ReentrantLock} + * when the reentrant property is not needed. + *
+ * This interface extends {@link Lock}, of which the {@link Lock#lock}, {@link Lock#lockInterruptibly}, + * {@link Lock#tryLock} and {@link Lock#unlock} methods are simply deferred to their usual mutex versions, + * respectively {@link #acquireUninterruptibly}, {@link #acquire}, {@link #tryAcquire} and + * {@link #release}. The {@link Lock#newCondition} method does not have a default implementation. + *
+ * This interface extends {@link AutoCloseable}, where {@link #close()} calls {@link #release()}. + * + * @author Martijn Muijsers under AGPL-3.0 + */ +@AnyThreadSafe +public interface Mutex extends CheckableLock, AutoCloseable { + + void acquireUninterruptibly(); + + void acquire() throws InterruptedException; + + void release(); + + boolean tryAcquire(); + + boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException; + + @Override + default void lock() { + this.acquireUninterruptibly(); + } + + @Override + default void lockInterruptibly() throws InterruptedException { + this.acquire(); + } + + @Override + default boolean tryLock() { + return this.tryAcquire(); + } + + @Override + default boolean tryLock(long l, @NotNull TimeUnit timeUnit) throws InterruptedException { + return this.tryAcquire(l, timeUnit); + } + + @Override + default void unlock() { + this.release(); + } + + /** + * Acquires this mutex in the style of a spin lock: repeatedly calls {@link #tryAcquire()} until successful. + */ + default void spinLock() { + while (!this.tryAcquire()) { + Thread.onSpinWait(); + } + } + + /** + * Acquires this mutex with {@link #acquire()}, then returns this instance. + *
+ * This can be used in the following way: + *

+ * + *  try (mutex.withLock()) {
+ *   // code
+ *  } + *
+ *

+ * + * @return This {@link Mutex}, which implements {@link AutoCloseable}. + */ + default Mutex withLock() throws InterruptedException { + this.acquire(); + return this; + } + + /** + * Acquires this mutex with {@link #spinLock()}, then returns this instance. + * + * @return This {@link Mutex}, which implements {@link AutoCloseable}. + * @see #withLock() + */ + default Mutex withSpinLock() { + this.spinLock(); + return this; + } + + @Override + default void close() { + this.release(); + } + + /** + * Instantiates a new {@link Mutex}, with the default implementation + * that should be geared towards performance. + */ + static @NotNull Mutex create() { + return new SemaphoreMutex(); + } + + /** + * Instantiates a new {@link Mutex} that we intend to use as a {@link Lock}, with a default implementation + * that should be geared towards performance of the {@link Lock} methods. + */ + static @NotNull Mutex createLock() { + return create(); + } + + @Override + default boolean isHeldByCurrentThread() { + throw new UnsupportedOperationException("isHeldByCurrentThread() is not supported for Mutex, and is not implemented by the implementation " + this.getClass().getName() + " either"); + } + +} diff --git a/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java new file mode 100644 index 0000000000000000000000000000000000000000..d31293a2a2151bc9fbdc6eb2175045b429fb4461 --- /dev/null +++ b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java @@ -0,0 +1,39 @@ +// Gale - mutex utility + +package org.galemc.gale.concurrent; + +import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; +import org.galemc.gale.executor.annotation.YieldFree; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; + +/** + * A {@link Mutex}, with implements the required methods by extending {@link Semaphore}, + * and throws {@link UnsupportedOperationException} for all {@link Lock} methods that do not have a default + * implementation in {@link Mutex}. + * + * @author Martijn Muijsers under AGPL-3.0 + */ +@AnyThreadSafe +@YieldFree +public class SemaphoreMutex extends Semaphore implements Mutex { + + public SemaphoreMutex() { + super(1); + } + + @NotNull + @Override + public Condition newCondition() { + throw new UnsupportedOperationException("newCondition() is not implemented for SemaphoreMutex"); + } + + @Override + public boolean isLocked() { + return this.availablePermits() == 0; + } + +}