• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  ** Copyright 2011, The Android Open Source Project
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.android.ide.eclipse.gldebugger;
18 
19 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message;
20 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message.Function;
21 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message.Type;
22 import com.android.sdklib.util.SparseArray;
23 
24 import java.io.DataInputStream;
25 import java.io.DataOutputStream;
26 import java.io.EOFException;
27 import java.io.FileInputStream;
28 import java.io.IOException;
29 import java.net.Socket;
30 import java.nio.ByteOrder;
31 import java.util.ArrayList;
32 
33 abstract interface ProcessMessage {
processMessage(final MessageQueue queue, final Message msg)34     abstract boolean processMessage(final MessageQueue queue, final Message msg)
35             throws IOException;
36 }
37 
38 public class MessageQueue implements Runnable {
39 
40     private boolean running = false;
41     private ByteOrder byteOrder;
42     private FileInputStream file; // if null, create and use socket
43     Thread thread = null;
44     private final ProcessMessage[] processes;
45     private ArrayList<Message> complete = new ArrayList<Message>(); // synchronized
46     private ArrayList<Message> commands = new ArrayList<Message>(); // synchronized
47     private GLFramesView sampleView;
48 
MessageQueue(GLFramesView sampleView, final ProcessMessage[] processes)49     public MessageQueue(GLFramesView sampleView, final ProcessMessage[] processes) {
50         this.sampleView = sampleView;
51         this.processes = processes;
52     }
53 
start(final ByteOrder byteOrder, final FileInputStream file)54     public void start(final ByteOrder byteOrder, final FileInputStream file) {
55         if (running)
56             return;
57         running = true;
58         this.byteOrder = byteOrder;
59         this.file = file;
60         thread = new Thread(this);
61         thread.start();
62     }
63 
stop()64     public void stop() {
65         if (!running)
66             return;
67         running = false;
68     }
69 
isRunning()70     public boolean isRunning() {
71         return running;
72     }
73 
sendCommands(final int contextId)74     private void sendCommands(final int contextId) throws IOException {
75         synchronized (commands) {
76             for (int i = 0; i < commands.size(); i++) {
77                 Message command = commands.get(i);
78                 if (command.getContextId() == contextId || command.getContextId() == 0) {
79                     sendMessage(commands.remove(i));
80                     i--;
81                 }
82             }
83         }
84     }
85 
addCommand(Message command)86     public void addCommand(Message command) {
87         synchronized (commands) {
88             commands.add(command);
89         }
90     }
91 
92     // these should only be accessed from the network thread;
93     // access call chain starts with run()
94     private DataInputStream dis = null;
95     private DataOutputStream dos = null;
96     private SparseArray<ArrayList<Message>> incoming = new SparseArray<ArrayList<Message>>();
97 
98     @Override
run()99     public void run() {
100         Socket socket = null;
101         if (file == null)
102             try {
103                 socket = new Socket();
104                 socket.connect(new java.net.InetSocketAddress("127.0.0.1", Integer
105                         .parseInt(sampleView.actionPort.getText())));
106                 dis = new DataInputStream(socket.getInputStream());
107                 dos = new DataOutputStream(socket.getOutputStream());
108             } catch (Exception e) {
109                 running = false;
110                 Error(e);
111             }
112         else
113             dis = new DataInputStream(file);
114 
115         while (running) {
116             try {
117                 if (file != null && file.available() == 0) {
118                     running = false;
119                     break;
120                 }
121             } catch (IOException e1) {
122                 e1.printStackTrace();
123                 assert false;
124             }
125 
126             Message msg = null;
127             if (incoming.size() > 0) { // find queued incoming
128                 for (int i = 0; i < incoming.size(); i++) {
129                     final ArrayList<Message> messages = incoming.valueAt(i);
130                     if (messages.size() > 0) {
131                         msg = messages.remove(0);
132                         break;
133                     }
134                 }
135             }
136             try {
137                 if (null == msg) // get incoming from network
138                     msg = receiveMessage(dis);
139                 processMessage(dos, msg);
140             } catch (IOException e) {
141                 Error(e);
142                 running = false;
143                 break;
144             }
145         }
146 
147         try {
148             if (socket != null)
149                 socket.close();
150             else
151                 file.close();
152         } catch (IOException e) {
153             Error(e);
154             running = false;
155         }
156 
157     }
158 
putMessage(final Message msg)159     private void putMessage(final Message msg) {
160         ArrayList<Message> existing = incoming.get(msg.getContextId());
161         if (existing == null)
162             incoming.put(msg.getContextId(), existing = new ArrayList<Message>());
163         existing.add(msg);
164     }
165 
receiveMessage(final int contextId)166     Message receiveMessage(final int contextId) throws IOException {
167         Message msg = receiveMessage(dis);
168         while (msg.getContextId() != contextId) {
169             putMessage(msg);
170             msg = receiveMessage(dis);
171         }
172         return msg;
173     }
174 
sendMessage(final Message msg)175     void sendMessage(final Message msg) throws IOException {
176         sendMessage(dos, msg);
177     }
178 
179     // should only be used by DefaultProcessMessage
180     private SparseArray<Message> partials = new SparseArray<Message>();
181 
getPartialMessage(final int contextId)182     Message getPartialMessage(final int contextId) {
183         return partials.get(contextId);
184     }
185 
186     // used to add BeforeCall to complete if it was skipped
completePartialMessage(final int contextId)187     void completePartialMessage(final int contextId) {
188         final Message msg = partials.get(contextId);
189         partials.remove(contextId);
190         assert msg != null;
191         assert msg.getType() == Type.BeforeCall;
192         if (msg != null)
193             synchronized (complete) {
194                 complete.add(msg);
195             }
196     }
197 
198     // can be used by other message processor as default processor
defaultProcessMessage(final Message msg, boolean expectResponse, boolean sendResponse)199     void defaultProcessMessage(final Message msg, boolean expectResponse,
200             boolean sendResponse) throws IOException {
201         final int contextId = msg.getContextId();
202         if (msg.getType() == Type.BeforeCall) {
203             if (sendResponse) {
204                 final Message.Builder builder = Message.newBuilder();
205                 builder.setContextId(contextId);
206                 builder.setType(Type.Response);
207                 builder.setExpectResponse(expectResponse);
208                 builder.setFunction(Function.CONTINUE);
209                 sendMessage(dos, builder.build());
210             }
211             assert partials.indexOfKey(contextId) < 0;
212             partials.put(contextId, msg);
213         } else if (msg.getType() == Type.AfterCall) {
214             if (sendResponse) {
215                 final Message.Builder builder = Message.newBuilder();
216                 builder.setContextId(contextId);
217                 builder.setType(Type.Response);
218                 builder.setExpectResponse(expectResponse);
219                 builder.setFunction(Function.SKIP);
220                 sendMessage(dos, builder.build());
221             }
222             assert partials.indexOfKey(contextId) >= 0;
223             final Message before = partials.get(contextId);
224             partials.remove(contextId);
225             assert before.getFunction() == msg.getFunction();
226             final Message completed = before.toBuilder().mergeFrom(msg)
227                     .setType(Type.CompleteCall).build();
228             synchronized (complete) {
229                 complete.add(completed);
230             }
231         } else if (msg.getType() == Type.CompleteCall) {
232             // this type should only be encountered on client after processing
233             assert file != null;
234             assert !msg.getExpectResponse();
235             assert !sendResponse;
236             assert partials.indexOfKey(contextId) < 0;
237             synchronized (complete) {
238                 complete.add(msg);
239             }
240         } else if (msg.getType() == Type.Response && msg.getFunction() == Function.SETPROP) {
241             synchronized (complete) {
242                 complete.add(msg);
243             }
244         } else
245             assert false;
246     }
247 
248     public Message removeCompleteMessage(int contextId) {
249         synchronized (complete) {
250             if (complete.size() == 0)
251                 return null;
252             if (0 == contextId) // get a message for any context
253                 return complete.remove(0);
254             for (int i = 0; i < complete.size(); i++) {
255                 Message msg = complete.get(i);
256                 if (msg.getContextId() == contextId) {
257                     complete.remove(i);
258                     return msg;
259                 }
260             }
261         }
262         return null;
263     }
264 
265     private Message receiveMessage(final DataInputStream dis)
266             throws IOException {
267         int len = 0;
268         try {
269             len = dis.readInt();
270             if (byteOrder == ByteOrder.LITTLE_ENDIAN)
271                 len = Integer.reverseBytes(len); // readInt reads BIT_ENDIAN
272         } catch (EOFException e) {
273             Error(new Exception("EOF"));
274         }
275         byte[] buffer = new byte[len];
276         int readLen = 0;
277         while (readLen < len) {
278             int read = -1;
279             try {
280                 read = dis.read(buffer, readLen, len - readLen);
281             } catch (EOFException e) {
282                 Error(new Exception("EOF"));
283             }
284             if (read < 0) {
285                 Error(new Exception("read length = " + read));
286                 return null;
287             } else
288                 readLen += read;
289         }
290         Message msg = Message.parseFrom(buffer);
291         sendCommands(msg.getContextId());
292         return msg;
293     }
294 
295     private void sendMessage(final DataOutputStream dos, final Message message)
296             throws IOException {
297         if (dos == null)
298             return;
299         assert message.getFunction() != Function.NEG;
300         final byte[] data = message.toByteArray();
301         if (byteOrder == ByteOrder.BIG_ENDIAN)
302             dos.writeInt(data.length);
303         else
304             dos.writeInt(Integer.reverseBytes(data.length));
305         dos.write(data);
306     }
307 
308     private void processMessage(final DataOutputStream dos, final Message msg) throws IOException {
309         if (msg.getExpectResponse()) {
310             assert dos != null; // readonly source cannot expectResponse
311             for (ProcessMessage process : processes)
312                 if (process.processMessage(this, msg))
313                     return;
314             defaultProcessMessage(msg, msg.getExpectResponse(), msg.getExpectResponse());
315         } else
316             defaultProcessMessage(msg, msg.getExpectResponse(), msg.getExpectResponse());
317     }
318 
319     void Error(Exception e) {
320         sampleView.showError(e);
321     }
322 }
323