Skip to content

Commit 1938168

Browse files
committed
CompleterManager
1 parent 1584f3d commit 1938168

File tree

2 files changed

+533
-0
lines changed

2 files changed

+533
-0
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright 2025 LiveKit, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import 'dart:async';
16+
17+
/// A manager for Completer instances that provides safe completion and automatic lifecycle management.
18+
///
19+
/// Features:
20+
/// - Safe completion (prevents double completion exceptions)
21+
/// - Automatic timeout handling
22+
/// - Clean state management and reusability
23+
/// - Only exposes Future, not the Completer itself
24+
/// - Thread-safe operations
25+
class CompleterManager<T> {
26+
Completer<T> _completer;
27+
Timer? _timeoutTimer;
28+
bool _isCompleted = false;
29+
30+
/// Creates a new CompleterManager with an active completer.
31+
CompleterManager() : _completer = Completer<T>();
32+
33+
/// Gets the current future. Creates a new completer if previous one was completed.
34+
Future<T> get future {
35+
if (_isCompleted) {
36+
_reset();
37+
}
38+
return _completer.future;
39+
}
40+
41+
/// Whether the current completer is completed.
42+
bool get isCompleted => _isCompleted;
43+
44+
/// Whether there's an active completer waiting for completion.
45+
bool get isActive => !_isCompleted;
46+
47+
/// Completes the current completer with the given value.
48+
/// Returns true if successfully completed, false if already completed.
49+
bool complete([FutureOr<T>? value]) {
50+
if (_isCompleted) {
51+
return false;
52+
}
53+
54+
_isCompleted = true;
55+
_timeoutTimer?.cancel();
56+
_timeoutTimer = null;
57+
58+
_completer.complete(value);
59+
return true;
60+
}
61+
62+
/// Completes the current completer with an error.
63+
/// Returns true if successfully completed with error, false if already completed.
64+
bool completeError(Object error, [StackTrace? stackTrace]) {
65+
if (_isCompleted) {
66+
return false;
67+
}
68+
69+
_isCompleted = true;
70+
_timeoutTimer?.cancel();
71+
_timeoutTimer = null;
72+
73+
_completer.completeError(error, stackTrace);
74+
return true;
75+
}
76+
77+
/// Sets up a timeout for the current completer.
78+
/// If the completer is not completed within the timeout, it will be completed with a TimeoutException.
79+
void setTimer(Duration timeout, {String? timeoutReason}) {
80+
if (_isCompleted) {
81+
return;
82+
}
83+
84+
_timeoutTimer?.cancel();
85+
_timeoutTimer = Timer(timeout, () {
86+
if (!_isCompleted) {
87+
final reason = timeoutReason ?? 'Operation timed out after $timeout';
88+
completeError(TimeoutException(reason, timeout));
89+
}
90+
});
91+
}
92+
93+
/// Resets the manager, canceling any pending operations and preparing for reuse.
94+
void reset() {
95+
_reset();
96+
}
97+
98+
void _reset() {
99+
_timeoutTimer?.cancel();
100+
_timeoutTimer = null;
101+
_isCompleted = false;
102+
_completer = Completer<T>();
103+
}
104+
105+
/// Disposes the manager, canceling any pending operations.
106+
void dispose() {
107+
_timeoutTimer?.cancel();
108+
_timeoutTimer = null;
109+
if (!_isCompleted) {
110+
_completer.completeError(StateError('CompleterManager disposed'));
111+
_isCompleted = true;
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)