 Issue 5731880649359360:
  Don't delay Firefox startup  (Closed)
    
  
    Issue 5731880649359360:
  Don't delay Firefox startup  (Closed) 
  | Index: lib/io.js | 
| =================================================================== | 
| --- a/lib/io.js | 
| +++ b/lib/io.js | 
| @@ -65,84 +65,131 @@ let IO = exports.IO = | 
| * Reads strings from a file asynchronously, calls listener.process() with | 
| * each line read and with a null parameter once the read operation is done. | 
| * The callback will be called when the operation is done. | 
| */ | 
| readFromFile: function(/**nsIFile|nsIURI*/ file, /**Boolean*/ decode, /**Object*/ listener, /**Function*/ callback, /**String*/ timeLineID) | 
| { | 
| try | 
| { | 
| + let processing = false; | 
| let buffer = ""; | 
| + let loadEvent = null; | 
| + let errorEvent = null; | 
| let uri = file instanceof Ci.nsIFile ? Services.io.newFileURI(file) : file; | 
| let request = new XMLHttpRequest(); | 
| request.mozBackgroundRequest = true; | 
| request.open("GET", uri.spec); | 
| request.responseType = "moz-chunked-text"; | 
| request.overrideMimeType("text/plain" + (decode ? "? charset=utf-8" : "")); | 
| - request.addEventListener("progress", function(event) | 
| + let onProgress = function(event) | 
| { | 
| if (timeLineID) | 
| { | 
| TimeLine.asyncStart(timeLineID); | 
| } | 
| let data = event.target.response; | 
| - let index = Math.max(data.lastIndexOf("\n"), data.lastIndexOf("\r")); | 
| + let index = (processing ? -1 : Math.max(data.lastIndexOf("\n"), data.lastIndexOf("\r"))); | 
| 
Felix Dahlke
2014/03/14 16:10:45
This doesn't really seem reentrant to me. What if
 
Wladimir Palant
2014/03/14 18:04:51
JavaScript is a single-threaded language, reentran
 
Felix Dahlke
2014/03/14 21:32:49
Ah, thought this was actually proper multithreadin
 | 
| if (index >= 0) | 
| { | 
| - let oldBuffer = buffer; | 
| - buffer = data.substr(index + 1); | 
| - data = data.substr(0, index + 1); | 
| - let lines = data.split(/[\r\n]+/); | 
| - lines.pop(); | 
| - lines[0] = oldBuffer + lines[0]; | 
| - for (let i = 0; i < lines.length; i++) | 
| - listener.process(lines[i]); | 
| + // Protect against reentrance in case the listener processes events. | 
| 
Felix Dahlke
2014/03/14 16:10:45
"processes multiple events" I suppose?
 
Wladimir Palant
2014/03/14 18:04:51
No, "processes events" = "spins the event loop"
 | 
| + processing = true; | 
| + try | 
| + { | 
| + let oldBuffer = buffer; | 
| + buffer = data.substr(index + 1); | 
| 
Felix Dahlke
2014/03/14 16:10:45
Is reference assignment atomic in Gecko? Otherwise
 
Wladimir Palant
2014/03/14 18:04:51
As I said, the only place where we might get reent
 | 
| + data = data.substr(0, index + 1); | 
| + let lines = data.split(/[\r\n]+/); | 
| + lines.pop(); | 
| + lines[0] = oldBuffer + lines[0]; | 
| + for (let i = 0; i < lines.length; i++) | 
| + listener.process(lines[i]); | 
| + } | 
| + finally | 
| + { | 
| + processing = false; | 
| + let e = { | 
| + target: {response: buffer} | 
| + }; | 
| + buffer = ""; | 
| + onProgress(e); | 
| + | 
| + if (loadEvent) | 
| + { | 
| + loadEvent = null; | 
| + onLoad(loadEvent); | 
| 
Felix Dahlke
2014/03/14 16:10:45
onLoad(null) doesn't seem to be what's desired her
 
Wladimir Palant
2014/03/14 18:04:51
True, we need to save the original event before nu
 | 
| + } | 
| + | 
| + if (errorEvent) | 
| + { | 
| + errorEvent = null; | 
| + onError(errorEvent); | 
| + } | 
| + } | 
| } | 
| else | 
| buffer += data; | 
| if (timeLineID) | 
| { | 
| TimeLine.asyncEnd(timeLineID); | 
| } | 
| - }, false); | 
| + }; | 
| - request.addEventListener("load", function(event) | 
| + let onLoad = function(event) | 
| { | 
| + if (processing) | 
| + { | 
| + // Still processing data, delay processing this event. | 
| + loadEvent = event; | 
| + return; | 
| + } | 
| + | 
| if (timeLineID) | 
| { | 
| TimeLine.asyncStart(timeLineID); | 
| } | 
| if (buffer !== "") | 
| listener.process(buffer); | 
| listener.process(null); | 
| if (timeLineID) | 
| { | 
| TimeLine.asyncEnd(timeLineID); | 
| TimeLine.asyncDone(timeLineID); | 
| } | 
| callback(null); | 
| - }, false); | 
| + }; | 
| - request.addEventListener("error", function() | 
| + let onError = function(event) | 
| { | 
| + if (processing) | 
| + { | 
| + // Still processing data, delay processing this event. | 
| + errorEvent = event; | 
| + return; | 
| + } | 
| + | 
| let e = Cc["@mozilla.org/js/xpc/Exception;1"].createInstance(Ci.nsIXPCException); | 
| e.initialize("File read operation failed", result, null, Components.stack, file, null); | 
| callback(e); | 
| if (timeLineID) | 
| { | 
| TimeLine.asyncDone(timeLineID); | 
| } | 
| - }, false); | 
| + }; | 
| + | 
| + request.addEventListener("progress", onProgress, false); | 
| + request.addEventListener("load", onLoad, false); | 
| + request.addEventListener("error", onError, false); | 
| request.send(null); | 
| } | 
| catch (e) | 
| { | 
| callback(e); | 
| } | 
| }, |