blob: 6e02e8121c976575f6794f5938a09b6faf5f707d [file] [log] [blame]
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:collection' show Queue;
import 'type_graph_nodes.dart';
const _numBuckets = 2;
/// This function applies a bucket index to each type information.
///
/// Current strategy:
/// Process call sites together effectively splitting intraprocedural type
/// refinement and local type refinement.
int _bucketForInfo(TypeInformation info) {
if (info is! CallSiteTypeInformation) return 0;
return 1;
}
/// A work queue for the inferrer. It filters out nodes that are tagged as
/// [TypeInformation.doNotEnqueue], as well as ensures through
/// [TypeInformation.inQueue] that a node is in the queue only once at
/// a time.
///
/// The queue uses a bucketed approach to allow the inferrer to make progress
/// on certain categories of types while also ensuring no category is starved
/// of resources. We grab a "snapshot" of the active bucket and process elements
/// from it until it is empty. Anything added to the bucket after we have
/// grabbed the snapshot is not processed immediately. Instead once the snapshot
/// is empty we move on to the next bucket. By ignoring elements after the
/// snapshot we prevent the queue from getting stuck on loops within the same
/// bucket.
class WorkQueue {
final List<Queue<TypeInformation>> buckets =
List.generate(_numBuckets, (_) => Queue(), growable: false);
int _length = 0;
Queue<TypeInformation> _activeQueue = Queue();
int _activeBucketIndex = 0;
void add(TypeInformation element) {
if (element.doNotEnqueue) return;
if (element.inQueue) return;
buckets[_bucketForInfo(element)].add(element);
element.inQueue = true;
_length++;
}
void addAll(Iterable<TypeInformation> all) {
all.forEach(add);
}
TypeInformation remove() {
while (_activeQueue.isEmpty) {
assert(_length != 0);
final bucket = buckets[_activeBucketIndex];
if (bucket.isNotEmpty) {
final tmp = _activeQueue;
_activeQueue = buckets[_activeBucketIndex];
buckets[_activeBucketIndex] = tmp;
}
_incrementBucketIndex();
}
final element = _activeQueue.removeFirst();
_length--;
element.inQueue = false;
return element;
}
void _incrementBucketIndex() {
_activeBucketIndex =
_activeBucketIndex == buckets.length - 1 ? 0 : _activeBucketIndex + 1;
}
bool get isEmpty => _length == 0;
int get length => _length;
}