blob: 95c5ffd095683dfb4c779a2262d92a16c0747a1a [file] [log] [blame]
/*
* 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
<<<<<<< Updated upstream
*
* 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
=======
*
* https://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
>>>>>>> Stashed changes
* limitations under the License.
*/
package org.apache.jdo.tck.api.persistencemanager;
import java.util.Map;
import java.util.Set;
import javax.jdo.JDOUserException;
import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;
import org.apache.jdo.tck.pc.mylib.PCPoint;
import org.apache.jdo.tck.util.BatchTestRunner;
import org.apache.jdo.tck.util.RogueBarrier;
import org.apache.jdo.tck.util.ThreadExceptionHandler;
/**
* <B>Title:</B> Thread Safe <br>
* <B>Keywords:</B> multithreaded <br>
* <B>Assertion ID:</B> A12.4-1. <br>
* <B>Assertion Description: </B> It is a requirement for all JDO implementations to be thread-safe.
* That is, the behavior of the implementation must be predictable in the presence of multiple
* application threads. This assertion will generate multiple test cases to be evaluated.
*/
public class ThreadSafe extends PersistenceManagerTest {
/** */
private static final String ASSERTION_FAILED = "Assertion A12.4-1 (ThreadSafe) failed: ";
/**
* The <code>main</code> is called when the class is directly executed from the command line.
*
* @param args The arguments passed to the program.
*/
public static void main(String[] args) {
BatchTestRunner.run(ThreadSafe.class);
}
private final int threadCount = 10;
private final ThreadExceptionHandler group = new ThreadExceptionHandler();
private final RogueBarrier barrier = new RogueBarrier(threadCount);
private int successCount = 0;
private int exceptionCount = 0;
/** */
public void testThreadSafe() {
if (debug) logger.debug("\nSTART testThreadSafe");
// test thread-safety of PMF.getPersistenceManager():
// pmf.getPM(), pm.close()
final PCPoint[] nullPC = new PCPoint[threadCount];
runThreads(nullPC, "Concurrent PMF.getPersistenceManager()", threadCount);
// test thread-safety of PMF.getPersistenceManager():
// pmf.getPM(), pm.makePersistent(private transient PC), pm.close()
final PCPoint[] localPC = new PCPoint[threadCount];
for (int i = 0; i < threadCount; i++) {
localPC[i] = new PCPoint(1, i);
}
runThreads(
localPC, "Concurrent PMF.getPersistenceManager()" + ".makePersistent()", threadCount);
// test thread-safety of PM.makePersistent():
// pmf.getPM(), pm.makePersistent(shared transient PC), pm.close()
final PCPoint[] sharedPC = new PCPoint[threadCount];
final PCPoint p1 = new PCPoint(3, 3);
for (int i = 0; i < threadCount; i++) {
sharedPC[i] = p1;
}
runThreads(sharedPC, "Concurrent PM.makePersistent(" + "shared transient PC)", 1);
}
/**
* @param pc objects
* @param header header
* @param toSucceed to succeed
*/
public void runThreads(Object[] pc, String header, int toSucceed) {
// start threads with their pc instance
final Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++) {
Thread t = new Thread(group, new PMThread(pc[i]));
t.setName("ThreadSafeID-" + i);
threads[i] = t;
t.start();
}
// wait for all threads to finish
for (int i = 0; i < threadCount; i++) {
while (true) {
try {
threads[i].join();
break;
} catch (InterruptedException e) { // swallow
}
}
}
checkResults(header, toSucceed);
}
/**
* @param header header
* @param toSucceed to succeed
*/
protected synchronized void checkResults(String header, int toSucceed) {
// check unhandled exceptions
final Set<Map.Entry<Thread, Throwable>> uncaught = group.getAllUncaughtExceptions();
if ((uncaught != null) && !uncaught.isEmpty()) {
StringBuilder report = new StringBuilder("Uncaught exceptions:\n");
for (Map.Entry<Thread, Throwable> next : uncaught) {
Thread thread = next.getKey();
Throwable problem = next.getValue();
report.append(header + ": Uncaught exception " + problem + " in thread " + thread + "\n");
}
fail(ASSERTION_FAILED, report.toString());
group.clear();
}
// check success count
if (successCount != toSucceed) {
fail(
ASSERTION_FAILED,
header
+ ": Incorrect number of "
+ "\"succeeding\" threads; expected="
+ toSucceed
+ " found="
+ successCount);
}
successCount = 0;
// check exception count
final int toExcept = threadCount - toSucceed;
if (exceptionCount != toExcept) {
fail(
ASSERTION_FAILED,
header
+ ": Incorrect number of "
+ "\"failing\" threads; expected="
+ toExcept
+ " found="
+ exceptionCount);
}
exceptionCount = 0;
}
/** */
protected synchronized void markSuccess() {
logger.debug("[" + Thread.currentThread().getName() + "]: success");
successCount++;
}
/** */
protected synchronized void markExpectedException() {
logger.debug("[" + Thread.currentThread().getName() + "]: expected exception");
exceptionCount++;
}
/** */
class PMThread implements Runnable {
private final Object pc;
/** */
PMThread(Object pc) {
this.pc = pc;
}
/** */
public void run() {
ThreadSafe.this.logger.debug("[" + Thread.currentThread().getName() + "]: running");
final PersistenceManager pm = pmf.getPersistenceManager();
try {
Transaction tx = pm.currentTransaction();
tx.begin();
if (pc != null) {
pm.makePersistent(pc);
}
tx.commit();
markSuccess();
} catch (JDOUserException ex) {
markExpectedException();
} finally {
barrier.await();
cleanupPM(pm);
}
}
}
}