ZetCode

Dart ResourceHandle

last modified October 18, 2026

The ResourceHandle interface in Dart wraps an operating system resource handle so that it can be passed to another process through a socket. It is part of the dart:io library and is used together with SocketControlMessage and RawSocket.sendMessage.

It is not a general-purpose RAII wrapper: it has no dispose method and does not close resources automatically when it goes out of scope.

Basic Definition

ResourceHandle is an abstract interface class with no generic type parameter. Create one with a factory such as ResourceHandle.fromFile, fromSocket, fromStdin, fromStdout, fromReadPipe, or fromWritePipe.

A handle represents one OS resource. Extract it with a matching toFile, toSocket, toReadPipe, or toWritePipe method. After extraction, no other to... method may be called on that handle.

Basic ResourceHandle Usage

This example wraps an open file, sends the handle over a Unix domain socket, and reads the file through the received handle. The small helper functions create a connected socket pair and wait for one control message.

main.dart
import 'dart:async';
import 'dart:io';

Future<(RawServerSocket, RawSocket, RawSocket)> unixPair(String path) async {
  var server = await RawServerSocket.bind(
    InternetAddress(path, type: InternetAddressType.unix),
    0,
  );
  var clientFuture = RawSocket.connect(
    InternetAddress(path, type: InternetAddressType.unix),
    0,
  );
  var serverSide = await server.first;
  var clientSide = await clientFuture;
  return (server, serverSide, clientSide);
}

Future<SocketMessage> receive(RawSocket socket) async {
  var completer = Completer<SocketMessage>();
  late StreamSubscription<RawSocketEvent> subscription;
  subscription = socket.listen((event) {
    if (event == RawSocketEvent.read) {
      var message = socket.readMessage();
      if (message != null && !completer.isCompleted) {
        completer.complete(message);
        subscription.cancel();
      }
    }
  });
  return completer.future;
}

Future<void> main() async {
  var dir = Directory.systemTemp.createTempSync('resource_handle');
  var path = '${dir.path}/socket';
  var (server, serverSide, clientSide) = await unixPair(path);

  var file = File('${dir.path}/data.txt');
  await file.writeAsString('Hello, ResourceHandle!');
  var raf = await file.open();
  var handle = ResourceHandle.fromFile(raf);

  clientSide.sendMessage([SocketControlMessage.fromHandles([handle])], [1]);

  var message = await receive(serverSide);
  var received = message.controlMessages
      .expand((control) => control.extractHandles())
      .first;
  var fileHandle = received.toFile();
  var bytes = await fileHandle.read(100);
  print('File content: ${String.fromCharCodes(bytes)}');

  await fileHandle.close();
  clientSide.close();
  await serverSide.close();
  await server.close();
  await file.delete();
  dir.deleteSync(recursive: true);
}

ResourceHandle.fromFile wraps the open RandomAccessFile. The receiver extracts the handle with toFile and reads it. The extracted handle owns a descriptor received over the socket, so it can be closed independently of the sender's file object.

$ dart main.dart
File content: Hello, ResourceHandle!

Manual Resource Release

This example demonstrates that releasing a resource is still an explicit operation. The wrapped file is closed through its original object.

main.dart
import 'dart:io';

Future<void> main() async {
  var file = File('resource.txt');
  await file.writeAsString('temporary data');

  var raf = await file.open();
  ResourceHandle.fromFile(raf);
  print('Wrapped file handle');

  await raf.close();
  await file.delete();
  print('Resource released');
}

ResourceHandle has no dispose method. Closing the underlying RandomAccessFile releases the OS resource, after which the file can be deleted safely.

$ dart main.dart
Wrapped file handle
Resource released

Error Handling with Resources

This example shows explicit cleanup of a wrapped resource when an error occurs.

main.dart
import 'dart:io';

Future<void> main() async {
  var file = File('database.tmp');
  await file.writeAsString('temporary database');

  var raf = await file.open();
  ResourceHandle.fromFile(raf);

  try {
    throw Exception('Database operation failed');
  } catch (e) {
    print('Error occurred: $e');
  } finally {
    await raf.close();
    await file.delete();
    print('Resource released');
  }
}

Because the handle performs no automatic cleanup, the file must be closed and deleted in a finally block. This keeps the error path from leaking the open file descriptor.

$ dart main.dart
Error occurred: Exception: Database operation failed
Resource released

Multiple Resources Management

This example manages several file handles in a collection and releases each one.

main.dart
import 'dart:io';

Future<void> main() async {
  var files = [File('log1.txt'), File('log2.txt')];
  await files[0].writeAsString('Log entry 1');
  await files[1].writeAsString('Log entry 2');

  var handles = <ResourceHandle>[];
  var openFiles = <RandomAccessFile>[];
  for (var file in files) {
    var raf = await file.open();
    openFiles.add(raf);
    handles.add(ResourceHandle.fromFile(raf));
  }

  print('Prepared ${handles.length} resource handles');
  for (var raf in openFiles) {
    await raf.close();
  }
  for (var file in files) {
    await file.delete();
  }
  print('All resources released');
}

There is no ResourceHandle.group helper. A plain list is enough when several handles need to be prepared together, and the original objects still control when the resources are closed.

$ dart main.dart
Prepared 2 resource handles
All resources released

Passing a Handle Over a Socket

This example transfers the read end of a pipe over a Unix domain socket and uses the received handle in the same process.

main.dart
import 'dart:async';
import 'dart:convert';
import 'dart:io';

Future<void> main() async {
  var dir = Directory.systemTemp.createTempSync('resource_handle');
  var path = '${dir.path}/socket';

  var server = await RawServerSocket.bind(
    InternetAddress(path, type: InternetAddressType.unix),
    0,
  );
  var clientFuture = RawSocket.connect(
    InternetAddress(path, type: InternetAddressType.unix),
    0,
  );

  var serverSide = await server.first;
  var clientSide = await clientFuture;

  var pipe = await Pipe.create();
  var handle = ResourceHandle.fromReadPipe(pipe.read);
  var control = SocketControlMessage.fromHandles([handle]);

  var sent = clientSide.sendMessage([control], [1, 2, 3]);
  print('sendMessage bytes: $sent');

  var message = Completer<SocketMessage>();
  var subscription = serverSide.listen((event) {
    if (event == RawSocketEvent.read) {
      var received = serverSide.readMessage();
      if (received != null && !message.isCompleted) {
        message.complete(received);
      }
    }
  });

  var receivedMessage = await message.future;
  print('data: ${receivedMessage.data}');

  var handles = receivedMessage.controlMessages
      .expand((control) => control.extractHandles())
      .toList();
  print('handles: ${handles.length}');

  var readPipe = handles.first.toReadPipe();
  var result = readPipe.transform(utf8.decoder).join();

  pipe.write.write('Hello over pipe!');
  await pipe.write.close();
  print(await result);

  await subscription.cancel();
  clientSide.close();
  await serverSide.close();
  await server.close();
  dir.deleteSync(recursive: true);
}

SocketControlMessage.fromHandles attaches file descriptors to a message sent with RawSocket.sendMessage. The receiver calls readMessage, extracts the control messages, and converts the received handle with toReadPipe. Handle passing over sockets is supported on macOS and Linux (excluding Android), but not on Windows, Android, or Fuchsia.

$ dart main.dart
sendMessage bytes: 3
data: [1, 2, 3]
handles: 1
Hello over pipe!

Best Practices

Source

Dart ResourceHandle Documentation

This tutorial covered Dart's ResourceHandle interface with practical examples showing file handles, explicit release, error cleanup, multiple resources, and transferring a pipe handle over a socket.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Dart tutorials.