Commit 05a213ef authored by Eugene Shen's avatar Eugene Shen

Thoroughly validate polling logic

parent 2b146ccf
......@@ -212,13 +212,12 @@
// i.e. {quiet_room: 3, busy_room: 429}
message_count_dict: {},
// a dict of room names to their delayRefresh promise queues
// i.e. {room: new RSVP.Queue().push(function () { return ... })}
// a dict of room names to an object with keys corresponding to
// their delayRefresh promise queues, i.e. queue: new RSVP.Queue()
// their POLL_DELAy_LIST indices, i.e. index: 3, and
// whether each is currently running refreshChat, i.e. lock: true
delay_refresh_dict: {},
// a dict of room names to whether each is currently running refreshChat
current_refresh_dict: {},
// true to use alert_icon_url, false to use default_icon_url
favicon_alert: false,
alert_icon_url: "https://pricespy.co.nz/favicon.ico",
......@@ -240,7 +239,7 @@
.declareAcquiredMethod("getSetting", "getSetting")
/* Join a new room room.
/* Join a new room.
* This function is acquired by gadget_erp5_chat_room.
* Parameters:
* - room: the name of the room to join
......@@ -377,9 +376,6 @@
"jio_storage_description",
"jio_configurator",
new RSVP.Queue()
.push(function () {
return gadget.updateHeader({page_title: "OfficeJS Chat"});
})
.push(function () {
return gadget.updateHeader({page_title: "OfficeJS Chat"});
})
......@@ -423,8 +419,7 @@
gadget.state.unread_room_dict[gadget.state.room] = false;
return gadget.changeState({
refresh_chat: true,
favicon_alert: false,
update: true
favicon_alert: false
});
};
return gadget.createRoom(user_email);
......@@ -473,9 +468,16 @@
default_jio_type: gadget.state.default_jio_type,
default_erp5_url: gadget.state.default_erp5_url,
default_dav_url: gadget.state.default_dav_url,
// XXX store room in description because description is
// indexed in ERP5, and only indexed properties can be queried
query: {
query: 'portal_type: "Text Post" AND room: "' + room + '"',
limit: [0, JIO_QUERY_MAX_LIMIT]
query: 'portal_type: "Text Post" AND description: "' + room + '"',
limit: [0, JIO_QUERY_MAX_LIMIT],
select_list: ["content"],
sort_on: [
["date_ms", "ascending"],
["content", "ascending"]
]
}
});
})
......@@ -485,8 +487,11 @@
.push(function () {
gadget.state.message_list_dict[room] = [];
gadget.state.message_count_dict[room] = 0;
gadget.state.current_refresh_dict[room] = false;
gadget.state.delay_refresh_dict[room] = new RSVP.Queue();
gadget.state.delay_refresh_dict[room] = {
queue: new RSVP.Queue(),
index: 0,
lock: false
};
gadget.state.id_to_name["chat-contact-" + nameToId(room)] = room;
return gadget.changeState({room: room, is_chat: false});
});
......@@ -505,7 +510,7 @@
.declareMethod("changeRoom", function (room) {
var gadget = this;
if (gadget.state.message_list_dict[room].length > 0) {
if (gadget.state.message_count_dict[room] > 0) {
return gadget.changeState({room: room, is_chat: true});
}
return gadget.changeState({room: room, is_chat: false, update: true});
......@@ -534,7 +539,10 @@
gadget.state.unread_room_dict[param_dict.room] = false;
return gadget.storeArchive(message)
.push(function () {
return gadget.changeState({refresh_chat: true, favicon_alert: false});
return gadget.changeState({
refresh_chat: true,
favicon_alert: false
});
});
})
......@@ -581,6 +589,7 @@
author: message.name,
date_ms: getTime(message),
room: message.room,
description: message.room,
content: JSON.stringify(message)
}]);
});
......@@ -593,7 +602,7 @@
* - poll_delay_index: the index in POLL_DELAY_LIST of
* the time in milliseconds to wait before refreshing again
* Requirements:
* - gadget.state.delay_refresh_dict[room] has new RSVP.Queue() in it
* - gadget.state.delay_refresh_dict[room].queue has new RSVP.Queue() in it
* Effects:
* - call refreshChat and wait a while before calling it again
*/
......@@ -602,21 +611,60 @@
var gadget = this;
return new RSVP.Queue()
.push(function () {
// the last element in POLL_DELAY_LIST is the maximum polling interval
if (poll_delay_index >= POLL_DELAY_LIST.length) {
poll_delay_index = POLL_DELAY_LIST.length - 1;
}
// do not wait for refreshChat to finish before starting the delay
// do not wait for refreshChat() to finish before starting the delay
// otherwise, the true delay would depend on the time of refreshChat()
return RSVP.all([
RSVP.delay(POLL_DELAY_LIST[poll_delay_index]),
gadget.refreshChat(room)
]);
})
/* there are four main cases:
* 1. the current polling interval is shorter than the stored interval,
* and refreshChat() is being executed with the stored interval
* - should cancel and overwrite the longer interval
* - the longer interval is both stored and being run
* - safe to cancel because refreshChat() is executed
* with the shorter interval immediately afterwards
* - so, cancel the longer interval, then overwrite it
* 2. the current polling interval is shorter than the stored interval,
* and refreshChat() is not being executed with the stored interval
* - should cancel and overwrite the longer interval
* - the longer interval is only being stored, not being run
* - so, overwrite it
* 3. the current polling interval is the same as the stored interval
* - should cancel and overwrite the longer interval
* - dangerous to cancel because the currently executing code in
* delayRefresh() could have been called by the stored interval!
* - so, overwrite it and do not cancel it, even if it is being run
* 4. the current polling interval is longer than the stored interval
* - should cancel and overwrite the longer interval
* - the longer interval is neither stored nor being run
* - so, do nothing
*/
.push(function () {
gadget.state.delay_refresh_dict[room].cancel();
gadget.state.delay_refresh_dict[room] = new RSVP.Queue()
if (poll_delay_index < gadget.state.delay_refresh_dict[room].index
&& !gadget.state.delay_refresh_dict[room].lock) {
gadget.state.delay_refresh_dict[room].queue.cancel();
}
if (poll_delay_index <= gadget.state.delay_refresh_dict[room].index) {
gadget.state.delay_refresh_dict[room] = {
// delayRefresh() is immediately called after this line
// and can be cancelled to overwrite longer intervals
queue: new RSVP.Queue()
.push(function () {
return gadget.delayRefresh(room, poll_delay_index + 1);
});
}),
// increase the polling interval over time
index: poll_delay_index + 1,
// only refreshChat() should modify the locks
lock: gadget.state.delay_refresh_dict[room].lock
};
}
});
})
......@@ -644,10 +692,12 @@
// lock so that at most one refreshChat() is running at any time
// multiple calls to repair() results in unmanageable duplication
if (gadget.state.current_refresh_dict[room]) {
// atomic because JavaScript is single threaded,
// so nothing else can run between the following four lines
if (gadget.state.delay_refresh_dict[room].lock) {
return;
}
gadget.state.current_refresh_dict[room] = true;
gadget.state.delay_refresh_dict[room].lock = true;
return gadget.getDeclaredGadget("room-gadget-" + room)
.push(function (sub_gadget) {
......@@ -680,6 +730,7 @@
j = 0;
for (i = 0; i < result_list.data.total_rows; i += 1) {
message = JSON.parse(result_list.data.rows[i].value.content);
if (typeof message === "object") {
while (j < old_list.length
&& getTime(old_list[j]) < getTime(message)) {
new_list.push(old_list[j]);
......@@ -692,6 +743,7 @@
}
new_list.push(message);
}
}
while (j < old_list.length) {
new_list.push(old_list[j]);
j += 1;
......@@ -709,9 +761,9 @@
// release the lock no matter what errors occur
.push(function () {
gadget.state.current_refresh_dict[room] = false;
gadget.state.delay_refresh_dict[room].lock = false;
}, function () {
gadget.state.current_refresh_dict[room] = false;
gadget.state.delay_refresh_dict[room].lock = false;
});
})
......@@ -857,11 +909,11 @@
var promise_list = [], room;
for (room in gadget.state.message_list_dict) {
if (gadget.state.message_list_dict.hasOwnProperty(room)
&& gadget.state.message_list_dict[room].length > 0) {
&& gadget.state.message_count_dict[room] > 0) {
promise_list.push(gadget.deployMessage({
name: gadget.state.name,
content: gadget.state.name + " has quit.",
room: room,
content: gadget.state.name + " has quit.",
color: "orange"
}));
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment