Add sequence numbers to game state (experimental)
This catches output dependency violations, e.g. two threads doing a read-modify-write without synchronization. New struct emptypedstr member seqno. Make sure all members of unit empobj_storage share it. Set it in ef_blank() and ef_set_uid(), step it in ef_write(). fairland and files don't use ef_set_uid(); need to set it manually in files.c's main() and file_sct_init(). Factor do_read() out of fillcache() to make it available for new get_seqno().
This commit is contained in:
parent
2aec870a14
commit
dc9d847b8b
20 changed files with 124 additions and 18 deletions
|
@ -44,6 +44,7 @@ struct comstr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short com_uid;
|
||||
unsigned com_seqno;
|
||||
time_t com_timestamp;
|
||||
natid com_owner;
|
||||
/* end of part matching struct empobj */
|
||||
|
|
|
@ -57,6 +57,7 @@ struct empobj {
|
|||
*/
|
||||
short ef_type;
|
||||
short uid;
|
||||
unsigned seqno;
|
||||
time_t timestamp;
|
||||
/* end of part matching struct emptypedstr */
|
||||
natid own; /* valid if EFF_OWNER is in table's flags */
|
||||
|
|
|
@ -67,6 +67,7 @@ struct empfile {
|
|||
struct emptypedstr {
|
||||
short ef_type;
|
||||
short uid;
|
||||
unsigned seqno;
|
||||
time_t timestamp;
|
||||
};
|
||||
|
||||
|
@ -187,6 +188,7 @@ extern int ef_close(int);
|
|||
extern int ef_flush(int);
|
||||
extern void ef_blank(int, int, void *);
|
||||
extern int ef_write(int, int, void *);
|
||||
extern void ef_set_uid(int, void *, int);
|
||||
extern int ef_extend(int, int);
|
||||
extern int ef_ensure_space(int, int, int);
|
||||
extern int ef_truncate(int, int);
|
||||
|
|
|
@ -40,6 +40,7 @@ struct gamestr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short game_uid;
|
||||
unsigned game_seqno;
|
||||
time_t game_timestamp;
|
||||
/* end of part matching struct empobj */
|
||||
char game_upd_disable; /* updates disabled? */
|
||||
|
|
|
@ -51,6 +51,7 @@ struct lndstr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short lnd_uid; /* unit id (land unit) */
|
||||
unsigned lnd_seqno;
|
||||
time_t lnd_timestamp; /* Last time this unit was touched */
|
||||
natid lnd_own; /* owner's country num */
|
||||
coord lnd_x; /* x location in abs coords */
|
||||
|
|
|
@ -44,6 +44,7 @@ struct lonstr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short l_uid;
|
||||
unsigned l_seqno;
|
||||
time_t l_timestamp;
|
||||
/* end of part matching struct empobj */
|
||||
natid l_loner; /* loan shark */
|
||||
|
|
|
@ -41,6 +41,7 @@ struct loststr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
int lost_uid;
|
||||
unsigned lost_seqno;
|
||||
time_t lost_timestamp; /* When it was lost */
|
||||
natid lost_owner; /* Who lost it */
|
||||
/* end of part matching struct empobj */
|
||||
|
|
|
@ -70,6 +70,7 @@ struct realmstr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short r_uid; /* realm table index */
|
||||
unsigned r_seqno;
|
||||
time_t r_timestamp; /* Last time this realm was touched */
|
||||
natid r_cnum; /* country number */
|
||||
/* end of part matching struct empobj */
|
||||
|
@ -82,6 +83,7 @@ struct natstr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short nat_uid; /* equals nat_cnum */
|
||||
unsigned nat_seqno;
|
||||
time_t nat_timestamp;
|
||||
natid nat_cnum; /* our country number */
|
||||
/* end of part matching struct empobj */
|
||||
|
|
|
@ -48,6 +48,7 @@ struct nwsstr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short nws_uid;
|
||||
unsigned nws_seqno;
|
||||
time_t nws_timestamp;
|
||||
/* end of part matching struct empobj */
|
||||
natid nws_ano; /* "actor" country # */
|
||||
|
|
|
@ -44,6 +44,7 @@ struct nukstr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short nuk_uid;
|
||||
unsigned nuk_seqno;
|
||||
time_t nuk_timestamp; /* Last time this nuke was touched */
|
||||
natid nuk_own;
|
||||
coord nuk_x, nuk_y; /* current loc of device */
|
||||
|
|
|
@ -48,6 +48,7 @@ struct plnstr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short pln_uid; /* plane unit id */
|
||||
unsigned pln_seqno;
|
||||
time_t pln_timestamp; /* Last time this plane was touched */
|
||||
natid pln_own; /* owning country */
|
||||
coord pln_x; /* plane x-y */
|
||||
|
|
|
@ -46,6 +46,7 @@ struct sctstr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short sct_uid; /* equals XYOFFSET(sct_x, sct_y) */
|
||||
unsigned sct_seqno;
|
||||
time_t sct_timestamp; /* Last time this sector was written to */
|
||||
natid sct_own; /* owner's country num */
|
||||
coord sct_x; /* x coord of sector */
|
||||
|
|
|
@ -65,6 +65,7 @@ struct shpstr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short shp_uid; /* unit id (ship #) */
|
||||
unsigned shp_seqno;
|
||||
time_t shp_timestamp; /* Last time this ship was touched. */
|
||||
natid shp_own; /* owner's country num */
|
||||
coord shp_x; /* x location in abs coords */
|
||||
|
|
|
@ -44,6 +44,7 @@ struct trdstr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short trd_uid;
|
||||
unsigned trd_seqno;
|
||||
time_t trd_timestamp;
|
||||
natid trd_owner;
|
||||
/* end of part matching struct empobj */
|
||||
|
|
|
@ -41,6 +41,7 @@ struct trtstr {
|
|||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
short trt_uid;
|
||||
unsigned trt_seqno;
|
||||
time_t trt_timestamp;
|
||||
/* end of part matching struct empobj */
|
||||
natid trt_cna; /* proposer */
|
||||
|
|
|
@ -584,7 +584,7 @@ doland(char op, int arg, char *p, struct sctstr *sect)
|
|||
return RET_SYN;
|
||||
sect->sct_x = newx;
|
||||
sect->sct_y = newy;
|
||||
sect->sct_uid = XYOFFSET(newx, newy);
|
||||
ef_set_uid(EF_SECTOR, §, XYOFFSET(newx, newy));
|
||||
break;
|
||||
case 'D':
|
||||
if (!sarg_xy(p, &newx, &newy))
|
||||
|
@ -767,7 +767,7 @@ doship(char op, int arg, char *p, struct shpstr *ship)
|
|||
ship->shp_rflags = arg;
|
||||
break;
|
||||
case 'U':
|
||||
ship->shp_uid = arg;
|
||||
ef_set_uid(EF_SHIP, ship, arg);
|
||||
break;
|
||||
case 'O':
|
||||
if (ship->shp_own)
|
||||
|
@ -871,7 +871,7 @@ dounit(char op, int arg, char *p, struct lndstr *land)
|
|||
land->lnd_land = arg;
|
||||
break;
|
||||
case 'U':
|
||||
land->lnd_uid = arg;
|
||||
ef_set_uid(EF_SHIP, land, arg);
|
||||
break;
|
||||
case 'O':
|
||||
if (land->lnd_own)
|
||||
|
|
|
@ -63,14 +63,14 @@ swaps(void)
|
|||
/* change the location of secta to that of sectb */
|
||||
secta.sct_x = sectb.sct_x;
|
||||
secta.sct_y = sectb.sct_y;
|
||||
secta.sct_uid = sectb.sct_uid;
|
||||
ef_set_uid(EF_SECTOR, §a, sectb.sct_uid);
|
||||
secta.sct_dist_x = sectb.sct_x;
|
||||
secta.sct_dist_y = sectb.sct_y;
|
||||
secta.sct_coastal = sectb.sct_coastal;
|
||||
/* change the location of sectb to where secta was */
|
||||
sectb.sct_x = tmp.sct_x;
|
||||
sectb.sct_y = tmp.sct_y;
|
||||
sectb.sct_uid = tmp.sct_uid;
|
||||
ef_set_uid(EF_SECTOR, §b, tmp.sct_uid);
|
||||
sectb.sct_dist_x = tmp.sct_x;
|
||||
sectb.sct_dist_y = tmp.sct_y;
|
||||
sectb.sct_coastal = tmp.sct_coastal;
|
||||
|
|
|
@ -48,7 +48,10 @@
|
|||
|
||||
static int ef_realloc_cache(struct empfile *, int);
|
||||
static int fillcache(struct empfile *, int);
|
||||
static int do_read(struct empfile *, void *, int, int);
|
||||
static int do_write(struct empfile *, void *, int, int, time_t);
|
||||
static int get_seqno(struct empfile *, int);
|
||||
static void new_seqno(struct empfile *, void *);
|
||||
static void do_blank(struct empfile *, void *, int, int);
|
||||
|
||||
/*
|
||||
|
@ -300,11 +303,28 @@ ef_read(int type, int id, void *into)
|
|||
*/
|
||||
static int
|
||||
fillcache(struct empfile *ep, int id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (CANT_HAPPEN(!ep->cache))
|
||||
return -1;
|
||||
|
||||
ret = do_read(ep, ep->cache, id, MIN(ep->csize, ep->fids - id));
|
||||
if (ret >= 0) {
|
||||
/* cache changed */
|
||||
ep->baseid = id;
|
||||
ep->cids = ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
do_read(struct empfile *ep, void *buf, int id, int count)
|
||||
{
|
||||
int n, ret;
|
||||
char *p;
|
||||
|
||||
if (CANT_HAPPEN(ep->fd < 0 || !ep->cache))
|
||||
if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0))
|
||||
return -1;
|
||||
|
||||
if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) {
|
||||
|
@ -313,21 +333,21 @@ fillcache(struct empfile *ep, int id)
|
|||
return -1;
|
||||
}
|
||||
|
||||
p = ep->cache;
|
||||
n = MIN(ep->csize, ep->fids - id) * ep->size;
|
||||
p = buf;
|
||||
n = count * ep->size;
|
||||
while (n > 0) {
|
||||
ret = read(ep->fd, p, n);
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR) {
|
||||
logerror("Error reading %s elt %d (%s)",
|
||||
ep->file,
|
||||
id + (int)((p - ep->cache) / ep->size),
|
||||
id + (int)((p - (char *)buf) / ep->size),
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
} else if (ret == 0) {
|
||||
logerror("Unexpected EOF reading %s elt %d",
|
||||
ep->file, id + (int)((p - ep->cache) / ep->size));
|
||||
ep->file, id + (int)((p - (char *)buf) / ep->size));
|
||||
break;
|
||||
} else {
|
||||
p += ret;
|
||||
|
@ -335,12 +355,7 @@ fillcache(struct empfile *ep, int id)
|
|||
}
|
||||
}
|
||||
|
||||
if (p == ep->cache)
|
||||
return -1; /* nothing read, old cache still ok */
|
||||
|
||||
ep->baseid = id;
|
||||
ep->cids = (p - ep->cache) / ep->size;
|
||||
return ep->cids;
|
||||
return (p - (char *)buf) / ep->size;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -425,6 +440,7 @@ ef_write(int type, int id, void *from)
|
|||
ep->prewrite(id, from);
|
||||
if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids))
|
||||
return 0; /* not implemented */
|
||||
new_seqno(ep, from);
|
||||
if (!(ep->flags & EFF_PRIVATE)) {
|
||||
if (do_write(ep, from, id, 1, time(NULL)) < 0)
|
||||
return 0;
|
||||
|
@ -442,6 +458,68 @@ ef_write(int type, int id, void *from)
|
|||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
ef_set_uid(int type, void *buf, int uid)
|
||||
{
|
||||
struct emptypedstr *elt;
|
||||
struct empfile *ep;
|
||||
|
||||
if (ef_check(type) < 0)
|
||||
return;
|
||||
ep = &empfile[type];
|
||||
if (!(ep->flags & EFF_TYPED))
|
||||
return;
|
||||
elt = buf;
|
||||
if (elt->uid == uid)
|
||||
return;
|
||||
elt->uid = uid;
|
||||
elt->seqno = get_seqno(ep, uid);
|
||||
}
|
||||
|
||||
static int
|
||||
get_seqno(struct empfile *ep, int id)
|
||||
{
|
||||
struct emptypedstr *elt;
|
||||
|
||||
if (!(ep->flags & EFF_TYPED))
|
||||
return 0;
|
||||
if (id < 0 || id >= ep->fids)
|
||||
return 0;
|
||||
if (id >= ep->baseid && id < ep->baseid + ep->cids)
|
||||
elt = (void *)(ep->cache + (id - ep->baseid) * ep->size);
|
||||
else {
|
||||
/* need a buffer, steal last cache slot */
|
||||
if (ep->cids == ep->csize)
|
||||
ep->cids--;
|
||||
elt = (void *)(ep->cache + ep->cids * ep->size);
|
||||
if (do_read(ep, elt, id, 1) < 0)
|
||||
return 0; /* deep trouble */
|
||||
}
|
||||
return elt->seqno;
|
||||
}
|
||||
|
||||
static void
|
||||
new_seqno(struct empfile *ep, void *buf)
|
||||
{
|
||||
struct emptypedstr *elt = buf;
|
||||
unsigned old_seqno;
|
||||
|
||||
if (!(ep->flags & EFF_TYPED))
|
||||
return;
|
||||
old_seqno = get_seqno(ep, elt->uid);
|
||||
#if 0
|
||||
if (CANT_HAPPEN(old_seqno != elt->seqno))
|
||||
old_seqno = MAX(old_seqno, elt->seqno);
|
||||
#else
|
||||
if (old_seqno != elt->seqno) {
|
||||
logerror("seqno mismatch ef_type=%d uid=%d: %d!=%d",
|
||||
ep->uid, elt->uid, old_seqno, elt->seqno);
|
||||
old_seqno = MAX(old_seqno, elt->seqno);
|
||||
}
|
||||
#endif
|
||||
elt->seqno = old_seqno + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extend table TYPE by COUNT elements.
|
||||
* Any pointers obtained from ef_ptr() become invalid.
|
||||
|
@ -504,9 +582,17 @@ ef_extend(int type, int count)
|
|||
void
|
||||
ef_blank(int type, int id, void *buf)
|
||||
{
|
||||
struct empfile *ep;
|
||||
struct emptypedstr *elt;
|
||||
|
||||
if (ef_check(type) < 0)
|
||||
return;
|
||||
do_blank(&empfile[type], buf, id, 1);
|
||||
ep = &empfile[type];
|
||||
do_blank(ep, buf, id, 1);
|
||||
if (ep->flags & EFF_TYPED) {
|
||||
elt = buf;
|
||||
elt->seqno = get_seqno(ep, elt->uid);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -111,7 +111,7 @@ delete_old_news(void)
|
|||
for (j = 0; getnews(i + j, &news); j++) {
|
||||
if (news.nws_vrb == 0)
|
||||
break;
|
||||
news.nws_uid = j;
|
||||
ef_set_uid(EF_NEWS, &news, j);
|
||||
putnews(j, &news);
|
||||
}
|
||||
CANT_HAPPEN(i + j != news_tail);
|
||||
|
|
|
@ -155,6 +155,7 @@ main(int argc, char *argv[])
|
|||
for (i = 1; i < MAXNOC; i++) {
|
||||
nat.ef_type = EF_NATION;
|
||||
nat.nat_cnum = nat.nat_uid = i;
|
||||
nat.nat_seqno = 0;
|
||||
putnat((&nat));
|
||||
}
|
||||
memset(&realm, 0, sizeof(realm));
|
||||
|
@ -164,6 +165,7 @@ main(int argc, char *argv[])
|
|||
for (j = 0; j < MAXNOR; j++) {
|
||||
realm.r_realm = j;
|
||||
realm.r_uid = (i * MAXNOR) + j;
|
||||
realm.r_seqno = 0;
|
||||
putrealm(&realm);
|
||||
}
|
||||
}
|
||||
|
@ -209,6 +211,7 @@ file_sct_init(coord x, coord y, struct sctstr *ptr)
|
|||
|
||||
sp->ef_type = EF_SECTOR;
|
||||
sp->sct_uid = XYOFFSET(x, y);
|
||||
sp->sct_seqno = 0;
|
||||
sp->sct_x = x;
|
||||
sp->sct_y = y;
|
||||
sp->sct_dist_x = x;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue