Skip to content

Dio().fetch() hangs in isolate #2399

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

Closed
mikes222 opened this issue May 5, 2025 · 4 comments
Closed

Dio().fetch() hangs in isolate #2399

mikes222 opened this issue May 5, 2025 · 4 comments

Comments

@mikes222
Copy link

mikes222 commented May 5, 2025

Package

dio

Version

5.8.0+1

Operating-System

Windows

Adapter

Default Dio

Output of flutter doctor -v

[√] Flutter (Channel stable, 3.29.3, on Microsoft Windows [Version 10.0.22631.5189], locale de-AT) [361ms]
    • Flutter version 3.29.3 on channel stable at C:\develop\flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision ea121f8859 (3 weeks ago), 2025-04-11 19:10:07 +0000
    • Engine revision cf56914b32
    • Dart version 3.7.2
    • DevTools version 2.42.3

[√] Windows Version (11 Pro 64-bit, 23H2, 2009) [2,7s]

[!] Android toolchain - develop for Android devices (Android SDK version 35.0.0) [283ms]
    • Android SDK at C:\Users\micro\AppData\Local\Android\sdk
    X cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    X Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/to/windows-android-setup for more details.

[√] Chrome - develop for the web [143ms]
    • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe

[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.8.2) [142ms]
    • Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community
    • Visual Studio Community 2022 version 17.8.34322.80
    • Windows 10 SDK version 10.0.22621.0

[√] Android Studio (version 2024.3) [13ms]
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 21.0.6+-13355223-b631.42)

[√] Connected device (4 available) [214ms]
    • sdk gphone64 x86 64 (mobile) • emulator-5554 • android-x64    • Android 15 (API 35) (emulator)
    • Windows (desktop)            • windows       • windows-x64    • Microsoft Windows [Version 10.0.22631.5189]
    • Chrome (web)                 • chrome        • web-javascript • Google Chrome 135.0.7049.115
    • Edge (web)                   • edge          • web-javascript • Microsoft Edge 136.0.3240.50

[√] Network resources [656ms]
    • All expected network resources are available.

! Doctor found issues in 1 category.
P

Dart Version

Dart SDK version: 3.7.2 (stable) (Tue Mar 11 04:27:50 2025 -0700) on "windows_x64"

Steps to Reproduce

I have created a method to fetch data from http(s) server. When calling this method in the main thread everything works. Unfortunately if I call the very same method in an isolate the last line in the following code snippet Response response = await responseFuture; hangs indefinitely. I am unable to debug what exactly causes this problem and therefore would like to get some hints how to find the root cause.

RequestOptions requestOptions = RequestOptions(
  path: source,
  method: "GET",
  connectTimeout: const Duration(seconds: 35),
  receiveTimeout: const Duration(seconds: 40),
  sendTimeout: const Duration(seconds: 62),
  //receiveDataWhenStatusError: true,
  followRedirects: true,
  responseType: ResponseType.stream,
);

final Dio _httpClient = Dio();

print("vor response");
Future<Response> responseFuture = _httpClient.fetch(requestOptions);
Response response = await responseFuture;

Expected Result

Not hanging in an isolate created with Isolate.spawn

Actual Result

hanging when awaiting the result in the isolate

@mikes222 mikes222 added h: need triage This issue needs to be categorized s: bug Something isn't working labels May 5, 2025
@AlexV525
Copy link
Member

Please try to provide a minimal reproducible example using Isolate.spawn.

@mikes222
Copy link
Author

  1. Please create a flutter project and insert the code below in a separate dart file (e.g. dio_test.dart).
  2. add dio to pubspec.yaml
  3. In your initState() method add the following line: DioTest.inIsolate = false;
  4. In your onPressed() method add either
    DioTest dioTest = DioTest();
    dioTest.perform();

or

IsolateDioTest dioTest = IsolateDioTest();
dioTest.perform();

import 'dart:async';
import 'dart:isolate';
import 'dart:ui';

import 'package:dio/dio.dart';

/// This is the wrapper method to perform a computation in an isolate.
@pragma('vm:entry-point')
class IsolateDioTest {
  Future<void> perform() async {
    return await FlutterIsolateInstance.isolateCompute(performStatic);
  }

  /// The entry point for the isolate.
  @pragma('vm:entry-point')
  static Future<void> performStatic() async {
    DioTest dioTest = DioTest();
    dioTest.perform();
  }
}

//////////////////////////////////////////////////////////////////////////////

typedef EntryPoint<V> = Future<V> Function();

//////////////////////////////////////////////////////////////////////////////

/// One instance for an isolate. Do not instantiate directly. Use [FlutterIsolateInstance.isolateCompute] instead.
class FlutterIsolateInstance<V> {
  SendPort? _sendPort;

  Isolate? _isolate;

  // complete() will be called if the isolate is ready to receive commands.
  final Completer _isolateCompleter = Completer();

  // complete() will be called if the isolate returns a result.
  Completer<V>? _isolateResultCompleter;

  FlutterIsolateInstance._();

  void dispose() {
    _isolate?.kill();
    _isolate = null;
    _sendPort = null;
  }

  /// Performs a single computation in an isolate and disposes the isolate afterwards.
  static Future<V> isolateCompute<V>(EntryPoint<V> entryPoint) async {
    FlutterIsolateInstance instance = FlutterIsolateInstance<V>._();
    await instance.start();
    V result = await instance.compute(entryPoint);
    instance.dispose();
    return result;
  }

  /// Starts a new isolate. Do not call this directly. Use [isolateCompute] instead. This runs in the main isolate.
  Future<void> start() async {
    ReceivePort receivePort = ReceivePort();

    _isolate = await Isolate.spawn<SendPort>(isolateEntryPoint, receivePort.sendPort);
    // let the listener run in background of the main isolate
    unawaited(_listenToIsolate(receivePort));
    return _isolateCompleter.future;
  }

  /// listen to the results of an isolate. This method runs in the main isolate.
  Future<void> _listenToIsolate(ReceivePort receivePort) async {
    await for (var data in receivePort) {
      //tileCache.addTileBitmap(job.tile, tileBitmap);
      //print("received from isolate: ${data.toString()}");
      if (data is SendPort) {
        // Receive the SendPort from the Isolate
        _sendPort = data;
        _isolateCompleter.complete();
      } else {
        _isolateResultCompleter!.complete(data);
      }
    }
  }

  /// Starts a single computation in an isolate. Do not call this method
  /// directly. Use [isolateCompute] instead.
  /// This method runs in the main isolate.
  Future<V> compute(EntryPoint<V> entryPoint) {
    final entryPointHandle = PluginUtilities.getCallbackHandle(entryPoint)!.toRawHandle();
    _sendPort!.send(entryPointHandle);
    _isolateResultCompleter = Completer();
    return _isolateResultCompleter!.future;
  }

  //////////////////////////////////////////////////////////////////////////////

  /// The first entry point called in the isolate. It establishes the communication with the main isolate, instantiates the isolate's class if necessary and
  /// waits for computational commands.
  @pragma('vm:entry-point')
  static Future<void> isolateEntryPoint(SendPort sendPort) async {
    // Open the ReceivePort to listen for incoming messages
    var receivePort = new ReceivePort();
    // Send message to main Isolate and inform it about this receiver
    sendPort.send(receivePort.sendPort);

    // Listen for messages from main isolate and returns the results.
    await for (var data in receivePort) {
      if (data is int) {
        try {
          final entryPointHandle = CallbackHandle.fromRawHandle(data);
          final entryPoint = PluginUtilities.getCallbackFromHandle(entryPointHandle);
          entryPoint!().then((result) {
            // return result to main isolate
            sendPort.send(result);
          });
        } catch (error, stacktrace) {
          print("Error: $error");
        }
      }
    }
    return;
  }
}

//////////////////////////////////////////////////////////////////////////////

class DioTest {
  // variable will be set to false in the main method
  static bool inIsolate = true;

  Future<void> perform() async {
    RequestOptions requestOptions = RequestOptions(path: "https://www.google.com", method: "GET");

    final Dio httpClient = Dio();

    Future<Response> responseFuture = httpClient.fetch(requestOptions);
    print("Request (in Isolate: $inIsolate): $requestOptions");
    Response response = await responseFuture;
    print("Response (in Isolate: $inIsolate): ${response.headers}");
  }
}

@mikes222
Copy link
Author

I found the problem myself. In

/// The entry point for the isolate.
@pragma('vm:entry-point')
static Future<void> performStatic() async {
  DioTest dioTest = DioTest();
  dioTest.perform();
}

The last statement should rather be await dioTest.perform();

Because of this the method returned instantly and the isolate was killed in the dispose() method :-(

Therefore I will close this issue.

@AlexV525
Copy link
Member

Cool, glad you've found the cause without much pain.

@AlexV525 AlexV525 added i: not related and removed h: need triage This issue needs to be categorized s: bug Something isn't working labels May 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants