blob: fecc24a351089b2724be59f09d34aca7a1f09536 [file] [log] [blame]
/*
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
package com.android.glesv2debugger;
import com.android.glesv2debugger.DebuggerMessage.Message;
import com.android.glesv2debugger.DebuggerMessage.Message.Function;
import com.android.glesv2debugger.DebuggerMessage.Message.Type;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
public class MessageQueue implements Runnable {
boolean running = false;
Thread thread = null;
ArrayList<Message> complete = new ArrayList<Message>(); // need synchronized
ArrayList<Message> commands = new ArrayList<Message>(); // need synchronized
SampleView sampleView;
public MessageQueue(SampleView sampleView) {
this.sampleView = sampleView;
}
public void Start() {
if (running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
public void Stop() {
if (!running)
return;
running = false;
}
public boolean IsRunning() {
return running;
}
void SendCommands(final int contextId) throws IOException {
synchronized (commands) {
for (int i = 0; i < commands.size(); i++) {
Message command = commands.get(i);
if (command.getContextId() == contextId || command.getContextId() == 0) {
SendMessage(commands.remove(i));
i--;
}
}
}
}
public void AddCommand(Message command) {
synchronized (commands) {
commands.add(command);
}
}
// these should only be accessed from the network thread;
// access call chain starts with run()
private DataInputStream dis = null;
private DataOutputStream dos = null;
private HashMap<Integer, ArrayList<Message>> incoming = new HashMap<Integer, ArrayList<Message>>();
@Override
public void run() {
Socket socket = new Socket();
try {
socket.connect(new java.net.InetSocketAddress("127.0.0.1", Integer
.parseInt(sampleView.actionPort.getText())));
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
} catch (Exception e) {
running = false;
Error(e);
}
while (running) {
Message msg = null;
if (incoming.size() > 0) { // find queued incoming
for (ArrayList<Message> messages : incoming.values())
if (messages.size() > 0) {
msg = messages.remove(0);
break;
}
}
try {
if (null == msg) // get incoming from network
msg = ReceiveMessage(dis);
ProcessMessage(dos, msg);
} catch (IOException e) {
Error(e);
running = false;
break;
}
}
try {
socket.close();
} catch (IOException e) {
Error(e);
running = false;
}
}
private void PutMessage(final Message msg) {
ArrayList<Message> existing = incoming.get(msg.getContextId());
if (existing == null)
incoming.put(msg.getContextId(), existing = new ArrayList<Message>());
existing.add(msg);
}
Message ReceiveMessage(final int contextId) throws IOException {
Message msg = ReceiveMessage(dis);
while (msg.getContextId() != contextId) {
PutMessage(msg);
msg = ReceiveMessage(dis);
}
return msg;
}
void SendMessage(final Message msg) throws IOException {
SendMessage(dos, msg);
}
// should only used by DefaultProcessMessage
private HashMap<Integer, Message> partials = new HashMap<Integer, Message>();
Message GetPartialMessage(final int contextId) {
return partials.get(contextId);
}
// used to add BeforeCall to complete if it was skipped
void CompletePartialMessage(final int contextId) {
final Message msg = partials.remove(contextId);
assert msg != null;
assert msg.getType() == Type.BeforeCall;
synchronized (complete) {
complete.add(msg);
}
}
// can be used by other message processor as default processor
void DefaultProcessMessage(final Message msg, boolean expectResponse,
boolean sendResponse)
throws IOException {
final int contextId = msg.getContextId();
final Message.Builder builder = Message.newBuilder();
builder.setContextId(contextId);
builder.setType(Type.Response);
builder.setExpectResponse(expectResponse);
if (msg.getType() == Type.BeforeCall) {
if (sendResponse) {
builder.setFunction(Function.CONTINUE);
SendMessage(dos, builder.build());
}
assert !partials.containsKey(contextId);
partials.put(contextId, msg);
} else if (msg.getType() == Type.AfterCall) {
if (sendResponse) {
builder.setFunction(Function.CONTINUE);
SendMessage(dos, builder.build());
}
assert partials.containsKey(contextId);
final Message before = partials.remove(contextId);
assert before.getFunction() == msg.getFunction();
final Message completed = before.toBuilder().mergeFrom(msg).build();
synchronized (complete) {
complete.add(completed);
}
} else
assert false;
}
public Message RemoveCompleteMessage(int contextId) {
synchronized (complete) {
if (complete.size() == 0)
return null;
if (0 == contextId) // get a message for any context
return complete.remove(0);
for (int i = 0; i < complete.size(); i++) {
Message msg = complete.get(i);
if (msg.getContextId() == contextId) {
complete.remove(i);
return msg;
}
}
}
return null;
}
private Message ReceiveMessage(final DataInputStream dis)
throws IOException {
int len = 0;
try {
len = dis.readInt();
} catch (EOFException e) {
Error(new Exception("EOF"));
}
byte[] buffer = new byte[len];
int readLen = 0;
while (readLen < len) {
int read = -1;
try {
read = dis.read(buffer, readLen, len - readLen);
} catch (EOFException e) {
Error(new Exception("EOF"));
}
if (read < 0) {
Error(new Exception("read length = " + read));
return null;
} else
readLen += read;
}
Message msg = Message.parseFrom(buffer);
SendCommands(msg.getContextId());
return msg;
}
private void SendMessage(final DataOutputStream dos, final Message message)
throws IOException {
assert message.getFunction() != Function.NEG;
final byte[] data = message.toByteArray();
dos.writeInt(data.length);
dos.write(data);
}
private void ProcessMessage(final DataOutputStream dos, final Message msg) throws IOException {
if (msg.getExpectResponse()) {
if (sampleView.shaderEditor.ProcessMessage(this, msg))
return;
else if (sampleView.breakpointOption.ProcessMessage(this, msg))
return;
else
DefaultProcessMessage(msg, msg.getExpectResponse(), msg.getExpectResponse());
} else
DefaultProcessMessage(msg, msg.getExpectResponse(), msg.getExpectResponse());
}
void Error(Exception e) {
sampleView.showError(e);
}
}