merge wstate back into status

this optimizes space usage, prospectively (we'd have to extend wstate
soon otherwise).

this partially reverts 4ffe1496.
This commit is contained in:
Oswald Buddenhagen 2022-06-16 10:40:34 +02:00
parent 6a78e2c5f6
commit b3155a8bcb
3 changed files with 45 additions and 47 deletions

View File

@ -722,11 +722,11 @@ box_opened2( sync_vars_t *svars, int t )
else else
warn( "Warning: sync record (%u,%u) has stray TUID. Ignoring.\n", srec->uid[F], srec->uid[N] ); warn( "Warning: sync record (%u,%u) has stray TUID. Ignoring.\n", srec->uid[F], srec->uid[N] );
} }
if (srec->wstate & W_PURGE) { if (srec->status & S_PURGE) {
t = srec->uid[F] ? F : N; t = srec->uid[F] ? F : N;
opts[t] |= OPEN_SETFLAGS; opts[t] |= OPEN_SETFLAGS;
} }
if (srec->wstate & W_UPGRADE) { if (srec->status & S_UPGRADE) {
t = !srec->uid[F] ? F : N; t = !srec->uid[F] ? F : N;
opts[t] |= OPEN_APPEND; opts[t] |= OPEN_APPEND;
opts[t^1] |= OPEN_OLD; opts[t^1] |= OPEN_OLD;
@ -970,7 +970,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
for (t = 0; t < 2; t++) { for (t = 0; t < 2; t++) {
if (srec->msg[t] && (srec->msg[t]->flags & F_DELETED)) if (srec->msg[t] && (srec->msg[t]->flags & F_DELETED))
srec->wstate |= W_DEL(t); srec->status |= S_DEL(t);
if (del[t]) { if (del[t]) {
// The target was newly expunged, so there is nothing to update. // The target was newly expunged, so there is nothing to update.
// The deletion is propagated in the opposite iteration. // The deletion is propagated in the opposite iteration.
@ -996,7 +996,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
if (svars->chan->ops[t] & OP_DELETE) { if (svars->chan->ops[t] & OP_DELETE) {
debug( " %sing delete\n", str_hl[t] ); debug( " %sing delete\n", str_hl[t] );
srec->aflags[t] = F_DELETED; srec->aflags[t] = F_DELETED;
srec->wstate |= W_DELETE; srec->status |= S_DELETE;
} else { } else {
debug( " not %sing delete\n", str_hl[t] ); debug( " not %sing delete\n", str_hl[t] );
} }
@ -1051,7 +1051,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
} }
} }
// This is separated, because the upgrade can come from the journal. // This is separated, because the upgrade can come from the journal.
if (srec->wstate & W_UPGRADE) { if (srec->status & S_UPGRADE) {
t = !srec->uid[F] ? F : N; t = !srec->uid[F] ? F : N;
tmsg = srec->msg[t^1]; tmsg = srec->msg[t^1];
if ((svars->chan->ops[t] & OP_EXPUNGE) && (srec->pflags & F_DELETED)) { if ((svars->chan->ops[t] & OP_EXPUNGE) && (srec->pflags & F_DELETED)) {
@ -1087,7 +1087,8 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
srec->status |= S_DUMMY(t); srec->status |= S_DUMMY(t);
JLOG( "_ %u %u", (srec->uid[F], srec->uid[N]), "placeholder only - was previously skipped" ); JLOG( "_ %u %u", (srec->uid[F], srec->uid[N]), "placeholder only - was previously skipped" );
} else { } else {
JLOG( "~ %u %u %u", (srec->uid[F], srec->uid[N], srec->status), "was previously skipped" ); JLOG( "~ %u %u %d", (srec->uid[F], srec->uid[N], srec->status & S_LOGGED),
"was previously skipped" );
} }
} else { } else {
if (!(svars->chan->ops[t] & OP_NEW)) if (!(svars->chan->ops[t] & OP_NEW))
@ -1141,7 +1142,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
JLOG( "+ %u %u", (srec->uid[F], srec->uid[N]), "fresh" ); JLOG( "+ %u %u", (srec->uid[F], srec->uid[N]), "fresh" );
} }
if (!(tmsg->flags & F_FLAGGED) && tmsg->size > svars->chan->stores[t]->max_size && if (!(tmsg->flags & F_FLAGGED) && tmsg->size > svars->chan->stores[t]->max_size &&
!(srec->wstate & W_UPGRADE) && !(srec->status & (S_DUMMY(F)|S_DUMMY(N)))) { !(srec->status & (S_DUMMY(F) | S_DUMMY(N) | S_UPGRADE))) {
srec->status |= S_DUMMY(t); srec->status |= S_DUMMY(t);
JLOG( "_ %u %u", (srec->uid[F], srec->uid[N]), "placeholder only - too big" ); JLOG( "_ %u %u", (srec->uid[F], srec->uid[N]), "placeholder only - too big" );
} }
@ -1206,7 +1207,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
((srec->status & (S_EXPIRE|S_EXPIRED)) == (S_EXPIRE|S_EXPIRED)) || ((srec->status & (S_EXPIRE|S_EXPIRED)) == (S_EXPIRE|S_EXPIRED)) ||
((srec->status & (S_EXPIRE|S_EXPIRED)) && (srec->msg[N]->flags & F_DELETED))) { ((srec->status & (S_EXPIRE|S_EXPIRED)) && (srec->msg[N]->flags & F_DELETED))) {
/* The message is excess or was already (being) expired. */ /* The message is excess or was already (being) expired. */
srec->wstate |= W_NEXPIRE; srec->status |= S_NEXPIRE;
debug( " pair(%u,%u) expired\n", srec->uid[F], srec->uid[N] ); debug( " pair(%u,%u) expired\n", srec->uid[F], srec->uid[N] );
if (svars->maxxfuid < srec->uid[F]) if (svars->maxxfuid < srec->uid[F])
svars->maxxfuid = srec->uid[F]; svars->maxxfuid = srec->uid[F];
@ -1229,23 +1230,24 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
if (!(srec->status & S_PENDING)) { if (!(srec->status & S_PENDING)) {
if (!srec->msg[N]) if (!srec->msg[N])
continue; continue;
uchar nex = (srec->wstate / W_NEXPIRE) & 1; uchar nex = (srec->status / S_NEXPIRE) & 1;
if (nex != ((srec->status / S_EXPIRED) & 1)) { if (nex != ((srec->status / S_EXPIRED) & 1)) {
/* The record needs a state change ... */ /* The record needs a state change ... */
if (nex != ((srec->status / S_EXPIRE) & 1)) { if (nex != ((srec->status / S_EXPIRE) & 1)) {
/* ... and we need to start a transaction. */ /* ... and we need to start a transaction. */
srec->status = (srec->status & ~S_EXPIRE) | (nex * S_EXPIRE); srec->status = (srec->status & ~S_EXPIRE) | (nex * S_EXPIRE);
JLOG( "~ %u %u %u", (srec->uid[F], srec->uid[N], srec->status), "expire %u - begin", nex ); JLOG( "~ %u %u %d", (srec->uid[F], srec->uid[N], srec->status & S_LOGGED),
"expire %u - begin", nex );
} else { } else {
/* ... but the "right" transaction is already pending. */ /* ... but the "right" transaction is already pending. */
debug( "-> pair(%u,%u): expire %u (pending)\n", srec->uid[F], srec->uid[N], nex ); debug( "-> pair(%u,%u): expire %u (pending)\n", srec->uid[F], srec->uid[N], nex );
} }
} else { } else {
/* Note: the "wrong" transaction may be pending here, /* Note: the "wrong" transaction may be pending here,
* e.g.: W_NEXPIRE = 0, S_EXPIRE = 1, S_EXPIRED = 0. */ * e.g.: S_NEXPIRE = 0, S_EXPIRE = 1, S_EXPIRED = 0. */
} }
} else { } else {
if (srec->wstate & W_NEXPIRE) { if (srec->status & S_NEXPIRE) {
JLOG( "= %u %u", (srec->uid[F], srec->uid[N]), "expire unborn" ); JLOG( "= %u %u", (srec->uid[F], srec->uid[N]), "expire unborn" );
// If we have so many new messages that some of them are instantly expired, // If we have so many new messages that some of them are instantly expired,
// but some are still propagated because they are important, we need to // but some are still propagated because they are important, we need to
@ -1270,14 +1272,14 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
continue; continue;
aflags = srec->aflags[t]; aflags = srec->aflags[t];
dflags = srec->dflags[t]; dflags = srec->dflags[t];
if (srec->wstate & (W_DELETE|W_PURGE)) { if (srec->status & (S_DELETE | S_PURGE)) {
if (!aflags) { if (!aflags) {
// This deletion propagation goes the other way round, or // This deletion propagation goes the other way round, or
// this deletion of a dummy happens on the other side. // this deletion of a dummy happens on the other side.
continue; continue;
} }
if (!srec->msg[t] && (svars->opts[t] & OPEN_OLD)) { if (!srec->msg[t] && (svars->opts[t] & OPEN_OLD)) {
// The message disappeared. This can happen, because the wstate may // The message disappeared. This can happen, because the status may
// come from the journal, and things could have happened meanwhile. // come from the journal, and things could have happened meanwhile.
continue; continue;
} }
@ -1286,7 +1288,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
if ((t == N) && ((shifted_bit(srec->status, S_EXPIRE, S_EXPIRED) ^ srec->status) & S_EXPIRED)) { if ((t == N) && ((shifted_bit(srec->status, S_EXPIRE, S_EXPIRED) ^ srec->status) & S_EXPIRED)) {
// ... but the actual action derives from the wanted state - // ... but the actual action derives from the wanted state -
// so that canceled transactions are rolled back as well. // so that canceled transactions are rolled back as well.
if (srec->wstate & W_NEXPIRE) if (srec->status & S_NEXPIRE)
aflags |= F_DELETED; aflags |= F_DELETED;
else else
dflags |= F_DELETED; dflags |= F_DELETED;
@ -1369,7 +1371,7 @@ msg_copied( int sts, uint uid, copy_vars_t *vars )
sync_rec_t *srec = vars->srec; sync_rec_t *srec = vars->srec;
switch (sts) { switch (sts) {
case SYNC_OK: case SYNC_OK:
if (!(srec->wstate & W_UPGRADE) && vars->msg->flags != srec->flags) { if (!(srec->status & S_UPGRADE) && vars->msg->flags != srec->flags) {
srec->flags = vars->msg->flags; srec->flags = vars->msg->flags;
JLOG( "* %u %u %u", (srec->uid[F], srec->uid[N], srec->flags), "%sed with flags", str_hl[t] ); JLOG( "* %u %u %u", (srec->uid[F], srec->uid[N], srec->flags), "%sed with flags", str_hl[t] );
} }
@ -1480,9 +1482,9 @@ flags_set( int sts, void *aux )
switch (sts) { switch (sts) {
case DRV_OK: case DRV_OK:
if (vars->aflags & F_DELETED) if (vars->aflags & F_DELETED)
srec->wstate |= W_DEL(t); srec->status |= S_DEL(t);
else if (vars->dflags & F_DELETED) else if (vars->dflags & F_DELETED)
srec->wstate &= ~W_DEL(t); srec->status &= ~S_DEL(t);
flags_set_p2( svars, srec, t ); flags_set_p2( svars, srec, t );
break; break;
} }
@ -1496,7 +1498,7 @@ flags_set( int sts, void *aux )
static void static void
flags_set_p2( sync_vars_t *svars, sync_rec_t *srec, int t ) flags_set_p2( sync_vars_t *svars, sync_rec_t *srec, int t )
{ {
if (srec->wstate & W_DELETE) { if (srec->status & S_DELETE) {
JLOG( "%c %u %u 0", ("><"[t], srec->uid[F], srec->uid[N]), "%sed deletion", str_hl[t] ); JLOG( "%c %u %u 0", ("><"[t], srec->uid[F], srec->uid[N]), "%sed deletion", str_hl[t] );
srec->uid[t^1] = 0; srec->uid[t^1] = 0;
} else { } else {
@ -1506,13 +1508,15 @@ flags_set_p2( sync_vars_t *svars, sync_rec_t *srec, int t )
srec->flags = nflags; srec->flags = nflags;
} }
if (t == N) { if (t == N) {
uchar nex = (srec->wstate / W_NEXPIRE) & 1; uchar nex = (srec->status / S_NEXPIRE) & 1;
if (nex != ((srec->status / S_EXPIRED) & 1)) { if (nex != ((srec->status / S_EXPIRED) & 1)) {
srec->status = (srec->status & ~S_EXPIRED) | (nex * S_EXPIRED); srec->status = (srec->status & ~S_EXPIRED) | (nex * S_EXPIRED);
JLOG( "~ %u %u %u", (srec->uid[F], srec->uid[N], srec->status), "expired %d - commit", nex ); JLOG( "~ %u %u %d", (srec->uid[F], srec->uid[N], srec->status & S_LOGGED),
"expired %d - commit", nex );
} else if (nex != ((srec->status / S_EXPIRE) & 1)) { } else if (nex != ((srec->status / S_EXPIRE) & 1)) {
srec->status = (srec->status & ~S_EXPIRE) | (nex * S_EXPIRE); srec->status = (srec->status & ~S_EXPIRE) | (nex * S_EXPIRE);
JLOG( "~ %u %u %u", (srec->uid[F], srec->uid[N], srec->status), "expire %d - cancel", nex ); JLOG( "~ %u %u %d", (srec->uid[F], srec->uid[N], srec->status & S_LOGGED),
"expire %d - cancel", nex );
} }
} }
} }
@ -1690,8 +1694,8 @@ box_closed_p2( sync_vars_t *svars, int t )
for (srec = svars->srecs; srec; srec = srec->next) { for (srec = svars->srecs; srec; srec = srec->next) {
if (srec->status & S_DEAD) if (srec->status & S_DEAD)
continue; continue;
if (!srec->uid[N] || ((srec->wstate & W_DEL(N)) && (svars->state[N] & ST_DID_EXPUNGE))) { if (!srec->uid[N] || ((srec->status & S_DEL(N)) && (svars->state[N] & ST_DID_EXPUNGE))) {
if (!srec->uid[F] || ((srec->wstate & W_DEL(F)) && (svars->state[F] & ST_DID_EXPUNGE)) || if (!srec->uid[F] || ((srec->status & S_DEL(F)) && (svars->state[F] & ST_DID_EXPUNGE)) ||
((srec->status & S_EXPIRED) && svars->maxuid[F] >= srec->uid[F] && svars->maxxfuid >= srec->uid[F])) { ((srec->status & S_EXPIRED) && svars->maxuid[F] >= srec->uid[F] && svars->maxxfuid >= srec->uid[F])) {
JLOG( "- %u %u", (srec->uid[F], srec->uid[N]), "killing" ); JLOG( "- %u %u", (srec->uid[F], srec->uid[N]), "killing" );
srec->status = S_DEAD; srec->status = S_DEAD;
@ -1699,7 +1703,7 @@ box_closed_p2( sync_vars_t *svars, int t )
JLOG( "> %u %u 0", (srec->uid[F], srec->uid[N]), "orphaning" ); JLOG( "> %u %u 0", (srec->uid[F], srec->uid[N]), "orphaning" );
srec->uid[N] = 0; srec->uid[N] = 0;
} }
} else if (srec->uid[F] && ((srec->wstate & W_DEL(F)) && (svars->state[F] & ST_DID_EXPUNGE))) { } else if (srec->uid[F] && ((srec->status & S_DEL(F)) && (svars->state[F] & ST_DID_EXPUNGE))) {
JLOG( "< %u %u 0", (srec->uid[F], srec->uid[N]), "orphaning" ); JLOG( "< %u %u 0", (srec->uid[F], srec->uid[N]), "orphaning" );
srec->uid[F] = 0; srec->uid[F] = 0;
} }

View File

@ -9,33 +9,30 @@
#include "sync.h" #include "sync.h"
#include "sync_p_enum.h" #include "sync_p_enum.h"
// This is the (mostly) persistent status of the sync record.
// Most of these bits are actually mutually exclusive. It is a
// bitfield to allow for easy testing for multiple states.
BIT_ENUM( BIT_ENUM(
S_DEAD, // ephemeral: the entry was killed and should be ignored S_DEAD, // ephemeral: the entry was killed and should be ignored
S_EXPIRE, // the entry is being expired (near side message removal scheduled) S_EXPIRE, // the entry is being expired (near side message removal scheduled)
S_EXPIRED, // the entry is expired (near side message removal confirmed) S_EXPIRED, // the entry is expired (near side message removal confirmed)
S_NEXPIRE, // temporary: new expiration state
S_PENDING, // the entry is new and awaits propagation (possibly a retry) S_PENDING, // the entry is new and awaits propagation (possibly a retry)
S_DUMMY(2), // f/n message is only a placeholder S_DUMMY(2), // f/n message is only a placeholder
S_SKIPPED, // pre-1.4 legacy: the entry was not propagated (message is too big) S_SKIPPED, // pre-1.4 legacy: the entry was not propagated (message is too big)
S_DEL(2), // ephemeral: f/n message would be subject to expunge
S_DELETE, // ephemeral: flags propagation is a deletion
S_UPGRADE, // ephemeral: upgrading placeholder, do not apply MaxSize
S_PURGE, // ephemeral: placeholder is being nuked
) )
// Ephemeral working set. // This is the persistent status of the sync record, with regard to the journal.
BIT_ENUM( #define S_LOGGED (S_EXPIRE | S_EXPIRED | S_PENDING | S_DUMMY(F) | S_DUMMY(N) | S_SKIPPED)
W_NEXPIRE, // temporary: new expiration state
W_DELETE, // ephemeral: flags propagation is a deletion
W_DEL(2), // ephemeral: f/n message would be subject to expunge
W_UPGRADE, // ephemeral: upgrading placeholder, do not apply MaxSize
W_PURGE, // ephemeral: placeholder is being nuked
)
typedef struct sync_rec { typedef struct sync_rec {
struct sync_rec *next; struct sync_rec *next;
/* string_list_t *keywords; */ /* string_list_t *keywords; */
uint uid[2]; uint uid[2];
message_t *msg[2]; message_t *msg[2];
uchar status, wstate, flags, pflags, aflags[2], dflags[2]; ushort status;
uchar flags, pflags, aflags[2], dflags[2];
char tuid[TUIDL]; char tuid[TUIDL];
} sync_rec_t; } sync_rec_t;

View File

@ -356,16 +356,15 @@ load_state( sync_vars_t *svars )
debug( "flags now %u\n", t3 ); debug( "flags now %u\n", t3 );
srec->flags = (uchar)t3; srec->flags = (uchar)t3;
srec->aflags[F] = srec->aflags[N] = 0; // Clear F_DELETED from purge srec->aflags[F] = srec->aflags[N] = 0; // Clear F_DELETED from purge
srec->wstate &= ~W_PURGE; srec->status &= ~S_PURGE;
break; break;
case '~': case '~':
debug( "status now %#x\n", t3 ); srec->status = (srec->status & ~S_LOGGED) | t3;
srec->status = (uchar)t3; debug( "status now %#x\n", srec->status );
break; break;
case '_': case '_':
debug( "has placeholder now\n" ); debug( "has placeholder now\n" );
srec->status = S_PENDING; // Pre-1.4 legacy only srec->status = S_PENDING | (!srec->uid[F] ? S_DUMMY(F) : S_DUMMY(N));
srec->status |= !srec->uid[F] ? S_DUMMY(F) : S_DUMMY(N);
break; break;
case '^': case '^':
debug( "is being upgraded, flags %u, srec flags %u\n", t3, t4 ); debug( "is being upgraded, flags %u, srec flags %u\n", t3, t4 );
@ -482,8 +481,7 @@ assign_uid( sync_vars_t *svars, sync_rec_t *srec, int t, uint uid )
srec->uid[t] = uid; srec->uid[t] = uid;
if (uid == svars->maxuid[t] + 1) if (uid == svars->maxuid[t] + 1)
svars->maxuid[t] = uid; svars->maxuid[t] = uid;
srec->status &= ~S_PENDING; srec->status &= ~(S_PENDING | S_UPGRADE);
srec->wstate &= ~W_UPGRADE;
srec->tuid[0] = 0; srec->tuid[0] = 0;
} }
@ -560,10 +558,9 @@ upgrade_srec( sync_vars_t *svars, sync_rec_t *srec )
srec->msg[t] = NULL; srec->msg[t] = NULL;
} }
// Mark the original entry for upgrade. // Mark the original entry for upgrade.
srec->status = (srec->status & ~(S_DUMMY(F)|S_DUMMY(N))) | S_PENDING; srec->status = (srec->status & ~(S_DUMMY(F) | S_DUMMY(N))) | S_PENDING | S_UPGRADE;
srec->wstate |= W_UPGRADE;
// Mark the placeholder for nuking. // Mark the placeholder for nuking.
nsrec->wstate = W_PURGE; nsrec->status = S_PURGE;
nsrec->aflags[t] = F_DELETED; nsrec->aflags[t] = F_DELETED;
return nsrec; return nsrec;
} }