ivy/test/crashpong1.ivy

211 строки
4.6 KiB
XML

#lang ivy1.7
module file(data,cont) = {
action write(d:data)
action read returns (d:data)
action sync(c:cont)
action synced(c:cont)
specification {
var contents : data
before write {
contents := d;
}
after read {
ensure d = contents
}
}
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;
}
>>>
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 seek to beginning of 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<char> 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<typename cont, typename cbtype> 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<`cont`,%`synced`>(c,`fildes`,`synced`, this));
>>>
}
action handle_synced(c:cont) = {
call synced(c)
}
}
trusted isolate iso = this
}
object intf = {
action ping
action pong
}
type side_t = {left,right}
object spec = {
individual side : side_t
after init {
side := left
}
before intf.ping {
assert side = left;
side := right
}
before intf.pong {
assert side = right;
side := left
}
}
type cbtype # call-back parameter
object left_player = {
individual ball : bool
instance backup : file(bool,cbtype)
after init {
ball := true;
call backup.write(ball)
}
action async = {
if ball {
ball := false;
call backup.write(ball);
call backup.sync(0); # 0 is an unused callback parameter
}
}
implement backup.synced(c:cbtype) {
call intf.ping;
}
implement intf.pong {
ball := true;
call backup.write(ball) # don't sync here, since it's ok to lose ball
}
action crash = *
after crash {
ball := backup.read;
}
conjecture left_player.ball -> spec.side = left
conjecture ball <-> backup.contents
}
object right_player = {
individual ball : bool
instance backup : file(bool,cbtype)
after init {
ball := false;
call backup.write(ball)
}
action async = {
if ball {
ball := false;
call backup.write(ball);
call backup.sync(0); # 0 is an unused callback parameter
}
}
implement backup.synced(c:cbtype) {
call intf.pong;
}
implement intf.ping {
ball := true;
call backup.write(ball) # don't sync here, since it's ok to lose ball
}
action crash = *
after crash {
ball := backup.read;
}
conjecture right_player.ball -> spec.side = right
conjecture ball <-> backup.contents
}
export left_player.async
export right_player.async
export left_player.crash
export right_player.crash
isolate iso_l = left_player with spec
isolate iso_r = right_player with spec
extract iso_impl = this