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