1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/b2g/simulator/packages/subprocess/lib/subprocess_worker_unix.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,248 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + */ 1.8 + 1.9 +/* 1.10 + * ChromeWorker Object subprocess.jsm on Unix-like systems (Linux, Mac OS X, ...) 1.11 + * to process stdin/stdout/stderr on separate threads. 1.12 + * 1.13 + */ 1.14 + 1.15 +// Being a ChromeWorker object, implicitly uses the following: 1.16 +// Components.utils.import("resource://gre/modules/ctypes.jsm"); 1.17 + 1.18 +'use strict'; 1.19 + 1.20 +const BufferSize = 1024; 1.21 + 1.22 +var libc = null; 1.23 +var libcFunc = {}; 1.24 + 1.25 + 1.26 +/* 1.27 + struct pollfd { 1.28 + int fd; // file descriptor 1.29 + short events; // events to look for 1.30 + short revents; // events returned 1.31 + }; 1.32 +*/ 1.33 + 1.34 +var pollfd = new ctypes.StructType("pollfd", 1.35 + [ {'fd': ctypes.int}, 1.36 + {'events': ctypes.short}, 1.37 + {'revents': ctypes.short} 1.38 + ]); 1.39 + 1.40 +var WriteBuffer = ctypes.uint8_t.array(BufferSize); 1.41 +var ReadBuffer = ctypes.char.array(BufferSize); 1.42 + 1.43 + 1.44 +const POLLIN = 0x0001; 1.45 +const POLLOUT = 0x0004; 1.46 + 1.47 +const POLLERR = 0x0008; // some poll error occurred 1.48 +const POLLHUP = 0x0010; // file descriptor was "hung up" 1.49 +const POLLNVAL = 0x0020; // requested events "invalid" 1.50 + 1.51 +const WNOHANG = 0x01; 1.52 + 1.53 +const pid_t = ctypes.int32_t; 1.54 + 1.55 +const INDEFINITE = -1; 1.56 +const NOWAIT = 0; 1.57 +const WAITTIME = 200 // wait time for poll() in ms 1.58 + 1.59 +function initLibc(libName) { 1.60 + postMessage({msg: "debug", data: "initialising library with "+ libName}); 1.61 + 1.62 + libc = ctypes.open(libName); 1.63 + 1.64 + libcFunc.pollFds = pollfd.array(1); 1.65 + 1.66 + // int poll(struct pollfd fds[], nfds_t nfds, int timeout); 1.67 + libcFunc.poll = libc.declare("poll", 1.68 + ctypes.default_abi, 1.69 + ctypes.int, 1.70 + libcFunc.pollFds, 1.71 + ctypes.unsigned_int, 1.72 + ctypes.int); 1.73 + 1.74 + //ssize_t write(int fd, const void *buf, size_t count); 1.75 + // NOTE: buf is declared as array of unsigned int8 instead of char to avoid 1.76 + // implicit charset conversion 1.77 + libcFunc.write = libc.declare("write", 1.78 + ctypes.default_abi, 1.79 + ctypes.int, 1.80 + ctypes.int, 1.81 + WriteBuffer, 1.82 + ctypes.int); 1.83 + 1.84 + //int read(int fd, void *buf, size_t count); 1.85 + libcFunc.read = libc.declare("read", 1.86 + ctypes.default_abi, 1.87 + ctypes.int, 1.88 + ctypes.int, 1.89 + ReadBuffer, 1.90 + ctypes.int); 1.91 + 1.92 + //int pipe(int pipefd[2]); 1.93 + libcFunc.pipefd = ctypes.int.array(2); 1.94 + 1.95 + //int close(int fd); 1.96 + libcFunc.close = libc.declare("close", 1.97 + ctypes.default_abi, 1.98 + ctypes.int, 1.99 + ctypes.int); 1.100 + 1.101 + //pid_t waitpid(pid_t pid, int *status, int options); 1.102 + libcFunc.waitpid = libc.declare("waitpid", 1.103 + ctypes.default_abi, 1.104 + pid_t, 1.105 + pid_t, 1.106 + ctypes.int.ptr, 1.107 + ctypes.int); 1.108 +} 1.109 + 1.110 +function closePipe(pipe) { 1.111 + libcFunc.close(pipe); 1.112 +} 1.113 + 1.114 +function writePipe(pipe, data) { 1.115 + 1.116 + postMessage({msg: "debug", data: "trying to write to "+pipe}); 1.117 + 1.118 + let numChunks = Math.floor(data.length / BufferSize); 1.119 + let pData = new WriteBuffer(); 1.120 + 1.121 + for (var chunk = 0; chunk <= numChunks; chunk ++) { 1.122 + let numBytes = chunk < numChunks ? BufferSize : data.length - chunk * BufferSize; 1.123 + for (var i=0; i < numBytes; i++) { 1.124 + pData[i] = data.charCodeAt(chunk * BufferSize + i) % 256; 1.125 + } 1.126 + 1.127 + let bytesWritten = libcFunc.write(pipe, pData, numBytes); 1.128 + if (bytesWritten != numBytes) { 1.129 + closePipe(); 1.130 + libc.close(); 1.131 + postMessage({ msg: "error", data: "error: wrote "+bytesWritten+" instead of "+numBytes+" bytes"}); 1.132 + close(); 1.133 + } 1.134 + } 1.135 + postMessage({msg: "info", data: "wrote "+data.length+" bytes of data"}); 1.136 +} 1.137 + 1.138 + 1.139 +function readString(data, length, charset) { 1.140 + var string = '', bytes = []; 1.141 + for(var i = 0;i < length; i++) { 1.142 + if(data[i] == 0 && charset != "null") // stop on NULL character for non-binary data 1.143 + break; 1.144 + 1.145 + bytes.push(data[i]); 1.146 + } 1.147 + 1.148 + return bytes; 1.149 +} 1.150 + 1.151 +function readPipe(pipe, charset, pid) { 1.152 + var p = new libcFunc.pollFds; 1.153 + p[0].fd = pipe; 1.154 + p[0].events = POLLIN | POLLERR | POLLHUP; 1.155 + p[0].revents = 0; 1.156 + var pollTimeout = WAITTIME; 1.157 + var exitCode = -1; 1.158 + var readCount = 0; 1.159 + var result, status = ctypes.int(); 1.160 + result = 0; 1.161 + 1.162 + 1.163 + const i=0; 1.164 + while (true) { 1.165 + if (result == 0) { 1.166 + result = libcFunc.waitpid(pid, status.address(), WNOHANG); 1.167 + if (result > 0) { 1.168 + pollTimeout = NOWAIT; 1.169 + exitCode = parseInt(status.value); 1.170 + postMessage({msg: "debug", data: "waitpid signaled subprocess stop, exitcode="+status.value }); 1.171 + } 1.172 + } 1.173 + var r = libcFunc.poll(p, 1, pollTimeout); 1.174 + if (r > 0) { 1.175 + if (p[i].revents & POLLIN) { 1.176 + postMessage({msg: "debug", data: "reading next chunk"}); 1.177 + readCount = readPolledFd(p[i].fd, charset); 1.178 + if (readCount == 0) break; 1.179 + } 1.180 + 1.181 + if (p[i].revents & POLLHUP) { 1.182 + postMessage({msg: "debug", data: "poll returned HUP"}); 1.183 + break; 1.184 + } 1.185 + else if (p[i].revents & POLLERR) { 1.186 + postMessage({msg: "error", data: "poll returned error"}); 1.187 + break; 1.188 + } 1.189 + else if (p[i].revents != POLLIN) { 1.190 + postMessage({msg: "error", data: "poll returned "+p[i]}); 1.191 + break; 1.192 + } 1.193 + } 1.194 + else 1.195 + if (pollTimeout == 0 || r < 0) break; 1.196 + } 1.197 + 1.198 + // continue reading until the buffer is empty 1.199 + while (readCount > 0) { 1.200 + readCount = readPolledFd(pipe, charset); 1.201 + } 1.202 + 1.203 + libcFunc.close(pipe); 1.204 + postMessage({msg: "done", data: exitCode }); 1.205 + libc.close(); 1.206 + close(); 1.207 +} 1.208 + 1.209 +function readPolledFd(pipe, charset) { 1.210 + var line = new ReadBuffer(); 1.211 + var r = libcFunc.read(pipe, line, BufferSize); 1.212 + 1.213 + if (r > 0) { 1.214 + var c = readString(line, r, charset); 1.215 + postMessage({msg: "data", data: c, count: c.length}); 1.216 + } 1.217 + return r; 1.218 +} 1.219 + 1.220 +onmessage = function (event) { 1.221 + switch (event.data.msg) { 1.222 + case "init": 1.223 + initLibc(event.data.libc); 1.224 + break; 1.225 + case "read": 1.226 + initLibc(event.data.libc); 1.227 + readPipe(event.data.pipe, event.data.charset, event.data.pid); 1.228 + break; 1.229 + case "write": 1.230 + // data contents: 1.231 + // msg: 'write' 1.232 + // data: the data (string) to write 1.233 + // pipe: ptr to pipe 1.234 + writePipe(event.data.pipe, event.data.data); 1.235 + postMessage({msg: "info", data: "WriteOK"}); 1.236 + break; 1.237 + case "close": 1.238 + postMessage({msg: "debug", data: "closing stdin\n"}); 1.239 + 1.240 + closePipe(event.data.pipe); 1.241 + postMessage({msg: "info", data: "ClosedOK"}); 1.242 + break; 1.243 + case "stop": 1.244 + libc.close(); // do not use libc after this point 1.245 + close(); 1.246 + break; 1.247 + default: 1.248 + throw("error: Unknown command"+event.data.msg+"\n"); 1.249 + } 1.250 + return; 1.251 +};