diff options
Diffstat (limited to 'src/include/pgstat.h')
-rw-r--r-- | src/include/pgstat.h | 81 |
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 * |