Skip to content

Idle SecureSocket consumes >100% CPU if the listener is paused #62037

@maluke

Description

@maluke

Self contained repro:

  • It starts a TLS server, connects to it and sends 100Kb of data.
  • The server accepts the connection, then does socket.listen(...).pause() (which is equivalent to sleeping or doing work inside async for (var pkt in socket) ....
  • This is enough to make the process consume 130% of CPU on my machine. (Process prints it itself, but it matches output from top)
  • When paused in a debugger there's an isolate doing _secureHandshake again and again.
  • If we send somewhat less data from the client, things work normally, I think the bug is how backpressure is applied when TLS buffers are full but OS buffers also have pending data.
  • Similar code on non-secure Socket does not reproduce the bug.
  • Splitting client and server into separate processes has the same issue (130% CPU is on the server process).
import 'dart:async';
import 'dart:io';

const port = 8443;

void main(List<String> args) async {
  var ctx = SecurityContext()
    ..usePrivateKeyBytes(key_pem.codeUnits)
    ..useCertificateChainBytes(chain_pem.codeUnits);
  var server = await SecureServerSocket.bind(InternetAddress.loopbackIPv4, port, ctx);
  unawaited(Future.delayed(Duration(seconds: 3)).whenComplete(print_cpu_usage));
  _main_client();
  await _main_server(server);
}

Future<void> _main_server(SecureServerSocket server) async {
  await for (var socket in server) {
    print("[server] Client connected");
    socket.listen((pkt) => print('[server] Received ${pkt.length} bytes')).pause();
  }
}

void _main_client() async {
  var socket = await SecureSocket.connect('localhost', port, onBadCertificate: (cert) => true);
  print('[client] Connected to server');
  socket.write('x' * 100_000);
  await socket.flush();
  await socket.close();
  socket.destroy();
  print('[client] done');
}

const chain_pem = '''
-----BEGIN CERTIFICATE-----
MIIBdjCCARugAwIBAgIUDzhDxwldDHzVPlRthjktkUO3wncwCgYIKoZIzj0EAwIw
EDEOMAwGA1UEAwwFZGVidWcwHhcNMjUxMTE5MTg0NTI0WhcNMjUxMTIwMTg0NTI0
WjAQMQ4wDAYDVQQDDAVkZWJ1ZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABO1P
nxX0VpZOQjtPB4xEwxfERCcOjmfgMiYAzP7sZGI2xIPhZHyPi1UKNUKBphhRlkGN
wO2oOprvp2yeVsWNXWijUzBRMB0GA1UdDgQWBBSuVL6YvTycIDNiZAsDua9SccoL
XjAfBgNVHSMEGDAWgBSuVL6YvTycIDNiZAsDua9SccoLXjAPBgNVHRMBAf8EBTAD
AQH/MAoGCCqGSM49BAMCA0kAMEYCIQD+EhtD7czOf01y1HyjpDNPN6Aew/d9lFjb
dG/+1JGTaAIhALevCT6Q3gsk7vIX7qCKitnjgcp6fMjd5187vhyVRFqT
-----END CERTIFICATE-----
''';
const key_pem = '''
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICisOLyuxuBM6hEluycCIZe19itEnBNKyt4Cy0etU+cAoAoGCCqGSM49
AwEHoUQDQgAE7U+fFfRWlk5CO08HjETDF8REJw6OZ+AyJgDM/uxkYjbEg+FkfI+L
VQo1QoGmGFGWQY3A7ag6mu+nbJ5WxY1daA==
-----END EC PRIVATE KEY-----
''';

Future<void> print_cpu_usage() async {
  var hz = await _get_clk_tck();

  var stat_raw = await File('/proc/self/stat').readAsString();
  var uptime_raw = await File('/proc/uptime').readAsString();

  var stat_after = stat_raw.substring(stat_raw.lastIndexOf(') ') + 2);
  var parts = stat_after.split(' ');

  // /proc/[pid]/stat fields (after ") "):
  // 0: state (3), 1: ppid (4), ..., 11: utime (14), 12: stime (15), 19: starttime (22)
  var utime = int.parse(parts[11]);
  var stime = int.parse(parts[12]);
  var starttime = int.parse(parts[19]);

  var uptime_sec = double.parse(uptime_raw.split(' ').first);

  var total_time_ticks = utime + stime;
  var total_time_sec = total_time_ticks / hz;
  var start_time_sec = starttime / hz;
  var elapsed_sec = uptime_sec - start_time_sec;

  var cpu_percent = elapsed_sec > 0 ? (total_time_sec / elapsed_sec) * 100.0 : 0.0;

  stdout.writeln('CPU usage (avg since start): ${cpu_percent.toStringAsFixed(2)}%');
}

Future<double> _get_clk_tck() async {
  try {
    var result = await Process.run('getconf', ['CLK_TCK']);
    if (result.exitCode == 0) {
      return double.parse((result.stdout as String).trim());
    }
  } catch (_) {}
  return 100.0; // common default on many Linux systems
}

Output:

[client] Connected to server
[server] Client connected
[client] done
CPU usage (avg since start): 130.12%
- Dart 3.10.0 (stable) (Thu Nov 6 05:24:55 2025 -0800) on "linux_x64"
- on linux / Linux 6.17.7-300.fc43.x86_64 #1 SMP PREEMPT_DYNAMIC Sun Nov  2 15:30:09 UTC 2025
- locale is en_US.UTF-8

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-vmUse area-vm for VM related issues, including code coverage, and the AOT and JIT backends.library-io

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions