Skip to content

Commit a98fa40

Browse files
authored
Merge pull request #631 from zeromq/gc-fixes
2 parents 6365733 + d1cae97 commit a98fa40

14 files changed

+213
-222
lines changed

.vscode/extensions.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"llvm-vs-code-extensions.vscode-clangd",
66
"xadillax.gyp",
77
"dbaeumer.vscode-eslint",
8-
"esbenp.prettier-vscode"
8+
"esbenp.prettier-vscode",
9+
"vadimcn.vscode-lldb"
910
]
1011
}

.vscode/launch.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"configurations": [
3+
{
4+
"name": "JS-Attach",
5+
"type": "node",
6+
"request": "attach",
7+
"port": 9229,
8+
"continueOnAttach": true,
9+
"autoAttachChildProcesses": true,
10+
"resolveSourceMapLocations": [
11+
"!**/node_modules/**",
12+
"!**/.vscode/extensions/hbenl.vscode-mocha-test-adapter-*/**"
13+
],
14+
"skipFiles": [
15+
"<node_internals>/**"
16+
],
17+
},
18+
{
19+
"type": "lldb",
20+
"request": "launch",
21+
"name": "Native-Launch",
22+
"preLaunchTask": "clean_build_debug",
23+
"program": "node",
24+
"suppressMultipleSessionWarning": true,
25+
"sourceLanguages": [
26+
"cpp"
27+
],
28+
"args": [
29+
"--inspect-brk=9229",
30+
"--expose-gc",
31+
"-r",
32+
"ts-node/register",
33+
"${workspaceFolder}/test/debug.ts"
34+
],
35+
}
36+
],
37+
"compounds": [
38+
{
39+
"name": "Node-Launch",
40+
"configurations": [
41+
"Native-Launch",
42+
"JS-Attach",
43+
]
44+
}
45+
]
46+
}

.vscode/settings.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{
22
"mochaExplorer.parallel": true,
3-
"mochaExplorer.globImplementation": "vscode"
3+
"mochaExplorer.globImplementation": "vscode",
4+
"mochaExplorer.nodeArgv": [
5+
"--expose-gc"
6+
],
7+
"mochaExplorer.debuggerConfig": "JS-Attach"
48
}

.vscode/tasks.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "clean_build_debug",
6+
"type": "shell",
7+
"command": "pnpm clean.release && pnpm build.debug",
8+
}
9+
]
10+
}

CONTRIBUTING.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,3 @@ tool similar to Cmake. GYP was originally created to generate native IDE project
3838
files (Visual Studio, Xcode) for building Chromium.
3939

4040
The `.gyp` file is structured as a Python dictionary.
41-
42-
## Weak-napi
43-
44-
https://www.npmjs.com/package/weak-napi On certain rarer occasions, you run into
45-
the need to be notified when a JavaScript object is going to be garbage
46-
collected. This feature is exposed to V8's C++ API, but not to JavaScript.
47-
48-
That's where weak-napi comes in! This module exports the JS engine's GC tracking
49-
functionality to JavaScript. This allows you to create weak references, and
50-
optionally attach a callback function to any arbitrary JS object.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"scripts": {
8585
"install": "(shx test -f ./script/build.js || run-s build.js) && cross-env npm_config_build_from_source=true aminya-node-gyp-build",
8686
"clean": "shx rm -rf ./build ./lib/ ./prebuilds ./script/*.js ./script/*.js.map ./script/*.d.ts ./script/*.tsbuildinfo",
87+
"clean.release": "shx rm -rf ./build/Release",
8788
"clean.temp": "shx rm -rf ./tmp && shx mkdir -p ./tmp",
8889
"build.library": "tsc -p ./src/tsconfig.json",
8990
"build.script": "tsc -p ./script/tsconfig.json && tsc -p ./script/tsconfig.esm.json",
@@ -95,10 +96,9 @@
9596
"build.native.debug": "node-gyp configure --debug && node-gyp configure --debug -- -f compile_commands_json && cross-env CMAKE_BUILD_TYPE=Debug node-gyp build --debug",
9697
"build": "run-s build.js build.native",
9798
"build.debug": "run-s build.js build.native.debug",
98-
"test.deps": "cd test && pnpm install && cd ..",
99-
"test": "run-s clean.temp test.deps build && mocha",
100-
"test.skip_gc_tests": "run-s clean.temp test.deps build.debug && cross-env SKIP_GC_TESTS=true mocha",
101-
"test.electron.main": "run-s clean.temp test.deps build && electron-mocha",
99+
"test": "run-s clean.temp build && mocha",
100+
"test.skip_gc_tests": "run-s clean.temp build.debug && cross-env SKIP_GC_TESTS=true mocha",
101+
"test.electron.main": "run-s clean.temp build && electron-mocha",
102102
"format": "prettier --write .",
103103
"test.electron.renderer": "run-s build && electron-mocha --renderer",
104104
"lint.clang-format": "clang-format -i -style=file ./src/*.cc ./src/*.h ./src/util/*.h",

test/debug.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import * as zmq from "../src"
2+
3+
import {getGcOrSkipTest} from "./unit/helpers"
4+
5+
async function main() {
6+
const gc = getGcOrSkipTest()
7+
8+
let weakRef: undefined | WeakRef<zmq.Context>
9+
const task = async () => {
10+
const context: zmq.Context | undefined = new zmq.Context()
11+
const dealer = new zmq.Dealer({context, linger: 0})
12+
weakRef = new WeakRef(context)
13+
14+
// dealer.close()
15+
}
16+
17+
await task()
18+
await gc()
19+
20+
console.log(weakRef?.deref())
21+
}
22+
23+
main().catch(err => {
24+
console.error(err)
25+
process.exit(1)
26+
})

test/package.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

test/pnpm-lock.yaml

Lines changed: 0 additions & 81 deletions
This file was deleted.

test/tsconfig.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{
22
"extends": "../tsconfig.json",
3-
"include": ["**/*.ts"]
3+
"include": [
4+
"**/*.ts"
5+
]
46
}

test/unit/helpers.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,43 @@ export async function captureEventsUntil(
235235

236236
return events
237237
}
238+
239+
// REAL typings for global.gc per
240+
// https://github.com/nodejs/node/blob/v20.0.0/deps/v8/src/extensions/gc-extension.cc
241+
interface GCFunction {
242+
(options: {
243+
execution?: "sync"
244+
flavor?: "regular" | "last-resort"
245+
type?: "major-snapshot" | "major" | "minor"
246+
filename?: string
247+
}): void
248+
(options: {
249+
execution?: "async"
250+
flavor?: "regular" | "last-resort"
251+
type?: "major-snapshot" | "major" | "minor"
252+
filename?: string
253+
}): Promise<void>
254+
(options: {
255+
execution?: "async" | "sync"
256+
flavor?: "regular" | "last-resort"
257+
type?: "major-snapshot" | "major" | "minor"
258+
filename?: string
259+
}): void | Promise<void>
260+
}
261+
262+
export function getGcOrSkipTest(test?: Mocha.Context) {
263+
if (process.env.SKIP_GC_TESTS === "true") {
264+
test?.skip()
265+
}
266+
267+
const gc = global.gc as undefined | GCFunction
268+
if (typeof gc !== "function") {
269+
throw new Error(
270+
"Garbage collection is not exposed. It may be enabled by the node --expose-gc flag or v8-expose-gc flag in Mocha. To skip GC tests, set the environment variable `SKIP_GC_TESTS`",
271+
)
272+
}
273+
// https://github.com/nodejs/node/blob/v20.0.0/deps/v8/src/extensions/gc-extension.h
274+
// per docs, we we're using use case 2 (Test that certain objects indeed are reclaimed)
275+
const asyncMajorGc = () => gc({type: "major", execution: "async"})
276+
return asyncMajorGc
277+
}

0 commit comments

Comments
 (0)