private static Thread StartEvilThread(EvilThreadArgument arg) { ThreadStart entry = () => { arg.ExceptionType = null; try { using (var command = arg.Connection.CreateCommand(arg.Statement)) { arg.StartEvent.WaitOne(); command.ExecuteNonQuery(); } } catch (Exception exception) { arg.ExceptionType = arg.Connection.Driver.GetExceptionType(exception); } arg.Connection.Rollback(); }; var thread = new Thread(entry); thread.Start(); return(thread); }
public virtual void DeadlockTest() { var table = schema.CreateTable(DeadlockTableName); CreatePrimaryKey(table); var column = table.CreateColumn("value", Driver.TypeMappings[typeof(int)].MapType()); column.IsNullable = true; ExecuteNonQuery(SqlDdl.Create(table)); Connection.BeginTransaction(); var tableRef = SqlDml.TableRef(table); var insert = SqlDml.Insert(tableRef); insert.Values.Add(tableRef[IdColumnName], 1); ExecuteNonQuery(insert); insert.Values.Clear(); insert.Values.Add(tableRef[IdColumnName], 2); ExecuteNonQuery(insert); Connection.Commit(); var update1To1 = SqlDml.Update(tableRef); update1To1.Where = tableRef[IdColumnName] == 1; update1To1.Values.Add(tableRef[column.Name], 1); var update1To2 = SqlDml.Update(tableRef); update1To2.Where = tableRef[IdColumnName] == 1; update1To2.Values.Add(tableRef[column.Name], 2); var update2To1 = SqlDml.Update(tableRef); update2To1.Where = tableRef[IdColumnName] == 2; update2To1.Values.Add(tableRef[column.Name], 1); var update2To2 = SqlDml.Update(tableRef); update2To2.Where = tableRef[IdColumnName] == 2; update2To2.Values.Add(tableRef[column.Name], 2); using (var connectionOne = this.Driver.CreateConnection()) { connectionOne.Open(); connectionOne.BeginTransaction(IsolationLevel.ReadCommitted); using (var connectionTwo = Driver.CreateConnection()) { connectionTwo.Open(); connectionTwo.BeginTransaction(IsolationLevel.ReadCommitted); using (var command = connectionOne.CreateCommand(update1To1)) command.ExecuteNonQuery(); using (var command = connectionTwo.CreateCommand(update2To2)) command.ExecuteNonQuery(); var startEvent = new EventWaitHandle(false, EventResetMode.ManualReset); var arg1 = new EvilThreadArgument { Connection = connectionOne, StartEvent = startEvent, Statement = update2To1 }; var arg2 = new EvilThreadArgument { Connection = connectionTwo, StartEvent = startEvent, Statement = update1To2 }; var thread1 = StartEvilThread(arg1); var thread2 = StartEvilThread(arg2); startEvent.Set(); thread1.Join(); thread2.Join(); startEvent.Close(); var actual = arg1.ExceptionType ?? arg2.ExceptionType ?? SqlExceptionType.Unknown; AssertExceptionType(SqlExceptionType.Deadlock, actual); } } }