From 4510165934eba7be94dea822e2fb1cfa89e70ca3 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 27 May 2011 15:49:59 +0200 Subject: gitweb.js: No need for inProgress in blame_incremental.js JavaScript is single-threaded, so there is no need for protection against re-entrancy via inProgress variable. In particular calls to setInterval handler are stacked if handler doesn't finish before new interrupt (before new interval). The same happens with events - they are (hopefully) stacked if even handler didn't finish work. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/static/js/blame_incremental.js | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/gitweb/static/js/blame_incremental.js b/gitweb/static/js/blame_incremental.js index 676da6b590..4841805288 100644 --- a/gitweb/static/js/blame_incremental.js +++ b/gitweb/static/js/blame_incremental.js @@ -420,8 +420,6 @@ function handleLine(commit, group) { // ---------------------------------------------------------------------- -var inProgress = false; // are we processing response - /**#@+ * @constant */ @@ -536,7 +534,7 @@ function processData(unprocessed, nextReadPos) { * * @param {XMLHttpRequest} xhr: XMLHttpRequest object * - * @globals pollTimer, commits, inProgress + * @globals pollTimer, commits */ function handleError(xhr) { errorInfo('Server error: ' + @@ -544,8 +542,6 @@ function handleError(xhr) { clearInterval(pollTimer); commits = {}; // free memory - - inProgress = false; } /** @@ -553,7 +549,7 @@ function handleError(xhr) { * * @param {XMLHttpRequest} xhr: XMLHttpRequest object (unused) * - * @globals pollTimer, commits, inProgress + * @globals pollTimer, commits */ function responseLoaded(xhr) { clearInterval(pollTimer); @@ -561,15 +557,13 @@ function responseLoaded(xhr) { fixColorsAndGroups(); writeTimeInterval(); commits = {}; // free memory - - inProgress = false; } /** * handler for XMLHttpRequest onreadystatechange event * @see startBlame * - * @globals xhr, inProgress + * @globals xhr */ function handleResponse() { @@ -609,13 +603,6 @@ function handleResponse() { return; } - // in case we were called before finished processing - if (inProgress) { - return; - } else { - inProgress = true; - } - // extract new whole (complete) lines, and process them while (xhr.prevDataLength !== xhr.responseText.length) { if (xhr.readyState === 4 && @@ -633,8 +620,6 @@ function handleResponse() { xhr.prevDataLength === xhr.responseText.length) { responseLoaded(xhr); } - - inProgress = false; } // ============================================================ -- cgit v1.2.3 From e8dd0e4063da6d0eecef1a6db99534e41f74b32e Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 27 May 2011 15:50:00 +0200 Subject: gitweb.js: No need for loop in blame_incremental's handleResponse() JavaScript is single-threaded, so there is no need for protecting against changes to XMLHttpRequest object behind event handler back. Therefore there is no need for loop that was here in case `xhr' got new changes while processing current changes. This should make code a bit more clear. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/static/js/blame_incremental.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/gitweb/static/js/blame_incremental.js b/gitweb/static/js/blame_incremental.js index 4841805288..27955ecd37 100644 --- a/gitweb/static/js/blame_incremental.js +++ b/gitweb/static/js/blame_incremental.js @@ -603,21 +603,16 @@ function handleResponse() { return; } - // extract new whole (complete) lines, and process them - while (xhr.prevDataLength !== xhr.responseText.length) { - if (xhr.readyState === 4 && - xhr.prevDataLength === xhr.responseText.length) { - break; - } + // extract new whole (complete) lines, and process them + if (xhr.prevDataLength !== xhr.responseText.length) { xhr.prevDataLength = xhr.responseText.length; var unprocessed = xhr.responseText.substring(xhr.nextReadPos); xhr.nextReadPos = processData(unprocessed, xhr.nextReadPos); - } // end while + } // did we finish work? - if (xhr.readyState === 4 && - xhr.prevDataLength === xhr.responseText.length) { + if (xhr.readyState === 4) { responseLoaded(xhr); } } -- cgit v1.2.3 From 42ab5d40deb6dff37705ce5aa16ac69d859a04e1 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 27 May 2011 15:50:01 +0200 Subject: gitweb.js: use setTimeout rather than setInterval in blame_incremental.js If there is a possibility that your logic could take longer to execute than the interval time, it is recommended that you recursively call a named function using window.setTimeout rather than window.setInterval. Therefore instead of using setInterval as an alternate way of invoking handleResponse (because some web browsers call onreadystatechange only once per each distinct state, and not for each server flush), use setTimeout and reset it from handleResponse. As a bonus this allows us to get rid of timer if it turns out that web browser calls onreadystatechange on each server flush. While at it get rid of `xhr' global variable, creating it instead as local variable in startBlame and passing it as parameter, and of `pollTimer' global variable, passing it as member of xhr object (xhr.pollTimer). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/static/js/blame_incremental.js | 55 +++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/gitweb/static/js/blame_incremental.js b/gitweb/static/js/blame_incremental.js index 27955ecd37..db6eb50584 100644 --- a/gitweb/static/js/blame_incremental.js +++ b/gitweb/static/js/blame_incremental.js @@ -29,7 +29,6 @@ /* ............................................................ */ /* utility/helper functions (and variables) */ -var xhr; // XMLHttpRequest object var projectUrl; // partial query + separator ('?' or ';') // 'commits' is an associative map. It maps SHA1s to Commit objects. @@ -431,8 +430,6 @@ var endRe = /^END ?([^ ]*) ?(.*)/; var curCommit = new Commit(); var curGroup = {}; -var pollTimer = null; - /** * Parse output from 'git blame --incremental [...]', received via * XMLHttpRequest from server (blamedataUrl), and call handleLine @@ -533,26 +530,34 @@ function processData(unprocessed, nextReadPos) { * Handle XMLHttpRequest errors * * @param {XMLHttpRequest} xhr: XMLHttpRequest object + * @param {Number} [xhr.pollTimer] ID of the timeout to clear * - * @globals pollTimer, commits + * @globals commits */ function handleError(xhr) { errorInfo('Server error: ' + xhr.status + ' - ' + (xhr.statusText || 'Error contacting server')); - clearInterval(pollTimer); + if (typeof xhr.pollTimer === "number") { + clearTimeout(xhr.pollTimer); + delete xhr.pollTimer; + } commits = {}; // free memory } /** * Called after XMLHttpRequest finishes (loads) * - * @param {XMLHttpRequest} xhr: XMLHttpRequest object (unused) + * @param {XMLHttpRequest} xhr: XMLHttpRequest object + * @param {Number} [xhr.pollTimer] ID of the timeout to clear * - * @globals pollTimer, commits + * @globals commits */ function responseLoaded(xhr) { - clearInterval(pollTimer); + if (typeof xhr.pollTimer === "number") { + clearTimeout(xhr.pollTimer); + delete xhr.pollTimer; + } fixColorsAndGroups(); writeTimeInterval(); @@ -563,9 +568,13 @@ function responseLoaded(xhr) { * handler for XMLHttpRequest onreadystatechange event * @see startBlame * - * @globals xhr + * @param {XMLHttpRequest} xhr: XMLHttpRequest object + * @param {Number} xhr.prevDataLength: previous value of xhr.responseText.length + * @param {Number} xhr.nextReadPos: start of unread part of xhr.responseText + * @param {Number} [xhr.pollTimer] ID of the timeout (to reset or cancel) + * @param {Boolean} fromTimer: if handler was called from timer */ -function handleResponse() { +function handleResponse(xhr, fromTimer) { /* * xhr.readyState @@ -614,6 +623,19 @@ function handleResponse() { // did we finish work? if (xhr.readyState === 4) { responseLoaded(xhr); + return; + } + + // if we get from timer, we have to restart it + // otherwise onreadystatechange gives us partial response, timer not needed + if (fromTimer) { + setTimeout(function () { + handleResponse(xhr, true); + }, 1000); + + } else if (typeof xhr.pollTimer === "number") { + clearTimeout(xhr.pollTimer); + delete xhr.pollTimer; } } @@ -629,11 +651,11 @@ function handleResponse() { * Called from 'blame_incremental' view after loading table with * file contents, a base for blame view. * - * @globals xhr, t0, projectUrl, div_progress_bar, totalLines, pollTimer + * @globals t0, projectUrl, div_progress_bar, totalLines */ function startBlame(blamedataUrl, bUrl) { - xhr = createRequestObject(); + var xhr = createRequestObject(); if (!xhr) { errorInfo('ERROR: XMLHttpRequest not supported'); return; @@ -652,8 +674,9 @@ function startBlame(blamedataUrl, bUrl) { xhr.prevDataLength = -1; // used to detect if we have new data xhr.nextReadPos = 0; // where unread part of response starts - xhr.onreadystatechange = handleResponse; - //xhr.onreadystatechange = function () { handleResponse(xhr); }; + xhr.onreadystatechange = function () { + handleResponse(xhr, false); + }; xhr.open('GET', blamedataUrl); xhr.setRequestHeader('Accept', 'text/plain'); @@ -661,7 +684,9 @@ function startBlame(blamedataUrl, bUrl) { // not all browsers call onreadystatechange event on each server flush // poll response using timer every second to handle this issue - pollTimer = setInterval(xhr.onreadystatechange, 1000); + xhr.pollTimer = setTimeout(function () { + handleResponse(xhr, true); + }, 1000); } /* end of blame_incremental.js */ -- cgit v1.2.3