excute db read and writes on different threads

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java             |  2 
src/main/java/eu/siacs/conversations/entities/Conversation.java                     |  4 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java            | 25 
src/main/java/eu/siacs/conversations/utils/ReplacingSerialSingleThreadExecutor.java |  2 
src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java          | 14 
5 files changed, 28 insertions(+), 19 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java 🔗

@@ -276,7 +276,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
 		this.messageCache = new HashMap<>();
 		this.sessions = new SessionMap(mXmppConnectionService, axolotlStore, account);
 		this.fetchStatusMap = new FetchStatusMap();
-		this.executor = new SerialSingleThreadExecutor();
+		this.executor = new SerialSingleThreadExecutor("Axolotl");
 	}
 
 	public String getOwnFingerprint() {

src/main/java/eu/siacs/conversations/entities/Conversation.java 🔗

@@ -762,7 +762,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
 
 	public boolean setNextMessage(String message) {
 		boolean changed = !getNextMessage().equals(message);
-		this.setAttribute(ATTRIBUTE_NEXT_MESSAGE,message);
+		this.setAttribute(ATTRIBUTE_NEXT_MESSAGE, message);
 		return changed;
 	}
 
@@ -851,7 +851,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
 	public boolean setAttribute(String key, String value) {
 		synchronized (this.attributes) {
 			try {
-				this.attributes.put(key, value);
+				this.attributes.put(key, value == null ? "" : value);
 				return true;
 			} catch (JSONException e) {
 				return false;

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -155,9 +155,10 @@ public class XmppConnectionService extends Service {
 	private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
 	public static final String ACTION_GCM_TOKEN_REFRESH = "gcm_token_refresh";
 	public static final String ACTION_GCM_MESSAGE_RECEIVED = "gcm_message_received";
-	private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor();
-	private final SerialSingleThreadExecutor mVideoCompressionExecutor = new SerialSingleThreadExecutor();
-	private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor();
+	private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor("FileAdding");
+	private final SerialSingleThreadExecutor mVideoCompressionExecutor = new SerialSingleThreadExecutor("VideoCompression");
+	private final SerialSingleThreadExecutor mDatabaseWriterExecutor = new SerialSingleThreadExecutor("DatabaseWriter");
+	private final SerialSingleThreadExecutor mDatabaseReaderExecutor = new SerialSingleThreadExecutor("DatabaseReader");
 	private ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor(true);
 	private final IBinder mBinder = new XmppConnectionBinder();
 	private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
@@ -874,7 +875,7 @@ public class XmppConnectionService extends Service {
 
 	public void expireOldMessages(final boolean resetHasMessagesLeftOnServer) {
 		mLastExpiryRun.set(SystemClock.elapsedRealtime());
-		mDatabaseExecutor.execute(new Runnable() {
+		mDatabaseWriterExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
 				long timestamp = getAutomaticMessageDeletionDate();
@@ -1450,7 +1451,7 @@ public class XmppConnectionService extends Service {
 					updateConversationUi();
 				}
 			};
-			mDatabaseExecutor.execute(runnable);
+			mDatabaseReaderExecutor.execute(runnable); //will contain one write command (expiry) but that's fine
 		}
 	}
 
@@ -1602,7 +1603,7 @@ public class XmppConnectionService extends Service {
 				}
 			}
 		};
-		mDatabaseExecutor.execute(runnable);
+		mDatabaseReaderExecutor.execute(runnable);
 	}
 
 	public List<Account> getAccounts() {
@@ -1715,7 +1716,7 @@ public class XmppConnectionService extends Service {
 				}
 			};
 			if (async) {
-				mDatabaseExecutor.execute(runnable);
+				mDatabaseReaderExecutor.execute(runnable);
 			} else {
 				runnable.run();
 			}
@@ -1892,7 +1893,7 @@ public class XmppConnectionService extends Service {
 					}
 				}
 			};
-			mDatabaseExecutor.execute(runnable);
+			mDatabaseWriterExecutor.execute(runnable);
 			this.accounts.remove(account);
 			updateAccountUi();
 			getNotificationService().updateErrorNotification();
@@ -3082,7 +3083,7 @@ public class XmppConnectionService extends Service {
 	}
 
 	public void updateConversation(final Conversation conversation) {
-		mDatabaseExecutor.execute(new Runnable() {
+		mDatabaseWriterExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
 				databaseBackend.updateConversation(conversation);
@@ -3364,7 +3365,7 @@ public class XmppConnectionService extends Service {
 					}
 				}
 			};
-			mDatabaseExecutor.execute(runnable);
+			mDatabaseWriterExecutor.execute(runnable);
 			updateUnreadCountBadge();
 			return true;
 		} else {
@@ -3441,7 +3442,7 @@ public class XmppConnectionService extends Service {
 				databaseBackend.writeRoster(account.getRoster());
 			}
 		};
-		mDatabaseExecutor.execute(runnable);
+		mDatabaseWriterExecutor.execute(runnable);
 
 	}
 
@@ -3662,7 +3663,7 @@ public class XmppConnectionService extends Service {
 				databaseBackend.updateConversation(conversation);
 			}
 		};
-		mDatabaseExecutor.execute(runnable);
+		mDatabaseWriterExecutor.execute(runnable);
 	}
 
 	public boolean sendBlockRequest(final Blockable blockable, boolean reportSpam) {

src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java 🔗

@@ -1,12 +1,14 @@
 package eu.siacs.conversations.utils;
 
 import android.os.Looper;
+import android.util.Log;
 
 import java.util.ArrayDeque;
 import java.util.Queue;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
+import eu.siacs.conversations.Config;
 import eu.siacs.conversations.services.AttachFileToConversationRunnable;
 
 public class SerialSingleThreadExecutor implements Executor {
@@ -14,12 +16,13 @@ public class SerialSingleThreadExecutor implements Executor {
 	final Executor executor = Executors.newSingleThreadExecutor();
 	protected final ArrayDeque<Runnable> tasks = new ArrayDeque<>();
 	private Runnable active;
+	private final String name;
 
-	public SerialSingleThreadExecutor() {
-		this(false);
+	public SerialSingleThreadExecutor(String name) {
+		this(name, false);
 	}
 
-	public SerialSingleThreadExecutor(boolean prepareLooper) {
+	public SerialSingleThreadExecutor(String name, boolean prepareLooper) {
 		if (prepareLooper) {
 			execute(new Runnable() {
 				@Override
@@ -28,6 +31,7 @@ public class SerialSingleThreadExecutor implements Executor {
 				}
 			});
 		}
+		this.name = name;
 	}
 
 	public synchronized void execute(final Runnable r) {
@@ -48,6 +52,10 @@ public class SerialSingleThreadExecutor implements Executor {
 	protected synchronized void scheduleNext() {
 		if ((active =  tasks.poll()) != null) {
 			executor.execute(active);
+			int remaining = tasks.size();
+			if (remaining > 0) {
+				Log.d(Config.LOGTAG,remaining+" remaining tasks on executor '"+name+"'");
+			}
 		}
 	}
 }