How to handle java.nio.channels.ClosedByInterruptException correctly?

4 posts / 0 new
Last post
DI (FH) Ludwig Ertl
ert@csp.at's picture
Offline
Mobile Conjurer
Joined: 22 Feb 2010
Posts:
How to handle java.nio.channels.ClosedByInterruptException correctly?

Hi,

When playing around with an Internet connection that downloads data asynchronously in the background and doing file operations in the foreground it can happen that the file handle gets closed unexpectedly by a Thread interruption on the Android runtime:

11-15 13:06:49.108 16722 16729 I MoSyncFile: maFileOpen (/data/data/com.mosync.app_mapctl/files//00000058.til, 3): 68
11-15 13:06:49.108 16722 16729 I MoSyncFile: maFileExists (68): true
11-15 13:06:49.108 16722 16729 I MoSyncFile: maFileTruncate (68)
11-15 13:06:49.108 16722 16729 I maWriteLog: fwrite (00198560, 30847, 1, 68)
11-15 13:06:49.108 16722 16729 I MoSyncFile: maFileWrite (68)
11-15 13:06:49.116 16722 16729 E MoSyncFile ERROR: writeByteBufferToFile Exception - java.nio.channels.ClosedByInterruptException
11-15 13:06:49.116 16722 16729 W System.err: java.nio.channels.ClosedByInterruptException
11-15 13:06:49.116 16722 16729 W System.err:    at java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:166)
11-15 13:06:49.116 16722 16729 W System.err:    at org.apache.harmony.nio.internal.FileChannelImpl.writeImpl(FileChannelImpl.java:555)
11-15 13:06:49.116 16722 16729 W System.err:    at org.apache.harmony.nio.internal.FileChannelImpl.write(FileChannelImpl.java:539)
11-15 13:06:49.116 16722 16729 W System.err:    at com.mosync.internal.android.MoSyncFile.writeByteBufferToFile(MoSyncFile.java:596)
11-15 13:06:49.116 16722 16729 W System.err:    at com.mosync.internal.android.MoSyncFile.maFileWrite(MoSyncFile.java:661)
11-15 13:06:49.116 16722 16729 W System.err:    at com.mosync.internal.android.MoSyncThread.maFileWrite(MoSyncThread.java:3854)
11-15 13:06:49.116 16722 16729 W System.err:    at com.mosync.internal.android.MoSyncThread.nativeRun(Native Method)
11-15 13:06:49.116 16722 16729 W System.err:    at com.mosync.internal.android.MoSyncThread.run(MoSyncThread.java:902)

How do I handle this correctly? I have no possibility to check whether my file operation fails due to this exception so that I can retry everything, or if it is caused by another error which I probably can't handle by retrying.

Please advise.

Regards,
Ludwig 

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
DI (FH) Ludwig Ertl
ert@csp.at's picture
Offline
Mobile Conjurer
Joined: 22 Feb 2010
Posts:

Here is what I think is happening in this case:

1) The main Thread is busy writing data. This is a blocking operation:

	private int writeByteBufferToFile(
		MoSyncFileHandle fileHandle,
		ByteBuffer byteBuffer)
	{
		try
		{
			int ret = fileHandle.mFileChannel.write(byteBuffer);
			log("writeByteBufferToFile bytes written: "  + ret);
			fileHandle.mCurrentPosition =
				(int)fileHandle.mFileChannel.position();
			return ret;
		}
		catch (Throwable t)
		{
			logerr("writeByteBufferToFile Exception - " + t);
			t.printStackTrace();
			return MA_FERR_GENERIC;
		}
	}

2) The Socket reader thread has received data or any does a state change and wants to notify the main thread about it by posting a message to it via postResultEvent:

	/**
	 * Post a event to the MoSync event queue.
	 */
	public void postEvent(int[] event)
	{
		mMoSyncThread.postEvent(event);
	}

		/**
		 * Post CONNOP result event. Here the state flag bits for 
		 * the event is cleared.
		 */
		public synchronized void postResultEvent(int opType, int result)
		{
			//Log.i("ConnectionObject.postResultEvent",
			//	"mHandle: " + mHandle +
			//	" mState: " + mState +
			//	" opType: " + opType +
			//	" result: " + result +
			//	" connobj (this): " + this);
	
			// Check that state is ongoing.
			MYASSERT((mState & opType) != 0);
			
			// Clear state bit.
			mState &= ~opType;
	
			int[] event = new int[4];
			event[0] = EVENT_TYPE_CONN;
			event[1] = mHandle;
			event[2] = opType;
			event[3] = result;
			
			//Log.i("ConnectionObject.postResultEvent",
			//	"Calling nativePostEvent");
			mMoSyncNetwork.postEvent(event);
		}

postEvent in the main thread does interrupt the thread:

	/**
	 * Post a event to the MoSync event queue.
	 */
	public synchronized void postEvent(int[] event)
	{
		// Add event to queue.
		nativePostEvent(event);
		// Wake up thread if sleeping.
		interrupt();
	}

With the call to interrupt(), the blocking write operation is cancelled throwing the exception and invalidating our handle.
Maybe you can guard all blocking operations with a Mutex that postEvent tries to wait for before calling interrupt() so that the operation remains synchronous. 
Just an idea, I'm not a Java programmer, it's just the way I would do it in C, I guess...

Regards,
Ludwig 

DI (FH) Ludwig Ertl
ert@csp.at's picture
Offline
Mobile Conjurer
Joined: 22 Feb 2010
Posts:

Hi again,

Here is a sample program to reproduce the problem (if it's not working for you, increase the loop count in the for-loop so that you have enough time to receive downloaded data):

#include <ma.h>
#include <conprint.h>
#define NUM_HANDLES		3
void ThreadingBug()
{
	MAHandle http[NUM_HANDLES];
	char szFile[256]={0}, Buffer[16384];
	MAHandle file;
	MAEvent event={0};
	int i;
	// Create HTTP connections
	for (i=0; i<sizeof(http)/sizeof(http[0]); i++)
		http[i] = maHttpCreate ("http://www.csp.at/joomla/index.php", HTTP_GET);
	// No main loop, doing heavy IO instead so that messages get queued
	// and interrupt the main thread
	maGetSystemProperty("mosync.path.local", szFile, sizeof(szFile));
	strcat (szFile, "testfil.txt");
	if ((file = maFileOpen(szFile, MA_ACCESS_READ_WRITE))<0)
	{
		lprintfln ("Error opening file %s\n", szFile);
		return;
	}
	if (maFileCreate(file)<0)
	{
		lprintfln ("Error on maFileCreate %s\n", szFile);
		maFileClose(file);
		return;
	}
	for (i=0; i<sizeof(http)/sizeof(http[0]); i++)
		maHttpFinish (http[i]);
	for (i=0; i<30000; i++)
	{
		if (maFileWrite(file, Buffer, sizeof(Buffer))<0)
		{
			lprintfln ("maFileWrite failed! Possibly the thread got interrupted!\n");
			break;
		}
		maFileSeek (file, 0, 0);
	}
	maFileDelete(file);
	maFileClose(file);
	for (i=0; i<sizeof(http)/sizeof(http[0]); i++)
		maConnClose (http[i]);

	maExit(0);
}

int MAMain() {
	ThreadingBug();
}

Outputs the following for me on the device, as expected:

11-17 10:06:39.905 18877 18884 I MoSyncFile: maFileSeek (1)
11-17 10:06:39.905 18877 18884 I MoSyncFile: maFileWrite (1)
11-17 10:06:39.921 18877 18884 E MoSyncFile ERROR: writeByteBufferToFile Exception - java.nio.channels.ClosedByInterruptException
11-17 10:06:39.921 18877 18884 W System.err: java.nio.channels.ClosedByInterruptException
11-17 10:06:39.928 18877 18884 W System.err:    at java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:166)
11-17 10:06:39.928 18877 18884 W System.err:    at org.apache.harmony.nio.internal.FileChannelImpl.writeImpl(FileChannelImpl.java:555)
11-17 10:06:39.928 18877 18884 W System.err:    at org.apache.harmony.nio.internal.FileChannelImpl.write(FileChannelImpl.java:539)
11-17 10:06:39.928 18877 18884 W System.err:    at com.mosync.internal.android.MoSyncFile.writeByteBufferToFile(MoSyncFile.java:596)
11-17 10:06:39.928 18877 18884 W System.err:    at com.mosync.internal.android.MoSyncFile.maFileWrite(MoSyncFile.java:661)
11-17 10:06:39.928 18877 18884 W System.err:    at com.mosync.internal.android.MoSyncThread.maFileWrite(MoSyncThread.java:3854)
11-17 10:06:39.928 18877 18884 W System.err:    at com.mosync.internal.android.MoSyncThread.nativeRun(Native Method)
11-17 10:06:39.928 18877 18884 W System.err:    at com.mosync.internal.android.MoSyncThread.run(MoSyncThread.java:902)
11-17 10:06:39.928 18877 18884 I maWriteLog: maFileWrite failed! Possibly the thread got interrupted!

Regards,
Ludwig 

Anders Malm
andersmalm's picture
Offline
Mobile Conjurer
Joined: 28 Aug 2008
Posts:

Hi Ludwig!

I made a fix for this. This was giving me a lot of problems when running applications on the Galaxy Nexus, seams like Ice Cream Sandwich handles multi cores in a more efficient manner than previous versions of Android. Need to do more testing but a fix will be added for MoSync 3.0.