aboutsummaryrefslogtreecommitdiff
path: root/src/include/pgstat.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/include/pgstat.h')
-rw-r--r--src/include/pgstat.h81
1 files changed, 53 insertions, 28 deletions
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 9fbc4925303..929987190cc 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -1035,11 +1035,12 @@ typedef struct PgBackendStatus
* 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.
+ * The above protocol needs 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. Hence,
+ * use the macros defined below for manipulating st_changecount, rather
+ * than touching it directly.
*/
int st_changecount;
@@ -1099,42 +1100,66 @@ typedef struct PgBackendStatus
} PgBackendStatus;
/*
- * Macros to load and store st_changecount with the memory barriers.
+ * Macros to load and store st_changecount with appropriate 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.
+ * Use PGSTAT_BEGIN_WRITE_ACTIVITY() before, and PGSTAT_END_WRITE_ACTIVITY()
+ * after, modifying the current process's PgBackendStatus data. Note that,
+ * since there is no mechanism for cleaning up st_changecount after an error,
+ * THESE MACROS FORM A CRITICAL SECTION. Any error between them will be
+ * promoted to PANIC, causing a database restart to clean up shared memory!
+ * Hence, keep the critical section as short and straight-line as possible.
+ * Aside from being safer, that minimizes the window in which readers will
+ * have to loop.
*
- * 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.
+ * Reader logic should follow this sketch:
+ *
+ * for (;;)
+ * {
+ * int before_ct, after_ct;
+ *
+ * pgstat_begin_read_activity(beentry, before_ct);
+ * ... copy beentry data to local memory ...
+ * pgstat_end_read_activity(beentry, after_ct);
+ * if (pgstat_read_activity_complete(before_ct, after_ct))
+ * break;
+ * CHECK_FOR_INTERRUPTS();
+ * }
+ *
+ * For extra safety, we generally use volatile beentry pointers, although
+ * the memory barriers should theoretically be sufficient.
*/
-#define pgstat_increment_changecount_before(beentry) \
- do { \
- beentry->st_changecount++; \
+#define PGSTAT_BEGIN_WRITE_ACTIVITY(beentry) \
+ do { \
+ START_CRIT_SECTION(); \
+ (beentry)->st_changecount++; \
pg_write_barrier(); \
} while (0)
-#define pgstat_increment_changecount_after(beentry) \
- do { \
+#define PGSTAT_END_WRITE_ACTIVITY(beentry) \
+ do { \
pg_write_barrier(); \
- beentry->st_changecount++; \
- Assert((beentry->st_changecount & 1) == 0); \
+ (beentry)->st_changecount++; \
+ Assert(((beentry)->st_changecount & 1) == 0); \
+ END_CRIT_SECTION(); \
} while (0)
-#define pgstat_save_changecount_before(beentry, save_changecount) \
- do { \
- save_changecount = beentry->st_changecount; \
- pg_read_barrier(); \
+#define pgstat_begin_read_activity(beentry, before_changecount) \
+ do { \
+ (before_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; \
+#define pgstat_end_read_activity(beentry, after_changecount) \
+ do { \
+ pg_read_barrier(); \
+ (after_changecount) = (beentry)->st_changecount; \
} while (0)
+#define pgstat_read_activity_complete(before_changecount, after_changecount) \
+ ((before_changecount) == (after_changecount) && \
+ ((before_changecount) & 1) == 0)
+
+
/* ----------
* LocalPgBackendStatus
*