beentry = MyBEEntry;
do
{
- beentry->st_changecount++;
+ pgstat_increment_changecount_before(beentry);
} while ((beentry->st_changecount & 1) == 0);
beentry->st_procpid = MyProcPid;
beentry->st_appname[NAMEDATALEN - 1] = '\0';
beentry->st_activity[pgstat_track_activity_query_size - 1] = '\0';
- beentry->st_changecount++;
- Assert((beentry->st_changecount & 1) == 0);
+ pgstat_increment_changecount_after(beentry);
/* Update app name to current GUC setting */
if (application_name)
* before and after. We use a volatile pointer here to ensure the
* compiler doesn't try to get cute.
*/
- beentry->st_changecount++;
+ pgstat_increment_changecount_before(beentry);
beentry->st_procpid = 0; /* mark invalid */
- beentry->st_changecount++;
- Assert((beentry->st_changecount & 1) == 0);
+ pgstat_increment_changecount_after(beentry);
}
* non-disabled state. As our final update, change the state and
* clear fields we will not be updating anymore.
*/
- beentry->st_changecount++;
+ pgstat_increment_changecount_before(beentry);
beentry->st_state = STATE_DISABLED;
beentry->st_state_start_timestamp = 0;
beentry->st_activity[0] = '\0';
/* st_xact_start_timestamp and st_waiting are also disabled */
beentry->st_xact_start_timestamp = 0;
beentry->st_waiting = false;
- beentry->st_changecount++;
- Assert((beentry->st_changecount & 1) == 0);
+ pgstat_increment_changecount_after(beentry);
}
return;
}
/*
* Now update the status entry
*/
- beentry->st_changecount++;
+ pgstat_increment_changecount_before(beentry);
beentry->st_state = state;
beentry->st_state_start_timestamp = current_timestamp;
beentry->st_activity_start_timestamp = start_timestamp;
}
- beentry->st_changecount++;
- Assert((beentry->st_changecount & 1) == 0);
+ pgstat_increment_changecount_after(beentry);
}
/* ----------
* st_changecount before and after. We use a volatile pointer here to
* ensure the compiler doesn't try to get cute.
*/
- beentry->st_changecount++;
+ pgstat_increment_changecount_before(beentry);
memcpy((char *) beentry->st_appname, appname, len);
beentry->st_appname[len] = '\0';
- beentry->st_changecount++;
- Assert((beentry->st_changecount & 1) == 0);
+ pgstat_increment_changecount_after(beentry);
}
/*
* st_changecount before and after. We use a volatile pointer here to
* ensure the compiler doesn't try to get cute.
*/
- beentry->st_changecount++;
+ pgstat_increment_changecount_before(beentry);
beentry->st_xact_start_timestamp = tstamp;
- beentry->st_changecount++;
- Assert((beentry->st_changecount & 1) == 0);
+ pgstat_increment_changecount_after(beentry);
}
/* ----------
*/
for (;;)
{
- int save_changecount = beentry->st_changecount;
+ int before_changecount;
+ int after_changecount;
+
+ pgstat_save_changecount_before(beentry, before_changecount);
localentry->backendStatus.st_procpid = beentry->st_procpid;
if (localentry->backendStatus.st_procpid > 0)
localentry->backendStatus.st_activity = localactivity;
}
- if (save_changecount == beentry->st_changecount &&
- (save_changecount & 1) == 0)
+ pgstat_save_changecount_after(beentry, after_changecount);
+ if (before_changecount == after_changecount &&
+ (before_changecount & 1) == 0)
break;
/* Make sure we can break out of loop if stuck... */
for (;;)
{
- int save_changecount = vbeentry->st_changecount;
+ int before_changecount;
+ int after_changecount;
+
+ pgstat_save_changecount_before(vbeentry, before_changecount);
found = (vbeentry->st_procpid == pid);
- if (save_changecount == vbeentry->st_changecount &&
- (save_changecount & 1) == 0)
+ pgstat_save_changecount_after(vbeentry, after_changecount);
+
+ if (before_changecount == after_changecount &&
+ (before_changecount & 1) == 0)
break;
/* Make sure we can break out of loop if stuck... */
#include "libpq/pqcomm.h"
#include "portability/instr_time.h"
#include "postmaster/pgarch.h"
+#include "storage/barrier.h"
#include "utils/hsearch.h"
#include "utils/relcache.h"
* st_changecount again. If the value hasn't changed, and if it's even,
* the copy is valid; otherwise start over. This makes updates cheap
* while reads are potentially expensive, but that's the tradeoff we want.
+ *
+ * The above protocol needs the memory barriers to ensure that
+ * the apparent order of execution is as it desires. Otherwise,
+ * for example, the CPU might rearrange the code so that st_changecount
+ * is incremented twice before the modification on a machine with
+ * weak memory ordering. This surprising result can lead to bugs.
*/
int st_changecount;
char *st_activity;
} PgBackendStatus;
+/*
+ * Macros to load and store st_changecount with the memory barriers.
+ *
+ * pgstat_increment_changecount_before() and
+ * pgstat_increment_changecount_after() need to be called before and after
+ * PgBackendStatus entries are modified, respectively. This makes sure that
+ * st_changecount is incremented around the modification.
+ *
+ * Also pgstat_save_changecount_before() and pgstat_save_changecount_after()
+ * need to be called before and after PgBackendStatus entries are copied into
+ * private memory, respectively.
+ */
+#define pgstat_increment_changecount_before(beentry) \
+ do { \
+ beentry->st_changecount++; \
+ pg_write_barrier(); \
+ } while (0)
+
+#define pgstat_increment_changecount_after(beentry) \
+ do { \
+ pg_write_barrier(); \
+ beentry->st_changecount++; \
+ Assert((beentry->st_changecount & 1) == 0); \
+ } while (0)
+
+#define pgstat_save_changecount_before(beentry, save_changecount) \
+ do { \
+ save_changecount = beentry->st_changecount; \
+ pg_read_barrier(); \
+ } while (0)
+
+#define pgstat_save_changecount_after(beentry, save_changecount) \
+ do { \
+ pg_read_barrier(); \
+ save_changecount = beentry->st_changecount; \
+ } while (0)
+
/* ----------
* LocalPgBackendStatus
*