001: /*
002:
003: Derby - Class org.apache.derbyTesting.functionTests.tests.lang.deadlockMode
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derbyTesting.functionTests.tests.lang;
023:
024: import java.sql.Connection;
025: import java.sql.DriverManager;
026: import java.sql.Statement;
027: import java.sql.PreparedStatement;
028: import java.sql.ResultSet;
029: import java.sql.ResultSetMetaData;
030: import java.sql.SQLException;
031: import java.sql.SQLWarning;
032:
033: import org.apache.derby.tools.ij;
034: import org.apache.derby.tools.JDBCDisplayUtil;
035:
036: /**
037: This tests for deadlock which can occur if two threads get a
038: row lock before getting a table lock on the same table. This can
039: happen if the lock obtained by the insert, update or delete result set
040: is a smaller range than the table scan result set. The insert, update or
041: delete result set lock is obtained first. For example, if the insert, update
042: or delete result set obtain a row lock and then the table scan obtains a
043: table scan lock, deadlock can occur since two threads can obtain the row lock
044: and then both thread will want the same table lock.
045: */
046:
047: public class deadlockMode {
048:
049: private static final int THREAD_COUNT = 20;
050: private static boolean passed = false;
051: private Object syncObject = new Object();
052: private int doneCount;
053:
054: private deadlockMode() {
055: }
056:
057: public static void main(String[] args) {
058: System.out.println("Test deadlockMode starting");
059:
060: deadlockMode tester = new deadlockMode();
061:
062: try {
063: // use the ij utility to read the property file and
064: // make the initial connection.
065: ij.getPropertyArg(args);
066: Connection conn = ij.startJBMS();
067:
068: setup(conn, true);
069: passed = true;
070: tester.runtest();
071: teardown(conn);
072: conn.close();
073:
074: } catch (Throwable e) {
075: System.out.println("FAIL: exception thrown:");
076: passed = false;
077: JDBCDisplayUtil.ShowException(System.out, e);
078: e.printStackTrace();
079: }
080:
081: if (passed)
082: System.out.println("PASS");
083: System.out.println("Test cursor finished");
084: }
085:
086: /**
087: * This method creates THREAD_COUNT threads which will all try to
088: * update the same table
089: */
090: private void runtest() throws InterruptedException {
091: Thread[] t = new Thread[THREAD_COUNT];
092: for (int i = 0; i < THREAD_COUNT; i++) {
093: t[i] = new Thread(new Runnable() {
094: public void run() {
095: startnew();
096: }
097: });
098: t[i].start();
099: }
100: boolean notdone = true;
101: while (notdone) {
102: synchronized (syncObject) {
103: if (doneCount == THREAD_COUNT)
104: notdone = false;
105: else
106: syncObject.wait();
107: }
108: }
109: }
110:
111: /**
112: * Keep track of how many are done so we can wait until all the
113: * threads are finished before saying we have passed
114: */
115: private void done() {
116: System.out.println("Done Thread");
117: synchronized (syncObject) {
118: doneCount++;
119: syncObject.notify();
120: }
121: }
122:
123: /**
124: * This method creates a connection, loads the query into cache using
125: * READ_COMMITTED and then tries to execute the query using SERIALIZABLE.
126: * If we don't update the lock mode based on the isolation level at
127: * execution time, the query can deadlock with other threads
128: * SERALIZABLE requires a table lock mode. READ_COMMITTED uses a row lock.
129: */
130: private void startnew() {
131: Connection conn = null;
132: try {
133: // make the initial connection.
134: conn = ij.startJBMS();
135: System.out.println("Starting thread");
136:
137: Statement stmt = conn.createStatement();
138: // execute a query to load cache
139: stmt.executeUpdate("update t set i = 456 where i = 456");
140: // set isolation level to serializable
141: conn.setAutoCommit(false);
142: stmt.execute("set isolation serializable");
143: for (int i = 0; i < 100; i++) {
144: stmt
145: .executeUpdate("update t set i = 456 where i = 456");
146: conn.commit();
147: }
148: done();
149:
150: } catch (Throwable e) {
151: synchronized (syncObject) {
152: System.out.println("FAIL: exception thrown:");
153: passed = false;
154: JDBCDisplayUtil.ShowException(System.out, e);
155: e.printStackTrace();
156: done();
157: }
158: } finally {
159: try {
160: if (conn != null) {
161: conn.rollback();
162: conn.close();
163: }
164: } catch (SQLException sqle) {
165: System.out.println("FAIL: exception thrown:");
166: passed = false;
167: JDBCDisplayUtil.ShowException(System.out, sqle);
168: sqle.printStackTrace();
169: }
170: }
171: }
172:
173: /**
174: * set up the table for the test
175: */
176: static void setup(Connection conn, boolean first)
177: throws SQLException {
178: Statement stmt = conn.createStatement();
179:
180: if (first) {
181: verifyCount(stmt.executeUpdate("create table t (i int)"), 0);
182:
183: } else {
184: verifyBoolean(stmt.execute("delete from t"), false);
185: }
186:
187: verifyCount(stmt.executeUpdate("insert into t values (1956)"),
188: 1);
189:
190: verifyCount(stmt.executeUpdate("insert into t values (456)"), 1);
191:
192: verifyCount(stmt.executeUpdate("insert into t values (180)"), 1);
193:
194: verifyCount(stmt.executeUpdate("insert into t values (3)"), 1);
195:
196: stmt.close();
197:
198: System.out.println("PASS: setup complete");
199: }
200:
201: /**
202: * clean up
203: */
204: static void teardown(Connection conn) throws SQLException {
205: Statement stmt = conn.createStatement();
206:
207: verifyCount(stmt.executeUpdate("drop table t"), 0);
208:
209: stmt.close();
210:
211: System.out.println("PASS: teardown complete");
212: }
213:
214: /**
215: * verify row count
216: */
217: static void verifyCount(int count, int expect) throws SQLException {
218: if (count != expect) {
219: System.out.println("FAIL: Expected " + expect + " got "
220: + count + " rows");
221: throw new SQLException("Wrong number of rows returned");
222: } else
223: System.out.println("PASS: expected and got " + count
224: + (count == 1 ? " row" : " rows"));
225: }
226:
227: /**
228: * verify boolean value
229: */
230: static void verifyBoolean(boolean got, boolean expect)
231: throws SQLException {
232: if (got != expect) {
233: System.out.println("FAIL: Expected " + expect + " got "
234: + got);
235: throw new SQLException("Wrong boolean returned");
236: } else
237: System.out.println("PASS: expected and got " + got);
238: }
239: }
|