PageRenderTime 42ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/projects/jgroups-2.10.0/src/org/jgroups/util/Queue.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 622 lines | 339 code | 104 blank | 179 comment | 88 complexity | 771ee59dff1d65928b6ae27fb2e3be38 MD5 | raw file
  1. // $Id: Queue.java,v 1.34 2009/08/18 11:52:14 belaban Exp $
  2. package org.jgroups.util;
  3. import org.jgroups.logging.Log;
  4. import org.jgroups.logging.LogFactory;
  5. import org.jgroups.TimeoutException;
  6. import java.util.*;
  7. /**
  8. * Elements are added at the tail and removed from the head. Class is thread-safe in that
  9. * 1 producer and 1 consumer may add/remove elements concurrently. The class is not
  10. * explicitely designed for multiple producers or consumers. Implemented as a linked
  11. * list, so that removal of an element at the head does not cause a right-shift of the
  12. * remaining elements (as in a Vector-based implementation).
  13. * @author Bela Ban
  14. */
  15. public class Queue {
  16. /*head and the tail of the list so that we can easily add and remove objects*/
  17. private Element head=null, tail=null;
  18. /*flag to determine the state of the queue*/
  19. private volatile boolean closed=false;
  20. /*current size of the queue*/
  21. private volatile int size=0;
  22. /* Lock object for synchronization. Is notified when element is added */
  23. private final Object mutex=new Object();
  24. /** Lock object for syncing on removes. It is notified when an object is removed */
  25. // Object remove_mutex=new Object();
  26. /*the number of end markers that have been added*/
  27. private int num_markers=0;
  28. /**
  29. * if the queue closes during the runtime
  30. * an endMarker object is added to the end of the queue to indicate that
  31. * the queue will close automatically when the end marker is encountered
  32. * This allows for a "soft" close.
  33. * @see Queue#close
  34. */
  35. private static final Object endMarker=new Object();
  36. protected static final Log log=LogFactory.getLog(Queue.class);
  37. /**
  38. * the class Element indicates an object in the queue.
  39. * This element allows for the linked list algorithm by always holding a
  40. * reference to the next element in the list.
  41. * if Element.next is null, then this element is the tail of the list.
  42. */
  43. static class Element {
  44. /*the actual value stored in the queue*/
  45. Object obj=null;
  46. /*pointer to the next item in the (queue) linked list*/
  47. Element next=null;
  48. /**
  49. * creates an Element object holding its value
  50. * @param o - the object to be stored in the queue position
  51. */
  52. Element(Object o) {
  53. obj=o;
  54. }
  55. /**
  56. * prints out the value of the object
  57. */
  58. public String toString() {
  59. return obj != null? obj.toString() : "null";
  60. }
  61. }
  62. /**
  63. * creates an empty queue
  64. */
  65. public Queue() {
  66. }
  67. /**
  68. * Returns the first element. Returns null if no elements are available.
  69. */
  70. public Object getFirst() {
  71. synchronized(mutex) {
  72. return head != null? head.obj : null;
  73. }
  74. }
  75. /**
  76. * Returns the last element. Returns null if no elements are available.
  77. */
  78. public Object getLast() {
  79. synchronized(mutex) {
  80. return tail != null? tail.obj : null;
  81. }
  82. }
  83. /**
  84. * returns true if the Queue has been closed
  85. * however, this method will return false if the queue has been closed
  86. * using the close(true) method and the last element has yet not been received.
  87. * @return true if the queue has been closed
  88. */
  89. public boolean closed() {
  90. synchronized(mutex) {
  91. return closed;
  92. }
  93. }
  94. /**
  95. * adds an object to the tail of this queue
  96. * If the queue has been closed with close(true) no exception will be
  97. * thrown if the queue has not been flushed yet.
  98. * @param obj - the object to be added to the queue
  99. * @exception QueueClosedException exception if closed() returns true
  100. */
  101. public void add(Object obj) throws QueueClosedException {
  102. if(obj == null) {
  103. if(log.isErrorEnabled()) log.error("argument must not be null");
  104. return;
  105. }
  106. /*lock the queue from other threads*/
  107. synchronized(mutex) {
  108. if(closed)
  109. throw new QueueClosedException();
  110. if(this.num_markers > 0)
  111. throw new QueueClosedException("queue has been closed. You can not add more elements. " +
  112. "Waiting for removal of remaining elements.");
  113. addInternal(obj);
  114. /*wake up all the threads that are waiting for the lock to be released*/
  115. mutex.notifyAll();
  116. }
  117. }
  118. public void addAll(Collection c) throws QueueClosedException {
  119. if(c == null) {
  120. if(log.isErrorEnabled()) log.error("argument must not be null");
  121. return;
  122. }
  123. /*lock the queue from other threads*/
  124. synchronized(mutex) {
  125. if(closed)
  126. throw new QueueClosedException();
  127. if(this.num_markers > 0)
  128. throw new QueueClosedException("queue has been closed. You can not add more elements. " +
  129. "Waiting for removal of remaining elements.");
  130. Object obj;
  131. for(Iterator it=c.iterator(); it.hasNext();) {
  132. obj=it.next();
  133. if(obj != null)
  134. addInternal(obj);
  135. }
  136. /*wake up all the threads that are waiting for the lock to be released*/
  137. mutex.notifyAll();
  138. }
  139. }
  140. public void addAll(List<Object> list) throws QueueClosedException {
  141. if(list == null) {
  142. if(log.isErrorEnabled()) log.error("argument must not be null");
  143. return;
  144. }
  145. /*lock the queue from other threads*/
  146. synchronized(mutex) {
  147. if(closed)
  148. throw new QueueClosedException();
  149. if(this.num_markers > 0)
  150. throw new QueueClosedException("queue has been closed. You can not add more elements. " +
  151. "Waiting for removal of remaining elements.");
  152. for(Object obj: list) {
  153. if(obj != null)
  154. addInternal(obj);
  155. }
  156. /*wake up all the threads that are waiting for the lock to be released*/
  157. mutex.notifyAll();
  158. }
  159. }
  160. /**
  161. * Removes 1 element from head or <B>blocks</B>
  162. * until next element has been added or until queue has been closed
  163. * @return the first element to be taken of the queue
  164. */
  165. public Object remove() throws QueueClosedException {
  166. Object retval;
  167. synchronized(mutex) {
  168. /*wait as long as the queue is empty. return when an element is present or queue is closed*/
  169. while(size == 0) {
  170. if(closed)
  171. throw new QueueClosedException();
  172. try {
  173. mutex.wait();
  174. }
  175. catch(InterruptedException ex) {
  176. }
  177. }
  178. if(closed)
  179. throw new QueueClosedException();
  180. /*remove the head from the queue, if we make it to this point, retval should not be null !*/
  181. retval=removeInternal();
  182. if(retval == null)
  183. if(log.isErrorEnabled()) log.error("element was null, should never be the case");
  184. }
  185. /*
  186. * we ran into an Endmarker, which means that the queue was closed before
  187. * through close(true)
  188. */
  189. // if(retval == endMarker) {
  190. // close(false); // mark queue as closed
  191. // throw new QueueClosedException();
  192. // }
  193. return retval;
  194. }
  195. /**
  196. * Removes 1 element from the head.
  197. * If the queue is empty the operation will wait for timeout ms.
  198. * if no object is added during the timeout time, a Timout exception is thrown
  199. * (bela Aug 2009) Note that the semantics of remove(long timeout) are weird - the method waits until an element has
  200. * been added, but doesn't do so in a loop ! So if we have 10 threads waiting on an empty queue, and 1 thread
  201. * adds an element, all 10 threads will return (but only 1 will have the element), therefore 9 will throw
  202. * a TimeoutException ! If I change this to the 'correct' semantics, however (e.g. the method removeWait() below),
  203. * GMS.ViewHandler doesn't work correctly anymore. I won't change this now, as Queue will get removed anyway in 3.0.
  204. * @param timeout - the number of milli seconds this operation will wait before it times out
  205. * @return the first object in the queue
  206. */
  207. public Object remove(long timeout) throws QueueClosedException, TimeoutException {
  208. Object retval;
  209. synchronized(mutex) {
  210. if(closed)
  211. throw new QueueClosedException();
  212. /*if the queue size is zero, we want to wait until a new object is added*/
  213. if(size == 0) {
  214. try {
  215. /*release the mutex lock and wait no more than timeout ms*/
  216. mutex.wait(timeout);
  217. }
  218. catch(InterruptedException ex) {
  219. }
  220. }
  221. /*we either timed out, or got notified by the mutex lock object*/
  222. if(closed)
  223. throw new QueueClosedException();
  224. /*get the next value*/
  225. retval=removeInternal();
  226. /*null result means we timed out*/
  227. if(retval == null) throw new TimeoutException("timeout=" + timeout + "ms");
  228. /*if we reached an end marker we are going to close the queue*/
  229. // if(retval == endMarker) {
  230. // close(false);
  231. // throw new QueueClosedException();
  232. // }
  233. /*at this point we actually did receive a value from the queue, return it*/
  234. return retval;
  235. }
  236. }
  237. public Object removeWait(long timeout) throws QueueClosedException, TimeoutException {
  238. synchronized(mutex) {
  239. if(closed)
  240. throw new QueueClosedException();
  241. final long end_time=System.currentTimeMillis() + timeout;
  242. long wait_time, current_time;
  243. /*if the queue size is zero, we want to wait until a new object is added*/
  244. while(size == 0 && (current_time=System.currentTimeMillis()) < end_time) {
  245. if(closed)
  246. throw new QueueClosedException();
  247. try {
  248. /*release the mutex lock and wait no more than timeout ms*/
  249. wait_time=end_time - current_time; // guarnteed to be > 0
  250. mutex.wait(wait_time);
  251. }
  252. catch(InterruptedException ex) {
  253. }
  254. }
  255. /*we either timed out, or got notified by the mutex lock object*/
  256. if(closed)
  257. throw new QueueClosedException();
  258. /*get the next value*/
  259. Object retval=removeInternal();
  260. /*null result means we timed out*/
  261. if(retval == null) throw new TimeoutException("timeout=" + timeout + "ms");
  262. return retval;
  263. }
  264. }
  265. /**
  266. * removes a specific object from the queue.
  267. * the object is matched up using the Object.equals method.
  268. * @param obj the actual object to be removed from the queue
  269. */
  270. public void removeElement(Object obj) throws QueueClosedException {
  271. Element el, tmp_el;
  272. if(obj == null) {
  273. if(log.isErrorEnabled()) log.error("argument must not be null");
  274. return;
  275. }
  276. synchronized(mutex) {
  277. if(closed) /*check to see if the queue is closed*/
  278. throw new QueueClosedException();
  279. el=head;
  280. /*the queue is empty*/
  281. if(el == null) return;
  282. /*check to see if the head element is the one to be removed*/
  283. if(el.obj.equals(obj)) {
  284. /*the head element matched we will remove it*/
  285. head=el.next;
  286. el.next=null;
  287. el.obj=null;
  288. /*check if we only had one object left
  289. *at this time the queue becomes empty
  290. *this will set the tail=head=null
  291. */
  292. if(size == 1)
  293. tail=head; // null
  294. decrementSize();
  295. return;
  296. }
  297. /*look through the other elements*/
  298. while(el.next != null) {
  299. if(el.next.obj.equals(obj)) {
  300. tmp_el=el.next;
  301. if(tmp_el == tail) // if it is the last element, move tail one to the left (bela Sept 20 2002)
  302. tail=el;
  303. el.next.obj=null;
  304. el.next=el.next.next; // point to the el past the next one. can be null.
  305. tmp_el.next=null;
  306. tmp_el.obj=null;
  307. decrementSize();
  308. break;
  309. }
  310. el=el.next;
  311. }
  312. }
  313. }
  314. /**
  315. * returns the first object on the queue, without removing it.
  316. * If the queue is empty this object blocks until the first queue object has
  317. * been added
  318. * @return the first object on the queue
  319. */
  320. public Object peek() throws QueueClosedException {
  321. Object retval;
  322. synchronized(mutex) {
  323. while(size == 0) {
  324. if(closed)
  325. throw new QueueClosedException();
  326. try {
  327. mutex.wait();
  328. }
  329. catch(InterruptedException ex) {
  330. }
  331. }
  332. if(closed)
  333. throw new QueueClosedException();
  334. retval=(head != null)? head.obj : null;
  335. }
  336. if(retval == endMarker) {
  337. close(false); // mark queue as closed
  338. throw new QueueClosedException();
  339. }
  340. return retval;
  341. }
  342. /**
  343. * returns the first object on the queue, without removing it.
  344. * If the queue is empty this object blocks until the first queue object has
  345. * been added or the operation times out
  346. * @param timeout how long in milli seconds will this operation wait for an object to be added to the queue
  347. * before it times out
  348. * @return the first object on the queue
  349. */
  350. public Object peek(long timeout) throws QueueClosedException, TimeoutException {
  351. Object retval;
  352. synchronized(mutex) {
  353. if(size == 0) {
  354. if(closed)
  355. throw new QueueClosedException();
  356. try {
  357. mutex.wait(timeout);
  358. }
  359. catch(InterruptedException ex) {
  360. }
  361. }
  362. if(closed)
  363. throw new QueueClosedException();
  364. retval=head != null? head.obj : null;
  365. if(retval == null) throw new TimeoutException("timeout=" + timeout + "ms");
  366. if(retval == endMarker) {
  367. close(false);
  368. throw new QueueClosedException();
  369. }
  370. return retval;
  371. }
  372. }
  373. /** Removes all elements from the queue. This method can succeed even when the queue is closed */
  374. public void clear() {
  375. synchronized(mutex) {
  376. head=tail=null;
  377. size=0;
  378. num_markers=0;
  379. mutex.notifyAll();
  380. }
  381. }
  382. /**
  383. Marks the queues as closed. When an <code>add</code> or <code>remove</code> operation is
  384. attempted on a closed queue, an exception is thrown.
  385. @param flush_entries When true, a end-of-entries marker is added to the end of the queue.
  386. Entries may be added and removed, but when the end-of-entries marker
  387. is encountered, the queue is marked as closed. This allows to flush
  388. pending messages before closing the queue.
  389. */
  390. public void close(boolean flush_entries) {
  391. synchronized(mutex) {
  392. if(flush_entries && size > 0) {
  393. try {
  394. add(endMarker); // add an end-of-entries marker to the end of the queue
  395. num_markers++;
  396. }
  397. catch(QueueClosedException closed_ex) {
  398. }
  399. return;
  400. }
  401. closed=true;
  402. mutex.notifyAll();
  403. }
  404. }
  405. /** Waits until the queue has been closed. Returns immediately if already closed
  406. * @param timeout Number of milliseconds to wait. A value <= 0 means to wait forever
  407. */
  408. public void waitUntilClosed(long timeout) {
  409. synchronized(mutex) {
  410. if(closed)
  411. return;
  412. try {
  413. mutex.wait(timeout);
  414. }
  415. catch(InterruptedException e) {
  416. }
  417. }
  418. }
  419. /**
  420. * resets the queue.
  421. * This operation removes all the objects in the queue and marks the queue open
  422. */
  423. public void reset() {
  424. synchronized(mutex) {
  425. num_markers=0;
  426. if(!closed)
  427. close(false);
  428. size=0;
  429. head=null;
  430. tail=null;
  431. closed=false;
  432. mutex.notifyAll();
  433. }
  434. }
  435. /**
  436. * Returns all the elements of the queue
  437. * @return A copy of the queue
  438. */
  439. public LinkedList values() {
  440. LinkedList retval=new LinkedList();
  441. synchronized(mutex) {
  442. Element el=head;
  443. while(el != null) {
  444. retval.add(el.obj);
  445. el=el.next;
  446. }
  447. }
  448. return retval;
  449. }
  450. /**
  451. * returns the number of objects that are currently in the queue
  452. */
  453. public int size() {
  454. synchronized(mutex) {
  455. return size - num_markers;
  456. }
  457. }
  458. /**
  459. * prints the size of the queue
  460. */
  461. public String toString() {
  462. return "Queue (" + size() + ") elements";
  463. }
  464. /* ------------------------------------- Private Methods ----------------------------------- */
  465. private final void addInternal(Object obj) {
  466. /*create a new linked list element*/
  467. Element el=new Element(obj);
  468. /*check the first element*/
  469. if(head == null) {
  470. /*the object added is the first element*/
  471. /*set the head to be this object*/
  472. head=el;
  473. /*set the tail to be this object*/
  474. tail=head;
  475. /*set the size to be one, since the queue was empty*/
  476. size=1;
  477. }
  478. else {
  479. /*add the object to the end of the linked list*/
  480. tail.next=el;
  481. /*set the tail to point to the last element*/
  482. tail=el;
  483. /*increase the size*/
  484. size++;
  485. }
  486. }
  487. /**
  488. * Removes the first element. Returns null if no elements in queue.
  489. * Always called with mutex locked (we don't have to lock mutex ourselves)
  490. */
  491. private Object removeInternal() {
  492. Element retval;
  493. Object obj;
  494. /*if the head is null, the queue is empty*/
  495. if(head == null)
  496. return null;
  497. retval=head; // head must be non-null now
  498. head=head.next;
  499. if(head == null)
  500. tail=null;
  501. decrementSize();
  502. if(head != null && head.obj == endMarker) {
  503. closed=true;
  504. mutex.notifyAll();
  505. }
  506. retval.next=null;
  507. obj=retval.obj;
  508. retval.obj=null;
  509. return obj;
  510. }
  511. /** Doesn't need to be synchronized; is always called from synchronized methods */
  512. final private void decrementSize() {
  513. size--;
  514. if(size < 0)
  515. size=0;
  516. }
  517. /* ---------------------------------- End of Private Methods -------------------------------- */
  518. }