135 lines
3.6 KiB
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';
|
|
}
|