1+ /*
2+ * Copyright (c) 2008 - 2013 MongoDB Inc., Inc. <http://mongodb.com>
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+ package com .mongodb ;
18+
19+ import java .util .Iterator ;
20+ import java .util .Queue ;
21+ import java .util .concurrent .ConcurrentLinkedQueue ;
22+ import java .util .concurrent .Semaphore ;
23+ import java .util .concurrent .TimeUnit ;
24+
25+ class ConcurrentPool <T > {
26+
27+ private final int maxSize ;
28+ private final ItemFactory <T > itemFactory ;
29+
30+ private final Queue <T > available = new ConcurrentLinkedQueue <T >();
31+ private final Semaphore permits ;
32+ private volatile boolean closed ;
33+
34+ /**
35+ * Factory for creating and closing pooled items.
36+ *
37+ * @param <T>
38+ */
39+ interface ItemFactory <T > {
40+ T create ();
41+
42+ void close (T t );
43+
44+ boolean shouldPrune (T t );
45+ }
46+
47+ /**
48+ * Initializes a new pool of objects.
49+ *
50+ * @param maxSize max to hold to at any given time. if < 0 then no limit
51+ * @param itemFactory factory used to create and close items in the pool
52+ */
53+ public ConcurrentPool (final int maxSize , final ItemFactory <T > itemFactory ) {
54+ this .maxSize = maxSize ;
55+ this .itemFactory = itemFactory ;
56+ permits = new Semaphore (maxSize , true );
57+ }
58+
59+ /**
60+ * Return an instance of T to the pool. This method simply calls {@code release(t, false)}
61+ *
62+ * @param t item to return to the pool
63+ */
64+ public void release (final T t ) {
65+ release (t , false );
66+ }
67+
68+ /**
69+ * call done when you are done with an object from the pool if there is room and the object is ok will get added
70+ *
71+ * @param t item to return to the pool
72+ * @param prune true if the item should be closed, false if it should be put back in the pool
73+ */
74+ public void release (final T t , final boolean prune ) {
75+ if (t == null ) {
76+ throw new IllegalArgumentException ("Can not return a null item to the pool" );
77+ }
78+ if (closed ) {
79+ close (t );
80+ return ;
81+ }
82+
83+ if (prune ) {
84+ close (t );
85+ } else {
86+ available .add (t );
87+ }
88+
89+ releasePermit ();
90+ }
91+
92+ /**
93+ * Gets an object from the pool. This method will block until a permit is available.
94+ *
95+ * @return An object from the pool.
96+ */
97+ public T get () {
98+ return get (-1 , TimeUnit .MILLISECONDS );
99+ }
100+
101+ /**
102+ * Gets an object from the pool - will block if none are available
103+ *
104+ * @param timeout negative - forever 0 - return immediately no matter what positive ms to wait
105+ * @param timeUnit the time unit of the timeout
106+ * @return An object from the pool, or null if can't get one in the given waitTime
107+ * @throws MongoTimeoutException if the timeout has been exceeded
108+ */
109+ public T get (final long timeout , final TimeUnit timeUnit ) {
110+ if (closed ) {
111+ throw new IllegalStateException ("The pool is closed" );
112+ }
113+
114+ if (!acquirePermit (timeout , timeUnit )) {
115+ throw new MongoTimeoutException (String .format ("Timeout waiting for a pooled item after %d %s" , timeout , timeUnit ));
116+ }
117+
118+ T t = available .poll ();
119+ if (t == null ) {
120+ t = createNewAndReleasePermitIfFailure ();
121+ }
122+
123+ return t ;
124+ }
125+
126+ public void prune () {
127+ int currentAvailableCount = getAvailableCount ();
128+ for (int numAttempts = 0 ; numAttempts < currentAvailableCount ; numAttempts ++) {
129+ if (!acquirePermit (10 , TimeUnit .MILLISECONDS )) {
130+ break ;
131+ }
132+ T cur = available .poll ();
133+ if (cur == null ) {
134+ releasePermit ();
135+ break ;
136+ }
137+ release (cur , itemFactory .shouldPrune (cur ));
138+ }
139+ }
140+
141+ public void ensureMinSize (final int minSize ) {
142+ while (getCount () < minSize ) {
143+ if (!acquirePermit (10 , TimeUnit .MILLISECONDS )) {
144+ break ;
145+ }
146+ release (createNewAndReleasePermitIfFailure ());
147+ }
148+ }
149+
150+ private T createNewAndReleasePermitIfFailure () {
151+ try {
152+ T newMember = itemFactory .create ();
153+ if (newMember == null ) {
154+ throw new MongoInternalException ("The factory for the pool created a null item" );
155+ }
156+ return newMember ;
157+ } catch (RuntimeException e ) {
158+ permits .release ();
159+ throw e ;
160+ }
161+ }
162+
163+ protected boolean acquirePermit (final long timeout , final TimeUnit timeUnit ) {
164+ try {
165+ if (closed ) {
166+ return false ;
167+ } else if (timeout >= 0 ) {
168+ return permits .tryAcquire (timeout , timeUnit );
169+ } else {
170+ permits .acquire ();
171+ return true ;
172+ }
173+ } catch (InterruptedException e ) {
174+ throw new MongoInterruptedException ("Interrupted acquiring a permit to retrieve an item from the pool " , e );
175+ }
176+ }
177+
178+ protected void releasePermit () {
179+ permits .release ();
180+ }
181+
182+ /**
183+ * Clears the pool of all objects.
184+ */
185+ public void close () {
186+ closed = true ;
187+ Iterator <T > iter = available .iterator ();
188+ while (iter .hasNext ()) {
189+ T t = iter .next ();
190+ close (t );
191+ iter .remove ();
192+ }
193+ }
194+
195+ public int getMaxSize () {
196+ return maxSize ;
197+ }
198+
199+ public int getInUseCount () {
200+ return maxSize - permits .availablePermits ();
201+ }
202+
203+ public int getAvailableCount () {
204+ return available .size ();
205+ }
206+
207+ public int getCount () {
208+ return getInUseCount () + getAvailableCount ();
209+ }
210+
211+ public String toString () {
212+ StringBuilder buf = new StringBuilder ();
213+ buf .append ("pool: " )
214+ .append (" maxSize: " ).append (maxSize )
215+ .append (" availableCount " ).append (getAvailableCount ())
216+ .append (" inUseCount " ).append (getInUseCount ());
217+ return buf .toString ();
218+ }
219+
220+ // swallow exceptions from ItemFactory.close()
221+ private void close (final T t ) {
222+ try {
223+ itemFactory .close (t );
224+ } catch (RuntimeException e ) {
225+ // ItemFactory.close() really should not throw
226+ }
227+ }
228+ }
0 commit comments