#lang ivy1.7 module file(pid,data) = { type cb action write(d:data) action read returns (d:data) action sync(c:cb) action run(^c:cb) = {} action synced(c:cb) = { var x : bool; call c.run(pid) } specification { var contents : data relation pending(C:cb) after init { # contents := ival; pending(C) := false; } before write { contents := d; } after read { ensure d = contents } before sync { require ~pending(c); pending(c) := true; } before synced { require pending(c); pending(c) := false; } } implementation { object fildes = {} <<< member int `fildes`; >>> <<< init { std::string pathname = "`this`.log"; int f = ::open(pathname.c_str(),O_RDWR|O_CREAT|O_TRUNC,0666); if (f < 0) { perror("cannot open file to write"); } `fildes` = f; // ivy_binary_ser sr; // __ser(sr,`ival`); // if (::write(`fildes`,&sr.res[0],sr.res.size()) < 0) { // perror("cannot write to file"); // } } >>> implement write { <<< if (::lseek(`fildes`,0,SEEK_SET) < 0) { perror("cannot seek to beginning of file"); } ivy_binary_ser sr; __ser(sr,d); if (::write(`fildes`,&sr.res[0],sr.res.size()) < 0) { perror("cannot write to file"); } if (::ftruncate(`fildes`,sr.res.size()) < 0) { perror("cannot truncate file"); } >>> } implement read { <<< if (::lseek(`fildes`,0,SEEK_SET) < 0) { perror("cannot seek to beginning of file"); } std::vector buf; ivy_socket_deser ds(`fildes`,buf); // initializer deserializer with zero bytes try { __deser(ds,d); // read the message } // If file has bad syntax, something really terrible happened so we give up catch (deser_err &){ std::cerr << "syntax error in log file"; exit(1); } >>> } <<< impl template class sync_reader : public reader { cont c; cbtype cb; int fd; ivy_class *ivy; public: sync_reader(cont c, int fd, cbtype cb, ivy_class *ivy) : c(c), fd(fd), cb(cb), ivy(ivy) {} int fdes() {return fd;} void read() { ivy->__lock(); if (::fsync(fd) < 0) { perror("failed to sync file"); } fd = -1; cb(c); ivy->__unlock(); } }; >>> implement sync { <<< install_reader(new sync_reader<`cb`,%`synced`>(c,`fildes`,`synced`, this)); >>> } action handle_synced(c:cb) = { call synced(c) } } trusted isolate iso = this } object packet = { type t interpret t -> bv[1] } ################################################################################ # # Concrete type of node addresses. # ################################################################################ object node = { type t interpret t -> bv[1] action next(x:t) returns (y:t) = { y := x + 1 } } ################################################################################ # # The transport service specification # ################################################################################ include trans instance trans : transport(net,packet.t,node.t) ################################################################################ # # The network service specification # ################################################################################ include udp instance net : udp_simple(node.t,trans.net_msg.t) ################################################################################ # # The high-level service specification # ################################################################################ object serv = { action grant(v:node.t) # grant lock to client action release(v:node.t) # client releases lock object spec = { relation lock(X:node.t) after init { lock(X) := X = 0 } before grant { assert ~lock(X); lock(v) := true } before release { assert lock(v); lock(v) := false } } } ################################################################################ # # The high-level protocol # ################################################################################ implementation { object proto(me:node.t) = { relation token instance backup : file(me,bool) after init { token := me = 0 } implement serv.release { if token { token := false; call backup.write(token); thunk [synccb] cb : backup.cb := { var pkt : packet.t; call trans.send(me,node.next(me),pkt) }; call backup.sync(cb) } } implement trans.recv(pkt:packet.t) { token := true; call backup.write(token); call serv.grant(me) } action crash = * after crash { token := backup.read; } private { invariant proto(X).token & proto(Y).token -> X = Y invariant trans.sent(D,P) & trans.sent(D1,P1) -> D = D1 & P = P1 invariant trans.sent(D,P) -> ~proto(X).token invariant serv.spec.lock(X) <-> proto(X).token invariant backup.contents -> token } } } export serv.release import serv.grant isolate iso_p = proto with serv,node,trans trusted isolate iso_t = trans with net,node trusted isolate iso_n = net with node # trusted isolate iso_pt = proto with serv,trans.impl,trans.seq_num,net.impl,node # extract iso_impl(me:node.t) = proto(me),trans.impl(me),net(me),node,trans.seq_num