diff --git a/app/src/main/java/eu/faircode/email/Core.java b/app/src/main/java/eu/faircode/email/Core.java index d774b85a34..41e5b25e04 100644 --- a/app/src/main/java/eu/faircode/email/Core.java +++ b/app/src/main/java/eu/faircode/email/Core.java @@ -145,6 +145,10 @@ class Core { private static final long SCREEN_ON_DURATION = 3000L; // milliseconds private static final int SYNC_BATCH_SIZE = 20; private static final int DOWNLOAD_BATCH_SIZE = 20; + private static final int SYNC_YIELD_COUNT = 100; + private static final long SYNC_YIELD_DURATION = 1000; // milliseconds + private static final int DOWNLOAD_YIELD_COUNT = 25; + private static final long DOWNLOAD_YIELD_DURATION = 1000; // milliseconds private static final long YIELD_DURATION = 200L; // milliseconds private static final long JOIN_WAIT_ALIVE = 5 * 60 * 1000L; // milliseconds private static final long JOIN_WAIT_INTERRUPT = 1 * 60 * 1000L; // milliseconds @@ -156,8 +160,6 @@ class Core { private static final long EXISTS_RETRY_DELAY = 20 * 1000L; // milliseconds private static final int FIND_RETRY_COUNT = 3; // times private static final long FIND_RETRY_DELAY = 5 * 1000L; // milliseconds - private static final long OPS_YIELD_INTERVAL = 15 * 1000L; // milliseconds - private static final long OPS_YIELD_DURATION = 500L; // milliseconds private static final Map> accountIdentities = new HashMap<>(); @@ -3481,6 +3483,7 @@ class Core { fp.add(GmailFolder.FetchProfileItem.THRID); // Add/update local messages + int synced = 0; Log.i(folder.name + " add=" + imessages.length); for (int i = imessages.length - 1; i >= 0; i -= SYNC_BATCH_SIZE) { state.ensureRunning("Sync/IMAP/sync/fetch"); @@ -3519,7 +3522,6 @@ class Core { state.ensureRunning("Sync/IMAP/sync"); try { - state.dutyCycle(true, account.name); // Some providers erroneously return old messages if (full.contains(isub[j])) try { @@ -3543,6 +3545,15 @@ class Core { false, download && initialize == 0, rules, state, stats); ids[from + j] = (message == null || message.ui_hide ? null : message.id); + + if (message != null && full.contains(isub[j])) + if ((++synced % SYNC_YIELD_COUNT) == 0) + try { + Log.i(folder.name + " yield synced=" + synced); + Thread.sleep(SYNC_YIELD_DURATION); + } catch (InterruptedException ex) { + Log.w(ex); + } } catch (MessageRemovedException ex) { Log.w(folder.name, ex); } catch (FolderClosedException ex) { @@ -3559,7 +3570,6 @@ class Core { } finally { // Free memory isub[j] = null; - state.dutyCycle(false, account.name); } } } @@ -3607,6 +3617,7 @@ class Core { db.folder().setFolderSyncState(folder.id, "downloading"); // Download messages/attachments + int downloaded = 0; Log.i(folder.name + " download=" + imessages.length); for (int i = imessages.length - 1; i >= 0; i -= DOWNLOAD_BATCH_SIZE) { state.ensureRunning("Sync/IMAP/download/fetch"); @@ -3631,14 +3642,22 @@ class Core { state.ensureRunning("Sync/IMAP/download"); try { - state.dutyCycle(true, account.name); - if (ids[from + j] != null) - downloadMessage( + if (ids[from + j] != null) { + boolean fetched = downloadMessage( context, account, folder, istore, ifolder, (MimeMessage) isub[j], ids[from + j], state, stats); + if (fetched) + if ((++downloaded % DOWNLOAD_YIELD_COUNT) == 0) + try { + Log.i(folder.name + " yield downloaded=" + downloaded); + Thread.sleep(DOWNLOAD_YIELD_DURATION); + } catch (InterruptedException ex) { + Log.w(ex); + } + } } catch (FolderClosedException ex) { throw ex; } catch (Throwable ex) { @@ -3646,7 +3665,6 @@ class Core { } finally { // Free memory isub[j] = null; - state.dutyCycle(false, account.name); } } } @@ -5817,31 +5835,27 @@ class Core { ",serial=" + serial + "]"; } - private Long last = null; - private long idle = 0, busy = 0; - private long started = 0; + private static class FolderPriority { + private long folder; + private int priority; - void dutyCycle(boolean start, String name) { - if (start) - started = new Date().getTime(); - else { - long end = new Date().getTime(); - if (last != null) - idle += (started - last); - last = end; - busy += (end - started); - if (busy + idle > OPS_YIELD_INTERVAL) { - long wait = (OPS_YIELD_DURATION - idle); - Log.i(name + " idle wait=" + wait); - if (wait > 0) - try { - Thread.sleep(wait); - } catch (InterruptedException ex) { - Log.w(ex); - } - idle = 0; - busy = 0; - } + FolderPriority(long folder, int priority) { + this.folder = folder; + this.priority = priority; + } + + @Override + public int hashCode() { + return (int) (this.folder * 37 + priority); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof FolderPriority) { + FolderPriority other = (FolderPriority) obj; + return (this.folder == other.folder && this.priority == other.priority); + } else + return false; } } } diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index eca9bfe4eb..9a1c851dd1 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -55,6 +55,7 @@ import com.sun.mail.iap.Argument; import com.sun.mail.iap.ProtocolException; import com.sun.mail.iap.Response; import com.sun.mail.imap.IMAPFolder; +import com.sun.mail.imap.IMAPMessage; import com.sun.mail.imap.IMAPStore; import com.sun.mail.imap.protocol.IMAPProtocol; import com.sun.mail.imap.protocol.IMAPResponse; @@ -136,6 +137,8 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences private static final int FAST_FAIL_COUNT = 3; private static final int FETCH_YIELD_DURATION = 50; // milliseconds private static final long WATCHDOG_INTERVAL = 60 * 60 * 1000L; // milliseconds + private static final long OPS_YIELD_INTERVAL = 15 * 1000L; // milliseconds + private static final long OPS_YIELD_DURATION = 500L; // milliseconds private static final String ACTION_NEW_MESSAGE_COUNT = BuildConfig.APPLICATION_ID + ".NEW_MESSAGE_COUNT"; @@ -1984,16 +1987,33 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences Log.i(account.name + " folder " + folder.name + " flags=" + ifolder.getPermanentFlags()); } + long start = new Date().getTime(); try { - state.dutyCycle(true, account.name); Core.processOperations(ServiceSynchronize.this, account, folder, partition, iservice, ifolder, state, serial); } finally { - state.dutyCycle(false, account.name); + long end = new Date().getTime(); + if (last != null) + idle += (start - last); + last = end; + busy += (end - start); + if (busy + idle > OPS_YIELD_INTERVAL) { + long wait = (OPS_YIELD_DURATION - idle); + Log.i(account.name + " ops idle wait=" + wait); + if (wait > 0) + try { + Thread.sleep(wait); + } catch (InterruptedException ex) { + Log.w(ex); + } + idle = 0; + busy = 0; + } } + } catch (Throwable ex) { Log.e(folder.name, ex); EntityLog.log(ServiceSynchronize.this, EntityLog.Type.Account, folder,