Fixes #1804, Fix mysterious hang with citeproc-js 1.0.125
Re-merge citeproc-js 1.0.125 and use an alternative, slightly less efficient mechanism for reading from the integration pipe that shouldn't run into problems with garbage collection. Instead of trying to read from the pipe directly, we now spawn sh and cat to read from the pipe and send the output to a temporary file. We then read the temporary file. Because we don't block a JS thread, there are no GC issues. This works for Fx 4 as well, so I'm removing that code. I don't know whether there were potential GC issues in Fx 4, but if there were, I don't want to know about them.
This commit is contained in:
parent
54cbf39aac
commit
7f9b29f751
|
@ -1752,7 +1752,7 @@ CSL.DateParser = function (txt) {
|
|||
};
|
||||
CSL.Engine = function (sys, style, lang, forceLang) {
|
||||
var attrs, langspec, localexml, locale;
|
||||
this.processor_version = "1.0.124";
|
||||
this.processor_version = "1.0.125";
|
||||
this.csl_version = "1.0";
|
||||
this.sys = sys;
|
||||
this.sys.xml = new CSL.System.Xml.Parsing();
|
||||
|
@ -4464,14 +4464,6 @@ CSL.Node.names = {
|
|||
} else {
|
||||
frontnames = [];
|
||||
}
|
||||
if (tnamesets.length > 0 && tnamesets.slice(-1)[0].species === "org") {
|
||||
tnamesets[0].organization_first = true;
|
||||
tnamesets.slice(-1)[0].organization_last = true;
|
||||
if (frontnames.length) {
|
||||
frontnames[0].free_agent_start = true;
|
||||
tnamesets.slice(-1)[0].free_agent_end = true;
|
||||
}
|
||||
}
|
||||
if (frontnames.length === 0) {
|
||||
if (tnamesets.length > 1) {
|
||||
if (tnamesets[0].species === "pers") {
|
||||
|
@ -4519,15 +4511,9 @@ CSL.Node.names = {
|
|||
}
|
||||
}
|
||||
}
|
||||
namesets = namesets.slice(0, 1);
|
||||
if (namesets.length) {
|
||||
if (namesets[0].species === "pers") {
|
||||
namesets[0].organization_first = false;
|
||||
namesets[0].after_people = false;
|
||||
namesets[0].free_agent_start = false;
|
||||
namesets[0].free_agent_end = false;
|
||||
} else {
|
||||
namesets[0].organization_last = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4579,32 +4565,65 @@ CSL.Node.names = {
|
|||
}
|
||||
cutinfo = state.tmp.names_cut;
|
||||
if (namesets[0].species === "pers") {
|
||||
if (state.tmp.cut_var) {
|
||||
namesets[0].names = namesets[0].names.slice(cutinfo.counts[state.tmp.cut_var]);
|
||||
}
|
||||
if (namesets[0].names.length === 0) {
|
||||
if (namesets[0].free_agent_start) {
|
||||
namesets[1].free_agent_start = true;
|
||||
}
|
||||
if (namesets[0].organization_first) {
|
||||
namesets[1].organization_first = true;
|
||||
}
|
||||
namesets = namesets.slice(1);
|
||||
}
|
||||
} else {
|
||||
namesets = namesets.slice(0, 1);
|
||||
if (namesets[0].organization_first) {
|
||||
namesets[0].organization_last = true;
|
||||
}
|
||||
}
|
||||
if (state.tmp.cut_var) {
|
||||
namesets[0].names = namesets[0].names.slice(cutinfo.counts[state.tmp.cut_var]);
|
||||
}
|
||||
if (namesets[0].names.length === 0) {
|
||||
namesets = namesets.slice(1);
|
||||
}
|
||||
}
|
||||
if (state.tmp.cut_var && cutinfo.used === state.tmp.cut_var) {
|
||||
llen = cutinfo.variable[state.tmp.cut_var].length - 1;
|
||||
for (ppos = llen; ppos > -1; ppos += -1) {
|
||||
obj = cutinfo.variable[state.tmp.cut_var][ppos];
|
||||
obj[0].blobs = obj[0].blobs.slice(0, obj[1]).concat(obj[0].blobs.slice(obj[1] + 1));
|
||||
}
|
||||
llen = cutinfo.variable[state.tmp.cut_var].length - 1;
|
||||
for (ppos = llen; ppos > -1; ppos += -1) {
|
||||
obj = cutinfo.variable[state.tmp.cut_var][ppos];
|
||||
obj[0].blobs = obj[0].blobs.slice(0, obj[1]).concat(obj[0].blobs.slice(obj[1] + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!state.output.getToken("and-org")) {
|
||||
state.output.addToken("and-org");
|
||||
}
|
||||
var prefix_single_org = " ";
|
||||
var prefix_multiple_org = ", ";
|
||||
var and_org = state.getTerm("and", "long", 0);
|
||||
var offset = 0;
|
||||
if (namesets.length > 1 && namesets[1].after_people) {
|
||||
var offset = 1
|
||||
}
|
||||
var numnamesets = 0;
|
||||
for (i = offset, ilen = namesets.length; i < ilen; i += 1) {
|
||||
if (namesets[i].species === 'org') {
|
||||
if (i > 0 && namesets[i - 1].species === 'pers' && !namesets[i].after_people) {
|
||||
namesets[i - 1].organization_first = true;
|
||||
} else {
|
||||
namesets [i].organization_first = true;
|
||||
}
|
||||
namesets[i].organization_last = true;
|
||||
numnamesets += 1;
|
||||
}
|
||||
}
|
||||
var namesetcount = 0;
|
||||
for (i = offset, ilen = namesets.length; i < ilen; i += 1) {
|
||||
if (namesets[i].species === 'org' && numnamesets > 1) {
|
||||
if ((i - offset) > 0 && numnamesets === (namesetcount + 1)) {
|
||||
if (namesets[i - 1].species === 'pers') {
|
||||
namesets[i - 2].institutions_and_join = true;
|
||||
} else {
|
||||
namesets[i - 1].institutions_and_join = true;
|
||||
}
|
||||
}
|
||||
namesetcount += 1;
|
||||
}
|
||||
}
|
||||
if (numnamesets > 1) {
|
||||
state.output.getToken("and-org").strings.prefix = prefix_single_org;
|
||||
if (numnamesets > 2) {
|
||||
namesets[offset].institutions_penultimate_group_start = true;
|
||||
namesets[namesets.length - 2].institutions_penultimate_group_end = true;
|
||||
state.output.getToken("and-org").strings.prefix = prefix_multiple_org;
|
||||
}
|
||||
state.output.getToken("and-org").strings.suffix = " ";
|
||||
}
|
||||
if (!state.output.getToken("institution")) {
|
||||
state.output.addToken("institution");
|
||||
}
|
||||
|
@ -4654,12 +4673,6 @@ CSL.Node.names = {
|
|||
state.output.getToken("and-pers").strings["prefix-single"] = " ";
|
||||
state.output.getToken("and-pers").strings["prefix-multiple"] = ", ";
|
||||
and_pers = state.getTerm("and", "long", 0);
|
||||
if (!state.output.getToken("and-org")) {
|
||||
state.output.addToken("and-org");
|
||||
}
|
||||
state.output.getToken("and-org").strings["prefix-single"] = " ";
|
||||
state.output.getToken("and-org").strings["prefix-multiple"] = ", ";
|
||||
and_org = state.getTerm("and", "long", 0);
|
||||
state.output.addToken("with");
|
||||
state.output.getToken("with").strings.prefix = ", ";
|
||||
state.output.getToken("with").strings.suffix = " ";
|
||||
|
@ -4862,7 +4875,10 @@ CSL.Node.names = {
|
|||
state.output.openLevel("trailing-names", state.tmp.cut_var);
|
||||
}
|
||||
if (nameset.after_people) {
|
||||
state.output.append("with", "with");
|
||||
state.output.append(with_term, "with");
|
||||
}
|
||||
if (nameset.institutions_penultimate_group_start) {
|
||||
state.output.openLevel("inner");
|
||||
}
|
||||
if (nameset.organization_first) {
|
||||
state.output.openLevel("institution-outer");
|
||||
|
@ -4894,6 +4910,9 @@ CSL.Node.names = {
|
|||
state.output.closeLevel("trailing-names");
|
||||
}
|
||||
state.output.closeLevel("institution-outer");
|
||||
if (nameset.institutions_penultimate_group_end) {
|
||||
state.output.closeLevel("inner");
|
||||
}
|
||||
} else {
|
||||
if (nameset.trailers1b_end) {
|
||||
state.output.closeLevel("trailing-names");
|
||||
|
@ -4901,6 +4920,9 @@ CSL.Node.names = {
|
|||
state.output.closeLevel("inner");
|
||||
state.output.openLevel("inner");
|
||||
}
|
||||
if (nameset.institutions_and_join) {
|
||||
state.output.append(and_org, "and-org");
|
||||
}
|
||||
}
|
||||
if (nameset.trailers3_end) {
|
||||
state.output.closeLevel("trailing-names");
|
||||
|
|
|
@ -35,6 +35,9 @@ const INTEGRATION_MIN_VERSION = "3.1a0";
|
|||
|
||||
Zotero.Integration = new function() {
|
||||
var _fifoFile = null;
|
||||
var _tmpFile = null;
|
||||
var _shCmd = null;
|
||||
var _shProc = null;
|
||||
var _osascriptFile;
|
||||
var _inProgress = false;
|
||||
var _integrationVersionsOK = null;
|
||||
|
@ -113,8 +116,7 @@ Zotero.Integration = new function() {
|
|||
|
||||
// try to initialize pipe
|
||||
try {
|
||||
var pipeInitialized = (Zotero.isFx4 ? _initializeIntegrationPipeFx4(_fifoFile) :
|
||||
_initializeIntegrationPipeFx36(_fifoFile));
|
||||
var pipeInitialized = _initializeIntegration();
|
||||
} catch(e) {
|
||||
Components.utils.reportError(e);
|
||||
}
|
||||
|
@ -181,79 +183,45 @@ Zotero.Integration = new function() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Initializes the Zotero Integration Pipe in Firefox 4+
|
||||
* Reads from the temp file set up to handle integration pipe and executes the appropriate
|
||||
* integration command
|
||||
*/
|
||||
function _initializeIntegrationPipeFx4(_fifoFile) {
|
||||
// ensure Firefox 4.0b9 or later, since earlier versions do not support ChromeWorkers
|
||||
// from XPCOM
|
||||
var verComp = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
|
||||
.getService(Components.interfaces.nsIVersionComparator);
|
||||
var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
|
||||
getService(Components.interfaces.nsIXULAppInfo);
|
||||
if(verComp.compare("2.0b9pre", appInfo.version) > 0) {
|
||||
Components.utils.reportError("Zotero word processor integration requires "+
|
||||
"Firefox 4.0b9 or later. Please update to the latest Firefox 4.0 beta.");
|
||||
return false;
|
||||
} else {
|
||||
Components.utils.import("resource://gre/modules/ctypes.jsm");
|
||||
|
||||
// get possible names for libc
|
||||
if(Zotero.isMac) {
|
||||
var possibleLibcs = ["/usr/lib/libc.dylib"];
|
||||
} else {
|
||||
var possibleLibcs = [
|
||||
"libc.so.6",
|
||||
"libc.so.6.1",
|
||||
"libc.so"
|
||||
];
|
||||
}
|
||||
|
||||
// try all possibilities
|
||||
while(possibleLibcs.length) {
|
||||
var libc = possibleLibcs.shift();
|
||||
try {
|
||||
var lib = ctypes.open(libc);
|
||||
break;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
// throw appropriate error on failure
|
||||
if(!lib) {
|
||||
throw "libc could not be loaded. Please post on the Zotero Forums so we can add "+
|
||||
"support for your operating system.";
|
||||
}
|
||||
|
||||
// int mkfifo(const char *path, mode_t mode);
|
||||
var mkfifo = lib.declare("mkfifo", ctypes.default_abi, ctypes.int, ctypes.char.ptr, ctypes.unsigned_int);
|
||||
|
||||
// make pipe
|
||||
var ret = mkfifo(_fifoFile.path, 0600);
|
||||
if(!_fifoFile.exists()) return false;
|
||||
lib.close();
|
||||
|
||||
// set up worker
|
||||
var worker = Components.classes["@mozilla.org/threads/workerfactory;1"]
|
||||
.createInstance(Components.interfaces.nsIWorkerFactory)
|
||||
.newChromeWorker("chrome://zotero/content/xpcom/integration_worker.js");
|
||||
worker.onmessage = function(event) {
|
||||
if(event.data[0] == "Exception") {
|
||||
throw event.data[1];
|
||||
} else if(event.data[0] == "Debug") {
|
||||
Zotero.debug(event.data[1]);
|
||||
} else {
|
||||
Zotero.Integration.execCommand(event.data[0], event.data[1], event.data[2]);
|
||||
var _integrationPipeObserver = {"observe":function() {
|
||||
var string = Zotero.File.getContents(_tmpFile);
|
||||
|
||||
if(string != "") {
|
||||
var parts = string.match(/^([^ \n]*) ([^ \n]*)(?: ([^\n]*))?\n?$/);
|
||||
if(parts) {
|
||||
var agent = parts[1].toString();
|
||||
var cmd = parts[2].toString();
|
||||
var document = parts[3] ? parts[3].toString() : null;
|
||||
|
||||
// remove temp file and halt reading on shutdown
|
||||
if(agent === "Zotero" && cmd === "shutdown") {
|
||||
_tmpFile.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
Zotero.Integration.execCommand(agent, cmd, document);
|
||||
} else {
|
||||
Components.utils.reportError("Integration Worker: Invalid input received: "+string);
|
||||
}
|
||||
worker.postMessage({"path":_fifoFile.path, "libc":libc});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_shProc.runAsync(_shCmd, _shCmd.length, _integrationPipeObserver);
|
||||
}};
|
||||
|
||||
/**
|
||||
* Initializes the Zotero Integration Pipe in Firefox 3.6
|
||||
*/
|
||||
function _initializeIntegrationPipeFx36(_fifoFile) {
|
||||
function _initializeIntegration() {
|
||||
// make a tmp file
|
||||
_tmpFile = Components.classes["@mozilla.org/file/directory_service;1"].
|
||||
getService(Components.interfaces.nsIProperties).
|
||||
get("TmpD", Components.interfaces.nsIFile);
|
||||
_tmpFile.append("zoteroIntegrationTmp");
|
||||
_tmpFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
|
||||
|
||||
// make a new pipe
|
||||
var mkfifo = Components.classes["@mozilla.org/file/local;1"].
|
||||
createInstance(Components.interfaces.nsILocalFile);
|
||||
|
@ -261,57 +229,36 @@ Zotero.Integration = new function() {
|
|||
if(!mkfifo.exists()) mkfifo.initWithPath("/bin/mkfifo");
|
||||
if(!mkfifo.exists()) mkfifo.initWithPath("/usr/local/bin/mkfifo");
|
||||
|
||||
if(mkfifo.exists()) {
|
||||
var main = Components.classes["@mozilla.org/thread-manager;1"].getService().mainThread;
|
||||
var background = Components.classes["@mozilla.org/thread-manager;1"].getService().newThread(0);
|
||||
// get sh
|
||||
var sh = Components.classes["@mozilla.org/file/local;1"].
|
||||
createInstance(Components.interfaces.nsILocalFile);
|
||||
sh.initWithPath("/bin/sh");
|
||||
|
||||
if(mkfifo.exists() && sh.exists()) {
|
||||
// create named pipe
|
||||
var proc = Components.classes["@mozilla.org/process/util;1"].
|
||||
createInstance(Components.interfaces.nsIProcess);
|
||||
proc.init(mkfifo);
|
||||
proc.run(true, [_fifoFile.path], 1);
|
||||
|
||||
function mainThread(agent, cmd, doc) {
|
||||
this.agent = agent;
|
||||
this.cmd = cmd;
|
||||
this.document = doc;
|
||||
}
|
||||
mainThread.prototype.run = function() {
|
||||
Zotero.Integration.execCommand(this.agent, this.cmd, this.document);
|
||||
}
|
||||
|
||||
function fifoThread() {}
|
||||
fifoThread.prototype.run = function() {
|
||||
var proc = Components.classes["@mozilla.org/process/util;1"].
|
||||
createInstance(Components.interfaces.nsIProcess);
|
||||
proc.init(mkfifo);
|
||||
proc.run(true, [_fifoFile.path], 1);
|
||||
|
||||
if(!_fifoFile.exists()) Zotero.debug("Could not initialize Zotero integration pipe");
|
||||
|
||||
var fifoStream = Components.classes["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Components.interfaces.nsIFileInputStream);
|
||||
var line = {};
|
||||
while(true) {
|
||||
fifoStream.QueryInterface(Components.interfaces.nsIFileInputStream);
|
||||
fifoStream.init(_fifoFile, -1, 0, 0);
|
||||
fifoStream.QueryInterface(Components.interfaces.nsILineInputStream);
|
||||
fifoStream.readLine(line);
|
||||
fifoStream.close();
|
||||
if(_fifoFile.exists()) {
|
||||
// begin reading from named pipe
|
||||
_shCmd = ["-c", "cat '"+_fifoFile.path.replace("'", "'\\''")+"' > '"+
|
||||
_tmpFile.path.replace("'", "'\\''")+"'"];
|
||||
Zotero.debug("Calling sh "+_shCmd.join(" "));
|
||||
|
||||
var parts = line.value.split(" ");
|
||||
var agent = parts[0];
|
||||
var cmd = parts[1];
|
||||
var document = parts.length >= 3 ? line.value.substr(agent.length+cmd.length+2) : null;
|
||||
if(agent == "Zotero" && cmd == "shutdown") return;
|
||||
main.dispatch(new mainThread(agent, cmd, document), background.DISPATCH_NORMAL);
|
||||
}
|
||||
_shProc = Components.classes["@mozilla.org/process/util;1"].
|
||||
createInstance(Components.interfaces.nsIProcess);
|
||||
_shProc.init(sh);
|
||||
_shProc.runAsync(_shCmd, _shCmd.length, _integrationPipeObserver);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fifoThread.prototype.QueryInterface = mainThread.prototype.QueryInterface = function(iid) {
|
||||
if (iid.equals(Components.interfaces.nsIRunnable) ||
|
||||
iid.equals(Components.interfaces.nsISupports)) return this;
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
||||
background.dispatch(new fifoThread(), background.DISPATCH_NORMAL);
|
||||
return true;
|
||||
Components.utils.reportError("Zotero: mkfifo failed -- not initializing integration pipe");
|
||||
return false;
|
||||
} else {
|
||||
Zotero.debug("mkfifo not found -- not initializing integration pipe");
|
||||
Components.utils.reportError("Zotero: mkfifo or sh not found -- not initializing integration pipe");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2009 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
onmessage = function(event) {
|
||||
var path = event.data.path;
|
||||
|
||||
// ctypes declarations follow
|
||||
var lib = ctypes.open(event.data.libc);
|
||||
|
||||
// int open(const char *path, int oflag, ...);
|
||||
var open = lib.declare("open", ctypes.default_abi, ctypes.int, ctypes.char.ptr, ctypes.int);
|
||||
|
||||
// ssize_t read(int fildes, void *buf, size_t nbyte);
|
||||
var read = lib.declare("read", ctypes.default_abi, ctypes.ssize_t, ctypes.int,
|
||||
ctypes.char.ptr, ctypes.size_t);
|
||||
|
||||
// int close(int fildes);
|
||||
var close = lib.declare("close", ctypes.default_abi, ctypes.int, ctypes.int);
|
||||
|
||||
// define buffer for reading from fifo
|
||||
const BUFFER_SIZE = 4096;
|
||||
|
||||
while(true) {
|
||||
var buf = ctypes.char.array(BUFFER_SIZE)("");
|
||||
|
||||
// open fifo (this will block until something writes to it)
|
||||
var fd = open(path, 0);
|
||||
|
||||
// read from fifo and close it
|
||||
read(fd, buf, BUFFER_SIZE-1);
|
||||
close(fd);
|
||||
|
||||
// extract message
|
||||
var string = buf.readString();
|
||||
var parts = string.match(/^([^ \n]*) ([^ \n]*)(?: ([^\n]*))?\n?$/);
|
||||
if(!parts) {
|
||||
postMessage(["Exception", "Integration Worker: Invalid input received: "+string]);
|
||||
continue;
|
||||
}
|
||||
var agent = parts[1].toString();
|
||||
var cmd = parts[2].toString();
|
||||
var document = parts[3] ? parts[3] : null;
|
||||
if(agent == "Zotero" && cmd == "shutdown") {
|
||||
postMessage(["Debug", "Integration Worker: Shutting down"]);
|
||||
lib.close();
|
||||
return;
|
||||
}
|
||||
postMessage([agent, cmd, document]);
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user