Skip to content

Conversation

@gspencergoog
Copy link
Collaborator

Description

This replaces the task queue called MultiFutureTracker with a TaskQueue, which fixes the concurrency issue where the old class didn't really wait for tasks to complete before starting a new one.

This uses a completer to provide a new future to wait for each task, uses a Queue to keep the tasks, and returns a Future<T> with the result of the task instead of waiting on a Future<void>.

There is also a future to wait on that will complete when the queue is empty (replacing wait) called tasksComplete.

Related Issues

Tests

  • Updated tests to use the new API.

@gspencergoog gspencergoog requested a review from srawlins August 5, 2021 21:46
@google-cla google-cla bot added the cla: yes Google CLA check succeeded. label Aug 5, 2021
@gspencergoog gspencergoog changed the title Fix concurrency issues Fix concurrency issues in tool execution Aug 5, 2021
Copy link
Member

@srawlins srawlins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great implementation. Just a few comments.

Copy link
Member

@srawlins srawlins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this, but I'd love a review from @jcollins-g as well before we merge.

Copy link
Contributor

@jcollins-g jcollins-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really good, a definite improvement on my original implementation. Function typedefs for the win, too!

class MultiFutureTracker<T> {
/// Approximate maximum number of simultaneous active Futures.
final int parallel;
typedef TaskQueueClosure<T> = Future<T> Function();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this makes things so much easier to read.


final Queue<_TaskQueueItem<T>> _pendingTasks = Queue<_TaskQueueItem<T>>();
final Set<_TaskQueueItem<T>> _activeTasks = <_TaskQueueItem<T>>{};
final Set<Completer<void>> _completeListeners = <Completer<void>>{};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this structure is more easily verified than the queue-length original version for waiting on completion. nice.

Future<T> add(TaskQueueClosure<T> task) {
final completer = Completer<T>();
_pendingTasks.add(_TaskQueueItem<T>(task, completer));
_processTasks();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC this seems like it will spawn a lot of _processTasks() that could potentially do "duplicate" work in the case where the _processTask() from the onComplete has it covered. This is probably fine though as the duplicates are written to collapse and do nothing. The old version probably got into trouble doing things cleverly to avoid this...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, possibly, but it would also make sure that when a new task is added, it starts running right away.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I was thinking of the case though when the maximum concurrency is reached and _processTasks() here doesn't have any useful effect. It seems like it should be OK just to inline _processTasks() here and eliminate an unawaited future that sometimes doesn't do anything. Your choice, and of course, only if it works just as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good point, that makes sense. OK, I inlined it.

Copy link
Contributor

@jcollins-g jcollins-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: yes Google CLA check succeeded.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Non-Dart Tool invocations aren't throttled

3 participants