diff --git a/internal/terraform/hook.go b/internal/terraform/hook.go index 2e457f2814..f7dbd6af04 100644 --- a/internal/terraform/hook.go +++ b/internal/terraform/hook.go @@ -114,9 +114,11 @@ type Hook interface { // function is called. Stopping() - // PostStateUpdate is called each time the state is updated. It receives - // a deep copy of the state, which it may therefore access freely without - // any need for locks to protect from concurrent writes from the caller. + // PostStateUpdate is called each time the state is updated. The caller must + // coordinate a lock for the state if necessary, such that the Hook may + // access it freely without any need for additional locks to protect from + // concurrent writes. Implementations which modify or retain the state after + // the call has returned must copy the state. PostStateUpdate(new *states.State) (HookAction, error) } diff --git a/internal/terraform/update_state_hook.go b/internal/terraform/update_state_hook.go index 3aa551df50..35dc4d82cd 100644 --- a/internal/terraform/update_state_hook.go +++ b/internal/terraform/update_state_hook.go @@ -5,13 +5,10 @@ package terraform // updateStateHook calls the PostStateUpdate hook with the current state. func updateStateHook(ctx EvalContext) error { - // In principle we could grab the lock here just long enough to take a - // deep copy and then pass that to our hooks below, but we'll instead - // hold the hook for the duration to avoid the potential confusing - // situation of us racing to call PostStateUpdate concurrently with - // different state snapshots. + // PostStateUpdate requires that the state be locked and safe to read for + // the duration of the call. stateSync := ctx.State() - state := stateSync.Lock().DeepCopy() + state := stateSync.Lock() defer stateSync.Unlock() // Call the hook