Repository: cayenne
Updated Branches:
  refs/heads/master f7e3bc614 -> 9382e4733


CAY-2382 Lack of synchronization in DataContext serialization


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/9382e473
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/9382e473
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/9382e473

Branch: refs/heads/master
Commit: 9382e47338c65d0208f731a153d571c7a2789faf
Parents: f7e3bc6
Author: Nikita Timofeev <stari...@gmail.com>
Authored: Thu Nov 30 12:03:25 2017 +0300
Committer: Nikita Timofeev <stari...@gmail.com>
Committed: Thu Nov 30 12:03:25 2017 +0300

----------------------------------------------------------------------
 .../org/apache/cayenne/access/DataContext.java  |   6 +-
 .../cayenne/DataContextConcurrencyIT.java       | 119 +++++++++++++++++++
 docs/doc/src/main/resources/RELEASE-NOTES.txt   |   1 +
 3 files changed, 124 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/9382e473/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java 
b/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
index de66464..f0b580d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
@@ -1060,8 +1060,10 @@ public class DataContext extends BaseContext {
     // ---------------------------------------------
 
     private void writeObject(ObjectOutputStream out) throws IOException {
-        out.defaultWriteObject();
-
+        // See CAY-2382
+        synchronized (getObjectStore()) {
+            out.defaultWriteObject();
+        }
         // Serialize local snapshots cache
         if (!isUsingSharedSnapshotCache()) {
             out.writeObject(objectStore.getDataRowCache());

http://git-wip-us.apache.org/repos/asf/cayenne/blob/9382e473/cayenne-server/src/test/java/org/apache/cayenne/DataContextConcurrencyIT.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/DataContextConcurrencyIT.java 
b/cayenne-server/src/test/java/org/apache/cayenne/DataContextConcurrencyIT.java
new file mode 100644
index 0000000..91aff7e
--- /dev/null
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/DataContextConcurrencyIT.java
@@ -0,0 +1,119 @@
+/*****************************************************************
+ *   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.
+ ****************************************************************/
+
+package org.apache.cayenne;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.unit.di.server.CayenneProjects;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.apache.cayenne.util.Util;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @since 4.1
+ */
+@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
+public class DataContextConcurrencyIT extends ServerCase {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(DataContextConcurrencyIT.class);
+
+    @Inject
+    private ObjectContext context;
+
+    /**
+     * see https://issues.apache.org/jira/browse/CAY-2382
+     *
+     * This test is probabilistic as it tries to catch concurrency problem.
+     * It can be false-negative (e.g. Travis build not always fails)
+     */
+    @Test
+    public void testCMEInContextSerialization() throws Exception {
+
+        // add some content to context, so it will be serializing slowly
+        for(int i=0; i<1000; i++) {
+            Artist artist = context.newObject(Artist.class);
+            artist.setArtistName("name " + i);
+        }
+
+        // add some barriers so threads will try to start synchronously
+        CountDownLatch startSignal = new CountDownLatch(1);
+        CountDownLatch serializationStartSignal = new CountDownLatch(1);
+
+        ExecutorService service = Executors.newFixedThreadPool(2);
+
+        // this task will modify context
+        Future<Boolean> resultObjectCreation = service.submit(() -> {
+            try {
+                // this thread will start when context serialized at least 
once,
+                // as it should be faster (but who knows...)
+                serializationStartSignal.await();
+            } catch (InterruptedException e) {
+                return false;
+            }
+            for(int i=0; i<1000; i++) {
+                Artist artist = context.newObject(Artist.class);
+                context.deleteObject(artist);
+            }
+            return true;
+        });
+
+        // this task will serialize context
+        Future<Boolean> resultSerialization = service.submit(() -> {
+            try {
+                // wait for outer thread start this one
+                startSignal.await();
+            } catch (InterruptedException e) {
+                return false;
+            }
+            for(int i=0; i<100; i++) {
+                try {
+                    // make one serialization, before starting modifying thread
+                    Util.cloneViaSerialization(context);
+                    if(i == 0) {
+                        serializationStartSignal.countDown();
+                    }
+                } catch (Exception e) {
+                    logger.error("Serialization failed", e);
+                    return false;
+                }
+            }
+
+            return true;
+        });
+
+        // make sure that everyone are ready
+        Thread.sleep(10);
+        startSignal.countDown();
+
+        assertTrue(resultObjectCreation.get(20, TimeUnit.SECONDS));
+        assertTrue(resultSerialization.get(20, TimeUnit.SECONDS));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/9382e473/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt 
b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index 0762b1f..7b98895 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -18,6 +18,7 @@ Bug Fixes:
 
 CAY-2370 ValueObjectType for byte[] fails lookup
 CAY-2380 ReferenceMap should not store or return null values
+CAY-2382 Lack of synchronization in DataContext serialization
 
 ----------------------------------
 Release: 4.1.M1

Reply via email to