AutoVacNumSignals /* must be last */
} AutoVacuumSignal;
+/*
+ * Autovacuum workitem array, stored in AutoVacuumShmem->av_workItems. This
+ * list is mostly protected by AutovacuumLock, except that if an item is
+ * marked 'active' other processes must not modify the work-identifying
+ * members.
+ */
+typedef struct AutoVacuumWorkItem
+{
+ AutoVacuumWorkItemType avw_type;
+ bool avw_used; /* below data is valid */
+ bool avw_active; /* being processed */
+ Oid avw_database;
+ Oid avw_relation;
+ BlockNumber avw_blockNumber;
+} AutoVacuumWorkItem;
+
+#define NUM_WORKITEMS 256
+
/*-------------
* The main autovacuum shmem struct. On shared memory we store this main
* struct and the array of WorkerInfo structs. This struct keeps:
* av_runningWorkers the WorkerInfo non-free queue
* av_startingWorker pointer to WorkerInfo currently being started (cleared by
* the worker itself as soon as it's up and running)
- * av_dsa_handle handle for allocatable shared memory
+ * av_workItems work item array
*
* This struct is protected by AutovacuumLock, except for av_signal and parts
- * of the worker list (see above). av_dsa_handle is readable unlocked.
+ * of the worker list (see above).
*-------------
*/
typedef struct
dlist_head av_freeWorkers;
dlist_head av_runningWorkers;
WorkerInfo av_startingWorker;
- dsa_handle av_dsa_handle;
- dsa_pointer av_workitems;
+ AutoVacuumWorkItem av_workItems[NUM_WORKITEMS];
} AutoVacuumShmemStruct;
static AutoVacuumShmemStruct *AutoVacuumShmem;
/* Pointer to my own WorkerInfo, valid on each worker */
static WorkerInfo MyWorkerInfo = NULL;
-/*
- * Autovacuum workitem array, stored in AutoVacuumShmem->av_workitems. This
- * list is mostly protected by AutovacuumLock, except that if it's marked
- * 'active' other processes must not modify the work-identifying members,
- * though changing the list pointers is okay.
- */
-typedef struct AutoVacuumWorkItem
-{
- AutoVacuumWorkItemType avw_type;
- Oid avw_database;
- Oid avw_relation;
- BlockNumber avw_blockNumber;
- bool avw_active;
- dsa_pointer avw_next; /* doubly linked list pointers */
- dsa_pointer avw_prev;
-} AutoVacuumWorkItem;
-
-#define NUM_WORKITEMS 256
-typedef struct
-{
- dsa_pointer avs_usedItems;
- dsa_pointer avs_freeItems;
-} AutovacWorkItems;
-
-static dsa_area *AutoVacuumDSA = NULL;
-
/* PID of launcher, valid only in worker while shutting down */
int AutovacuumLauncherPid = 0;
static void avl_sigusr2_handler(SIGNAL_ARGS);
static void avl_sigterm_handler(SIGNAL_ARGS);
static void autovac_refresh_stats(void);
-static void remove_wi_from_list(dsa_pointer *list, dsa_pointer wi_ptr);
-static void add_wi_to_list(dsa_pointer *list, dsa_pointer wi_ptr);
*/
rebuild_database_list(InvalidOid);
- /*
- * Set up our DSA so that backends can install work-item requests. It may
- * already exist as created by a previous launcher; and we may even be
- * already attached to it, if we're here after longjmp'ing above.
- */
- if (!AutoVacuumShmem->av_dsa_handle)
- {
- LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
- AutoVacuumDSA = dsa_create(AutovacuumLock->tranche);
- /* make sure it doesn't go away even if we do */
- dsa_pin(AutoVacuumDSA);
- dsa_pin_mapping(AutoVacuumDSA);
- AutoVacuumShmem->av_dsa_handle = dsa_get_handle(AutoVacuumDSA);
- /* delay array allocation until first request */
- AutoVacuumShmem->av_workitems = InvalidDsaPointer;
- LWLockRelease(AutovacuumLock);
- }
- else if (AutoVacuumDSA == NULL)
- {
- AutoVacuumDSA = dsa_attach(AutoVacuumShmem->av_dsa_handle);
- dsa_pin_mapping(AutoVacuumDSA);
- }
-
/* loop until shutdown request */
while (!got_SIGTERM)
{
{
char dbname[NAMEDATALEN];
- if (AutoVacuumShmem->av_dsa_handle)
- {
- /* First use of DSA in this worker, so attach to it */
- Assert(!AutoVacuumDSA);
- AutoVacuumDSA = dsa_attach(AutoVacuumShmem->av_dsa_handle);
- dsa_pin_mapping(AutoVacuumDSA);
- }
-
/*
* Report autovac startup to the stats collector. We deliberately do
* this before InitPostgres, so that the last_autovac_time will get
int effective_multixact_freeze_max_age;
bool did_vacuum = false;
bool found_concurrent_worker = false;
+ int i;
/*
* StartTransactionCommand and CommitTransactionCommand will automatically
/*
* Perform additional work items, as requested by backends.
*/
- if (AutoVacuumShmem->av_workitems)
+ LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
+ for (i = 0; i < NUM_WORKITEMS; i++)
{
- dsa_pointer wi_ptr;
- AutovacWorkItems *workitems;
+ AutoVacuumWorkItem *workitem = &AutoVacuumShmem->av_workItems[i];
- LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
+ if (!workitem->avw_used)
+ continue;
+ if (workitem->avw_active)
+ continue;
+
+ /* claim this one, and release lock while performing it */
+ workitem->avw_active = true;
+ LWLockRelease(AutovacuumLock);
+
+ perform_work_item(workitem);
/*
- * Scan the list of pending items, and process the inactive ones in
- * our database.
+ * Check for config changes before acquiring lock for further
+ * jobs.
*/
- workitems = (AutovacWorkItems *)
- dsa_get_address(AutoVacuumDSA, AutoVacuumShmem->av_workitems);
- wi_ptr = workitems->avs_usedItems;
-
- while (wi_ptr != InvalidDsaPointer)
+ CHECK_FOR_INTERRUPTS();
+ if (got_SIGHUP)
{
- AutoVacuumWorkItem *workitem;
-
- workitem = (AutoVacuumWorkItem *)
- dsa_get_address(AutoVacuumDSA, wi_ptr);
-
- if (workitem->avw_database == MyDatabaseId && !workitem->avw_active)
- {
- dsa_pointer next_ptr;
-
- /* claim this one */
- workitem->avw_active = true;
-
- LWLockRelease(AutovacuumLock);
-
- perform_work_item(workitem);
-
- /*
- * Check for config changes before acquiring lock for further
- * jobs.
- */
- CHECK_FOR_INTERRUPTS();
- if (got_SIGHUP)
- {
- got_SIGHUP = false;
- ProcessConfigFile(PGC_SIGHUP);
- }
-
- LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
-
- /* Put the array item back for the next user */
- next_ptr = workitem->avw_next;
- remove_wi_from_list(&workitems->avs_usedItems, wi_ptr);
- add_wi_to_list(&workitems->avs_freeItems, wi_ptr);
- wi_ptr = next_ptr;
- }
- else
- wi_ptr = workitem->avw_next;
+ got_SIGHUP = false;
+ ProcessConfigFile(PGC_SIGHUP);
}
- /* all done */
- LWLockRelease(AutovacuumLock);
+ LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
+
+ /* and mark it done */
+ workitem->avw_active = false;
+ workitem->avw_used = false;
}
+ LWLockRelease(AutovacuumLock);
/*
* We leak table_toast_map here (among other things), but since we're
AutoVacuumRequestWork(AutoVacuumWorkItemType type, Oid relationId,
BlockNumber blkno)
{
- AutovacWorkItems *workitems;
- dsa_pointer wi_ptr;
- AutoVacuumWorkItem *workitem;
+ int i;
LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
/*
- * It may be useful to de-duplicate the list upon insertion. For the only
- * currently existing caller, this is not necessary.
+ * Locate an unused work item and fill it with the given data.
*/
-
- /* First use in this process? Set up DSA */
- if (!AutoVacuumDSA)
- {
- if (!AutoVacuumShmem->av_dsa_handle)
- {
- /* autovacuum launcher not started; nothing can be done */
- LWLockRelease(AutovacuumLock);
- return;
- }
- AutoVacuumDSA = dsa_attach(AutoVacuumShmem->av_dsa_handle);
- dsa_pin_mapping(AutoVacuumDSA);
- }
-
- /* First use overall? Allocate work items array */
- if (AutoVacuumShmem->av_workitems == InvalidDsaPointer)
+ for (i = 0; i < NUM_WORKITEMS; i++)
{
- int i;
- AutovacWorkItems *workitems;
-
- AutoVacuumShmem->av_workitems =
- dsa_allocate_extended(AutoVacuumDSA,
- sizeof(AutovacWorkItems) +
- NUM_WORKITEMS * sizeof(AutoVacuumWorkItem),
- DSA_ALLOC_NO_OOM);
- /* if out of memory, silently disregard the request */
- if (AutoVacuumShmem->av_workitems == InvalidDsaPointer)
- {
- LWLockRelease(AutovacuumLock);
- dsa_detach(AutoVacuumDSA);
- AutoVacuumDSA = NULL;
- return;
- }
-
- /* Initialize each array entry as a member of the free list */
- workitems = dsa_get_address(AutoVacuumDSA, AutoVacuumShmem->av_workitems);
+ AutoVacuumWorkItem *workitem = &AutoVacuumShmem->av_workItems[i];
- workitems->avs_usedItems = InvalidDsaPointer;
- workitems->avs_freeItems = InvalidDsaPointer;
- for (i = 0; i < NUM_WORKITEMS; i++)
- {
- /* XXX surely there is a simpler way to do this */
- wi_ptr = AutoVacuumShmem->av_workitems + sizeof(AutovacWorkItems) +
- sizeof(AutoVacuumWorkItem) * i;
- workitem = (AutoVacuumWorkItem *) dsa_get_address(AutoVacuumDSA, wi_ptr);
-
- workitem->avw_type = 0;
- workitem->avw_database = InvalidOid;
- workitem->avw_relation = InvalidOid;
- workitem->avw_active = false;
-
- /* put this item in the free list */
- workitem->avw_next = workitems->avs_freeItems;
- workitems->avs_freeItems = wi_ptr;
- }
- }
+ if (workitem->avw_used)
+ continue;
- workitems = (AutovacWorkItems *)
- dsa_get_address(AutoVacuumDSA, AutoVacuumShmem->av_workitems);
+ workitem->avw_used = true;
+ workitem->avw_active = false;
+ workitem->avw_type = type;
+ workitem->avw_database = MyDatabaseId;
+ workitem->avw_relation = relationId;
+ workitem->avw_blockNumber = blkno;
- /* If array is full, disregard the request */
- if (workitems->avs_freeItems == InvalidDsaPointer)
- {
- LWLockRelease(AutovacuumLock);
- dsa_detach(AutoVacuumDSA);
- AutoVacuumDSA = NULL;
- return;
+ /* done */
+ break;
}
- /* remove workitem struct from free list ... */
- wi_ptr = workitems->avs_freeItems;
- remove_wi_from_list(&workitems->avs_freeItems, wi_ptr);
-
- /* ... initialize it ... */
- workitem = dsa_get_address(AutoVacuumDSA, wi_ptr);
- workitem->avw_type = type;
- workitem->avw_database = MyDatabaseId;
- workitem->avw_relation = relationId;
- workitem->avw_blockNumber = blkno;
- workitem->avw_active = false;
-
- /* ... and put it on autovacuum's to-do list */
- add_wi_to_list(&workitems->avs_usedItems, wi_ptr);
-
LWLockRelease(AutovacuumLock);
-
- dsa_detach(AutoVacuumDSA);
- AutoVacuumDSA = NULL;
}
/*
dlist_init(&AutoVacuumShmem->av_freeWorkers);
dlist_init(&AutoVacuumShmem->av_runningWorkers);
AutoVacuumShmem->av_startingWorker = NULL;
+ memset(AutoVacuumShmem->av_workItems, 0,
+ sizeof(AutoVacuumWorkItem) * NUM_WORKITEMS);
worker = (WorkerInfo) ((char *) AutoVacuumShmem +
MAXALIGN(sizeof(AutoVacuumShmemStruct)));
pgstat_clear_snapshot();
}
-
-/*
- * Simplistic open-coded list implementation for objects stored in DSA.
- * Each item is doubly linked, but we have no tail pointer, and the "prev"
- * element of the first item is null, not the list.
- */
-
-/*
- * Remove a work item from the given list.
- */
-static void
-remove_wi_from_list(dsa_pointer *list, dsa_pointer wi_ptr)
-{
- AutoVacuumWorkItem *workitem = dsa_get_address(AutoVacuumDSA, wi_ptr);
- dsa_pointer next = workitem->avw_next;
- dsa_pointer prev = workitem->avw_prev;
-
- workitem->avw_next = workitem->avw_prev = InvalidDsaPointer;
-
- if (next != InvalidDsaPointer)
- {
- workitem = dsa_get_address(AutoVacuumDSA, next);
- workitem->avw_prev = prev;
- }
-
- if (prev != InvalidDsaPointer)
- {
- workitem = dsa_get_address(AutoVacuumDSA, prev);
- workitem->avw_next = next;
- }
- else
- *list = next;
-}
-
-/*
- * Add a workitem to the given list
- */
-static void
-add_wi_to_list(dsa_pointer *list, dsa_pointer wi_ptr)
-{
- if (*list == InvalidDsaPointer)
- {
- /* list is empty; item is now singleton */
- *list = wi_ptr;
- }
- else
- {
- AutoVacuumWorkItem *workitem = dsa_get_address(AutoVacuumDSA, wi_ptr);
- AutoVacuumWorkItem *old = dsa_get_address(AutoVacuumDSA, *list);
-
- /* Put item at head of list */
- workitem->avw_next = *list;
- old->avw_prev = wi_ptr;
- *list = wi_ptr;
- }
-}