Trans buggy
include queue
include timeout
include seqnum
module transport(lower,packet,id) = {
relation sent(D:id,P:packet)
action send(src:id,dst:id,pkt:packet)
action recv(dst:id,pkt:packet)
object spec = {
after init {
sent(D,P) := false
}
before send {
assert ~sent(dst,pkt);
sent(dst,pkt) := true
}
before recv {
assert sent(dst,pkt);
sent(dst,pkt) := false
}
}
instance seq_num : sequence_numbers
these type describe the format of messages
type mtype = {msg_t, ack_t}
object net_msg = {
type t = struct {
mty : mtype,
src : id,
num : seq_num.t,
payload : packet
}
}
object impl(me:id) = {
Create one outgoing message queue for each host
and a timout for each queue.
instance mq(D:id) : message_queue(net_msg,seq_num)
instance timer(D:id) : timeout_sec
Keep track of the latest sequence number sent and received
on each channel.
individual send_seq(S:id) : seq_num.t
individual recv_seq(S:id) : seq_num.t
init recv_seq(S) = 0 & send_seq(S) = 0
Implementations of interface actions
implement send(dst:id,pkt:packet) {
local msg : net_msg.t, seq : seq_num.t {
net_msg.mty(msg) := msg_t;
net_msg.src(msg) := me;
net_msg.num(msg) := send_seq(dst);
net_msg.payload(msg) := pkt;
send_seq(dst) := seq_num.next(send_seq(dst));
call mq(dst).enqueue(msg);
call lower.send(me,dst,msg)
}
}
Receiving a message is the most complicated. First, we send
an ack. Then, if the sequence number is correct, we call the
application layer action determined by the message type.
implement lower.recv(msg:net_msg.t) {
local src:id,seq:seq_num.t {
seq := net_msg.num(msg);
src := net_msg.src(msg);
if seq <= recv_seq(src) & net_msg.mty(msg) ~= ack_t {
local ack : net_msg.t {
net_msg.mty(ack) := ack_t;
net_msg.src(ack) := me;
net_msg.num(ack) := seq;
call lower.send(me,src,ack)
}
};
if net_msg.mty(msg) = ack_t {
call mq(src).delete_all(seq)
}
else if seq >= recv_seq(src) {
recv_seq(src) := seq_num.next(recv_seq(src));
call recv(me,net_msg.payload(msg))
}
}
}
If an outgoing channel times out and the queue is not empty,
we pick an arbitrary message in the queue and retransmit it.
implement timer.timeout(dst:id) {
if ~mq(dst).empty {
call lower.send(me,dst,mq(dst).pick_one)
}
}
}
}