Thanks to visit codestin.com
Credit goes to github.com

Skip to content

using() does not show a useful stacktrace on double frees. #2246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
dickermoshe opened this issue Apr 27, 2025 · 0 comments
Open

using() does not show a useful stacktrace on double frees. #2246

dickermoshe opened this issue Apr 27, 2025 · 0 comments

Comments

@dickermoshe
Copy link
Contributor

dickermoshe commented Apr 27, 2025

The using((arena) => ...) function provided by jni is great for ensuring that all references are disposed at the end of the code block.

There are many instances where the arena is not used to dispose the reference.
When using:

  • booleanValue(releaseOriginal: true)
  • intValue(releaseOriginal: true)
  • toDartString(releaseOriginal: true)
  • etc.

It is quite easy to mistakenly use a releaseBy(...) in conjunction with one of these functions and get yourself a DoubleReleaseError.

However using provides no helpful information about where this 2nd release is registered.


Possible Solutions

Don't throw on double release

My personal favorite

My Custom Function

import 'dart:ffi';

import 'package:ffi/ffi.dart';
/// Runs [computation] with a new [Arena], and releases all allocations at the
/// end.
///
/// If the return value of [computation] is a [Future], all allocations are
/// released when the future completes.
///
/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_
/// cleaned up.
/// 
/// Does not throw DoubleReleaseError.
R myUsing<R>(
  R Function(Arena) computation, [
  Allocator wrappedAllocator = calloc,
]) {
  final arena = Arena(wrappedAllocator);
  var isAsync = false;
  try {
    final result = computation(arena);
    if (result is Future) {
      isAsync = true;
      return result.whenComplete(() {
            try {
              arena.releaseAll();
            } on DoubleReleaseError {
              // ignore
            }
          })
          as R;
    }
    return result;
  } finally {
    if (!isAsync) {
      try {
        arena.releaseAll();
      } on DoubleReleaseError {
        // ignore
      }
    }
  }
}

Throw on release

Add/replace an option called booleanValue(releaseWith: arena) to each built-in converter:

using((arena ){
  return JString().toDartString(releaseWith:arena);
})

The arena should store references as a set of references (Set<JReference>) or Map of references to release Callbacks (Map<JReference,Function>), adding duplicate references will throw an upon registration.
Stack traces would then contain useful information.

This is similar to the syntax that the ffi uses anyway:

final pTitle = 'Window title'.toNativeUtf16(allocator: arena);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests

2 participants