Skip to content

Commit

Permalink
Dart: fix race condition between garbage collection of finalizable ob…
Browse files Browse the repository at this point in the history
…jects and native function calls (#1633)

----- Motivation -----
If the generated class is not marked with 'Finalizable' interface,
then, when methods are invoked on its object, the object can be
garbage collected in the middle of method execution.

For instance let's take a look at the following example from functional
tests:

'AsyncClass().asyncVoid(false)'

1. AsyncClass object is created and its lifetime is tied with the native
    finalizer, which deletes shared_ptr (this.handle) that owns resources.
2. Then, inside 'asyncVoid(false)' we use only 'this.handle' member to
    pass the opaque handle to the native function. For garbage collector
    after the handle is passed 'this' object is no longer used by anybody.
    Therefore, it is not needed – can be finalized.
3. If the garbage collection kicks in before the native call, then the
    'AsyncClass' object is garbage collected, its finalizer is run and therefore,
    the resources on C++ side are deleted. It causes the segmentation fault.

We have a race condition between the garbage collection and execution
of native functions.

The consequences can be dramatic -- the finalizer registered for
the object may be executed before the native function call inside
the member methods. The finalizers delete C++ resources tied with
the object. This can lead to segmentation fault.

----- Solution -----
The 'Finalizable' interface is a viable solution proposed by Dart:FFI
documentation. It ensures, that 'this' object outlives the whole method
call -- it guarantees, that the native function call finishes before the
finalizer is run.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
  • Loading branch information
pwrobeldev authored Dec 12, 2024
1 parent 97cd521 commit 2167a72
Show file tree
Hide file tree
Showing 130 changed files with 6,630 additions and 245 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/functional-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ jobs:
- name: Install Dart SDK
run: |
DART_RELEASE_CHANNEL=stable
DART_VERSION=2.12.0
DART_VERSION=2.18.1
wget -nv https://storage.googleapis.com/dart-archive/channels/${DART_RELEASE_CHANNEL}/release/${DART_VERSION}/linux_packages/dart_${DART_VERSION}-1_amd64.deb
sudo apt -y install ./dart_${DART_VERSION}-1_amd64.deb
- name: Build and run functional tests
Expand Down Expand Up @@ -373,7 +373,7 @@ jobs:
- name: Install Dart SDK
run: |
DART_RELEASE_CHANNEL=stable
DART_VERSION=2.12.0
DART_VERSION=2.18.1
wget -nv https://storage.googleapis.com/dart-archive/channels/${DART_RELEASE_CHANNEL}/release/${DART_VERSION}/linux_packages/dart_${DART_VERSION}-1_amd64.deb
sudo apt -y install ./dart_${DART_VERSION}-1_amd64.deb
- name: Build and run functional tests
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Gluecodium project Release Notes

## Unreleased
### Bug fixes:
* Dart: fixed race condition between garbage collector (which could trigger finalizer) and native method calls in finalizable classes.

## 13.10.0
Release date 2024-11-28
### Features:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{!!
!
! Copyright (C) 2016-2019 HERE Europe B.V.
! Copyright (C) 2016-2024 HERE Europe B.V.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
Expand All @@ -20,7 +20,7 @@
!}}
{{>dart/DartDocumentation}}{{>dart/DartAttributes}}
abstract class {{resolveName}}{{!!
}}{{#if this.parents}} implements {{#this.parents}}{{resolveName}}{{#if iter.hasNext}}, {{/if}}{{/this.parents}}{{/if}} {
}} implements {{#if this.parents}}{{#this.parents}}{{resolveName}}, {{/this.parents}}{{/if}}Finalizable {
{{#set parent=this container=this}}{{#constructors}}
{{prefixPartial "dart/DartFunctionDocs" " "}}
factory {{resolveName parent}}{{>dart/DartConstructorName}}({{!!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{!!
!
! Copyright (C) 2016-2020 HERE Europe B.V.
! Copyright (C) 2016-2024 HERE Europe B.V.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
Expand All @@ -20,7 +20,7 @@
!}}
{{>dart/DartDocumentation}}{{>dart/DartAttributes}}
abstract class {{resolveName}}{{!!
}}{{#if this.parents}} implements {{#this.parents}}{{resolveName}}{{#if iter.hasNext}}, {{/if}}{{/this.parents}}{{/if}} {
}} implements {{#if this.parents}}{{#this.parents}}{{resolveName}}, {{/this.parents}}{{/if}}Finalizable {
{{#if inheritedFunctions functions inheritedProperties properties logic="or"}}
{{prefixPartial "dart/DartDocumentation" " "}}
factory {{resolveName}}(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{!!
!
! Copyright (C) 2016-2020 HERE Europe B.V.
! Copyright (C) 2016-2024 HERE Europe B.V.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -42,7 +42,7 @@ final _{{resolveName "Ffi"}}CreateProxy = __lib.catchArgumentError(() => __lib.n
Pointer<Void> Function(int, int, Object, Pointer)
>('{{libraryName}}_{{resolveName "FfiSnakeCase"}}_create_proxy'));

class {{resolveName}}$Impl {
class {{resolveName}}$Impl implements Finalizable {
final Pointer<Void> handle;
{{resolveName}}$Impl(this.handle);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{!!
!
! Copyright (C) 2016-2021 HERE Europe B.V.
! Copyright (C) 2016-2024 HERE Europe B.V.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -41,7 +41,7 @@ class _LazyIterator<E> implements Iterator<E> {
}

/// @nodoc
class LazyList<E> extends Iterable<E> implements List<E> {
class LazyList<E> extends Iterable<E> implements List<E>, Finalizable {
static final _cannotModify = "Cannot modify an unmodifiable list";
final Pointer<Void> handle;
Expand Down
Loading

0 comments on commit 2167a72

Please sign in to comment.