a8f3a6d48633602d1f708bb6838c3a0367c93a6e
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / project / FileContentQueue.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
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 package com.intellij.openapi.project;
17
18 import com.intellij.ide.caches.FileContent;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.progress.ProcessCanceledException;
22 import com.intellij.openapi.progress.ProgressIndicator;
23 import com.intellij.openapi.progress.ProgressManager;
24 import com.intellij.openapi.vfs.VirtualFile;
25 import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28
29 import java.io.IOException;
30 import java.util.Collection;
31 import java.util.concurrent.ArrayBlockingQueue;
32
33 /**
34 * @author peter
35 */
36 @SuppressWarnings({"SynchronizeOnThis"})
37 public class FileContentQueue {
38   private static final Logger LOG = Logger.getInstance("#com.intellij.ide.startup.FileContentQueue");
39   private static final long SIZE_THRESHOLD = 1024*1024;
40   private long myTotalSize;
41
42   private final ArrayBlockingQueue<FileContent> myQueue = new ArrayBlockingQueue<FileContent>(256);
43   private FileContent myPushbackBuffer;
44
45   public void queue(final Collection<VirtualFile> files, @Nullable final ProgressIndicator indicator) {
46     final Runnable contentLoadingRunnable = new Runnable() {
47       public void run() {
48         try {
49           for (VirtualFile file : files) {
50             if (indicator != null) {
51               indicator.checkCanceled();
52             }
53             put(file);
54           }
55
56           // put end-of-queue marker only if not canceled
57           try {
58             myQueue.put(new FileContent(null));
59           }
60           catch (InterruptedException e) {
61             LOG.error(e);
62           }
63         }
64         catch (ProcessCanceledException e) {
65           // Do nothing, exit the thread.
66         }
67         catch (InterruptedException e) {
68           LOG.error(e);
69         }
70       }
71     };
72
73     ApplicationManager.getApplication().executeOnPooledThread(contentLoadingRunnable);
74   }
75
76   private void put(VirtualFile file) throws InterruptedException {
77     final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
78
79     FileContent content = new FileContent(file);
80
81     if (file.isValid()) {
82       final long contentLength = content.getLength();
83       boolean counterUpdated = false;
84       try {
85         if (contentLength < PersistentFS.MAX_INTELLISENSE_FILESIZE) {
86           synchronized (this) {
87             while (myTotalSize > SIZE_THRESHOLD) {
88               if (indicator != null) {
89                 indicator.checkCanceled();
90               }
91               wait(300);
92             }
93             myTotalSize += contentLength;
94             counterUpdated = true;
95           }
96
97           content.getBytes(); // Reads the content bytes and caches them.
98         }
99       }
100       catch (IOException e) {
101         LOG.info(e);
102         if (counterUpdated) {
103           synchronized (this) {
104             myTotalSize -= contentLength;   // revert size counter
105             notifyAll();
106           }
107         }
108         content.setEmptyContent();
109       }
110       catch (ProcessCanceledException e) {
111         if (counterUpdated) {
112           synchronized (this) {
113             myTotalSize -= contentLength;   // revert size counter
114             notifyAll();
115           }
116         }
117         throw e;
118       }
119       catch (InterruptedException e) {
120         if (counterUpdated) {
121           synchronized (this) {
122             myTotalSize -= contentLength;   // revert size counter
123             notifyAll();
124           }
125         }
126         return;
127       }
128       catch (Throwable e) {
129         LOG.error(e);
130       }
131     }
132     else {
133       content.setEmptyContent();
134     }
135
136     myQueue.put(content);
137   }
138
139   public FileContent take() {
140     FileContent result;
141     synchronized (this) {
142       if (myPushbackBuffer != null) {
143         result = myPushbackBuffer;
144         myPushbackBuffer = null;
145         return result;
146       }
147     }
148
149     try {
150       result = myQueue.take();
151     }
152     catch (InterruptedException e) {
153       throw new RuntimeException(e);
154     }
155
156     final VirtualFile file = result.getVirtualFile();
157     if (file == null) {
158       return null;
159     }
160     if (result.getLength() < PersistentFS.MAX_INTELLISENSE_FILESIZE) {
161       synchronized (this) {
162         try {
163           myTotalSize -= result.getLength();
164         }
165         finally {
166           notifyAll();
167         }
168       }
169     }
170
171     return result;
172   }
173
174   public synchronized void pushback(@NotNull FileContent content) {
175     LOG.assertTrue(myPushbackBuffer == null, "Pushback buffer is already full");
176     myPushbackBuffer = content;
177   }
178 }