You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2016/09/04 07:20:36 UTC

[1/5] logging-log4j2 git commit: LOG4J2-1349 add ability to freeze() context data for use in copy-on-write scenarios

Repository: logging-log4j2
Updated Branches:
  refs/heads/LOG4J2-1349-gcfree-threadcontext 7aac69c81 -> 189f87d13


LOG4J2-1349 add ability to freeze() context data for use in copy-on-write scenarios


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/945bfe04
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/945bfe04
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/945bfe04

Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 945bfe041caa7b645c7f838234b1331a2ab96609
Parents: 7aac69c
Author: rpopma <rp...@apache.org>
Authored: Sun Sep 4 15:53:17 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Sun Sep 4 15:53:17 2016 +0900

----------------------------------------------------------------------
 .../org/apache/logging/log4j/spi/MutableContextData.java | 11 +++++++++++
 1 file changed, 11 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/945bfe04/log4j-api/src/main/java/org/apache/logging/log4j/spi/MutableContextData.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/MutableContextData.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/MutableContextData.java
index 8bc64e8..1ee60c9 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/MutableContextData.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/MutableContextData.java
@@ -65,4 +65,15 @@ public interface MutableContextData extends ContextData {
      *          {@link #forEach(TriConsumer, Object)}.
      */
     void remove(final String key);
+
+    /**
+     * Ensures that further changes to this object are ignored.
+     */
+    void freeze();
+
+    /**
+     * Returns {@code true} if this object has been {@linkplain #freeze() frozen}, {@code false} otherwise.
+     * @return  {@code true} if this object has been {@linkplain #freeze() frozen}, {@code false} otherwise
+     */
+    boolean isFrozen();
 }
\ No newline at end of file


[4/5] logging-log4j2 git commit: LOG4J2-1349 bugfix: putAll() should not overwrite existing values

Posted by rp...@apache.org.
LOG4J2-1349 bugfix: putAll() should not overwrite existing values


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/863cc124
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/863cc124
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/863cc124

Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 863cc124dfdc0fe05128a8c144c6413f92b5d99f
Parents: e59f15f
Author: rpopma <rp...@apache.org>
Authored: Sun Sep 4 15:56:02 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Sun Sep 4 15:56:02 2016 +0900

----------------------------------------------------------------------
 .../main/java/org/apache/logging/log4j/util/ArrayContextData.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/863cc124/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
index 3b665b3..01b2c8c 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
@@ -243,7 +243,7 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
         assertNotFrozen();
         assertNoConcurrentModification();
 
-        if (source instanceof ArrayContextData) {
+        if (source instanceof ArrayContextData && this.size == 0) {
             initFrom0((ArrayContextData) source);
         } else if (source != null) {
             if (source == this) {


[2/5] logging-log4j2 git commit: LOG4J2-1349 freeze context data so that shared copies cannot be modified

Posted by rp...@apache.org.
LOG4J2-1349 freeze context data so that shared copies cannot be modified


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/950b62f6
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/950b62f6
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/950b62f6

Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 950b62f6272ea1d6a44d49320614f5113ff8aea2
Parents: 945bfe0
Author: rpopma <rp...@apache.org>
Authored: Sun Sep 4 15:54:02 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Sun Sep 4 15:54:02 2016 +0900

----------------------------------------------------------------------
 .../logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/950b62f6/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
index 81c69d5..57348dd 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
@@ -104,6 +104,7 @@ class CopyOnWriteSortedArrayThreadContextMap implements ThreadContextMap, Thread
         MutableContextData map = localMap.get();
         map = map == null ? createMutableContextData() : createMutableContextData(map);
         map.putValue(key, value);
+        map.freeze();
         localMap.set(map);
     }
 
@@ -117,6 +118,7 @@ class CopyOnWriteSortedArrayThreadContextMap implements ThreadContextMap, Thread
         for (final Map.Entry<String, String> entry : values.entrySet()) {
             map.putValue(entry.getKey(), entry.getValue());
         }
+        map.freeze();
         localMap.set(map);
     }
 
@@ -132,6 +134,7 @@ class CopyOnWriteSortedArrayThreadContextMap implements ThreadContextMap, Thread
         if (map != null) {
             final MutableContextData copy = createMutableContextData(map);
             copy.remove(key);
+            copy.freeze();
             localMap.set(copy);
         }
     }


[3/5] logging-log4j2 git commit: LOG4J2-1349 implement support for freeze() and isFrozen(); add tests

Posted by rp...@apache.org.
LOG4J2-1349 implement support for freeze() and isFrozen(); add tests


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/e59f15f6
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/e59f15f6
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/e59f15f6

Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: e59f15f65a7b1b39f0f61b9b7e2b3f28ada387c0
Parents: 950b62f
Author: rpopma <rp...@apache.org>
Authored: Sun Sep 4 15:55:10 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Sun Sep 4 15:55:10 2016 +0900

----------------------------------------------------------------------
 .../logging/log4j/util/ArrayContextData.java    |  63 +++++-
 .../log4j/util/ArrayContextDataTest.java        | 208 +++++++++++++++++++
 2 files changed, 267 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e59f15f6/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
index bdc9951..3b665b3 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.io.InvalidObjectException;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.ConcurrentModificationException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -70,6 +71,7 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
      * An empty array instance to share when the table is not inflated.
      */
     private static final String[] EMPTY = {};
+    private static final String FROZEN = "Frozen collection cannot be modified";
 
     private transient String[] keys = EMPTY;
     private transient Object[] values = EMPTY;
@@ -86,6 +88,8 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
     // If table == EMPTY_TABLE then this is the initial capacity at which the
     // table will be created when inflated.
     private int threshold;
+    private boolean immutable;
+    private transient boolean iterating;
 
     public ArrayContextData() {
         this(DEFAULT_INITIAL_CAPACITY);
@@ -107,8 +111,26 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
         }
     }
 
+    private void assertNotFrozen() {
+        if (immutable) {
+            throw new UnsupportedOperationException(FROZEN);
+        }
+    }
+
+    private void assertNoConcurrentModification() {
+        if (iterating) {
+            throw new ConcurrentModificationException();
+        }
+    }
+
     @Override
     public void clear() {
+        if (keys == EMPTY) {
+            return;
+        }
+        assertNotFrozen();
+        assertNoConcurrentModification();
+
         Arrays.fill(keys, 0, size, null);
         Arrays.fill(values, 0, size, null);
         size = 0;
@@ -130,6 +152,16 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
     }
 
     @Override
+    public void freeze() {
+        immutable = true;
+    }
+
+    @Override
+    public boolean isFrozen() {
+        return immutable;
+    }
+
+    @Override
     public Map<String, String> getCopy() {
         return asMap();
     }
@@ -182,6 +214,9 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
 
     @Override
     public void putValue(final String key, final Object value) {
+        assertNotFrozen();
+        assertNoConcurrentModification();
+
         if (keys == EMPTY) {
             inflateTable(threshold);
         }
@@ -205,9 +240,15 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
 
     @Override
     public void putAll(final ContextData source) {
+        assertNotFrozen();
+        assertNoConcurrentModification();
+
         if (source instanceof ArrayContextData) {
             initFrom0((ArrayContextData) source);
         } else if (source != null) {
+            if (source == this) {
+                return; // this.putAll(this) does not modify this collection
+            }
             source.forEach(PUT_ALL, this);
         }
     }
@@ -261,8 +302,12 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
         if (keys == EMPTY) {
             return;
         }
+
         final int index = indexOfKey(key);
         if (index >= 0) {
+            assertNotFrozen();
+            assertNoConcurrentModification();
+
             System.arraycopy(keys, index + 1, keys, index, size - index);
             System.arraycopy(values, index + 1, values, index, size - index);
             size--;
@@ -292,16 +337,26 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
     @SuppressWarnings("unchecked")
     @Override
     public <V> void forEach(BiConsumer<String, ? super V> action) {
-        for (int i = 0; i < size; i++) {
-            action.accept(keys[i], (V) values[i]);
+        iterating = true;
+        try {
+            for (int i = 0; i < size; i++) {
+                action.accept(keys[i], (V) values[i]);
+            }
+        } finally {
+            iterating = false;
         }
     }
 
     @SuppressWarnings("unchecked")
     @Override
     public <V, T> void forEach(TriConsumer<String, ? super V, T> action, T state) {
-        for (int i = 0; i < size; i++) {
-            action.accept(keys[i], (V) values[i], state);
+        iterating = true;
+        try {
+            for (int i = 0; i < size; i++) {
+                action.accept(keys[i], (V) values[i], state);
+            }
+        } finally {
+            iterating = false;
         }
     }
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e59f15f6/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java
index e3ac0a4..ae79888 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java
+++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java
@@ -21,6 +21,7 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.lang.reflect.Field;
+import java.util.ConcurrentModificationException;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -181,6 +182,213 @@ public class ArrayContextDataTest {
     }
 
     @Test
+    public void testPutAll_KeepsExistingValues() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("a", "aaa");
+        original.put("b", "bbb");
+        original.put("c", "ccc");
+        assertEquals("size", 3, original.size());
+
+        // add empty context data
+        original.putAll(new ArrayContextData());
+        assertEquals("size after put empty", 3, original.size());
+        assertEquals("aaa", original.get("a"));
+        assertEquals("bbb", original.get("b"));
+        assertEquals("ccc", original.get("c"));
+
+        final ArrayContextData other = new ArrayContextData();
+        other.put("1", "111");
+        other.put("2", "222");
+        other.put("3", "333");
+        original.putAll(other);
+
+        assertEquals("size after put other", 6, original.size());
+        assertEquals("aaa", original.get("a"));
+        assertEquals("bbb", original.get("b"));
+        assertEquals("ccc", original.get("c"));
+        assertEquals("111", original.get("1"));
+        assertEquals("222", original.get("2"));
+        assertEquals("333", original.get("3"));
+    }
+
+    @Test
+    public void testPutAllSelfDoesNotModify() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("a", "aaa");
+        original.put("b", "bbb");
+        original.put("c", "ccc");
+        assertEquals("size", 3, original.size());
+
+        // putAll with self
+        original.putAll(original);
+        assertEquals("size after put empty", 3, original.size());
+        assertEquals("aaa", original.get("a"));
+        assertEquals("bbb", original.get("b"));
+        assertEquals("ccc", original.get("c"));
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationBiConsumerPut() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("a", "aaa");
+        original.forEach(new BiConsumer<String, Object>() {
+            @Override
+            public void accept(final String s, final Object o) {
+                original.put("c", "other");
+            }
+        });
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationBiConsumerPutValue() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("a", "aaa");
+        original.forEach(new BiConsumer<String, Object>() {
+            @Override
+            public void accept(final String s, final Object o) {
+                original.putValue("c", "other");
+            }
+        });
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationBiConsumerRemove() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("a", "aaa");
+        original.forEach(new BiConsumer<String, Object>() {
+            @Override
+            public void accept(final String s, final Object o) {
+                original.remove("a");
+            }
+        });
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationBiConsumerClear() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("a", "aaa");
+        original.forEach(new BiConsumer<String, Object>() {
+            @Override
+            public void accept(final String s, final Object o) {
+                original.clear();
+            }
+        });
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationTriConsumerPut() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("a", "aaa");
+        original.forEach(new TriConsumer<String, Object, Object>() {
+            @Override
+            public void accept(final String s, final Object o, final Object o2) {
+                original.put("c", "other");
+            }
+        }, null);
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationTriConsumerPutValue() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("a", "aaa");
+        original.forEach(new TriConsumer<String, Object, Object>() {
+            @Override
+            public void accept(final String s, final Object o, final Object o2) {
+                original.putValue("c", "other");
+            }
+        }, null);
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationTriConsumerRemove() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("a", "aaa");
+        original.forEach(new TriConsumer<String, Object, Object>() {
+            @Override
+            public void accept(final String s, final Object o, final Object o2) {
+                original.remove("a");
+            }
+        }, null);
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationTriConsumerClear() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("a", "aaa");
+        original.forEach(new TriConsumer<String, Object, Object>() {
+            @Override
+            public void accept(final String s, final Object o, final Object o2) {
+                original.clear();
+            }
+        }, null);
+    }
+
+    @Test
+    public void testInitiallyNotFrozen() {
+        assertFalse(new ArrayContextData().isFrozen());
+    }
+
+    @Test
+    public void testIsFrozenAfterCallingFreeze() {
+        final ArrayContextData original = new ArrayContextData();
+        assertFalse("before freeze", original.isFrozen());
+        original.freeze();
+        assertTrue("after freeze", original.isFrozen());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testFreezeProhibitsPut() {
+        final ArrayContextData original = new ArrayContextData();
+        original.freeze();
+        original.put("a", "aaa");
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testFreezeProhibitsPutValue() {
+        final ArrayContextData original = new ArrayContextData();
+        original.freeze();
+        original.putValue("a", "aaa");
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testFreezeProhibitsRemove() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("b", "bbb");
+        original.freeze();
+        original.remove("b"); // existing key: modifies the collection
+    }
+
+    @Test
+    public void testFreezeAllowsRemoveOfNonExistingKey() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("b", "bbb");
+        original.freeze();
+        original.remove("a"); // no actual modification
+    }
+
+    @Test
+    public void testFreezeAllowsRemoveIfEmpty() {
+        final ArrayContextData original = new ArrayContextData();
+        original.freeze();
+        original.remove("a"); // no exception
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testFreezeProhibitsClear() {
+        final ArrayContextData original = new ArrayContextData();
+        original.put("a", "aaa");
+        original.freeze();
+        original.clear();
+    }
+
+    @Test
+    public void testFreezeAllowsClearIfEmpty() {
+        final ArrayContextData original = new ArrayContextData();
+        original.freeze();
+        original.clear();
+    }
+
+    @Test
     public void testPutInsertsInAlphabeticOrder() throws Exception {
         final ArrayContextData original = new ArrayContextData();
         original.put("a", "avalue");


[5/5] logging-log4j2 git commit: LOG4J2-1349 log event implementations should not try to modify shared copies of copy-on-write context data

Posted by rp...@apache.org.
LOG4J2-1349 log event implementations should not try to modify shared copies of copy-on-write context data


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/189f87d1
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/189f87d1
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/189f87d1

Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 189f87d138d9e7f7df05468c60a011f29298919e
Parents: 863cc12
Author: rpopma <rp...@apache.org>
Authored: Sun Sep 4 16:07:17 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Sun Sep 4 16:07:17 2016 +0900

----------------------------------------------------------------------
 .../apache/logging/log4j/core/async/RingBufferLogEvent.java  | 8 +++++++-
 .../org/apache/logging/log4j/core/impl/Log4jLogEvent.java    | 7 ++++++-
 .../org/apache/logging/log4j/core/impl/MutableLogEvent.java  | 6 +++++-
 3 files changed, 18 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/189f87d1/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
index d20be14..423578c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
@@ -381,9 +381,15 @@ public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequen
         this.message = null;
         this.thrown = null;
         this.thrownProxy = null;
-        this.contextData.clear();
         this.contextStack = null;
         this.location = null;
+        if (contextData != null) {
+            if (contextData.isFrozen()) { // came from CopyOnWrite thread context
+                contextData = null;
+            } else {
+                contextData.clear();
+            }
+        }
 
         trimMessageText();
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/189f87d1/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
index 8e152f3..19b7afd 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
@@ -132,8 +132,13 @@ public class Log4jLogEvent implements LogEvent {
                 if (other.getContextData() instanceof MutableContextData) {
                     this.contextData = (MutableContextData) other.getContextData();
                 } else {
-                    this.contextData.clear();
+                    if (this.contextData.isFrozen()) {
+                        this.contextData = ContextDataFactory.createContextData();
+                    } else {
+                        this.contextData.clear();
+                    }
                     this.contextData.putAll(other.getContextData());
+
                 }
                 this.thrownProxy = other.getThrownProxy();
                 this.source = other.getSource();

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/189f87d1/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
index 97cb5d6..2a4f59b 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
@@ -120,7 +120,11 @@ public class MutableLogEvent implements LogEvent, ReusableMessage {
         thrownProxy = null;
         source = null;
         if (contextData != null) {
-            contextData.clear();
+            if (contextData.isFrozen()) { // came from CopyOnWrite thread context
+                contextData = null;
+            } else {
+                contextData.clear();
+            }
         }
         contextStack = null;