Skip to content

Instantly share code, notes, and snippets.

@AlastairTaft
Created June 16, 2015 09:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AlastairTaft/d28b79e6523fe817be82 to your computer and use it in GitHub Desktop.
Save AlastairTaft/d28b79e6523fe817be82 to your computer and use it in GitHub Desktop.
A console application to break safe threads with bad threads using SqlConnection objects
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
namespace ThreadCorruptionTest
{
class Program
{
static void Main(string[] args)
{
int threadCount = 100;
for (int i = 1; i <= threadCount; i++)
{
new Thread(new ThreadStart(Program.WorkerThread)).Start();
}
}
static int tidCounter = 0;
static UInt32 value = 0;
static SqlConnection globalConn = null;
static SqlConnection GetConn(string connect, bool useGlobalConn)
{
SqlConnection connLocal = null;
if (useGlobalConn)
{
connLocal = globalConn;
if (null == connLocal)
{
connLocal = new SqlConnection(connect);
connLocal.Open();
globalConn = connLocal;
return connLocal;
}
else
{
return connLocal;
}
}
else
{
connLocal = new SqlConnection(connect);
connLocal.Open();
return connLocal;
}
}
static object consoleLock = new object();
static void WorkerThread()
{
int tid = Interlocked.Increment(ref tidCounter);
string select = string.Format("select {0} as [f{1}]", tid, tid);
string fieldName = string.Format("f{0}", tid);
string connect = "server=(local);user id=XXXXX;password=XXXXX;";
int readCount = 0;
bool useGlobalConnThread = (0 == (tid % 5));
SqlConnection conn = null;
SqlCommand cmd = null;
SqlDataReader dr = null;
for (; ; )
{
try
{
conn = GetConn(connect, useGlobalConnThread);
cmd = conn.CreateCommand();
cmd.CommandText = select;
cmd.CommandTimeout = 5;
dr = cmd.ExecuteReader();
dr.Read();
int fieldValue = (int)dr[fieldName];
System.Diagnostics.Debug.Assert(fieldValue == tid);
readCount++;
if (0 == (readCount % 2500))
{
Console.WriteLine("Thread{0} reads{1}", tid, readCount);
}
}
catch (IndexOutOfRangeException iorEx)
{
lock (consoleLock)
{
Console.WriteLine("Caught targeted exception IndexOutOfRangeException:");
Console.WriteLine("expected -> {0}", fieldName);
Console.WriteLine("actual -> {0}", dr.GetName(0));
if (useGlobalConnThread)
{
Console.WriteLine("Ok, I'm a bad thread and I'm sharing SqlConnections, I know this is going to happen.");
}
else
{
Console.WriteLine("Hey, I'm a safe thread and just a victim!");
System.Diagnostics.Debug.Assert(false);
}
}
}
catch (Exception ex)
{
lock (consoleLock)
{
Console.WriteLine(ex.Message);
if (useGlobalConnThread)
{
Console.WriteLine("Ok, I'm a bad thread and I'm sharing SqlConnections, I know this is going to happen.");
}
else
{
Console.WriteLine("Hey, I'm a safe thread and just a victim!");
}
// Reset globalConn on failure.
SqlConnection connLocal = GetConn(connect, false);
SqlConnection connToClose = globalConn;
globalConn = connLocal;
try { connToClose.Close(); }
catch (Exception) { };
}
}
finally
{
if (!useGlobalConnThread)
{
if (null != conn) conn.Close();
}
if (null != cmd) cmd.Dispose();
if (null != dr) dr.Close();
conn = null;
cmd = null;
dr = null;
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment