Generation numbers to catch write back of stale copies
authorMarkus Armbruster <armbru@pond.sub.org>
Thu, 31 Dec 2009 12:14:36 +0000 (13:14 +0100)
committerMarkus Armbruster <armbru@pond.sub.org>
Tue, 19 Jan 2010 07:36:01 +0000 (08:36 +0100)
Oops when a stale copy is written back, i.e. the processor was yielded
since the copy was made.  Such bugs are difficult to spot.  Sequence
numbers catch them when they do actual harm (they also catch different
bugs).  Generation numbers catch them even when they don't.

New ef_generation to count generations.  Call new ef_make_stale() to
increment it whenever the processor may be yielded.

New struct emptypedstr member generation.  To conserve space, make it
a bit-field of twelve bits, i.e. generations are only recorded modulo
2^12.  Make sure all members of unit empobj_storage share it.  It is
only used in copies; its value on disk and in the cache is
meaningless.  Copies with generation other than ef_generation are
stale.  Stale copies that are a multiple of 2^12 generations old can't
be detected, but that is sufficiently improbable.

Set generation to ef_generation by calling new ef_mark_fresh() when
making copies in ef_read() and ef_blank().  nav_ship() and
fltp_to_list() make copies without going through ef_read(), and
therefore need to call ef_mark_fresh() as well.  Also call it in
obj_changed() to make check_sect_ok() & friends freshen their argument
when it is unchanged.

New must_be_fresh() oopses when its argument is stale.  Call it in
ef_write() to catch write back of stale copies.

22 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/nuke.h
include/plane.h
include/sect.h
include/ship.h
include/trade.h
include/treaty.h
src/lib/common/file.c
src/lib/empthread/io.c
src/lib/empthread/lwp.c
src/lib/empthread/ntthread.c
src/lib/empthread/pthread.c
src/lib/subs/check.c
src/lib/update/nav_ship.c
src/lib/update/sail.c

index 1f03fa78e7026b40d6ab3c1b3fd80a88f876071b..c6b628b3d35f38e151baa727543c3448e9cfa7fe 100644 (file)
@@ -44,6 +44,7 @@ struct comstr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned com_seqno: 12;
+    unsigned com_generation: 12;
     int com_uid;
     time_t com_timestamp;
     natid com_owner;
index 9574073d071e65f7437f6b450330f9f7c19b267d..183021eb54dfc1b30702aee26eaa25e7edb8d46d 100644 (file)
@@ -57,6 +57,7 @@ struct empobj {
      */
     signed ef_type: 8;
     unsigned seqno: 12;
+    unsigned generation: 12;
     int uid;
     time_t timestamp;
     /* end of part matching struct emptypedstr */
index 4c0424b42d73f8ac55a1d13a496850ed08f54d15..58146d31cdb8e73106fb145a0cb6ae7b7fb82fae 100644 (file)
@@ -87,6 +87,7 @@ struct empfile {
 struct emptypedstr {
     signed ef_type: 8;
     unsigned seqno: 12;
+    unsigned generation: 12;
     int uid;
     time_t timestamp;
 };
@@ -203,6 +204,8 @@ enum {
 
 extern struct castr *ef_cadef(int);
 extern int ef_read(int, int, void *);
+extern void ef_make_stale(void);
+extern void ef_mark_fresh(int, void *);
 extern void *ef_ptr(int, int);
 extern char *ef_nameof(int);
 extern time_t ef_mtime(int);
index a58d1bdb884b3e35a7b50c51d6567bc62d7d1e88..109920d212b174e16350850f23aa69df503dac3f 100644 (file)
@@ -40,6 +40,7 @@ struct gamestr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned game_seqno: 12;
+    unsigned game_generation: 12;
     int game_uid;
     time_t game_timestamp;
     /* end of part matching struct empobj */
index 5b1fb707954641194e0410bf41bfc1e4f1163519..d3c174a046e4c443e61a56db5a9ca79de8c20cb9 100644 (file)
@@ -51,6 +51,7 @@ struct lndstr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned lnd_seqno: 12;
+    unsigned lnd_generation: 12;
     int lnd_uid;               /* unit id (land #) */
     time_t lnd_timestamp;      /* Last time this unit was touched */
     natid lnd_own;             /* owner's country num */
index d4fae4c701f441730a71561764c59580fde9dbe5..713d88c230565efd0a5873d7eaf2e262763cf5e0 100644 (file)
@@ -44,6 +44,7 @@ struct lonstr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned l_seqno: 12;
+    unsigned l_generation: 12;
     int l_uid;
     time_t l_timestamp;
     /* end of part matching struct empobj */
index 0e40470c3319973b99e8d51779b9e6f5a473695d..9fa7ab132f26b79424fb4311408aee4fb6a1ad46 100644 (file)
@@ -41,6 +41,7 @@ struct loststr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned lost_seqno: 12;
+    unsigned lost_generation: 12;
     int lost_uid;
     time_t lost_timestamp;     /* When it was lost */
     natid lost_owner;          /* Who lost it */
index 5b658bdbb89c2de289e32fdd257154ba39f5b3db..d64f8036c796a7575b7ea42405e22e36043f42fc 100644 (file)
@@ -72,6 +72,7 @@ struct realmstr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned r_seqno: 12;
+    unsigned r_generation: 12;
     int r_uid;                 /* realm table index */
     time_t r_timestamp;                /* Last time this realm was touched */
     natid r_cnum;              /* country number */
@@ -85,6 +86,7 @@ struct natstr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned nat_seqno: 12;
+    unsigned nat_generation: 12;
     int nat_uid;               /* equals nat_cnum */
     time_t nat_timestamp;
     natid nat_cnum;            /* our country number */
index 6a0db83fff36f56d4e8d92741aecee34084ffd9a..ec697c711331505ca2f739013e64be14d54869f0 100644 (file)
@@ -44,6 +44,7 @@ struct nukstr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned nuk_seqno: 12;
+    unsigned nuk_generation: 12;
     int nuk_uid;               /* unit id (nuke #) */
     time_t nuk_timestamp;      /* Last time this nuke was touched */
     natid nuk_own;
index e77ee93acef4438ffab683ae145d9d20cd73b850..e080099b644fbed5b8b539766a11cd163e991a7a 100644 (file)
@@ -48,6 +48,7 @@ struct plnstr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned pln_seqno: 12;
+    unsigned pln_generation: 12;
     int pln_uid;               /* unit id (plane #) */
     time_t pln_timestamp;      /* Last time this plane was touched */
     natid pln_own;             /* owning country */
index 9b6fe8d8e18d7e6d27926259ba68a8ba1c0bd645..62e634cea1e357eb947cf2b2307a9cf3d614d1b5 100644 (file)
@@ -46,6 +46,7 @@ struct sctstr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned sct_seqno: 12;
+    unsigned sct_generation: 12;
     int sct_uid;               /* equals XYOFFSET(sct_x, sct_y) */
     time_t sct_timestamp;      /* Last time this sector was written to */
     natid sct_own;             /* owner's country num */
index 23cefa1f79e7eca718c2c150f3dd57d301fb122e..c4425b80a366a4be383c91b448b6a53a79d82f5b 100644 (file)
@@ -65,6 +65,7 @@ struct shpstr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned shp_seqno: 12;
+    unsigned shp_generation: 12;
     int shp_uid;               /* unit it (ship #) */
     time_t shp_timestamp;      /* Last time this ship was touched. */
     natid shp_own;             /* owner's country num */
index 72ebdbbb90b3d9261850fc1b4af76cb4f15c29b0..41889265c98a7b19369911017aed3a2ccf86df04 100644 (file)
@@ -44,6 +44,7 @@ struct trdstr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned trd_seqno: 12;
+    unsigned trd_generation: 12;
     int trd_uid;
     time_t trd_timestamp;
     natid trd_owner;
index 440d7f533aad73b9523bfec76223bcab92dca992..f1c4cbb0ae39b33c540ab8395b3439f6dfff37fe 100644 (file)
@@ -41,6 +41,7 @@ struct trtstr {
     /* initial part must match struct empobj */
     signed ef_type: 8;
     unsigned trt_seqno: 12;
+    unsigned trt_generation: 12;
     int trt_uid;
     time_t trt_timestamp;
     /* end of part matching struct empobj */
index f32bafa4bb61e67d49f15ef9d7353b911a022540..b59e1ab529ca01e12ae44f36e35d2ebf3d9a93db 100644 (file)
@@ -57,9 +57,12 @@ static int do_read(struct empfile *, void *, int, int);
 static int do_write(struct empfile *, void *, int, int);
 static unsigned get_seqno(struct empfile *, int);
 static void new_seqno(struct empfile *, void *);
+static void must_be_fresh(struct empfile *, void *);
 static void do_blank(struct empfile *, void *, int, int);
 static int ef_check(int);
 
+static unsigned ef_generation;
+
 /*
  * Open the file-backed table TYPE (EF_SECTOR, ...).
  * HOW are flags to control operation.  Naturally, immutable flags are
@@ -360,6 +363,7 @@ ef_read(int type, int id, void *into)
        cachep = ep->cache + (id - ep->baseid) * ep->size;
     }
     memcpy(into, cachep, ep->size);
+    ef_mark_fresh(type, into);
 
     if (ep->postread)
        ep->postread(id, into);
@@ -522,9 +526,11 @@ ef_write(int type, int id, void *from)
        if (ep->onresize && ep->onresize(type) < 0)
            return 0;
     }
-    if (id >= ep->baseid && id < ep->baseid + ep->cids)
+    if (id >= ep->baseid && id < ep->baseid + ep->cids) {
        cachep = ep->cache + (id - ep->baseid) * ep->size;
-    else
+       if (cachep != from)
+           must_be_fresh(ep, from);
+    } else
        cachep = NULL;
     if (ep->prewrite)
        ep->prewrite(id, cachep, from);
@@ -607,6 +613,35 @@ new_seqno(struct empfile *ep, void *buf)
     elt->seqno = old_seqno + 1;
 }
 
+void
+ef_make_stale(void)
+{
+    ef_generation++;
+}
+
+void
+ef_mark_fresh(int type, void *buf)
+{
+    struct empfile *ep;
+
+    if (ef_check(type) < 0)
+       return;
+    ep = &empfile[type];
+    if (!(ep->flags & EFF_TYPED))
+       return;
+    ((struct emptypedstr *)buf)->generation = ef_generation;
+}
+
+static void
+must_be_fresh(struct empfile *ep, void *buf)
+{
+    struct emptypedstr *elt = buf;
+
+    if (!(ep->flags & EFF_TYPED))
+       return;
+    CANT_HAPPEN(elt->generation != (ef_generation & 0xfff));
+}
+
 /*
  * Extend table TYPE by COUNT elements.
  * Any pointers obtained from ef_ptr() become invalid.
@@ -684,6 +719,7 @@ ef_blank(int type, int id, void *buf)
        elt = buf;
        elt->seqno = get_seqno(ep, elt->uid);
     }
+    ef_mark_fresh(type, buf);
 }
 
 /*
index 6679dfc667e77cb6de50dc340bb93b3a8f1d3606..8016044d3e7060840bd784f3dec04360f5a9e384 100644 (file)
@@ -51,6 +51,7 @@
 #include <unistd.h>
 #include "empio.h"
 #include "empthread.h"
+#include "file.h"
 #include "ioqueue.h"
 #include "misc.h"
 #include "queue.h"
@@ -196,6 +197,9 @@ io_output(struct iop *iop, int wait)
     struct iovec iov[16];
     int n, res, cc;
 
+    if (wait)
+       ef_make_stale();
+
     if (!ioq_qsize(iop->output))
        return 0;
 
index 5889c5f125d5c8503a08e43a30fa36f51e3daf24..d13737121a80a8e19139232abec0e4b3abd96a88 100644 (file)
@@ -37,6 +37,7 @@
 #include <signal.h>
 #include <time.h>
 #include "empthread.h"
+#include "file.h"
 #include "misc.h"
 
 /* Flags that were passed to empth_init() */
@@ -65,6 +66,7 @@ empth_create(void (*entry)(void *), int size, int flags,
 {
     if (!flags)
        flags = empth_flags;
+    ef_make_stale();
     return lwpCreate(1, entry, size, flags, name, 0, NULL, ud);
 }
 
@@ -89,18 +91,21 @@ empth_set_name(empth_t *thread, char *name)
 void
 empth_exit(void)
 {
+    ef_make_stale();
     lwpExit();
 }
 
 void
 empth_yield(void)
 {
+    ef_make_stale();
     lwpYield();
 }
 
 int
 empth_select(int fd, int flags, struct timeval *timeout)
 {
+    ef_make_stale();
     return lwpSleepFd(fd, flags, timeout);
 }
 
@@ -113,6 +118,7 @@ empth_wakeup(empth_t *a)
 int
 empth_sleep(time_t until)
 {
+    ef_make_stale();
     return lwpSleepUntil(until);
 }
 
@@ -123,6 +129,7 @@ empth_wait_for_signal(void)
     int sig, err;
     time_t now;
 
+    ef_make_stale();
     sigemptyset(&set);
     sigaddset(&set, SIGHUP);
     sigaddset(&set, SIGINT);
@@ -153,12 +160,14 @@ empth_rwlock_destroy(empth_rwlock_t *rwlock)
 void
 empth_rwlock_wrlock(empth_rwlock_t *rwlock)
 {
+    ef_make_stale();
     lwp_rwlock_wrlock(rwlock);
 }
 
 void
 empth_rwlock_rdlock(empth_rwlock_t *rwlock)
 {
+    ef_make_stale();
     lwp_rwlock_rdlock(rwlock);
 }
 
index 579672204865d586a2c7a68b77b055565ae61e01..67ae3ef49966f330941dfc81495efb73d2f9b543 100644 (file)
@@ -56,6 +56,7 @@
 #include <process.h>
 #include "misc.h"
 #include "empthread.h"
+#include "file.h"
 #include "prototypes.h"
 #include "server.h"
 #include "sys/socket.h"
@@ -423,6 +424,7 @@ empth_create(void (*entry)(void *), int size, int flags,
     empth_t *pThread = NULL;
 
     loc_debug("creating new thread %s", name);
+    ef_make_stale();
 
     pThread = malloc(sizeof(*pThread));
     if (!pThread) {
@@ -501,6 +503,7 @@ empth_exit(void)
     empth_t *pThread = TlsGetValue(dwTLSIndex);
 
     loc_debug("empth_exit");
+    ef_make_stale();
     loc_BlockThisThread();
 
     TlsSetValue(dwTLSIndex, NULL);
@@ -516,6 +519,7 @@ empth_exit(void)
 void
 empth_yield(void)
 {
+    ef_make_stale();
     loc_BlockThisThread();
     Sleep(0);
     loc_RunThisThread(NULL);
@@ -541,6 +545,7 @@ empth_select(int fd, int flags, struct timeval *timeout)
 
     loc_debug("%s select on %d",
              flags == EMPTH_FD_READ ? "read" : "write", fd);
+    ef_make_stale();
     loc_BlockThisThread();
 
     hEventObject[0] = WSACreateEvent();
@@ -615,6 +620,7 @@ empth_sleep(time_t until)
     empth_t *pThread = TlsGetValue(dwTLSIndex);
     DWORD result;
 
+    ef_make_stale();
     loc_BlockThisThread();
 
     do {
@@ -645,6 +651,7 @@ empth_request_shutdown(void)
 int
 empth_wait_for_signal(void)
 {
+    ef_make_stale();
     loc_BlockThisThread();
     loc_RunThisThread(hShutdownEvent);
     return SIGTERM;
@@ -696,6 +703,7 @@ empth_rwlock_destroy(empth_rwlock_t *rwlock)
 void
 empth_rwlock_wrlock(empth_rwlock_t *rwlock)
 {
+    ef_make_stale();
     /* block any new readers */
     ResetEvent(rwlock->can_read);
     rwlock->nwrite++;
@@ -707,6 +715,7 @@ empth_rwlock_wrlock(empth_rwlock_t *rwlock)
 void
 empth_rwlock_rdlock(empth_rwlock_t *rwlock)
 {
+    ef_make_stale();
     loc_BlockThisThread();
     loc_RunThisThread(rwlock->can_read);
     ResetEvent(rwlock->can_write);
index 3378b50bc7ba75ec8c191afe074e7a39a8f343d7..32293aecb33e6ecd1b5303fc8030c71cfef4e6da 100644 (file)
@@ -51,6 +51,7 @@
 #include <unistd.h>
 #include "misc.h"
 #include "empthread.h"
+#include "file.h"
 #include "prototypes.h"
 
 struct empth_t {
@@ -187,6 +188,7 @@ empth_create(void (*entry)(void *), int size, int flags,
     int eno;
 
     empth_status("creating new thread %s", name);
+    ef_make_stale();
 
     ctx = malloc(sizeof(empth_t));
     if (!ctx) {
@@ -262,6 +264,7 @@ empth_exit(void)
     empth_t *ctx = pthread_getspecific(ctx_key);
 
     empth_status("empth_exit");
+    ef_make_stale();
     pthread_mutex_unlock(&mtx_ctxsw);
     free(ctx->name);
     free(ctx);
@@ -271,6 +274,7 @@ empth_exit(void)
 void
 empth_yield(void)
 {
+    ef_make_stale();
     pthread_mutex_unlock(&mtx_ctxsw);
     pthread_mutex_lock(&mtx_ctxsw);
     empth_restorectx();
@@ -286,6 +290,7 @@ empth_select(int fd, int flags, struct timeval *timeout)
     empth_t *ctx;
     int res = 0;
 
+    ef_make_stale();
     pthread_mutex_unlock(&mtx_ctxsw);
     empth_status("select on %d for %d", fd, flags);
 
@@ -353,6 +358,7 @@ empth_sleep(time_t until)
     struct timeval tv;
     int res;
 
+    ef_make_stale();
     pthread_mutex_unlock(&mtx_ctxsw);
     do {
        now = time(NULL);
@@ -373,6 +379,7 @@ empth_wait_for_signal(void)
     sigset_t set;
     int sig, err;
 
+    ef_make_stale();
     sigemptyset(&set);
     sigaddset(&set, SIGHUP);
     sigaddset(&set, SIGINT);
@@ -426,6 +433,7 @@ empth_rwlock_wrlock(empth_rwlock_t *rwlock)
 {
     empth_status("wrlock %s %d %d",
                 rwlock->name, rwlock->nread, rwlock->nwrite);
+    ef_make_stale();
     rwlock->nwrite++;
     while (rwlock->nread != 0 || rwlock->nwrite != 1) {
        empth_status("waiting for wrlock %s", rwlock->name);
@@ -441,6 +449,7 @@ empth_rwlock_rdlock(empth_rwlock_t *rwlock)
 {
     empth_status("rdlock %s %d %d",
                 rwlock->name, rwlock->nread, rwlock->nwrite);
+    ef_make_stale();
     while (rwlock->nwrite) {
        empth_status("waiting for rdlock %s", rwlock->name);
        pthread_cond_wait(&rwlock->can_read, &mtx_ctxsw);
index 77f91b72d71ebd7c453c378f3f3d37ffd41b23e2..360ce913a63c7dc5520679baa2567cf20029621e 100644 (file)
@@ -53,7 +53,11 @@ obj_changed(struct empobj *obj, size_t sz)
     get_empobj(obj->ef_type, obj->uid, &old);
     memcpy(&tobj, obj, sz);
     old.gen.timestamp = tobj.gen.timestamp = 0;
-    return memcmp(&tobj, &old, sz);
+    old.gen.generation = tobj.gen.generation = 0;
+    if (memcmp(&tobj, &old, sz))
+       return 1;
+    ef_mark_fresh(obj->ef_type, obj);
+    return 0;
 }
 
 int
index 32c01c08f6fa3a92d6cd578a65f43c23a811049e..0a0206988912e644e6fdf81ecf312eea1c976eeb 100644 (file)
@@ -268,6 +268,7 @@ nav_ship(struct shpstr *sp)
     mlp = malloc(sizeof(struct ulist));
     mlp->chrp = (struct empobj_chr *)(mchr + sp->shp_type);
     mlp->unit.ship = *sp;
+    ef_mark_fresh(EF_SHIP, &mlp->unit.ship);
     mlp->mobil = sp->shp_mobil;
     emp_insque(&mlp->queue, &ship_list);
 
index c10c211ea1cf09e550a801b898003bc591e6e501..a52408333188a09f82dfc66590beeb8a62b2a2f1 100644 (file)
@@ -347,6 +347,7 @@ fltp_to_list(struct fltheadstr *fltp, struct emp_qelem *list)
        sp = getshipp(fe->num);
        mlp->chrp = (struct empobj_chr *)(mchr + sp->shp_type);
        mlp->unit.ship = *sp;
+       ef_mark_fresh(EF_SHIP, &mlp->unit.ship);
        mlp->mobil = fe->mobil;
        emp_insque(&mlp->queue, list);
     }