Skip to content

Commit

Permalink
Fix pending timer issue with ref.keepAlive() (#3156)
Browse files Browse the repository at this point in the history
fixes #1941
  • Loading branch information
rrousselGit committed Nov 26, 2023
1 parent b7056b5 commit 8158609
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 11 deletions.
4 changes: 0 additions & 4 deletions .github/workflows/check_generation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@ name: Check code-generation

on:
pull_request:
paths-ignore:
- "**.md"
push:
branches:
- master
- dev
paths-ignore:
- "**.md"

jobs:
check_generation:
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/cleanup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Cleanup Workflow

on:
workflow_run:
workflows:
- "build"
- "changelog"
- check_generation
- riverpod_lint
types:
- completed

jobs:
cleanup:
runs-on: ubuntu-latest

steps:
- name: Cleanup
run: echo "This is the cleanup job."
5 changes: 5 additions & 0 deletions packages/flutter_riverpod/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## Unreleased fix

- Fix "pending timer" issue inside tests when using `ref.keepAlive()`.
- Fix `Ref.invalidate`/`Ref.refresh` not throwing on circular dependency.

## 2.4.8 - 2023-11-20

Fix exceptions when using multiple root `ProviderContainers`/`ProviderScopes`.
Expand Down
23 changes: 23 additions & 0 deletions packages/flutter_riverpod/test/consumer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,29 @@ import 'package:flutter_test/flutter_test.dart';
import 'utils.dart';

void main() {
testWidgets('Riverpod test', (tester) async {
// Regression test for https://github.com/rrousselGit/riverpod/pull/3156

final streamProvider = StreamProvider.autoDispose((ref) async* {});
final provider1 = Provider.autoDispose((ref) {
ref.keepAlive();

ref.watch(streamProvider);
});

await tester.pumpWidget(
ProviderScope(
child: Consumer(
builder: (context, ref, child) {
ref.watch(provider1);

return const SizedBox();
},
),
),
);
});

testWidgets('Passes key', (tester) async {
await tester.pumpWidget(
ProviderScope(
Expand Down
5 changes: 5 additions & 0 deletions packages/hooks_riverpod/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## Unreleased fix

- Fix "pending timer" issue inside tests when using `ref.keepAlive()`.
- Fix `Ref.invalidate`/`Ref.refresh` not throwing on circular dependency.

## 2.4.8 - 2023-11-20

Fix exceptions when using multiple root `ProviderContainers`/`ProviderScopes`.
Expand Down
1 change: 1 addition & 0 deletions packages/riverpod/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Unreleased fix

- Fix "pending timer" issue inside tests when using `ref.keepAlive()`.
- Fix `Ref.invalidate`/`Ref.refresh` not throwing on circular dependency.

## 2.4.8 - 2023-11-20
Expand Down
9 changes: 3 additions & 6 deletions packages/riverpod/lib/src/framework/container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -624,19 +624,16 @@ final b = Provider((ref) => ref.watch(a), dependencies: [a]);
/// This will destroy the state of all providers associated with this
/// [ProviderContainer] and call [Ref.onDispose] listeners.
void dispose() {
if (_disposed) {
return;
}
if (_disposed) return;

_disposed = true;
_parent?._children.remove(this);

_disposed = true;
if (_root == null) scheduler.dispose();

for (final element in getAllProviderElementsInOrder().toList().reversed) {
element.dispose();
}

if (_root == null) scheduler.dispose();
}

/// Traverse the [ProviderElementBase]s associated with this [ProviderContainer].
Expand Down
11 changes: 10 additions & 1 deletion packages/riverpod/lib/src/framework/scheduler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ void _defaultVsync(void Function() task) {
/// Providers are disposed if they spent at least one full frame without any listener.
@internal
class ProviderScheduler {
var _disposed = false;

/// A way to override [vsync], used by Flutter to synchronize a container
/// with the widget tree.
@internal
Expand Down Expand Up @@ -61,7 +63,13 @@ class ProviderScheduler {
}

void _scheduleTask() {
if (_pendingTaskCompleter != null) return;
// Don't schedule a task if there is already one pending or if the scheduler
// is disposed.
// It is possible that during disposal of a ProviderContainer, if a provider
// uses ref.keepAlive(), the keepAlive closure will try to schedule a task.
// In this case, we don't want to schedule a task as the container is already
// disposed.
if (_pendingTaskCompleter != null || _disposed) return;
_pendingTaskCompleter = Completer<void>();
vsync(_task);
}
Expand Down Expand Up @@ -122,6 +130,7 @@ class ProviderScheduler {
}

void dispose() {
_disposed = true;
_pendingTaskCompleter?.complete();
_pendingTaskCompleter = null;
}
Expand Down

0 comments on commit 8158609

Please sign in to comment.