node-ffi-rs icon indicating copy to clipboard operation
node-ffi-rs copied to clipboard

Electron app crashes when using freepointer

Open Japerre opened this issue 1 year ago • 3 comments

ffi-rs version 1.2.5

Print current Node.js info with the following code

ffi-rs/package.json:

{
  "name": "ffi-rs",
  "version": "1.2.5",
  "main": "index.js",
  "types": "index.d.ts",
  "description": "A module written in Rust and N-API provides interface (FFI) features for Node.js",
  "napi": {
    "name": "ffi-rs",
    "triples": {
      "additional": [
        "aarch64-apple-darwin",
        "aarch64-unknown-linux-gnu",
        "aarch64-unknown-linux-musl",
        "arm-unknown-linux-gnueabihf",
        "i686-pc-windows-msvc",
        "x86_64-unknown-linux-musl",
        "aarch64-pc-windows-msvc"
      ]
    }
  },
  "author": "zhangyuang",
  "homepage": "https://github.com/zhangyuang/node-ffi-rs#readme",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/zhangyuang/node-ffi-rs.git"
  },
  "keywords": [
    "ffi",
    "rust",
    "node.js",
    "napi"
  ],
  "files": [
    "index.js",
    "index.d.ts",
    "README.md"
  ],
  "license": "MIT",
  "dependencies": {},
  "devDependencies": {
    "@napi-rs/cli": "^2.15.2",
    "@types/node": "^20.8.7",
    "benny": "^3.7.1",
    "conventional-changelog-cli": "^4.1.0",
    "esno": "^4.0.0",
    "ffi-napi": "^4.0.3",
    "koa": "^2.14.2",
    "shelljs": "^0.8.5",
    "typescript": "^5.4.5"
  },
  "scripts": {
    "artifacts": "napi artifacts",
    "build:c": "node scripts/compile.js",
    "build:dev": "env=development node scripts/build.js",
    "build": "node scripts/build.js",
    "publish:npm": "node scripts/publish.js",
    "test": "esno ./tests/index.ts",
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add . && git commit -m \"docs: update changelog.md\" && git push origin master",
    "pub": "npm version patch && git push origin master --tags && npm run changelog",
    "pub:alpha": "npm version prerelease --preid=alpha && git push origin master --tags"
  },
  "optionalDependencies": {
    "@yuuang/ffi-rs-darwin-arm64": "1.2.5",
    "@yuuang/ffi-rs-darwin-x64": "1.2.5",
    "@yuuang/ffi-rs-linux-arm-gnueabihf": "1.2.5",
    "@yuuang/ffi-rs-linux-arm64-gnu": "1.2.5",
    "@yuuang/ffi-rs-linux-arm64-musl": "1.2.5",
    "@yuuang/ffi-rs-linux-x64-gnu": "1.2.5",
    "@yuuang/ffi-rs-linux-x64-musl": "1.2.5",
    "@yuuang/ffi-rs-win32-arm64-msvc": "1.2.5",
    "@yuuang/ffi-rs-win32-ia32-msvc": "1.2.5",
    "@yuuang/ffi-rs-win32-x64-msvc": "1.2.5"
  }
}

$ ls node_modules/@yuuang

ffi-rs-win32-ia32-msvc/ ffi-rs-win32-x64-msvc/

Current Node.js arch

x64 win32

Descibe your problem in detail

I am trying out the example for passing function pointers to C functions. I slightly altered the example so that the funcExternal function pointer gets freed when the C++ code is done with it. This is done by setting a callback function that frees the function pointer.

typedef void (*CleanupFunctionPointer)();

CleanupFunctionPointer cleanupFunction = nullptr;

extern "C" void setCleanupFunction(CleanupFunctionPointer cleanup) {
  cleanupFunction = cleanup;
}

typedef const void (*FunctionPointer)(int a, bool b, char *c, double d,
  char **e, int *f);

extern "C" void callFunction(FunctionPointer func) {
  for (int i = 0; i < 2; i++) {
    int a = 100;
    
    ... left out for brevity ....

    func(a, b, c, d, stringArray, i32Array);

    free(c);
    free(stringArray[0]);
    free(stringArray[1]);
    free(stringArray);
    free(i32Array);
  }

  if (cleanupFunction != nullptr) {
    cleanupFunction();
    cleanupFunction = nullptr;
  }

}

The following javascript code sets the callback in C++.

const cleanupFunction = () => {
    console.log("C++ is done, freeing function pointer.");
    // free function memory when it is not in use
    
    setTimeout(() => {
        freePointer({
            paramsType: [funcConstructor({
                paramsType: [
                    DataType.I32,
                    DataType.Boolean,
                    DataType.String,
                    DataType.Double,
                    arrayConstructor({
                        type: DataType.StringArray,
                        length: 2
                    }),
                    arrayConstructor({
                        type: DataType.I32Array,
                        length: 3
                    }),
                ],
                retType: DataType.Void,
            })],
            paramsValue: funcExternal,
            pointerType: PointerType.CPointer
        })
    }, 1000);
}

const cleanupPointer = createPointer({
    paramsType: [funcConstructor({
        paramsType: [],
        retType: DataType.Void,
    })],
    paramsValue: [cleanupFunction]
})

load({
    library: "libsum",
    funcName: "setCleanupFunction",
    retType: DataType.Void,
    paramsType: [
        DataType.External,
    ],
    paramsValue: unwrapPointer(cleanupPointer),
})

Result

a:  100
b:  false
c:  Hello, World!
d:  100.11
e:  [ 'Hello', 'world' ]
f:  [ 101, 202, 303 ]
a:  100
b:  false
c:  Hello, World!
d:  100.11
e:  [ 'Hello', 'world' ]
f:  [ 101, 202, 303 ]
C++ is done, freeing function pointer.

The JS function is called twice from within the 'callFunction' in C++, after which the JS function pointer(funcExternal) should get freed with the cleanupFunction callback.

This results in crashing the process.

Remarks

This does not happen when using PointerType.RsPointer for pointerType field in freePointer object.

What's your expected result

freeing the funcExternal pointer in JS without crashing process.

Japerre avatar Feb 21 '25 09:02 Japerre

Why do you call CleanupFunction in c? If you want to free function memory, you can call the freePointer method in Node.js directly

zhangyuang avatar Feb 21 '25 11:02 zhangyuang

How can we know that C is done with the functionpointer?

In the code example for Function, the functionpointer is freed after 'func' is called from within the C function. But In the C function we call the function 'func' twice, so it's freed before C is done using it.

At least if I'm understanding correctly..

thx in advance for your guidance!

Japerre avatar Feb 21 '25 12:02 Japerre

You dont need to know when the function is end,the function pointer memory only occupy some memory without any side effect.If you want to free function memory in c,you can store the function pointer in c directly and free it no need to call freePointer in Node.js

发自我的iPhone

------------------ Original ------------------ From: Japerre @.> Date: Fri,Feb 21,2025 8:32 PM To: zhangyuang/node-ffi-rs @.> Cc: yuuang @.>, Comment @.> Subject: Re: [zhangyuang/node-ffi-rs] Electron app crashes when usingfreepointer (Issue #96)

How can we know that C is done with the functionpointer?

In the code example for Function, the functionpointer is freed after 'func' is called from within the C function. But In the C function we call the function 'func' twice, so it's freed before C is done using it.

At least if I'm understanding correctly..

thx in advance for your guidance!

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***> Japerre left a comment (zhangyuang/node-ffi-rs#96)

How can we know that C is done with the functionpointer?

In the code example for Function, the functionpointer is freed after 'func' is called from within the C function. But In the C function we call the function 'func' twice, so it's freed before C is done using it.

At least if I'm understanding correctly..

thx in advance for your guidance!

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>

zhangyuang avatar Feb 21 '25 12:02 zhangyuang