p1/network/PostOffice.java

135 lines
3.6 KiB
Java

package nachos.network;
import nachos.machine.*;
import nachos.threads.*;
/**
* A collection of message queues, one for each local port. A
* <tt>PostOffice</tt> interacts directly with the network hardware. Because
* of the network hardware, we are guaranteed that messages will never be
* corrupted, but they might get lost.
*
* <p>
* The post office uses a "postal worker" thread to wait for messages to arrive
* from the network and to place them in the appropriate queues. This cannot
* be done in the receive interrupt handler because each queue (implemented
* with a <tt>SynchList</tt>) is protected by a lock.
*/
public class PostOffice {
/**
* Allocate a new post office, using an array of <tt>SynchList</tt>s.
* Register the interrupt handlers with the network hardware and start the
* "postal worker" thread.
*/
public PostOffice() {
messageReceived = new Semaphore(0);
messageSent = new Semaphore(0);
sendLock = new Lock();
queues = new SynchList[MailMessage.portLimit];
for (int i=0; i<queues.length; i++)
queues[i] = new SynchList();
Runnable receiveHandler = new Runnable() {
public void run() { receiveInterrupt(); }
};
Runnable sendHandler = new Runnable() {
public void run() { sendInterrupt(); }
};
Machine.networkLink().setInterruptHandlers(receiveHandler,
sendHandler);
KThread t = new KThread(new Runnable() {
public void run() { postalDelivery(); }
});
t.fork();
}
/**
* Retrieve a message on the specified port, waiting if necessary.
*
* @param port the port on which to wait for a message.
*
* @return the message received.
*/
public MailMessage receive(int port) {
Lib.assertTrue(port >= 0 && port < queues.length);
Lib.debug(dbgNet, "waiting for mail on port " + port);
MailMessage mail = (MailMessage) queues[port].removeFirst();
if (Lib.test(dbgNet))
System.out.println("got mail on port " + port + ": " + mail);
return mail;
}
/**
* Wait for incoming messages, and then put them in the correct mailbox.
*/
private void postalDelivery() {
while (true) {
messageReceived.P();
Packet p = Machine.networkLink().receive();
MailMessage mail;
try {
mail = new MailMessage(p);
}
catch (MalformedPacketException e) {
continue;
}
if (Lib.test(dbgNet))
System.out.println("delivering mail to port " + mail.dstPort
+ ": " + mail);
// atomically add message to the mailbox and wake a waiting thread
queues[mail.dstPort].add(mail);
}
}
/**
* Called when a packet has arrived and can be dequeued from the network
* link.
*/
private void receiveInterrupt() {
messageReceived.V();
}
/**
* Send a message to a mailbox on a remote machine.
*/
public void send(MailMessage mail) {
if (Lib.test(dbgNet))
System.out.println("sending mail: " + mail);
sendLock.acquire();
Machine.networkLink().send(mail.packet);
messageSent.P();
sendLock.release();
}
/**
* Called when a packet has been sent and another can be queued to the
* network link. Note that this is called even if the previous packet was
* dropped.
*/
private void sendInterrupt() {
messageSent.V();
}
private SynchList[] queues;
private Semaphore messageReceived; // V'd when a message can be dequeued
private Semaphore messageSent; // V'd when a message can be queued
private Lock sendLock;
private static final char dbgNet = 'n';
}