All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.questdb.mp.SimpleWaitingLock Maven / Gradle / Ivy

/*******************************************************************************
 *     ___                  _   ____  ____
 *    / _ \ _   _  ___  ___| |_|  _ \| __ )
 *   | | | | | | |/ _ \/ __| __| | | |  _ \
 *   | |_| | |_| |  __/\__ \ |_| |_| | |_) |
 *    \__\_\\__,_|\___||___/\__|____/|____/
 *
 *  Copyright (c) 2014-2019 Appsicle
 *  Copyright (c) 2019-2023 QuestDB
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/

package io.questdb.mp;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;

/**
 * Simple blocking lock which allows 2 arbitrary threads to compete for ownership. Behaviour is undefined for more than 2 threads.
 * The lock is not reentrant.
 */
public final class SimpleWaitingLock {
    private final AtomicReference ownerOrWaiter = new AtomicReference<>(null);

    public boolean isLocked() {
        return ownerOrWaiter.get() != null;
    }

    /**
     * Acquires the lock. The method will block until the lock is acquired.
     */
    public void lock() {
        tryLock(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
    }

    /**
     * Tries to acquire the lock, allowing for a specified timeout period.
     * If the lock is not acquired within the given timeout, the method returns false.
     *
     * @param timeout the maximum time to wait for acquiring the lock, in the given TimeUnit
     * @param unit    the time unit of the timeout parameter
     * @return true if the lock was acquired within the timeout period, false otherwise
     */
    public boolean tryLock(long timeout, TimeUnit unit) {
        Thread currentThread = Thread.currentThread();
        Thread expectedOwner = null;
        long deadline = System.nanoTime() + unit.toNanos(timeout); // this might overflow, but that's OK. we subtract current nanoTime and that makes it positive again
        for (long remainingNanos = deadline - System.nanoTime(); remainingNanos > 0; remainingNanos = deadline - System.nanoTime()) {
            if (ownerOrWaiter.compareAndSet(expectedOwner, currentThread)) {
                if (expectedOwner == null) {
                    // there was no owner before -> we acquired the lock and we are the new owner. yay!
                    return true;
                }
                // CAS succeeded, but there was an owner before -> we are a waiter
                LockSupport.parkNanos(remainingNanos);
            }
            expectedOwner = ownerOrWaiter.get();
        }
        return false;
    }

    /**
     * Tries to acquire the lock. If the lock is available, it is acquired and the method returns true.
     * If the lock is already acquired by another thread, the method returns false.
     *
     * @return true if the lock was acquired, false otherwise
     */
    public boolean tryLock() {
        return ownerOrWaiter.compareAndSet(null, Thread.currentThread());
    }

    /**
     * Releases the lock, allowing other threads to acquire it.
     * 

* If the lock is not currently owned by any thread, an IllegalStateException is thrown. * If there is another thread waiting to acquire the lock, it is unparked. * If there is no waiting thread, the method returns without any additional action. *

* Implementation note: This method does not validate the calling thread is the owner of the lock. The method * releases the lock regardless of the calling thread. This makes it somewhat similar to a single permit semaphore. * In such case it may set the unpark flag for the formerly owning thread. The unpark flag should have no negative * side effect on the formerly owning thread as programs must be designed to handle spurious wakeups anyway. * * @throws IllegalStateException if the lock is not owned by any thread */ public void unlock() { Thread currentThread = Thread.currentThread(); Thread waitingOrOwningThread = ownerOrWaiter.getAndSet(null); if (waitingOrOwningThread == null) { throw new IllegalStateException("Lock is not owned"); } if (waitingOrOwningThread == currentThread) { // no waiters return; } LockSupport.unpark(waitingOrOwningThread); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy