001 /** 002 * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved. 003 * 004 * This library is free software; you can redistribute it and/or modify it under 005 * the terms of the GNU Lesser General Public License as published by the Free 006 * Software Foundation; either version 2.1 of the License, or (at your option) 007 * any later version. 008 * 009 * This library is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 011 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 012 * details. 013 */ 014 015 package com.liferay.portal.kernel.concurrent; 016 017 import java.util.concurrent.TimeUnit; 018 import java.util.concurrent.locks.AbstractQueuedSynchronizer; 019 020 /** 021 * A synchronizer based on the JDK's AQS framework to simulate a single winner 022 * competition. This synchronizer supports cyclical competition. In this 023 * situation, loser threads should try again. The single winner thread will lock 024 * the latch while other threads will block on the latch by calling 025 * <code>await</code>. After the winner thread finishes its job, it should call 026 * <code>done</code> which will open the latch. All blocking loser threads can 027 * pass the latch at the same time. 028 * 029 * <p> 030 * See LPS-3744 for a sample use case. 031 * </p> 032 * 033 * @author Shuyang Zhou 034 */ 035 public class CompeteLatch { 036 037 /** 038 * This method should only be called by a loser thread. If the latch is 039 * locked, that means the winner is executing its job and all loser threads 040 * that call this method will be blocked. If the latch is not locked, that 041 * means the winner has finished its job and all the loser threads calling 042 * this method will return immediately. If the winner thread calls this 043 * method before his job completed, then all threads will deadlock. 044 * 045 * @throws InterruptedException if the current thread is interrupted 046 */ 047 public void await() throws InterruptedException { 048 _sync.acquireSharedInterruptibly(1); 049 } 050 051 /** 052 * This method should only be called by a loser thread. If the latch is 053 * locked, that means the winner is executing its job and all loser threads 054 * that call this method will be blocked for the given waiting time. If the 055 * latch is not locked, that means the winner has finished its job and all 056 * the loser threads calling this method will return immediately. If the 057 * winner thread calls this method before his job completed, then all 058 * threads will deadlock. 059 * 060 * @param timeout the timeout value 061 * @param timeUnit the time unit 062 * @return <code>true</code> if the latch was open, <code>false</code> if 063 * the waiting time elapsed before the latch be opened. 064 * @throws InterruptedException if the current thread is interrupted 065 */ 066 public boolean await(long timeout, TimeUnit timeUnit) 067 throws InterruptedException { 068 069 return _sync.tryAcquireSharedNanos(1, timeUnit.toNanos(timeout)); 070 } 071 072 /** 073 * Tells the current thread to join the competition. Return immediately 074 * whether or not the current thread is the winner thread or a loser thread. 075 * No matter how many threads join this competition, only one thread can be 076 * the winner thread. 077 * 078 * @return <code>true</code> if the current thread is the winner thread 079 */ 080 public boolean compete() { 081 return _sync._tryInitAcquireShared(); 082 } 083 084 /** 085 * This method should only be called by the winner thread. The winner thread 086 * calls this method to indicate that it has finished its job and unlocks 087 * the latch to allow all loser threads return from the <code>await</code> 088 * method. If a loser thread does call this method when a winner thread has 089 * locked the latch, the latch will break and the winner thread may be put 090 * into a non thread safe state. You should never have to do this except to 091 * get out of a deadlock. If no one threads have locked the latch, then 092 * calling this method has no effect. This method will return immediately. 093 * 094 * @return <code>true</code> if this call opens the latch, 095 * <code>false</code> if the latch is already open 096 */ 097 public boolean done() { 098 return _sync.releaseShared(1); 099 } 100 101 /** 102 * Returns <code>true</code> if the latch is locked. This method should not 103 * be used to test the latch before joining a competition because it is not 104 * thread safe. The only purpose for this method is to give external systems 105 * a way to monitor the latch which is usually be used for deadlock 106 * detection. 107 * 108 * @return <code>true</code> if the latch is locked; <code>false</code> 109 * otherwise 110 */ 111 public boolean isLocked() { 112 return _sync._isLocked(); 113 } 114 115 private Sync _sync = new Sync(); 116 117 private class Sync extends AbstractQueuedSynchronizer { 118 119 @Override 120 protected int tryAcquireShared(int arg) { 121 if (getState() == 0) { 122 return 1; 123 } 124 else { 125 return -1; 126 } 127 } 128 129 @Override 130 protected boolean tryReleaseShared(int arg) { 131 if (compareAndSetState(1, 0)) { 132 return true; 133 } 134 else { 135 return false; 136 } 137 } 138 139 private boolean _isLocked() { 140 if (getState() == 1) { 141 return true; 142 } 143 else { 144 return false; 145 } 146 } 147 148 private boolean _tryInitAcquireShared() { 149 if (compareAndSetState(0, 1)) { 150 return true; 151 } 152 else { 153 return false; 154 } 155 } 156 157 } 158 159 }