diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c index 94fb236daf..12847e35ea 100644 --- a/src/backend/nodes/list.c +++ b/src/backend/nodes/list.c @@ -906,6 +906,72 @@ list_delete_last(List *list) return list_truncate(list, list_length(list) - 1); } +/* + * Delete the first N cells of the list. + * + * The List is pfree'd if the request causes all cells to be deleted. + */ +List * +list_delete_first_n(List *list, int n) +{ + check_list_invariants(list); + + /* No-op request? */ + if (n <= 0) + return list; + + /* Delete whole list? */ + if (n >= list_length(list)) + { + list_free(list); + return NIL; + } + + /* + * Otherwise, we normally just collapse out the removed elements. But for + * debugging purposes, move the whole list contents someplace else. + * + * (Note that we *must* keep the contents in the same memory context.) + */ +#ifndef DEBUG_LIST_MEMORY_USAGE + memmove(&list->elements[0], &list->elements[n], + (list->length - n) * sizeof(ListCell)); + list->length -= n; +#else + { + ListCell *newelems; + int newmaxlen = list->length - n; + + newelems = (ListCell *) + MemoryContextAlloc(GetMemoryChunkContext(list), + newmaxlen * sizeof(ListCell)); + memcpy(newelems, &list->elements[n], newmaxlen * sizeof(ListCell)); + if (list->elements != list->initial_elements) + pfree(list->elements); + else + { + /* + * As in enlarge_list(), clear the initial_elements[] space and/or + * mark it inaccessible. + */ +#ifdef CLOBBER_FREED_MEMORY + wipe_mem(list->initial_elements, + list->max_length * sizeof(ListCell)); +#else + VALGRIND_MAKE_MEM_NOACCESS(list->initial_elements, + list->max_length * sizeof(ListCell)); +#endif + } + list->elements = newelems; + list->max_length = newmaxlen; + list->length = newmaxlen; + check_list_invariants(list); + } +#endif + + return list; +} + /* * Generate the union of two lists. This is calculated by copying * list1 via list_copy(), then adding to it all the members of list2 diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 3412d31117..109f93d109 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -4139,7 +4139,7 @@ add_function_defaults(List *args, int pronargs, HeapTuple func_tuple) if (ndelete < 0) elog(ERROR, "not enough default arguments"); if (ndelete > 0) - defaults = list_copy_tail(defaults, ndelete); + defaults = list_delete_first_n(defaults, ndelete); /* And form the combined argument list, not modifying the input list */ return list_concat_copy(args, defaults); diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 3cec8de7da..542f9167aa 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -1691,7 +1691,7 @@ func_get_detail(List *funcname, ndelete = list_length(defaults) - best_candidate->ndargs; if (ndelete > 0) - defaults = list_copy_tail(defaults, ndelete); + defaults = list_delete_first_n(defaults, ndelete); *argdefaults = defaults; } } diff --git a/src/backend/storage/sync/sync.c b/src/backend/storage/sync/sync.c index 4a2ed414b0..d4083e8a56 100644 --- a/src/backend/storage/sync/sync.c +++ b/src/backend/storage/sync/sync.c @@ -69,6 +69,7 @@ typedef struct { FileTag tag; /* identifies handler and file */ CycleCtr cycle_ctr; /* checkpoint_cycle_ctr when request was made */ + bool canceled; /* true if request has been canceled */ } PendingUnlinkEntry; static HTAB *pendingOps = NULL; @@ -195,13 +196,18 @@ void SyncPostCheckpoint(void) { int absorb_counter; + ListCell *lc; absorb_counter = UNLINKS_PER_ABSORB; - while (pendingUnlinks != NIL) + foreach(lc, pendingUnlinks) { - PendingUnlinkEntry *entry = (PendingUnlinkEntry *) linitial(pendingUnlinks); + PendingUnlinkEntry *entry = (PendingUnlinkEntry *) lfirst(lc); char path[MAXPGPATH]; + /* Skip over any canceled entries */ + if (entry->canceled) + continue; + /* * New entries are appended to the end, so if the entry is new we've * reached the end of old entries. @@ -231,15 +237,13 @@ SyncPostCheckpoint(void) errmsg("could not remove file \"%s\": %m", path))); } - /* And remove the list entry */ - pendingUnlinks = list_delete_first(pendingUnlinks); - pfree(entry); + /* Mark the list entry as canceled, just in case */ + entry->canceled = true; /* * As in ProcessSyncRequests, we don't want to stop absorbing fsync * requests for a long time when there are many deletions to be done. - * We can safely call AbsorbSyncRequests() at this point in the loop - * (note it might try to delete list entries). + * We can safely call AbsorbSyncRequests() at this point in the loop. */ if (--absorb_counter <= 0) { @@ -247,6 +251,26 @@ SyncPostCheckpoint(void) absorb_counter = UNLINKS_PER_ABSORB; } } + + /* + * If we reached the end of the list, we can just remove the whole list + * (remembering to pfree all the PendingUnlinkEntry objects). Otherwise, + * we must keep the entries at or after "lc". + */ + if (lc == NULL) + { + list_free_deep(pendingUnlinks); + pendingUnlinks = NIL; + } + else + { + int ntodelete = list_cell_number(pendingUnlinks, lc); + + for (int i = 0; i < ntodelete; i++) + pfree(list_nth(pendingUnlinks, i)); + + pendingUnlinks = list_delete_first_n(pendingUnlinks, ntodelete); + } } /* @@ -486,17 +510,14 @@ RememberSyncRequest(const FileTag *ftag, SyncRequestType type) entry->canceled = true; } - /* Remove matching unlink requests */ + /* Cancel matching unlink requests */ foreach(cell, pendingUnlinks) { PendingUnlinkEntry *entry = (PendingUnlinkEntry *) lfirst(cell); if (entry->tag.handler == ftag->handler && syncsw[ftag->handler].sync_filetagmatches(ftag, &entry->tag)) - { - pendingUnlinks = foreach_delete_current(pendingUnlinks, cell); - pfree(entry); - } + entry->canceled = true; } } else if (type == SYNC_UNLINK_REQUEST) @@ -508,6 +529,7 @@ RememberSyncRequest(const FileTag *ftag, SyncRequestType type) entry = palloc(sizeof(PendingUnlinkEntry)); entry->tag = *ftag; entry->cycle_ctr = checkpoint_cycle_ctr; + entry->canceled = false; pendingUnlinks = lappend(pendingUnlinks, entry); diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h index 30f98c4595..c3f47db888 100644 --- a/src/include/nodes/pg_list.h +++ b/src/include/nodes/pg_list.h @@ -564,6 +564,7 @@ extern pg_nodiscard List *list_delete_int(List *list, int datum); extern pg_nodiscard List *list_delete_oid(List *list, Oid datum); extern pg_nodiscard List *list_delete_first(List *list); extern pg_nodiscard List *list_delete_last(List *list); +extern pg_nodiscard List *list_delete_first_n(List *list, int n); extern pg_nodiscard List *list_delete_nth_cell(List *list, int n); extern pg_nodiscard List *list_delete_cell(List *list, ListCell *cell);