001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.util;
029
030import java.util.concurrent.locks.Condition;
031import java.util.concurrent.locks.ReentrantLock;
032
033/**
034 * Lock which can be acquired with two different priorities.
035 *
036 * <p>The lock can be only be held by one thread, but can be locked reentrantly by the owner. All waiting threads who requested the lock with high priority will be granted the lock first if it becomes available, before any threads who requested the lock with low priority.
037 */
038public class CmsPriorityLock {
039
040    /** The current thread holding the lock. */
041    private Thread m_holder = null;
042
043    /** How many times the current lock owner, if any, is holding the lock. */
044    private int m_holdCount = 0;
045
046    /** The underlying lock. */
047    private ReentrantLock m_lock = new ReentrantLock(true);
048
049    /** Condition object used to signal that high priority waiters can acquire the lock. */
050    private Condition m_highCond = m_lock.newCondition();
051
052    /** Condition object used to signal that low priority waiters can acquire the lock. */
053    private Condition m_lowCond = m_lock.newCondition();
054
055    /** Number of waiters who want to acquire the lock with high priority. */
056    private int m_waitHigh;
057
058    /** Number of waiters who want to acquire the lock with low priority. */
059    private int m_waitLow;
060
061    /**
062     * Creates a new instance.
063     */
064    public CmsPriorityLock() {
065
066    }
067
068    /**
069     * Acquire the lock, with given priority.
070     *
071     * @param highPriority true for high-priority acquisition, false for low-priority
072     */
073    public void lock(boolean highPriority) {
074
075        m_lock.lock();
076        try {
077            Thread current = Thread.currentThread();
078            if (current == m_holder) {
079                m_holdCount++;
080                return;
081            }
082            if (highPriority) {
083                m_waitHigh += 1;
084                while (m_holder != null) {
085                    m_highCond.awaitUninterruptibly();
086                }
087                m_waitHigh -= 1;
088                m_holder = current;
089                m_holdCount = 1;
090            } else {
091                m_waitLow += 1;
092                while ((m_holder != null) || (m_waitHigh > 0)) {
093                    m_lowCond.awaitUninterruptibly();
094                }
095                m_waitLow -= 1;
096                m_holder = current;
097                m_holdCount = 1;
098            }
099        } finally {
100            m_lock.unlock();
101        }
102
103    }
104
105    /**
106     * Releases the lock.
107     *
108     * @throws IllegalMonitorStateException if called by a thread not owning the lock
109     */
110    public void unlock() {
111
112        m_lock.lock();
113        try {
114            Thread current = Thread.currentThread();
115            if (current != m_holder) {
116                throw new IllegalMonitorStateException();
117            }
118            m_holdCount -= 1;
119            if (m_holdCount == 0) {
120                m_holder = null;
121                if (m_waitHigh > 0) {
122                    m_highCond.signal();
123                } else if (m_waitLow > 0) {
124                    m_lowCond.signal();
125                }
126            }
127        } finally {
128            m_lock.unlock();
129        }
130
131    }
132}