mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-22 07:28:16 +01:00
exit threads when BT off; add util to report deadlocks
I've seen a deadlock in the BT stuff. Now they'll be caught and reported via Crashlytics on DEBUG builds.
This commit is contained in:
parent
e23129c0e7
commit
1505a443ac
2 changed files with 193 additions and 83 deletions
|
@ -248,6 +248,8 @@ public class BTService extends XWJIService {
|
||||||
if ( BTEnabled() ) {
|
if ( BTEnabled() ) {
|
||||||
enqueueWork( context, BTService.class, sJobID, intent );
|
enqueueWork( context, BTService.class, sJobID, intent );
|
||||||
// Log.d( TAG, "enqueueWork(%s)", cmdFrom( intent, BTAction.values() ) );
|
// Log.d( TAG, "enqueueWork(%s)", cmdFrom( intent, BTAction.values() ) );
|
||||||
|
} else {
|
||||||
|
Log.d( TAG, "enqueueWork(): BT disabled so doing nothing" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,6 +500,8 @@ public class BTService extends XWJIService {
|
||||||
default:
|
default:
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.d( TAG, "onHandleWorkImpl(): BT disabled so doing nothing" );
|
||||||
}
|
}
|
||||||
} // onHandleWorkImpl()
|
} // onHandleWorkImpl()
|
||||||
|
|
||||||
|
@ -525,7 +529,6 @@ public class BTService extends XWJIService {
|
||||||
|
|
||||||
static void startYourself()
|
static void startYourself()
|
||||||
{
|
{
|
||||||
Log.d( TAG, "startYourself()" );
|
|
||||||
synchronized ( s_listener ) {
|
synchronized ( s_listener ) {
|
||||||
if ( s_listener[0] == null ) {
|
if ( s_listener[0] == null ) {
|
||||||
s_listener[0] = new BTListenerThread();
|
s_listener[0] = new BTListenerThread();
|
||||||
|
@ -732,7 +735,7 @@ public class BTService extends XWJIService {
|
||||||
final String className = getClass().getSimpleName();
|
final String className = getClass().getSimpleName();
|
||||||
final AtomicInteger nDone = new AtomicInteger();
|
final AtomicInteger nDone = new AtomicInteger();
|
||||||
Log.d( TAG, "%s.run() starting", className );
|
Log.d( TAG, "%s.run() starting", className );
|
||||||
while ( !mFinishing ) {
|
while ( !mFinishing && BTEnabled() ) {
|
||||||
try {
|
try {
|
||||||
List<PacketAccumulator> pas = getHasData(); // blocks
|
List<PacketAccumulator> pas = getHasData(); // blocks
|
||||||
Thread[] threads = new Thread[pas.size()];
|
Thread[] threads = new Thread[pas.size()];
|
||||||
|
@ -1096,6 +1099,7 @@ public class BTService extends XWJIService {
|
||||||
{
|
{
|
||||||
long waitFromNow;
|
long waitFromNow;
|
||||||
// Log.d( TAG, "getNextReadyMS() IN" );
|
// Log.d( TAG, "getNextReadyMS() IN" );
|
||||||
|
try ( DbgUtils.DeadlockWatch dw = new DbgUtils.DeadlockWatch( this ) ) {
|
||||||
synchronized ( this ) {
|
synchronized ( this ) {
|
||||||
if ( 0 == mCmds.size() ) { // nothing to send
|
if ( 0 == mCmds.size() ) { // nothing to send
|
||||||
waitFromNow = Long.MAX_VALUE;
|
waitFromNow = Long.MAX_VALUE;
|
||||||
|
@ -1109,16 +1113,19 @@ public class BTService extends XWJIService {
|
||||||
|
|
||||||
Log.d( TAG, "%s.getNextReadyMS() => %dms", this, waitFromNow );
|
Log.d( TAG, "%s.getNextReadyMS() => %dms", this, waitFromNow );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return waitFromNow;
|
return waitFromNow;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setNoHost()
|
void setNoHost()
|
||||||
{
|
{
|
||||||
// Log.d( TAG, "setNoHost() IN" );
|
// Log.d( TAG, "setNoHost() IN" );
|
||||||
|
try ( DbgUtils.DeadlockWatch dw = new DbgUtils.DeadlockWatch( this ) ) {
|
||||||
synchronized ( this ) {
|
synchronized ( this ) {
|
||||||
mLastFailTime = System.currentTimeMillis();
|
mLastFailTime = System.currentTimeMillis();
|
||||||
++mFailCount;
|
++mFailCount;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Log.d( TAG, "setNoHost() OUT" );
|
// Log.d( TAG, "setNoHost() OUT" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1140,6 +1147,7 @@ public class BTService extends XWJIService {
|
||||||
List<BTCmd> localCmds = null;
|
List<BTCmd> localCmds = null;
|
||||||
List<Integer> localGameIDs = null;
|
List<Integer> localGameIDs = null;
|
||||||
|
|
||||||
|
try ( DbgUtils.DeadlockWatch dw = new DbgUtils.DeadlockWatch( this ) ) {
|
||||||
synchronized ( this ) {
|
synchronized ( this ) {
|
||||||
byte[] data = mOP.bos.toByteArray();
|
byte[] data = mOP.bos.toByteArray();
|
||||||
if ( 0 < data.length ) {
|
if ( 0 < data.length ) {
|
||||||
|
@ -1184,6 +1192,7 @@ public class BTService extends XWJIService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Log.d( TAG, "writeAndCheck(): reading replies" );
|
Log.d( TAG, "writeAndCheck(): reading replies" );
|
||||||
int nDone = 0;
|
int nDone = 0;
|
||||||
|
@ -1328,16 +1337,13 @@ public class BTService extends XWJIService {
|
||||||
|
|
||||||
private void append( BTCmd cmd, OutputPair op ) throws IOException
|
private void append( BTCmd cmd, OutputPair op ) throws IOException
|
||||||
{
|
{
|
||||||
// Log.d( TAG, "append() IN" );
|
|
||||||
synchronized ( this ) {
|
|
||||||
append( cmd, 0, op );
|
append( cmd, 0, op );
|
||||||
}
|
}
|
||||||
// Log.d( TAG, "append() OUT" );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void append( BTCmd cmd, int gameID, OutputPair op ) throws IOException
|
private void append( BTCmd cmd, int gameID, OutputPair op ) throws IOException
|
||||||
{
|
{
|
||||||
// Log.d( TAG, "append() IN" );
|
// Log.d( TAG, "append() IN" );
|
||||||
|
try ( DbgUtils.DeadlockWatch dw = new DbgUtils.DeadlockWatch( this ) ) {
|
||||||
synchronized ( this ) {
|
synchronized ( this ) {
|
||||||
if ( 0 == mCmds.size() ) {
|
if ( 0 == mCmds.size() ) {
|
||||||
mStamp = System.currentTimeMillis();
|
mStamp = System.currentTimeMillis();
|
||||||
|
@ -1353,15 +1359,18 @@ public class BTService extends XWJIService {
|
||||||
tellSomebody();
|
tellSomebody();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Log.d( TAG, "append(%s): now %s", cmd, this );
|
// Log.d( TAG, "append(%s): now %s", cmd, this );
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetBackoff()
|
void resetBackoff()
|
||||||
{
|
{
|
||||||
// Log.d( TAG, "resetBackoff() IN" );
|
// Log.d( TAG, "resetBackoff() IN" );
|
||||||
|
try ( DbgUtils.DeadlockWatch dw = new DbgUtils.DeadlockWatch( this ) ) {
|
||||||
synchronized ( this ) {
|
synchronized ( this ) {
|
||||||
mFailCount = 0;
|
mFailCount = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Log.d( TAG, "resetBackoff() OUT" );
|
// Log.d( TAG, "resetBackoff() OUT" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1379,9 +1388,11 @@ public class BTService extends XWJIService {
|
||||||
private void tellSomebody()
|
private void tellSomebody()
|
||||||
{
|
{
|
||||||
// Log.d( TAG, "tellSomebody() IN" );
|
// Log.d( TAG, "tellSomebody() IN" );
|
||||||
|
try ( DbgUtils.DeadlockWatch dw = new DbgUtils.DeadlockWatch( sBlocker ) ) {
|
||||||
synchronized ( sBlocker ) {
|
synchronized ( sBlocker ) {
|
||||||
sBlocker.notifyAll();
|
sBlocker.notifyAll();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Log.d( TAG, "tellSomebody() OUT" );
|
// Log.d( TAG, "tellSomebody() OUT" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1483,6 +1494,7 @@ public class BTService extends XWJIService {
|
||||||
List<PacketAccumulator> result = new ArrayList<>();
|
List<PacketAccumulator> result = new ArrayList<>();
|
||||||
while ( 0 == result.size() ) {
|
while ( 0 == result.size() ) {
|
||||||
long newMin = 5 * 60 * 1000;
|
long newMin = 5 * 60 * 1000;
|
||||||
|
try ( DbgUtils.DeadlockWatch dw = new DbgUtils.DeadlockWatch( sSenders ) ) {
|
||||||
synchronized ( sSenders ) {
|
synchronized ( sSenders ) {
|
||||||
for ( String addr : sSenders.keySet() ) {
|
for ( String addr : sSenders.keySet() ) {
|
||||||
PacketAccumulator pa = sSenders.get( addr );
|
PacketAccumulator pa = sSenders.get( addr );
|
||||||
|
@ -1494,6 +1506,7 @@ public class BTService extends XWJIService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( result.size() == 0 ) {
|
if ( result.size() == 0 ) {
|
||||||
synchronized ( sBlocker ) {
|
synchronized ( sBlocker ) {
|
||||||
|
|
|
@ -31,7 +31,6 @@ import android.text.format.Time;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Formatter;
|
import java.util.Formatter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.eehouse.android.xw4.loc.LocUtils;
|
import org.eehouse.android.xw4.loc.LocUtils;
|
||||||
|
|
||||||
|
@ -168,4 +167,102 @@ public class DbgUtils {
|
||||||
// return dump.toString();
|
// return dump.toString();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
private static List<DeadlockWatch> sLockHolders = new ArrayList<>();
|
||||||
|
|
||||||
|
public static class DeadlockWatch extends Thread implements AutoCloseable {
|
||||||
|
private static final long DEFAULT_SLEEP_MS = 10 * 1000;
|
||||||
|
final private Object mOwner;
|
||||||
|
private long mStartStamp;
|
||||||
|
// private long mGotItTime = 0;
|
||||||
|
private boolean mCloseFired = false;
|
||||||
|
private String mStartStack;
|
||||||
|
|
||||||
|
// There's a race between this constructor and the synchronized()
|
||||||
|
// block that follows its try-with-resources. Oh well.
|
||||||
|
DeadlockWatch( Object syncObj )
|
||||||
|
{
|
||||||
|
mOwner = BuildConfig.DEBUG ? syncObj : null;
|
||||||
|
if ( BuildConfig.DEBUG ) {
|
||||||
|
mStartStack = android.util.Log.getStackTraceString(new Exception());
|
||||||
|
// Log.d( TAG, "__init(owner=%d): %s", mOwner.hashCode(), mStartStack );
|
||||||
|
mStartStamp = System.currentTimeMillis();
|
||||||
|
synchronized ( sLockHolders ) {
|
||||||
|
sLockHolders.add( this );
|
||||||
|
// Log.d( TAG, "added for owner %d", mOwner.hashCode() );
|
||||||
|
}
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// public void gotIt( Object obj )
|
||||||
|
// {
|
||||||
|
// if ( BuildConfig.DEBUG ) {
|
||||||
|
// Assert.assertTrue( obj == mOwner );
|
||||||
|
// mGotItTime = System.currentTimeMillis();
|
||||||
|
// // Log.d( TAG, "%s got lock after %dms", obj, mGotItTime - mStartStamp );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
if ( BuildConfig.DEBUG ) {
|
||||||
|
mCloseFired = true;
|
||||||
|
// Assert.assertTrue( 0 < mGotItTime ); // did you forget to call gotIt? :-)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
if ( BuildConfig.DEBUG ) {
|
||||||
|
long sleepMS = DEFAULT_SLEEP_MS;
|
||||||
|
try {
|
||||||
|
Thread.sleep( sleepMS );
|
||||||
|
|
||||||
|
if ( !mCloseFired ) {
|
||||||
|
DeadlockWatch likelyCulprit = null;
|
||||||
|
synchronized ( sLockHolders ) {
|
||||||
|
for ( DeadlockWatch sc : sLockHolders ) {
|
||||||
|
if ( sc.mOwner == mOwner && sc != this ) {
|
||||||
|
likelyCulprit = sc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String msg = new StringBuilder()
|
||||||
|
.append("timer fired!!!!")
|
||||||
|
.append( "lock sought by: " )
|
||||||
|
.append( mStartStack )
|
||||||
|
.append( "lock likely held by: " )
|
||||||
|
.append( likelyCulprit.mStartStack )
|
||||||
|
.toString();
|
||||||
|
CrashTrack.logAndSend( TAG, msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSelf();
|
||||||
|
} catch ( InterruptedException ie ) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeSelf()
|
||||||
|
{
|
||||||
|
if ( BuildConfig.DEBUG ) {
|
||||||
|
synchronized ( sLockHolders ) {
|
||||||
|
int start = sLockHolders.size();
|
||||||
|
// Log.d( TAG, "removing for owner %d", mOwner.hashCode() );
|
||||||
|
sLockHolders.remove( this );
|
||||||
|
Assert.assertTrue( start - 1 == sLockHolders.size() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return super.toString() + "; startStack: " + mStartStack;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue