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());
+ }
+ }
+}