@sjcrh/proteinpaint-rust 2.192.0 → 2.195.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +0 -8
- package/index.js +2 -164
- package/package.json +1 -1
- package/src/gdcGRIN2.rs +0 -1261
- package/src/gdcmaf.rs +0 -318
package/Cargo.toml
CHANGED
|
@@ -75,10 +75,6 @@ path="src/sv.rs"
|
|
|
75
75
|
name="cluster"
|
|
76
76
|
path="src/cluster.rs"
|
|
77
77
|
|
|
78
|
-
[[bin]]
|
|
79
|
-
name="gdcmaf"
|
|
80
|
-
path="src/gdcmaf.rs"
|
|
81
|
-
|
|
82
78
|
[[bin]]
|
|
83
79
|
name="wilcoxon"
|
|
84
80
|
path="src/wilcoxon.rs"
|
|
@@ -103,10 +99,6 @@ path="src/readHDF5.rs"
|
|
|
103
99
|
name="validateHDF5"
|
|
104
100
|
path="src/validateHDF5.rs"
|
|
105
101
|
|
|
106
|
-
[[bin]]
|
|
107
|
-
name="gdcGRIN2"
|
|
108
|
-
path="src/gdcGRIN2.rs"
|
|
109
|
-
|
|
110
102
|
[[bin]]
|
|
111
103
|
name="cerno"
|
|
112
104
|
path="src/cerno.rs"
|
package/index.js
CHANGED
|
@@ -13,14 +13,11 @@ Standard output of the rust binary is returned.
|
|
|
13
13
|
// Import necessary modules
|
|
14
14
|
import fs from 'fs'
|
|
15
15
|
import path from 'path'
|
|
16
|
-
import { spawn
|
|
17
|
-
import { Readable
|
|
18
|
-
import { promisify } from 'util'
|
|
16
|
+
import { spawn } from 'child_process'
|
|
17
|
+
import { Readable } from 'stream'
|
|
19
18
|
|
|
20
19
|
const __dirname = import.meta.dirname // set __dirname for consistency with cjs code
|
|
21
20
|
|
|
22
|
-
const execPromise = promisify(exec)
|
|
23
|
-
|
|
24
21
|
// Check if rust binary directory exists and is not empty
|
|
25
22
|
const binaryDir = path.join(__dirname, '/target/release/')
|
|
26
23
|
if (!fs.existsSync(binaryDir)) throw `missing rust binary directory='${binaryDir}'`
|
|
@@ -82,162 +79,3 @@ export function run_rust(binfile, input_data, args = [], { signal } = {}) {
|
|
|
82
79
|
})
|
|
83
80
|
})
|
|
84
81
|
}
|
|
85
|
-
|
|
86
|
-
// May need to add an abort signal as argument like in run_rust above.
|
|
87
|
-
// Or it's likely not needed since a closed stream connection from a web browser
|
|
88
|
-
// will already trigger an error. `stream_rust()` was heavily tested manually
|
|
89
|
-
// while troubleshooting and fixing the idle rust processes the led to memory leaks
|
|
90
|
-
// as part of `/gdc/mafBuild` handler code, leave this code as-is for now.
|
|
91
|
-
export function stream_rust(binfile, input_data, emitJson) {
|
|
92
|
-
const binpath = path.join(__dirname, '/target/release/', binfile)
|
|
93
|
-
|
|
94
|
-
const ps = spawn(binpath)
|
|
95
|
-
const childStream = new Transform({
|
|
96
|
-
transform(chunk, encoding, callback) {
|
|
97
|
-
this.push(chunk)
|
|
98
|
-
callback()
|
|
99
|
-
}
|
|
100
|
-
})
|
|
101
|
-
// we only want to run this interval loop inside a container, not in dev/test CI
|
|
102
|
-
if (binfile == 'gdcmaf') trackByPid(ps.pid, binfile)
|
|
103
|
-
const stderr = []
|
|
104
|
-
try {
|
|
105
|
-
// from route handler -> input_data -> ps.stdin -> ps.stdout -> transformed stream -> express response.pipe()
|
|
106
|
-
Readable.from(input_data)
|
|
107
|
-
.pipe(ps.stdin)
|
|
108
|
-
.on('error', err => {
|
|
109
|
-
emitErrors({ error: `error piping input data to spawned ${binfile} process` })
|
|
110
|
-
})
|
|
111
|
-
} catch (error) {
|
|
112
|
-
console.log(`Error piping input_data into ${binfile}`, error)
|
|
113
|
-
return
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// uncomment to trigger childStream.destroy()
|
|
117
|
-
// setTimeout(() => { console.log(74, 'childStream.destroy()'); childStream.destroy();}, 1000)
|
|
118
|
-
// childStream.destroy() does not seem to trigger ps.stdout.pipe('...').on('error') callback,
|
|
119
|
-
// which is okay as long as the server doesn't crash and ps get's killed eventually
|
|
120
|
-
ps.stdout.pipe(childStream).on('error', err => console.log('ps.stdout.pipe(childStream) error', err))
|
|
121
|
-
|
|
122
|
-
ps.stderr.on('data', data => stderr.push(data))
|
|
123
|
-
|
|
124
|
-
ps.on('close', code => {
|
|
125
|
-
if (trackedPids.has(ps.pid)) trackedPids.delete(ps.pid)
|
|
126
|
-
if (stderr.length || killedPids.has(ps.pid) || code !== 0) {
|
|
127
|
-
emitErrors(null, ps.pid, code)
|
|
128
|
-
} else {
|
|
129
|
-
emitJson()
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
ps.on('error', err => {
|
|
133
|
-
if (trackedPids.has(ps.pid)) trackedPids.delete(ps.pid)
|
|
134
|
-
// console.log(74, `stream_rust().on('error')`, err)
|
|
135
|
-
emitErrors(null, ps.pid)
|
|
136
|
-
})
|
|
137
|
-
ps.on('SIGTERM', err => {
|
|
138
|
-
console.log(err)
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
function emitErrors(error, pid, code = 0) {
|
|
142
|
-
// concatenate stderr uint8arr into a string
|
|
143
|
-
let errors = stderr.join('').trim()
|
|
144
|
-
if (error) errors += `\n` + error
|
|
145
|
-
if (pid && killedPids.has(ps.pid) && !trackedPids.has(ps.pid)) {
|
|
146
|
-
errors += '\n' + JSON.stringify({ error: `server error: MAF file processing terminated (expired process)` })
|
|
147
|
-
killedPids.delete(pid)
|
|
148
|
-
} else if (pid && code !== 0) {
|
|
149
|
-
// may result from errors in spawned process code, or external signal (like `kill -9` in terminal)
|
|
150
|
-
errors += '\n' + JSON.stringify({ error: `server error: MAF file processing terminated (code=${code})` })
|
|
151
|
-
}
|
|
152
|
-
emitJson(errors)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// on('end') will duplicate ps.on('close') event above
|
|
156
|
-
// childStream.on('end', () => console.log(`childStream.on(end)`))
|
|
157
|
-
|
|
158
|
-
// this may duplicate ps.on('error'), unless the error happened within the transform
|
|
159
|
-
childStream.on('error', err => {
|
|
160
|
-
console.log('stream_rust childStream.on(error)', err)
|
|
161
|
-
try {
|
|
162
|
-
childStream.destroy(err)
|
|
163
|
-
} catch (e) {
|
|
164
|
-
console.log(e)
|
|
165
|
-
}
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
function endStream() {
|
|
169
|
-
try {
|
|
170
|
-
if (!childStream.writableEnded) {
|
|
171
|
-
console.log('trigger childStream.destroy() in endStream()')
|
|
172
|
-
childStream.destroy()
|
|
173
|
-
}
|
|
174
|
-
} catch (e) {
|
|
175
|
-
console.log('error triggering childStream.destroy()', e)
|
|
176
|
-
}
|
|
177
|
-
try {
|
|
178
|
-
if (!ps.killed) {
|
|
179
|
-
console.log('trigger ps.kill() in endStream()')
|
|
180
|
-
ps.kill()
|
|
181
|
-
}
|
|
182
|
-
if (trackedPids.has(ps.pid)) trackedPids.delete(ps.pid)
|
|
183
|
-
} catch (e) {
|
|
184
|
-
console.log('error triggering ps.kill()', e)
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return { rustStream: childStream, endStream }
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const trackedPids = new Map() // will be used to monitor expired processes
|
|
192
|
-
const killedPids = new Set() // will be used to detect killed processes, to help with error detection
|
|
193
|
-
const PSKILL_INTERVAL_MS = 30000 // every 30 seconds
|
|
194
|
-
let psKillInterval
|
|
195
|
-
|
|
196
|
-
// default maxElapsed = 5 * 60 * 1000 millisecond = 300000 or 5 minutes, change to 0 to test
|
|
197
|
-
// may allow configuration of maxElapsed by dataset/argument
|
|
198
|
-
function trackByPid(pid, name, maxElapsed = 300000) {
|
|
199
|
-
if (!pid) return
|
|
200
|
-
// only track by value (integer, string), not reference object
|
|
201
|
-
// NOTE: a reused/reassigned process.pid will be replaced by the most recent process
|
|
202
|
-
trackedPids.set(pid, { name, expires: Date.now() + maxElapsed })
|
|
203
|
-
if (!psKillInterval) psKillInterval = setInterval(killExpiredProcesses, PSKILL_INTERVAL_MS)
|
|
204
|
-
// uncomment below to test
|
|
205
|
-
// console.log([...trackedPids.entries()])
|
|
206
|
-
// if (maxElapsed < 10000) setTimeout(killExpiredProcesses, 1000) // uncomment for testing only
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
//
|
|
210
|
-
// Use one setInterval() to monitor >= 1 process,
|
|
211
|
-
// instead of a separate setTimeout() for each process.
|
|
212
|
-
// This is more reliable as setTimeout would use spawned ps.kill(),
|
|
213
|
-
// which may not exist when the timeout callback is executed and
|
|
214
|
-
// thus would require clearTimeout(closured_variable). Tracking by
|
|
215
|
-
// pid does not rely on a usable 'ps' variable to kill itself.
|
|
216
|
-
//
|
|
217
|
-
function killExpiredProcesses() {
|
|
218
|
-
//console.log(149, 'killExpiredProcesses()')
|
|
219
|
-
killedPids.clear()
|
|
220
|
-
const time = Date.now()
|
|
221
|
-
for (const [pid, info] of trackedPids.entries()) {
|
|
222
|
-
if (info.expires > time) continue
|
|
223
|
-
try {
|
|
224
|
-
// true if process exists
|
|
225
|
-
process.kill(pid, 0)
|
|
226
|
-
} catch (_) {
|
|
227
|
-
// no need to kill, but remove from tracking
|
|
228
|
-
trackedPids.delete(pid)
|
|
229
|
-
// prevent misleading logs of 'unable to kill ...'
|
|
230
|
-
continue
|
|
231
|
-
}
|
|
232
|
-
const label = `rust process ${info.name} (pid=${pid})`
|
|
233
|
-
try {
|
|
234
|
-
// detect if process exists before killing it
|
|
235
|
-
process.kill(pid, 'SIGTERM')
|
|
236
|
-
trackedPids.delete(pid)
|
|
237
|
-
killedPids.add(pid)
|
|
238
|
-
console.log(`killed ${label}`)
|
|
239
|
-
} catch (err) {
|
|
240
|
-
console.log(`unable to kill ${label}`, err)
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
package/package.json
CHANGED