/*
 * Copyright 2018 the original author or authors.
 *
 * Licensed 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.
 */

package org.gradle.api.internal;

import org.gradle.api.Action;

public class DefaultMutationGuard extends AbstractMutationGuard {
    private final ThreadLocal<Boolean> mutationGuardState = new ThreadLocal<Boolean>() {
        @Override
        protected Boolean initialValue() {
            return Boolean.TRUE;
        }
    };

    @Override
    public boolean isMutationAllowed() {
        return mutationGuardState.get();
    }

    @Override
    protected <T> Action<? super T> newActionWithMutation(final Action<? super T> action, final boolean allowMutationMethods) {
        return new Action<T>() {
            @Override
            public void execute(T t) {
                boolean oldIsMutationAllowed = isMutationAllowed();
                mutationGuardState.set(allowMutationMethods);
                try {
                    action.execute(t);
                } finally {
                    mutationGuardState.set(oldIsMutationAllowed);
                }
            }
        };
    }
}

@@ -1009,39 +1009,156 @@
     
-return mutationGuardState.get()
+boolean mutationAllowed = mutationGuardState.get();%0A        removeThreadLocalStateIfMutationAllowed(mutationAllowed);%0A        return mutationAllowed
 ;%0A  
@@ -1445,33 +1445,38 @@
 lowed = 
-isM
+m
 utation
-Allowed
+GuardState.get
 ();%0A    
@@ -1634,33 +1634,36 @@
                 
-m
+setM
 utationGuardStat
@@ -1667,18 +1667,201 @@
 tate
-.set
 (oldIs
+MutationAllowed);%0A                %7D%0A            %7D%0A        %7D;%0A    %7D%0A%0A    private void setMutationGuardState(boolean newState) %7B%0A        if (newState) %7B%0A            removeThreadLocalStateIf
 Muta
@@ -1863,32 +1863,37 @@
 fMutationAllowed
+(true
 );%0A             
@@ -1891,42 +1891,1102 @@
     
-        %7D%0A            %7D
+%7D else %7B%0A            mutationGuardState.set(false);%0A        %7D%0A    %7D%0A%0A    /**%0A     * Removes the thread local for %60mutationGuardState%60 if its value is the default value (true).%0A     *%0A     * There are many instances of %60DefaultMutationGuard%60 in a Gradle run, e.g. one for each configuration.%0A     * Each of those instances creates a new thread local for %60Daemon worker%60.%0A     * After a build, those thread local instances should be removed from the ThreadLocalMap by garbage collection automatically.%0A     * It looks like CMS does a good job for removing the unused entries from the ThreadLocalMap, though G1 does not.%0A     * So if you are running builds in quick succession, e.g. for profiling, there can be a quick slowdown after some time.
 %0A     
+*%0A
    
+  * This methods removes the elements from the ThreadLocalMap when possible, thus avoiding the problem.%0A     *%0A     * See https://github.com/gradle/gradle/issues/13835.%0A     */%0A    private void removeThreadLocalStateIfMutationAllowed(boolean mutationAllowed) %7B%0A        if (mutationAllowed) %7B%0A            mutationGuardState.remove();%0A        
 %7D
-;
 %0A   

Remove MutationGuard thread local to avoid performance problems