1+ import 'dart:async' ;
2+
13import 'package:mutex/mutex.dart' ;
24
35import 'json_cache.dart' ;
6+ import 'json_cache_hollow.dart' ;
7+
8+ // ignore_for_file: prefer_void_to_null
9+
10+ /// [JsonCacheMem.init] initialization error callback.
11+ typedef OnInitError = FutureOr <Null > Function (Object , StackTrace );
412
513/// Thread-safe in-memory [JsonCache] decorator.
614///
@@ -9,17 +17,70 @@ import 'json_cache.dart';
917/// TODO: limit the maximum number of cache entries via "size" parameter in
1018/// constructors.
1119///
12- /// It encapsulates a slower chache but keeps its own data in-memory.
20+ /// It encapsulates a slower cache and keeps its own data in-memory.
1321class JsonCacheMem implements JsonCache {
14- /// Encapsulates a slower [level2] cache and utilizes a static memory that
15- /// will be shared (without race conditions) among all [JsonCacheMem] objects
16- /// that have been instantiated with this constructor.
17- JsonCacheMem (JsonCache level2) : this .mem (level2, _shrMem, _shrMutex);
18-
19- /// Cache with custom memory and an optional custom mutex.
20- JsonCacheMem .mem (JsonCache level2, Map <String , Map <String , dynamic >?> mem,
21- [ReadWriteMutex ? mutex])
22- : _level2 = level2,
22+ /// In-memory level1 cache with an optional level2 instance.
23+ ///
24+ /// ATTENTION: if you do not pass an object to the parameter [level2] , the
25+ /// data will remain cached in-memory only; that is, no data will be persited
26+ /// to the user's device's local storage area. Indeed, not persisting data on
27+ /// the user's device might be the desired behavior if you are at the
28+ /// prototyping or mocking phase. However, its unlikely to be the right
29+ /// behavior in production code.
30+ JsonCacheMem ([JsonCache ? level2])
31+ : this .ext (_shrMem, level2: level2, mutex: _shrMutex);
32+
33+ /// Cache with initial data.
34+ ///
35+ /// Besides copying data from [init] to its internal shared memory, it
36+ /// encapsulates a [level2] cache that is supposed to persist data to the
37+ /// user's device's local storage area.
38+ ///
39+ /// It also provides a type of transaction guarantee whereby, if an error
40+ /// occurs while copying [init] to the cache, it tries to revert the cached
41+ /// data to its previous state before rethrowing the exception. Finally, after
42+ /// reverting the cached data, it invokes [onInitError] .
43+ JsonCacheMem .init (
44+ Map <String , Map <String , dynamic >?> init, {
45+ JsonCache ? level2,
46+ OnInitError ? onInitError,
47+ }) : _level2 = level2 ?? const JsonCacheHollow (),
48+ _memory = _shrMem,
49+ _mutex = _shrMutex {
50+ final copy = Map <String , Map <String , dynamic >?>.of (init);
51+ final initFut = _mutex.protectWrite (() async {
52+ final writes = < String > []; // the list of written keys.
53+ for (final entry in copy.entries) {
54+ final key = entry.key;
55+ final value = entry.value;
56+ if (value != null ) {
57+ writes.add (key);
58+ try {
59+ await _level2.refresh (key, value);
60+ _memory[key] = value;
61+ } catch (error) {
62+ for (final write in writes) {
63+ await _level2.remove (write);
64+ _memory.remove (write);
65+ }
66+ rethrow ;
67+ }
68+ }
69+ }
70+ });
71+ if (onInitError != null ) {
72+ initFut.onError (onInitError);
73+ }
74+ }
75+
76+ /// Cache with an external memory and an optional custom mutex.
77+ ///
78+ /// **Note**: the memory [mem] will **not** be copied deeply.
79+ JsonCacheMem .ext (
80+ Map <String , Map <String , dynamic >?> mem, {
81+ JsonCache ? level2,
82+ ReadWriteMutex ? mutex,
83+ }) : _level2 = level2 ?? const JsonCacheHollow (),
2384 _memory = mem,
2485 _mutex = mutex ?? ReadWriteMutex ();
2586
@@ -80,11 +141,16 @@ class JsonCacheMem implements JsonCache {
80141 @override
81142 Future <Map <String , dynamic >?> value (String key) async {
82143 return _mutex.protectRead (() async {
83- if (! _memory.containsKey (key)) {
84- _memory[key] = await _level2.value (key);
144+ final cachedL1 = _memory[key];
145+ if (cachedL1 != null ) return Map <String , dynamic >.of (cachedL1);
146+
147+ final cachedL2 = await _level2.value (key);
148+ if (cachedL2 != null ) {
149+ _memory[key] = cachedL2;
150+ return Map <String , dynamic >.of (cachedL2);
85151 }
86- final cached = _memory[key];
87- return cached == null ? cached : Map < String , dynamic >. of (cached) ;
152+
153+ return null ;
88154 });
89155 }
90156}
0 commit comments