| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /*------------------------------------------------------------------------- |
| * |
| * idle_tracker.c |
| * Implementation of the idle tracker that tracks whether a process is idle |
| * (and therefore is incapable of responding to a runaway cleanup event). |
| * This module coordinates with the runaway cleaner to ensure that an active |
| * process cannot become idle before cleaning up for a pending runaway event. |
| * |
| *------------------------------------------------------------------------- |
| */ |
| |
| #include "postgres.h" |
| #include "cdb/cdbvars.h" |
| #include "miscadmin.h" |
| #include "storage/ipc.h" |
| #include "utils/session_state.h" |
| #include "utils/vmem_tracker.h" |
| |
| /* External dependencies within the runaway cleanup framework */ |
| extern bool vmemTrackerInited; |
| extern volatile EventVersion *CurrentVersion; |
| extern volatile EventVersion *latestRunawayVersion; |
| extern void RunawayCleaner_StartCleanup(void); |
| extern bool RunawayCleaner_IsCleanupInProgress(void ); |
| |
| /* The event version when this process was last activated */ |
| EventVersion activationVersion = 0; |
| |
| /* The event version when this process was last deactivated */ |
| EventVersion deactivationVersion = 0; |
| |
| /* |
| * Is this process active (i.e., contributing 1 to activeProcessCount of the MySessionState). |
| * As the activation/deactivation version can overlap in case there was no |
| * runaway event in between activation and deactivation, this boolean flag |
| * will indicate whether the activation or the deactivation event happened last. |
| */ |
| bool isProcessActive = false; |
| |
| void IdleTracker_ShmemInit(void); |
| void IdleTracker_Init(void); |
| void IdleTracker_Shutdown(void); |
| |
| /* |
| * Initializes the idle tracker's shared memory states. |
| */ |
| void |
| IdleTracker_ShmemInit() |
| { |
| if(!IsUnderPostmaster) |
| { |
| Assert(*CurrentVersion > 0); |
| /* |
| * Set both activation and deactivation version greater than |
| * latestRunawayVersion so that they consider that runaway |
| * event as irrelevant. |
| */ |
| activationVersion = *CurrentVersion; |
| deactivationVersion = *CurrentVersion; |
| /* We will soon activate it, once the IdleTracker_Init() is called */ |
| isProcessActive = false; |
| } |
| } |
| |
| /* |
| * Initializes the per-process states of the idle tracker. |
| */ |
| void |
| IdleTracker_Init() |
| { |
| Assert(!vmemTrackerInited); |
| Assert(!isProcessActive); |
| /* Every process comes as pre-activated. */ |
| IdleTracker_ActivateProcess(); |
| Assert(0 < MySessionState->activeProcessCount); |
| } |
| |
| /* |
| * Deactivates and shuts down the idle tracker. |
| */ |
| void |
| IdleTracker_Shutdown() |
| { |
| Assert(!vmemTrackerInited); |
| /* |
| * Unsubscribe this process from future runaway cleanup consideration |
| * and cleanup one last time if necessary |
| */ |
| IdleTracker_DeactivateProcess(); |
| Assert(!isProcessActive); |
| } |
| |
| /* |
| * Marks the current process as active to the runaway detector. The runaway |
| * detector needs at least one active process that can cleanup, if flagged as |
| * runaway. If all the processes are idle, the session is incapable of responding |
| * to a runaway cleanup request, and therefore is not considered a runaway |
| * candidate by the runaway detector. |
| */ |
| void |
| IdleTracker_ActivateProcess() |
| { |
| if (0 >= VmemTracker_GetDynamicMemoryQuotaSema() && (Gp_role == GP_ROLE_EXECUTE)) |
| { |
| return; |
| } |
| |
| if (NULL != MySessionState) |
| { |
| Assert(!isProcessActive); |
| /* No new runaway event can come in */ |
| SpinLockAcquire(&MySessionState->spinLock); |
| |
| /* No atomic update necessary as the update is protected by spin lock */ |
| MySessionState->activeProcessCount += 1; |
| activationVersion = *CurrentVersion; |
| isProcessActive = true; |
| |
| Assert(activationVersion >= deactivationVersion); |
| /* |
| * Release spinLock as we no longer contend for isRunaway. |
| */ |
| SpinLockRelease(&MySessionState->spinLock); |
| } |
| } |
| |
| /* |
| * Marks the current process as idle; i.e., it is no longer able to respond |
| * to a runaway cleanup. However, before it returns from this method, it |
| * would trigger one last runaway cleanup for a pre-dactivation era runaway |
| * event, if necessary. |
| */ |
| void |
| IdleTracker_DeactivateProcess() |
| { |
| if (0 >= VmemTracker_GetDynamicMemoryQuotaSema() && (Gp_role == GP_ROLE_EXECUTE)) |
| { |
| return; |
| } |
| |
| if (NULL != MySessionState) |
| { |
| /* |
| * Verify that deactivation during proc_exit_inprogress is protected in |
| * critical section or the interrupt is disabled so that we don't attempt |
| * any runaway cleanup |
| */ |
| AssertImply(proc_exit_inprogress, CritSectionCount > 0 || InterruptHoldoffCount > 0); |
| |
| /* |
| * When an idle process receives a SIGTERM process, the signal handler |
| * die() calls the cleanup directly, so we get here for an idle process. |
| * Instead of re-activating it forcefully, just special case it |
| * and don't do anything during process exit for already inactive processes. |
| */ |
| if (proc_exit_inprogress && ! isProcessActive) |
| { |
| Assert(deactivationVersion >= activationVersion); |
| return; |
| } |
| |
| Assert(isProcessActive); |
| Assert(deactivationVersion <= activationVersion); |
| |
| /* No new runaway event can come in */ |
| SpinLockAcquire(&MySessionState->spinLock); |
| |
| Assert(MySessionState->activeProcessCount <= MySessionState->pinCount); |
| /* No atomic update necessary as the update is protected by spin lock */ |
| MySessionState->activeProcessCount -= 1; |
| Assert(0 <= MySessionState->activeProcessCount); |
| isProcessActive = false; |
| |
| /* Save the point where we reduced the activeProcessCount */ |
| deactivationVersion = *CurrentVersion; |
| /* |
| * Release spinLock as we no longer contend for isRunaway. |
| */ |
| SpinLockRelease(&MySessionState->spinLock); |
| |
| /* |
| * We are still deactivated (i.e., activeProcessCount is decremented). If an ERROR is indeed thrown |
| * from the VmemTracker_StartCleanupIfRunaway, the VmemTracker_RunawayCleanupDoneForProcess() |
| * method would reactivate this process. |
| */ |
| RunawayCleaner_StartCleanup(); |
| |
| /* At this point the process must be clean, unless we don't have a runaway event before deactivation */ |
| Assert(*latestRunawayVersion > deactivationVersion || |
| !RunawayCleaner_IsCleanupInProgress()); |
| |
| /* At least 1 process is deactivated at this point */ |
| Assert(MySessionState->activeProcessCount < MySessionState->pinCount); |
| } |
| |
| /* At this point the process is ready to be blocked in ReadCommand() */ |
| } |