diff --git a/Gruntfile.js b/Gruntfile.js
index d3eae9a55..8a941f895 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -61,6 +61,7 @@ module.exports = function(grunt) {
'libtextsecure/sync_request.js',
'libtextsecure/contacts_parser.js',
'libtextsecure/ProvisioningCipher.js',
+ 'libtextsecure/task_with_timeout.js',
],
dest: 'js/libtextsecure.js',
},
diff --git a/js/libtextsecure.js b/js/libtextsecure.js
index e914aa122..bca94a2a0 100644
--- a/js/libtextsecure.js
+++ b/js/libtextsecure.js
@@ -38094,7 +38094,8 @@ var TextSecureServer = (function() {
}.bind(this));
},
queueTask: function(task) {
- return this.pending = this.pending.then(task, task);
+ var taskWithTimeout = textsecure.createTaskWithTimeout(task);
+ return this.pending = this.pending.then(taskWithTimeout, taskWithTimeout);
},
cleanSignedPreKeys: function() {
var nextSignedKeyId = textsecure.storage.get('signedKeyId');
@@ -38420,21 +38421,29 @@ MessageReceiver.prototype.extend({
return textsecure.storage.unprocessed.remove(id);
},
queueDecryptedEnvelope: function(envelope, plaintext) {
- console.log('queueing decrypted envelope', this.getEnvelopeId(envelope));
- var handleDecryptedEnvelope = this.handleDecryptedEnvelope.bind(this, envelope, plaintext);
- this.pending = this.pending.then(handleDecryptedEnvelope, handleDecryptedEnvelope);
+ var id = this.getEnvelopeId(envelope);
+ console.log('queueing decrypted envelope', id);
+
+ var task = this.handleDecryptedEnvelope.bind(this, envelope, plaintext);
+ var taskWithTimeout = textsecure.createTaskWithTimeout(task, 'queueEncryptedEnvelope ' + id);
+
+ this.pending = this.pending.then(taskWithTimeout, taskWithTimeout);
return this.pending.catch(function(error) {
- console.log('queueDecryptedEnvelope error:', error && error.stack ? error.stack : error);
+ console.log('queueDecryptedEnvelope error handling envelope', id, ':', error && error.stack ? error.stack : error);
});
},
queueEnvelope: function(envelope) {
- console.log('queueing envelope', this.getEnvelopeId(envelope));
- var handleEnvelope = this.handleEnvelope.bind(this, envelope);
- this.pending = this.pending.then(handleEnvelope, handleEnvelope);
+ var id = this.getEnvelopeId(envelope);
+ console.log('queueing envelope', id);
+
+ var task = this.handleEnvelope.bind(this, envelope);
+ var taskWithTimeout = textsecure.createTaskWithTimeout(task, 'queueEnvelope ' + id);
+
+ this.pending = this.pending.then(taskWithTimeout, taskWithTimeout);
return this.pending.catch(function(error) {
- console.log('queueEnvelope error:', error && error.stack ? error.stack : error);
+ console.log('queueEnvelope error handling envelope', id, ':', error && error.stack ? error.stack : error);
});
},
// Same as handleEnvelope, just without the decryption step. Necessary for handling
@@ -39325,8 +39334,10 @@ MessageSender.prototype = {
},
queueJobForNumber: function(number, runJob) {
+ var taskWithTimeout = textsecure.createTaskWithTimeout(runJob, 'queueJobForNumber ' + number);
+
var runPrevious = this.pendingMessages[number] || Promise.resolve();
- var runCurrent = this.pendingMessages[number] = runPrevious.then(runJob, runJob);
+ var runCurrent = this.pendingMessages[number] = runPrevious.then(taskWithTimeout, taskWithTimeout);
runCurrent.then(function() {
if (this.pendingMessages[number] === runCurrent) {
delete this.pendingMessages[number];
@@ -39969,4 +39980,70 @@ libsignal.ProvisioningCipher = function() {
};
})();
+
+/*
+ * vim: ts=4:sw=4:expandtab
+ */
+(function () {
+ window.textsecure = window.textsecure || {};
+
+ window.textsecure.createTaskWithTimeout = function(task, id, options) {
+ options = options || {};
+ options.timeout = options.timeout || (1000 * 60 * 2); // two minutes
+
+ var errorForStack = new Error('for stack');
+ return function() {
+ return new Promise(function(resolve, reject) {
+ var complete = false;
+ var timer = setTimeout(function() {
+ if (!complete) {
+ var message =
+ (id || '')
+ + ' task did not complete in time. Calling stack: '
+ + errorForStack.stack;
+
+ console.log(message);
+ return reject(new Error(message));
+ }
+ }.bind(this), options.timeout);
+ var clearTimer = function() {
+ try {
+ var localTimer = timer;
+ if (localTimer) {
+ timer = null;
+ clearTimeout(localTimer);
+ }
+ }
+ catch (error) {
+ console.log(
+ id || '',
+ 'task ran into problem canceling timer. Calling stack:',
+ errorForStack.stack
+ );
+ }
+ };
+
+ var success = function(result) {
+ clearTimer();
+ complete = true;
+ return resolve(result);
+ };
+ var failure = function(error) {
+ clearTimer();
+ complete = true;
+ return reject(error);
+ };
+
+ var promise = task();
+ if (!promise || !promise.then) {
+ clearTimer();
+ complete = true;
+ return resolve(promise);
+ }
+
+ return promise.then(success, failure);
+ });
+ };
+ };
+})();
})();
diff --git a/js/models/conversations.js b/js/models/conversations.js
index bbf94ecb9..8864087d2 100644
--- a/js/models/conversations.js
+++ b/js/models/conversations.js
@@ -411,7 +411,10 @@
queueJob: function(callback) {
var previous = this.pending || Promise.resolve();
- var current = this.pending = previous.then(callback, callback);
+
+ var taskWithTimeout = textsecure.createTaskWithTimeout(callback, 'conversation ' + this.id);
+
+ var current = this.pending = previous.then(taskWithTimeout, taskWithTimeout);
current.then(function() {
if (this.pending === current) {
diff --git a/js/models/messages.js b/js/models/messages.js
index 04eb8d074..35ab939f9 100644
--- a/js/models/messages.js
+++ b/js/models/messages.js
@@ -480,9 +480,6 @@
};
message.save().then(function() {
-
- // throw new Error('Something went wrong!');
-
conversation.save().then(function() {
try {
conversation.trigger('newmessage', message);
diff --git a/libtextsecure/account_manager.js b/libtextsecure/account_manager.js
index 8ba9d0d94..f0993dc04 100644
--- a/libtextsecure/account_manager.js
+++ b/libtextsecure/account_manager.js
@@ -146,7 +146,8 @@
}.bind(this));
},
queueTask: function(task) {
- return this.pending = this.pending.then(task, task);
+ var taskWithTimeout = textsecure.createTaskWithTimeout(task);
+ return this.pending = this.pending.then(taskWithTimeout, taskWithTimeout);
},
cleanSignedPreKeys: function() {
var nextSignedKeyId = textsecure.storage.get('signedKeyId');
diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js
index e68456a9b..29e764d55 100644
--- a/libtextsecure/message_receiver.js
+++ b/libtextsecure/message_receiver.js
@@ -182,21 +182,29 @@ MessageReceiver.prototype.extend({
return textsecure.storage.unprocessed.remove(id);
},
queueDecryptedEnvelope: function(envelope, plaintext) {
- console.log('queueing decrypted envelope', this.getEnvelopeId(envelope));
- var handleDecryptedEnvelope = this.handleDecryptedEnvelope.bind(this, envelope, plaintext);
- this.pending = this.pending.then(handleDecryptedEnvelope, handleDecryptedEnvelope);
+ var id = this.getEnvelopeId(envelope);
+ console.log('queueing decrypted envelope', id);
+
+ var task = this.handleDecryptedEnvelope.bind(this, envelope, plaintext);
+ var taskWithTimeout = textsecure.createTaskWithTimeout(task, 'queueEncryptedEnvelope ' + id);
+
+ this.pending = this.pending.then(taskWithTimeout, taskWithTimeout);
return this.pending.catch(function(error) {
- console.log('queueDecryptedEnvelope error:', error && error.stack ? error.stack : error);
+ console.log('queueDecryptedEnvelope error handling envelope', id, ':', error && error.stack ? error.stack : error);
});
},
queueEnvelope: function(envelope) {
- console.log('queueing envelope', this.getEnvelopeId(envelope));
- var handleEnvelope = this.handleEnvelope.bind(this, envelope);
- this.pending = this.pending.then(handleEnvelope, handleEnvelope);
+ var id = this.getEnvelopeId(envelope);
+ console.log('queueing envelope', id);
+
+ var task = this.handleEnvelope.bind(this, envelope);
+ var taskWithTimeout = textsecure.createTaskWithTimeout(task, 'queueEnvelope ' + id);
+
+ this.pending = this.pending.then(taskWithTimeout, taskWithTimeout);
return this.pending.catch(function(error) {
- console.log('queueEnvelope error:', error && error.stack ? error.stack : error);
+ console.log('queueEnvelope error handling envelope', id, ':', error && error.stack ? error.stack : error);
});
},
// Same as handleEnvelope, just without the decryption step. Necessary for handling
diff --git a/libtextsecure/sendmessage.js b/libtextsecure/sendmessage.js
index 46d187d50..68ea346b5 100644
--- a/libtextsecure/sendmessage.js
+++ b/libtextsecure/sendmessage.js
@@ -149,8 +149,10 @@ MessageSender.prototype = {
},
queueJobForNumber: function(number, runJob) {
+ var taskWithTimeout = textsecure.createTaskWithTimeout(runJob, 'queueJobForNumber ' + number);
+
var runPrevious = this.pendingMessages[number] || Promise.resolve();
- var runCurrent = this.pendingMessages[number] = runPrevious.then(runJob, runJob);
+ var runCurrent = this.pendingMessages[number] = runPrevious.then(taskWithTimeout, taskWithTimeout);
runCurrent.then(function() {
if (this.pendingMessages[number] === runCurrent) {
delete this.pendingMessages[number];
diff --git a/libtextsecure/task_with_timeout.js b/libtextsecure/task_with_timeout.js
new file mode 100644
index 000000000..ee2ab363f
--- /dev/null
+++ b/libtextsecure/task_with_timeout.js
@@ -0,0 +1,65 @@
+/*
+ * vim: ts=4:sw=4:expandtab
+ */
+(function () {
+ window.textsecure = window.textsecure || {};
+
+ window.textsecure.createTaskWithTimeout = function(task, id, options) {
+ options = options || {};
+ options.timeout = options.timeout || (1000 * 60 * 2); // two minutes
+
+ var errorForStack = new Error('for stack');
+ return function() {
+ return new Promise(function(resolve, reject) {
+ var complete = false;
+ var timer = setTimeout(function() {
+ if (!complete) {
+ var message =
+ (id || '')
+ + ' task did not complete in time. Calling stack: '
+ + errorForStack.stack;
+
+ console.log(message);
+ return reject(new Error(message));
+ }
+ }.bind(this), options.timeout);
+ var clearTimer = function() {
+ try {
+ var localTimer = timer;
+ if (localTimer) {
+ timer = null;
+ clearTimeout(localTimer);
+ }
+ }
+ catch (error) {
+ console.log(
+ id || '',
+ 'task ran into problem canceling timer. Calling stack:',
+ errorForStack.stack
+ );
+ }
+ };
+
+ var success = function(result) {
+ clearTimer();
+ complete = true;
+ return resolve(result);
+ };
+ var failure = function(error) {
+ clearTimer();
+ complete = true;
+ return reject(error);
+ };
+
+ var promise = task();
+ if (!promise || !promise.then) {
+ clearTimer();
+ complete = true;
+ return resolve(promise);
+ }
+
+ return promise.then(success, failure);
+ });
+ };
+ };
+})();
diff --git a/libtextsecure/test/index.html b/libtextsecure/test/index.html
index 419d1885a..15d007461 100644
--- a/libtextsecure/test/index.html
+++ b/libtextsecure/test/index.html
@@ -28,9 +28,10 @@
-
-
-
+
+
+
+
@@ -39,5 +40,6 @@
+