View Javadoc

1   /**
2    * 
3    */
4   package org.sourceforge.jemm.database.components;
5   
6   import java.util.Iterator;
7   import java.util.Set;
8   import java.util.concurrent.CountDownLatch;
9   import java.util.concurrent.TimeUnit;
10  
11  import org.apache.log4j.Logger;
12  import org.sourceforge.jemm.database.ObjectAccessor;
13  import org.sourceforge.jemm.database.components.interfaces.DBGarbageSweeper;
14  import org.sourceforge.jemm.database.components.interfaces.DBObjectHandler;
15  import org.sourceforge.jemm.database.components.se.StorageEngineGCIF;
16  import org.sourceforge.jemm.database.components.types.GCInfo;
17  import org.sourceforge.jemm.types.ID;
18  import org.sourceforge.jemm.util.LockManager;
19  
20  /**
21   * @author Rory Graves
22   *
23   */
24  public class DefaultDBGarbageSweeper implements DBGarbageSweeper {
25  	
26  	private static final Logger LOG = Logger.getLogger(DefaultDBGarbageSweeper.class);
27  	
28      private final ObjectAccessor objectAccessor;
29      private final StorageEngineGCIF gcIF;    
30      private final LockManager<ID> gcLock = new LockManager<ID>();
31      
32      private GCMode gcMode;
33      private ObjectStatusListenerImpl osListener = new ObjectStatusListenerImpl();
34      
35      private volatile boolean shutdown = false;
36  
37  	private final CountDownLatch shutdownLatch = new CountDownLatch(1);
38  	
39  
40      private class ObjectStatusListenerImpl implements ObjectStatusListener {
41  		@Override
42  		public void initialisationComplete(ID id) {
43  			try {
44  				gcLock.acquire(id);
45  				GCInfo info = gcIF.get(id);
46  				info.initFinished();
47  				gcIF.save(info);				
48  			}finally {
49  				gcLock.release(id);
50  			}
51  		}
52  
53  		@Override
54  		public void objectCreated(ID id) {
55  			try {
56  				gcLock.acquire(id);
57  				GCInfo info = new GCInfo(id);
58  				gcIF.save(info);				
59  			}finally {
60  				gcLock.release(id);
61  			}
62  		}
63  
64  		@Override
65  		public void rootRefAdded(ID id) {
66  			try {
67  				gcLock.acquire(id);
68  				GCInfo info = gcIF.get(id);
69  				info.incrementRootCount();
70  				gcIF.save(info);				
71  			}finally {
72  				gcLock.release(id);
73  			}
74  		}
75  
76  		@Override
77  		public void rootRefCleared(ID id) {
78  			try {
79  				gcLock.acquire(id);
80  				GCInfo info = gcIF.get(id);
81  				info.decrementRootCount();
82  				gcIF.save(info);				
83  			}finally {
84  				gcLock.release(id);
85  			}
86  		}
87  
88  		@Override
89  		public void clientRefAdded(ID id) {
90  			try {
91  				gcLock.acquire(id);
92  				GCInfo info = gcIF.get(id);
93  				info.incrementClientRefCount();
94  				gcIF.save(info);				
95  			}finally {
96  				gcLock.release(id);
97  			}
98  		}
99  
100 		@Override
101 		public void clientRefCleared(ID id) {
102 			try {
103 				gcLock.acquire(id);
104 				GCInfo info = gcIF.get(id);
105 				info.decrementClientRefCount();
106 				gcIF.save(info);				
107 			}finally {
108 				gcLock.release(id);
109 			}
110 		}
111     }
112     
113     private class Sweeper implements Runnable {
114 
115         @Override
116         public void run() {
117         	boolean done = false;
118             while(!done) {
119             	if(!shutdown)
120             		pause(100);
121 
122             	if(shutdown) {
123                 	shutdownLatch.countDown();
124                 	done = true;
125                 } else {
126                 	try {
127                 		internalRunGCCycle();
128                 	}catch(Exception e) {
129                 		LOG.error("Error generated whilst performing GC",e);
130                 	}
131             	}
132             }            
133         }
134 
135         private void pause(long time) {
136             try {
137                 Thread.sleep(time);                    
138             } catch (InterruptedException e) {
139                 e.printStackTrace();
140             }
141         }
142     }
143     
144     public DefaultDBGarbageSweeper(StorageEngine storageEngine,DBObjectHandler objectHandler,GCMode gcMode) {
145     	this.gcIF = storageEngine.getGCIF();
146         this.objectAccessor = objectHandler;
147         this.gcMode = gcMode;
148         if(gcMode == GCMode.AUTO) {
149             Thread thread = new Thread(new Sweeper());
150             thread.setDaemon(true);
151             thread.start();
152         }
153     }
154     
155     private int internalRunGCCycle() {
156         gcInitialise();
157         boolean modified;
158         do {
159         	modified = gcVisitPass(); 
160         }while(modified);
161         
162         int cleaned = gcCleanup();
163         return cleaned;
164     }
165     
166     private int gcCleanup() {
167     	int count = 0;
168     	for (Iterator<ID> itr = objectAccessor.idIterator(); itr.hasNext();) {
169 			ID id = itr.next();
170 			try {
171 				gcLock.acquire(id);
172 				GCInfo info = gcIF.get(id);
173 				if(!info.isLive()) {
174 	        		objectAccessor.removeObject(id);
175 					gcIF.remove(id);
176 	        		count++;
177 				}
178 			}finally {
179 				gcLock.release(id);
180 			}
181 		}
182     	
183     	return count;    	
184     }
185     
186     private void gcInitialise() {
187     	for (Iterator<ID> itr = objectAccessor.idIterator(); itr.hasNext();) {
188 			ID id = itr.next();
189 		
190 			try {
191 				gcLock.acquire(id);
192 				GCInfo info = gcIF.get(id);
193 				if(info.gcInitialise())
194 					gcIF.save(info);
195 			}finally {
196 				gcLock.release(id);
197 			}
198 		}
199     }
200 
201     private boolean gcVisitPass() {
202     	boolean changed = false;
203     	for (Iterator<ID> itr = objectAccessor.idIterator(); itr.hasNext();) {
204 			ID id = itr.next();
205 			boolean objChanged = visitObject(id);
206 			
207 			if(objChanged) {
208 				touchChildren(id);
209 				changed = true;
210 			}
211 		}
212         return changed;
213     }
214 
215     private boolean visitObject(ID id) {
216     	boolean objChanged = false;
217 		try {
218 			gcLock.acquire(id);
219 			GCInfo info = gcIF.get(id);
220 	        if(info.getGCStatus() == GCStatus.GREY) {
221 	            info.setGCStatus(GCStatus.BLACK);
222 				gcIF.save(info);				
223 	            objChanged = true;
224 	        }
225 							
226 		}finally {
227 			gcLock.release(id);
228 		}
229 		return objChanged;
230     }
231     
232     private void touchObect(ID id) {
233 		try {
234 			gcLock.acquire(id);
235 			GCInfo info = gcIF.get(id);
236 			boolean changed = info.touch();
237 			if(changed)
238 				gcIF.save(info);				
239 		} finally {
240 			gcLock.release(id);
241 		}
242     }
243     
244     private void touchChildren(ID id) {
245     	Set<ID> childrenIds = objectAccessor.getObjectChildren(id);
246     	
247     	if(childrenIds.size() > 0)
248     		for (ID childId : childrenIds)
249     			touchObect(childId);
250 	}
251 
252 	public void runGCCycle() {
253         if(gcMode == GCMode.AUTO)
254             throw new IllegalStateException("Cannot manually trigger MemoryStore gc whilst running in auto mode");
255 
256         internalRunGCCycle();
257     }
258 
259 	public ObjectStatusListener getObjectStatusListener() {
260 		return osListener;
261 	}
262 
263 	@Override
264 	public void shutdown() {
265 		shutdown = true;
266 		if(gcMode == GCMode.AUTO)
267 			try {
268 				if(!shutdownLatch.await(5000,TimeUnit.MILLISECONDS))
269 					LOG.error("Timeout whilst waiting for GC shutdown");
270 			} catch (InterruptedException e) {
271 				LOG.error("InterruptedException thrown whilst waiting for shutdown",e);
272 			}
273 	}
274 }