View Javadoc

1   /**
2    * Copyright (C) 2006 - 2010
3    *   Pawel Kedzior
4    *   Tomasz Kmiecik
5    *   Kamil Pietak
6    *   Krzysztof Sikora
7    *   Adam Wos
8    *   and other students of AGH University of Science and Technology
9    *   as indicated in each file separately.
10   *
11   * This file is part of jAgE.
12   *
13   * jAgE is free software: you can redistribute it and/or modify
14   * it under the terms of the GNU General Public License as published by
15   * the Free Software Foundation, either version 3 of the License, or
16   * (at your option) any later version.
17   *
18   * jAgE is distributed in the hope that it will be useful,
19   * but WITHOUT ANY WARRANTY; without even the implied warranty of
20   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21   * GNU General Public License for more details.
22   *
23   * You should have received a copy of the GNU General Public License
24   * along with jAgE.  If not, see http://www.gnu.org/licenses/
25   */
26  /*
27   * File: ReaderWriterLock.java
28   * Created: 2008-10-07
29   * Author: awos
30   * $Id: ReaderWriterLock.java 1013 2011-10-20 22:37:38Z faber $
31   */
32  
33  package org.jage.util;
34  
35  import java.util.HashMap;
36  import java.util.Map;
37  
38  /**
39   * <tt>ReaderWriterLock</tt> solves the classic problem of readers and writers. <br>
40   * It prevents starvation of readers and writers.
41   * <p>
42   * Assumptions: <br>
43   * <li>If there are any waiting writers new readers have to wait (at least) until the first of them finishes writing
44   * <li>If there are any waiting readers they will be resumed before the next writing
45   * 
46   * @author AGH AgE Team
47   */
48  public class ReaderWriterLock {
49  
50  	/**
51  	 * Number of readers which have set the lock.
52  	 */
53  	private int readerCount = 0;
54  
55  	/**
56  	 * Threads of all readers key: thread of a reader value: number of reader locks aquired by the thread
57  	 */
58  	private final Map<Thread, Integer> readersToCounts = new HashMap<Thread, Integer>();
59  
60  	/**
61  	 * Indicates that writer lock have been set.
62  	 */
63  	private boolean writerLock = false;
64  
65  	/**
66  	 * Number of readers waiting.
67  	 */
68  	private int waitingReaders = 0;
69  
70  	/**
71  	 * Number of writers waiting.
72  	 */
73  	private int waitingWriters = 0;
74  
75  	/**
76  	 * Number of readers to be woken.
77  	 */
78  	private int wakeUpReaders = 0;
79  
80  	/**
81  	 * Number of writers to be woken. Actually only zero and 1 is allowed.
82  	 */
83  	private int wakeUpWriters = 0;
84  
85  	/**
86  	 * Common monitor for all kinds of events. _monitor.notifyAll() must be called whenever an event occurs.
87  	 */
88  	private final Object monitor = new Object();
89  
90  	/**
91  	 * Constructor.
92  	 */
93  	public ReaderWriterLock() {
94  		// empty
95  	}
96  
97  	/**
98  	 * Waits for waking up all readers or writers, which should be woken. In order to call this, _monitor must be
99  	 * synchronized.
100 	 * 
101 	 * @throws InterruptedException
102 	 */
103 	private void waitForWakingUp() throws InterruptedException {
104 		while (wakeUpWriters != 0 || wakeUpReaders != 0) {
105 			monitor.wait();
106 		}
107 	}
108 
109 	/**
110 	 * Acquires reader's lock.
111 	 * 
112 	 * @throws InterruptedException
113 	 */
114 	public void acquireReaderLock() throws InterruptedException {
115 		synchronized (monitor) {
116 			waitForWakingUp();
117 
118 			// If a writer aquired a lock then wait,
119 			// if a writer is waiting then also wait,
120 			// if you already aquired the lock in this thread, than
121 			// don't wait, even if there is a writer waiting because it will
122 			// cause a deadlock.
123 			if (writerLock || (waitingWriters > 0 && !hasReaderLock(Thread.currentThread()))) {
124 				waitingReaders++;
125 				try {
126 					do {
127 						monitor.wait();
128 						if (wakeUpReaders > 0) {
129 							wakeUpReaders--;
130 							monitor.notifyAll();
131 							break;
132 						}
133 					} while (true);
134 				} finally {
135 					waitingReaders--;
136 				}
137 			}
138 			readerCount++;
139 			addReader(Thread.currentThread());
140 		}
141 	}
142 
143 	private void addReader(Thread thread) {
144 		Integer count = readersToCounts.get(thread);
145 		if (count == null) {
146 			readersToCounts.put(thread, Integer.valueOf(1));
147 		} else {
148 			readersToCounts.put(thread, Integer.valueOf(count.intValue() + 1));
149 		}
150 	}
151 
152 	private void removeReader(Thread thread) {
153 		Integer count = readersToCounts.get(thread);
154 		readersToCounts.put(thread, Integer.valueOf(count.intValue() - 1));
155 	}
156 
157 	private boolean hasReaderLock(Thread thread) {
158 		Integer count = readersToCounts.get(thread);
159 		if ((count != null) && (count.intValue() > 0)) {
160 			return true;
161 		}
162 		return false;
163 	}
164 
165 	/**
166 	 * Releases reader's lock.
167 	 * 
168 	 * @throws InterruptedException
169 	 */
170 	public void releaseReaderLock() throws InterruptedException {
171 		synchronized (monitor) {
172 			waitForWakingUp();
173 			// when all which should be woken, are woken then:
174 			readerCount--;
175 			removeReader(Thread.currentThread());
176 			if (readerCount == 0) {
177 				if (waitingWriters >= 1) {
178 					wakeUpWriters = 1;
179 					monitor.notifyAll();
180 				}
181 			}
182 		}
183 	}
184 
185 	/**
186 	 * Acquires writer's lock.
187 	 * 
188 	 * @throws InterruptedException
189 	 */
190 	public void acquireWriterLock() throws InterruptedException {
191 		synchronized (monitor) {
192 			waitForWakingUp();
193 
194 			if (readerCount > 0 || writerLock) {
195 				waitingWriters++;
196 				try {
197 					do {
198 						monitor.wait();
199 						if (wakeUpWriters == 1) {
200 							wakeUpWriters = 0;
201 							monitor.notifyAll();
202 							break;
203 						}
204 					} while (true);
205 				} finally {
206 					waitingWriters--;
207 				}
208 			}
209 			writerLock = true;
210 		}
211 	}
212 
213 	/**
214 	 * Releases the writer's lock.
215 	 * 
216 	 * @throws InterruptedException
217 	 */
218 	public void releaseWriterLock() throws InterruptedException {
219 		synchronized (monitor) {
220 			waitForWakingUp();
221 
222 			writerLock = false;
223 			if (waitingReaders > 0) {
224 				wakeUpReaders = waitingReaders;
225 				monitor.notifyAll();
226 			} else {
227 				if (waitingWriters >= 1) {
228 					wakeUpWriters = 1;
229 					monitor.notifyAll();
230 				}
231 			}
232 		}
233 	}
234 }