Author: mgrigorov
Date: Thu Jul  7 09:17:13 2011
New Revision: 1143721

URL: http://svn.apache.org/viewvc?rev=1143721&view=rev
Log:
WICKET-3876 Improve synchronization in AsynchronousDataStore buffer structure

Optimize AsynchronousDataStore#removeData() - LinkedBlockingQueue is smart 
enough to remove entries in concurrent environment with its iterator#remove(). 
(In JDK6 it is even smarter).
Remove the getters for Entry's attributes - use the attributes directly.


Added:
    
wicket/trunk/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousDataStoreTest.java
Modified:
    
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java

Modified: 
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java?rev=1143721&r1=1143720&r2=1143721&view=diff
==============================================================================
--- 
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java
 (original)
+++ 
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java
 Thu Jul  7 09:17:13 2011
@@ -16,6 +16,7 @@
  */
 package org.apache.wicket.pageStore;
 
+import java.util.Iterator;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -144,7 +145,7 @@ public class AsynchronousDataStore imple
                        log.debug(
                                "Returning the data of a non-stored entry with 
sessionId '{}' and pageId '{}'",
                                sessionId, id);
-                       return entry.getData();
+                       return entry.data;
                }
                byte[] data = dataStore.getData(sessionId, id);
 
@@ -185,14 +186,18 @@ public class AsynchronousDataStore imple
         */
        public void removeData(final String sessionId)
        {
-               // make a copy to iterate to avoid 
ConcurrentModificationException
-               Entry[] entriesCopy = entries.toArray(new 
Entry[entries.size()]);
-               for (Entry entry : entriesCopy)
+               for (Iterator<Entry> itor = entries.iterator(); itor.hasNext();)
                {
-                       if (entry.getSessionId().equals(sessionId))
+                       Entry entry = itor.next();
+                       if (entry != null) // this check is not needed in JDK6
                        {
-                               entryMap.remove(getKey(entry));
-                               entries.remove(entry);
+                               String entrySessionId = entry.sessionId;
+
+                               if (sessionId.equals(entrySessionId))
+                               {
+                                       entryMap.remove(getKey(entry));
+                                       itor.remove();
+                               }
                        }
                }
 
@@ -247,7 +252,7 @@ public class AsynchronousDataStore imple
         */
        private static String getKey(final Entry entry)
        {
-               return getKey(entry.getSessionId(), entry.getPageId());
+               return getKey(entry.sessionId, entry.pageId);
        }
 
        /**
@@ -266,21 +271,6 @@ public class AsynchronousDataStore imple
                        this.data = Args.notNull(data, "data");
                }
 
-               public String getSessionId()
-               {
-                       return sessionId;
-               }
-
-               public int getPageId()
-               {
-                       return pageId;
-               }
-
-               public byte[] getData()
-               {
-                       return data;
-               }
-
                @Override
                public int hashCode()
                {
@@ -362,7 +352,7 @@ public class AsynchronousDataStore imple
                                if (entry != null)
                                {
                                        log.debug("Saving asynchronously: 
{}...", entry);
-                                       
dataStore.storeData(entry.getSessionId(), entry.getPageId(), entry.getData());
+                                       dataStore.storeData(entry.sessionId, 
entry.pageId, entry.data);
                                        entryMap.remove(getKey(entry));
                                }
                        }

Added: 
wicket/trunk/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousDataStoreTest.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousDataStoreTest.java?rev=1143721&view=auto
==============================================================================
--- 
wicket/trunk/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousDataStoreTest.java
 (added)
+++ 
wicket/trunk/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousDataStoreTest.java
 Thu Jul  7 09:17:13 2011
@@ -0,0 +1,133 @@
+/*
+ * 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.wicket.pageStore;
+
+import java.security.SecureRandom;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.wicket.versioning.InMemoryPageStore;
+import org.junit.Test;
+
+/**
+ * Tests for {@link AsynchronousDataStore}
+ */
+public class AsynchronousDataStoreTest
+{
+       /** the data store under test */
+       private static IDataStore DATA_STORE = new AsynchronousDataStore(new 
InMemoryPageStore(), 100);
+
+       /** the data for each page */
+       private static final byte[] DATA = new byte[] { 1, 2, 3 };
+
+       /** the used jsessionid's */
+       private static final String[] SESSIONS = new String[] { "s1", "s2", 
"s3" };
+
+       /** the ids for the stored/removed pages */
+       private static final int[] PAGE_IDS = new int[] { 1, 2, 3, 4, 5, 6, 7, 
8, 9, 10 };
+
+       /** how many operations to execute */
+       private static final int EXECUTIONS = 10000;
+
+       /** used to wait the executions */
+       private static final CountDownLatch LATCH = new 
CountDownLatch(EXECUTIONS);
+
+       /** the execution types */
+       private static final Runnable[] TASKS = new Runnable[] { new 
StoreTask(), new GetTask(),
+                       new RemovePageInSessionTask(), new RemoveSessionTask() 
};
+
+       private static final SecureRandom RND = new SecureRandom();
+
+       /**
+        * Executes random mutator and accessor operations on {@link 
AsynchronousDataStore} validating
+        * that the used data structures can be used simultaneously.
+        * 
+        * @throws Exception
+        */
+       @Test
+       public void randomOperations() throws Exception
+       {
+               ExecutorService executorService = 
Executors.newFixedThreadPool(50);
+
+               for (int i = 0; i < EXECUTIONS; i++)
+               {
+                       Runnable task = TASKS[RND.nextInt(TASKS.length)];
+                       executorService.submit(task);
+               }
+               LATCH.await();
+               executorService.shutdown();
+       }
+
+       private static abstract class AbstractTask implements Runnable
+       {
+
+               protected abstract void r();
+
+               public void run()
+               {
+                       r();
+                       LATCH.countDown();
+               }
+
+               protected String getSessionId()
+               {
+                       return SESSIONS[RND.nextInt(SESSIONS.length)];
+               }
+
+               protected int getPageId()
+               {
+                       return PAGE_IDS[RND.nextInt(PAGE_IDS.length)];
+               }
+       }
+
+       private static class StoreTask extends AbstractTask
+       {
+               @Override
+               public void r()
+               {
+                       DATA_STORE.storeData(getSessionId(), getPageId(), DATA);
+               }
+       }
+
+       private static class GetTask extends AbstractTask
+       {
+               @Override
+               public void r()
+               {
+                       DATA_STORE.getData(getSessionId(), getPageId());
+               }
+       }
+
+       private static class RemovePageInSessionTask extends AbstractTask
+       {
+               @Override
+               public void r()
+               {
+                       DATA_STORE.removeData(getSessionId(), getPageId());
+               }
+       }
+
+       private static class RemoveSessionTask extends AbstractTask
+       {
+               @Override
+               public void r()
+               {
+                       DATA_STORE.removeData(getSessionId());
+               }
+       }
+}


Reply via email to