|
/// <disclaimer>
/// This software is protected by your own conscience (c).
/// You can use it however you want and wherever you want.
/// You are allowed to change this code, copy this code, or delete this code.
/// You can buy this code; sell this code; present this code to your mom on her birthday.
/// You can replace authors name with your own. You also can replace all the code with your own leaving
/// only authors name.
/// The only thing you cannot do, is to violate this license agreement. You simply are not able to.
/// </disclaimer>
/// <author>
/// Sergei Zotin
/// szotin@shaw.ca
/// Burnaby, BC, Canada
/// Feel free to contact me for bug reports, feature requests and contract offers.
/// </author>
/// <version>1.6</version>
using System;
using System.Threading;
using ZEN.Threading;
using System.IO;
class A
{
private B b;
private int x;
private int y;
// NEW: By using private lock object instead of this, we guarantee that users of A will not
// NEW: take part in our synchization game.
// NEW: That allow us to make some assumptions about a state of locks.
private string lockObject = "LockA";
public B TheB
{
// NEW: It is never involved in cascade locks (we can be sure, because lock object is now private),
// NEW: so deadlock cannot be a problem here.
// NEW: We can use lock(lockObject) instead of LockManager.Lock(lockObject), which works much faster.
get
{
lock ( lockObject )
{
return b;
}
}
// NEW: It is never involved in cascade locks (we can be sure, because lock object is now private),
// NEW: so deadlock cannot be a problem here.
// NEW: We can use lock(lockObject) instead of LockManager.Lock(lockObject), which works much faster.
set
{
lock ( lockObject )
{
b = value;
}
}
}
public int X
{
get
{
LockManager.Lock ( lockObject );
try
{
return x;
}
finally
{
LockManager.Unlock ( lockObject );
}
}
// NEW: 1. We can change the order of assignments to avoud undoing. The idea is: just not to set x before b.X
// NEW: is set.
// NEW: 2. Now we use private lock object, so we can be sure that external components do not lock either A or B.
// NEW: At the same time we never use X internally, so when it is called, either A or B are never locked. This
// NEW: means that even if LockManager.LockCount is not 1, we can fix the deadlock right here. External locks
// NEW: don't take part in it. LockManager.LockCount is a time consuming operation (it makes some loops throw
// NEW: internal LockManager arrays, which themselves are synchronized). Avoiding it definitely improves
// NEW: performance.
set
{
bool retry;
do
{
retry = false;
LockManager.Lock ( lockObject );
try
{
Thread.Sleep ( 10 ); // just to increase the odds of a deadlock
b.X = value;
x = value;
}
catch ( DeadlockException )
{
retry = true;
}
finally
{
LockManager.Unlock ( lockObject );
}
} while ( retry );
}
}
public int Y
{
get
{
LockManager.Lock ( lockObject );
try
{
return y;
}
finally
{
LockManager.Unlock ( lockObject );
}
}
set
{
LockManager.Lock ( lockObject );
try
{
Thread.Sleep ( 10 ); // just to increase the odds of a deadlock
y = value;
}
finally
{
LockManager.Unlock ( lockObject );
}
}
}
}
class B
{
private A a;
private int x;
private int y;
// NEW: By using private lock object instead of this, we guarantee that users of A will not
// NEW: take part in our synchization game.
// NEW: That allow us to make some assumptions about a state of locks.
private string lockObject = "LockB";
public A TheA
{
// NEW: It is never involved in cascade locks (we can be sure, because lock object is now private),
// NEW: so deadlock cannot be a problem here.
// NEW: We can use lock(lockObject) instead of LockManager.Lock(lockObject), which works much faster.
get
{
lock ( lockObject )
{
return a;
}
}
// NEW: It is never involved in cascade locks (we can be sure, because lock object is now private),
// NEW: so deadlock cannot be a problem here.
// NEW: We can use lock(lockObject) instead of LockManager.Lock(lockObject), which works much faster.
set
{
lock ( lockObject )
{
a = value;
}
}
}
public int X
{
get
{
LockManager.Lock ( lockObject );
try
{
return x;
}
finally
{
LockManager.Unlock ( lockObject );
}
}
set
{
LockManager.Lock ( lockObject );
try
{
Thread.Sleep ( 10 ); // just to increase the odds of a deadlock
x = value;
}
finally
{
LockManager.Unlock ( lockObject );
}
}
}
public int Y
{
get
{
LockManager.Lock ( lockObject );
try
{
return y;
}
finally
{
LockManager.Unlock ( lockObject );
}
}
// NEW: 1. We can change the order of assignments to avoud undoing. The idea is: just not to set y before a.Y
// NEW: is set.
// NEW: 2. Now we use private lock object, so we can be sure that external components do not lock either A or B.
// NEW: At the same time we never use X internally, so when it is called, either A or B are never locked. This
// NEW: means that even if LockManager.LockCount is not 1, we can fix the deadlock right here. External locks
// NEW: don't take part in it. LockManager.LockCount is a time consuming operation (it makes some loops throw
// NEW: internal LockManager arrays, which themselves are synchronized). Avoiding it definitely improves
// NEW: performance.
set
{
bool retry;
do
{
retry = false;
LockManager.Lock ( lockObject );
try
{
Thread.Sleep ( 10 ); // just to increase the odds of a deadlock
a.Y = value;
y = value;
}
catch ( DeadlockException )
{
retry = true;
}
finally
{
LockManager.Unlock ( lockObject );
}
} while ( retry );
}
}
}
class Step4
{
private A a;
private B b;
private Step4 ( A _a, B _b )
{
a = _a;
b = _b;
a.TheB = b;
b.TheA = a;
}
private void RunX ()
{
for ( int i = 0; i < 20; i++ )
{
a.X = i;
System.Console.WriteLine ( "Thread X: a.X={0}; b.X={1}", a.X, b.X );
}
}
private void RunY ()
{
for ( int i = 0; i < 20; i++ )
{
b.Y = i;
System.Console.WriteLine ( "Thread Y: a.Y={0}; b.Y={1}", a.Y, b.Y );
}
}
// NEW: We know the deadlocks are there. We can save some time by not logging them.
// NEW: So we don't turn the log on.
[STAThread]
static void Main ( string[] args )
{
A a = new A();
B b = new B();
Step4 step4 = new Step4 ( a, b );
Thread threadX = new Thread ( new ThreadStart ( step4.RunX ) );
Thread threadY = new Thread ( new ThreadStart ( step4.RunY ) );
threadX.Name = "X";
threadY.Name = "Y";
threadX.Start ();
threadY.Start ();
threadX.Join ();
threadY.Join ();
System.Console.WriteLine ( "Press Enter..." );
System.Console.In.Read();
System.Environment.Exit ( 0 );
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.