]> git.pond.sub.org Git - empserver/commitdiff
Add sequence numbers to game state (experimental)
authorMarkus Armbruster <armbru@pond.sub.org>
Fri, 21 Mar 2008 13:22:07 +0000 (14:22 +0100)
committerMarkus Armbruster <armbru@pike.pond.sub.org>
Wed, 7 May 2008 08:33:41 +0000 (10:33 +0200)
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().

20 files changed:
include/commodity.h
include/empobj.h
include/file.h
include/game.h
include/land.h
include/loan.h
include/lost.h
include/nat.h
include/news.h
include/nuke.h
include/plane.h
include/sect.h
include/ship.h
include/trade.h
include/treaty.h
src/lib/commands/edit.c
src/lib/commands/swap.c
src/lib/common/file.c
src/lib/subs/nreport.c
src/util/files.c

index c375766196d2632b78a83401439f62113892e66c..d515364e7c8937eefdd4fc6ca646d1b5686803ed 100644 (file)
@@ -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 */
index 2610c18ee6b1406693d645b90a761fcb9bc10b81..63931c1062c90b1b45ab16c4b678bdcf0dc8faf6 100644 (file)
@@ -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 */
index 795177840a35759f4d1073944c27fb0e68df6693..9d724f6587d78415c03b8ebc1e171985854661ec 100644 (file)
@@ -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);
index 8a52509c5313f4a848b2d1ab84149d523fd06c18..1a343c7f9fb6a84c7a6e74176922a6c52b86a8d5 100644 (file)
@@ -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? */
index feb47c6aef852c4b28fc9b570b18a8ae913e9b7b..73ca0b8276a5b7b66e99585ece8fb713d8ef7a5b 100644 (file)
@@ -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 */
index 542af6c8399d140b9d812ab4ad42666742fcf576..d18c4d88a6f0a99f45b6d4ab17be3ab1f412f7ef 100644 (file)
@@ -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 */
index c371f936c2a962fdcab935b477abaa07c38eb880..1bb930796338f5c403b3564c00b149ac981eebb7 100644 (file)
@@ -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 */
index b2f2bd5f81b8b5df37502cd7c5b9f4f1a314b07f..58b8067fcedab35b219d1b8803735876236fdaa9 100644 (file)
@@ -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 */
index c736114444bfedd1d1f33cc4ee53690d7645f10e..5665a92fa223cc77fb562daeb9fe3d59d6f85bfc 100644 (file)
@@ -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 # */
index 2f7fcdaa1ac98fedf4df0b7a0660dc8393ed604e..054cdb4c34c507044bc705b52f48dbb8c3823583 100644 (file)
@@ -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 */
index 2133f3628097b3d099240a46d634a46af350d7d8..485ebb51e69856a479128b61d850a5b7763d7705 100644 (file)
@@ -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 */
index d8a109afa37ade44452be6284d00f4dedc1accf2..408b1600bd7a94ac58a73080dcaad35e0721d81f 100644 (file)
@@ -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 */
index 1f7fbf8ab9edfabd88fd4ea0d995ab4d491a792c..d09a07b0d1c500d2f7b4ee70dafa88e1cb34c79a 100644 (file)
@@ -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 */
index 307f990b4a40fa4dc1f0ba8e941f20e006224d1b..074a77da4a0922df5c4362603dc7ce82eb240c8c 100644 (file)
@@ -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 */
index d5efd77e6d0e90c7795017dd988f7c53f2d848ab..31491eeff139b2ad0cc3ea736d5b23a5dc42e7b6 100644 (file)
@@ -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 */
index 52279b8414aa0f2f3c7020e43a31af1d1e9825b5..cabcbabfa019097677d7a8aa99e7094018d36e00 100644 (file)
@@ -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, &sect, 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)
index a28b89913c38ae3c266f01166e00eeef40c0ed12..309977c5eaffadcd314ece6b3091ecb936f4ea8c 100644 (file)
@@ -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, &secta, 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, &sectb, tmp.sct_uid);
     sectb.sct_dist_x = tmp.sct_x;
     sectb.sct_dist_y = tmp.sct_y;
     sectb.sct_coastal = tmp.sct_coastal;
index 9586bc4dde4856a54cb5688953bb5e3cac2e6778..ce13d14b51ee26427e430ed75edbc6c50c024c5b 100644 (file)
 
 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);
+    }
 }
 
 /*
index 8a53cbeedecc6656dd674b938a68752fea4ea695..2dffb2681c758b3fae4f005105e8b920174c9f19 100644 (file)
@@ -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);
index 4618ba6efc7c7dfaa5f1d47523950420a58fdebf..a7c802b7fdc809dc8ae3c24d4e0d3dce3e91fde4 100644 (file)
@@ -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;