Skip to content

Commit

Permalink
wip - improve gc tests more
Browse files Browse the repository at this point in the history
  • Loading branch information
rotu committed Jun 16, 2024
1 parent 1f81c4a commit a1715e1
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 144 deletions.
23 changes: 9 additions & 14 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,15 @@ jobs:
ARCH: ${{ matrix.ARCH }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4

# - name: Cache
# uses: actions/cache@v4
# with:
# path: |
# ./node_modules/
# ./build/
# key: "cache-OS:${{ matrix.os }}-arch:${{ matrix.node_arch }}-ZMQ_DRAFT:${{ matrix.zmq_draft }}-Node:${{ matrix.node_version }}-${{ hashFiles('./package.json') }}"
# restore-keys: |
# "cache-OS:${{ matrix.os }}-arch:${{ matrix.node_arch }}-ZMQ_DRAFT:${{ matrix.zmq_draft }}-Node:${{ matrix.node_version }}-"
- name: Cache
uses: actions/cache@v4
with:
path: |
./node_modules/
./build/
key: "cache-OS:${{ matrix.os }}-arch:${{ matrix.node_arch }}-ZMQ_DRAFT:${{ matrix.zmq_draft }}-Node:${{ matrix.node_version }}-${{ hashFiles('./package.json') }}"
restore-keys: |
"cache-OS:${{ matrix.os }}-arch:${{ matrix.node_arch }}-ZMQ_DRAFT:${{ matrix.zmq_draft }}-Node:${{ matrix.node_version }}-"
- name: Setup Cpp
if: ${{ !matrix.docker }}
Expand All @@ -108,7 +106,6 @@ jobs:
- name: Install Node
if: ${{ !matrix.docker }}
uses: actions/setup-node@v4
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node_version }}
architecture: ${{ matrix.node_arch }}
Expand Down Expand Up @@ -155,7 +152,6 @@ jobs:
- name: Test (Debug)
if: ${{ !matrix.docker }}
uses: nick-fields/retry@v3
uses: nick-fields/retry@v3
with:
timeout_minutes: 5
max_attempts: 1
Expand All @@ -180,7 +176,6 @@ jobs:
- name: Tests + GC Tests (Release)
if: ${{ !matrix.docker }}
uses: nick-fields/retry@v3
uses: nick-fields/retry@v3
with:
timeout_minutes: 5
max_attempts: 1
Expand Down
1 change: 1 addition & 0 deletions test/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"no-await-in-loop": "off",
"require-await": "off",
Expand Down
6 changes: 0 additions & 6 deletions test/package.json

This file was deleted.

79 changes: 0 additions & 79 deletions test/pnpm-lock.yaml

This file was deleted.

15 changes: 14 additions & 1 deletion test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
{
"extends": "../tsconfig.json",
"include": ["**/*.ts"]
"include": [
"**/*.ts"
],
"ts-node": {
"files": true
},
"compilerOptions": {
"types": [
"mocha"
],
"lib": [
"ESNext"
]
}
}
40 changes: 40 additions & 0 deletions test/unit/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,43 @@ export async function captureEventsUntil(

return events
}

// REAL typings for global.gc per
// https://github.com/nodejs/node/blob/v20.0.0/deps/v8/src/extensions/gc-extension.cc
interface GCFunction {
(options: {
execution?: "sync"
flavor?: "regular" | "last-resort"
type?: "major-snapshot" | "major" | "minor"
filename?: string
}): void
(options: {
execution?: "async"
flavor?: "regular" | "last-resort"
type?: "major-snapshot" | "major" | "minor"
filename?: string
}): Promise<void>
(options: {
execution?: "async" | "sync"
flavor?: "regular" | "last-resort"
type?: "major-snapshot" | "major" | "minor"
filename?: string
}): void | Promise<void>
}

export function getGcOrSkipTest(test: Mocha.Context) {
if (process.env.SKIP_GC_TESTS) {
test.skip()
}

const gc = globalThis.gc as undefined | GCFunction
if (typeof gc !== "function") {
throw new Error(
"Garbage collection is not exposed. It may be enabled by the node --expose-gc flag. To skip GC tests, set the environment variable `SKIP_GC_TESTS`",
)
}
// https://github.com/nodejs/node/blob/v20.0.0/deps/v8/src/extensions/gc-extension.h
// per docs, we we're using use case 2 (Test that certain objects indeed are reclaimed)
const asyncMajorGc = () => gc({type: "major", execution: "async"})
return asyncMajorGc
}
23 changes: 7 additions & 16 deletions test/unit/socket-close-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import * as zmq from "../../src"

import {assert} from "chai"
import {testProtos, uniqAddress} from "./helpers"
import {testProtos, uniqAddress, getGcOrSkipTest} from "./helpers"
import {isFullError} from "../../src/errors"

for (const proto of testProtos("tcp", "ipc", "inproc")) {
Expand Down Expand Up @@ -107,9 +107,7 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
})

it("should release reference to context", async function () {
if (process.env.SKIP_GC_TESTS) {
this.skip()
}
const gc = getGcOrSkipTest(this)
this.slow(200)

let weakRef: undefined | WeakRef<any>
Expand All @@ -125,10 +123,7 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
}

await task()
global.gc?.()
await new Promise(resolve => {
setTimeout(resolve, 5)
})
await gc()

assert.isDefined(weakRef)
assert.isUndefined(weakRef!.deref())
Expand All @@ -137,9 +132,7 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {

describe("in gc finalizer", function () {
it("should release reference to context", async function () {
if (process.env.SKIP_GC_TESTS) {
this.skip()
}
const gc = getGcOrSkipTest(this)
if (process.env.SKIP_GC_FINALIZER_TESTS) {
this.skip()
}
Expand All @@ -152,14 +145,12 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
new zmq.Dealer({context, linger: 0})
weakRef = new WeakRef(context)
context = undefined
global.gc?.()
await gc()
}

await task()
global.gc?.()
await new Promise(resolve => {
setTimeout(resolve, 5)
})
await gc()

assert.isDefined(weakRef)
assert.isUndefined(weakRef!.deref())
})
Expand Down
38 changes: 12 additions & 26 deletions test/unit/socket-send-receive-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import * as zmq from "../../src"

import {assert} from "chai"
import {testProtos, uniqAddress} from "./helpers"
import {testProtos, uniqAddress, getGcOrSkipTest} from "./helpers"
import {isFullError} from "../../src/errors"

for (const proto of testProtos("tcp", "ipc", "inproc")) {
Expand Down Expand Up @@ -91,9 +91,7 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
})

it("should copy and release small buffers", async function () {
if (process.env.SKIP_GC_TESTS) {
this.skip()
}
const gc = getGcOrSkipTest(this)
let weakRef: undefined | WeakRef<any>
sockA.connect(uniqAddress(proto))
const send = async (size: number) => {
Expand All @@ -103,18 +101,13 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
}

await send(16)
global.gc?.()
await new Promise(resolve => {
setTimeout(resolve, 5)
})
await gc()
assert.isDefined(weakRef)
assert.isUndefined(weakRef!.deref())
})

it("should retain large buffers", async function () {
if (process.env.SKIP_GC_TESTS) {
this.skip()
}
const gc = getGcOrSkipTest(this)
let weakRef: undefined | WeakRef<any>

sockA.connect(uniqAddress(proto))
Expand All @@ -125,12 +118,9 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
}

await send(1025)
global.gc?.()
await new Promise(resolve => {
setTimeout(resolve, 5)
})
await gc()
assert.isDefined(weakRef)
assert.isUndefined(weakRef!.deref())
assert.isDefined(weakRef.deref())
})
})

Expand Down Expand Up @@ -338,13 +328,11 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
})

it("should release buffers", async function () {
if (process.env.SKIP_GC_TESTS) {
this.skip()
}
const gc = await getGcOrSkipTest(this)

const weakRefs: WeakRef<any>[] = []

const n = 10
let released = 0

const send = async (size: number) => {
for (let i = 0; i < n; i++) {
Expand All @@ -365,7 +353,8 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {

/* Repeated GC to allow inproc messages from being collected. */
for (let i = 0; i < 5; i++) {
global.gc?.()
await gc()

await new Promise(resolve => {
setTimeout(resolve, 2)
})
Expand All @@ -377,14 +366,11 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
})

it("should release buffers after echo", async function () {
if (process.env.SKIP_GC_TESTS) {
this.skip()
}
const gc = getGcOrSkipTest(this)

const weakRefs: WeakRef<any>[] = []

const n = 10
let released = 0

const echo = async () => {
for (let i = 0; i < n; i++) {
Expand Down Expand Up @@ -412,10 +398,10 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {

/* Repeated GC to allow inproc messages from being collected. */
for (let i = 0; i < 5; i++) {
global.gc?.()
await new Promise(resolve => {
setTimeout(resolve, 2)
})
await gc()
}

assert.lengthOf(weakRefs, n * 3)
Expand Down
3 changes: 1 addition & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"module": "commonjs",
"types": [
"node",
"mocha"
],
"strictPropertyInitialization": false, // TODO
"strict": true,
Expand All @@ -21,7 +20,7 @@
"sourceMap": true,
"esModuleInterop": true,
"lib": [
"ES2020",
"ES2022",
"dom"
]
}
Expand Down

0 comments on commit a1715e1

Please sign in to comment.