001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tctest;
005:
006: import EDU.oswego.cs.dl.util.concurrent.CyclicBarrier;
007: import EDU.oswego.cs.dl.util.concurrent.SynchronizedRef;
008:
009: import com.tc.object.config.ConfigVisitor;
010: import com.tc.object.config.DSOClientConfigHelper;
011: import com.tc.object.config.TransparencyClassSpec;
012: import com.tc.object.config.spec.CyclicBarrierSpec;
013: import com.tc.simulator.app.ApplicationConfig;
014: import com.tc.simulator.listener.ListenerProvider;
015: import com.tc.util.Assert;
016: import com.tctest.runner.AbstractErrorCatchingTransparentApp;
017:
018: import java.util.HashMap;
019: import java.util.Iterator;
020: import java.util.Map;
021: import java.util.Map.Entry;
022:
023: public class NewObjectCreationRaceTest extends TransparentTestBase {
024:
025: private static final int NODE_COUNT = 3;
026:
027: public void setUp() throws Exception {
028: super .setUp();
029: getTransparentAppConfig().setClientCount(NODE_COUNT)
030: .setIntensity(1);
031: initializeTestRunner();
032: }
033:
034: protected Class getApplicationClass() {
035: return NewObjectCreationRaceTestApp.class;
036: }
037:
038: public static class NewObjectCreationRaceTestApp extends
039: AbstractErrorCatchingTransparentApp {
040:
041: // NOTE: it is very important to NOT reference
042: // this root in the "fault" nodes, until after it has the new object in it
043: private Map root;
044:
045: private final CyclicBarrier barrier;
046:
047: public NewObjectCreationRaceTestApp(String appId,
048: ApplicationConfig cfg, ListenerProvider listenerProvider) {
049: super (appId, cfg, listenerProvider);
050:
051: if (getParticipantCount() < 2) {
052: throw new AssertionError(
053: "need at least two nodes for this test to work");
054: }
055:
056: barrier = new CyclicBarrier(getParticipantCount());
057: }
058:
059: protected void runTest() throws Throwable {
060: int n = barrier.barrier();
061:
062: if (n == 0) {
063: runCreateNode();
064: } else {
065: runFaultNode();
066: }
067: }
068:
069: private void runFaultNode() throws Exception {
070: barrier.barrier();
071:
072: final Map faultedRoot;
073:
074: // fault the root under the same lock it was modified under
075: // this avoids a ConcurrentModificationException
076: synchronized (barrier) {
077: faultedRoot = root;
078: }
079:
080: for (Iterator i = faultedRoot.entrySet().iterator(); i
081: .hasNext();) {
082: Map.Entry entry = (Entry) i.next();
083: Object key = entry.getKey();
084: System.out.println(key);
085: Object value = entry.getValue();
086: System.out.println(value);
087:
088: if (value instanceof Ref) {
089: Object ref = ((Ref) value).getRef();
090: System.out.println(ref);
091: }
092: }
093:
094: // unblock the create node
095: barrier.barrier();
096:
097: // wait for the create node to commit it's change
098: barrier.barrier();
099:
100: // make sure the delta is in there
101: synchronized (faultedRoot) {
102: Object o = faultedRoot.get("delta");
103: Assert.assertEquals("non-null", o);
104: Assert.assertEquals(5, faultedRoot.size());
105: }
106: }
107:
108: private void runCreateNode() throws Throwable {
109: final SynchronizedRef error = new SynchronizedRef(null);
110:
111: // create root in this node only
112: root = new HashMap();
113:
114: final Object newObj = new Object();
115: final Ref newRefToOtherNewObject = new Ref(new Object());
116:
117: synchronized (root) {
118: root.put("delta", null);
119: root.put("new object", newObj);
120: root.put("new ref to new obj", newRefToOtherNewObject);
121:
122: Runnable otherTxn = new Runnable() {
123: public void run() {
124: try {
125: synchronized (barrier) {
126: root
127: .put(
128: "ref to new with ref to created",
129: new Ref(newObj));
130: root
131: .put(
132: "ref to created with ref to created",
133: newRefToOtherNewObject);
134: }
135: } catch (Throwable err) {
136: error.set(err);
137: }
138: }
139: };
140:
141: Thread t1 = new Thread(otherTxn);
142: t1.start();
143: t1.join();
144:
145: checkError(error);
146:
147: // unblock the "fault" nodes. Need to do this in another thread so that the
148: // TXN the current thread is in doesn't commit()
149: Runnable doBarrier = new Runnable() {
150: public void run() {
151: try {
152: barrier.barrier();
153: } catch (Throwable err) {
154: error.set(err);
155: }
156: }
157: };
158: Thread t2 = new Thread(doBarrier);
159: t2.start();
160: t2.join();
161:
162: checkError(error);
163:
164: // block at least until the other node(s) cause the problematic fault. This also needs to be done in another
165: // thread so that this thread's TXN does not commit
166: Thread t3 = new Thread(doBarrier);
167: t3.start();
168: t3.join();
169:
170: checkError(error);
171:
172: // create a change (ie. delta DNA) in the original transaction
173: root.put("delta", "non-null");
174:
175: barrier.barrier();
176: }
177:
178: }
179:
180: private void checkError(SynchronizedRef error) throws Throwable {
181: Throwable t = (Throwable) error.get();
182: if (t != null) {
183: throw t;
184: }
185: }
186:
187: public static void visitL1DSOConfig(ConfigVisitor visitor,
188: DSOClientConfigHelper config) {
189: new CyclicBarrierSpec().visit(visitor, config);
190:
191: String testClass;
192: TransparencyClassSpec spec;
193: String methodExpression;
194:
195: testClass = Ref.class.getName();
196: spec = config.getOrCreateSpec(testClass);
197:
198: testClass = NewObjectCreationRaceTest.class.getName();
199: spec = config.getOrCreateSpec(testClass);
200: methodExpression = "* " + testClass + "*.*(..)";
201: config.addWriteAutolock(methodExpression);
202:
203: config.addIncludePattern(testClass + "$*");
204:
205: testClass = NewObjectCreationRaceTestApp.class.getName();
206: spec = config.getOrCreateSpec(testClass);
207:
208: methodExpression = "* " + testClass + "*.*(..)";
209: config.addWriteAutolock(methodExpression);
210: spec.addRoot("barrier", "barrier");
211: spec.addRoot("root", "root");
212: }
213:
214: }
215:
216: private static class Ref {
217: private final Object ref;
218:
219: Ref(Object ref) {
220: this .ref = ref;
221: }
222:
223: Object getRef() {
224: return ref;
225: }
226: }
227:
228: }
|