Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | 2x 2x 2x 2x 10x 10x 11x 11x 6x 6x 6x 6x 6x 2x 2x 1x 4x 4x 4x 4x 4x 3x 1x 2x 2x 2x 2x 2x 2x 2x 10x 12x 12x 12x 1x 12x 1x 1x 1x 1x 12x 12x 11x 11x 10x 10x 11x | import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage';
import { Subject } from 'rxjs';
/**
* @class storeService
* @description Provides methods to interact with the storage system, including key-value operations (`Get`, `Set`, `Del`).
* Supports scoped keys and ensures thread-safe access with a mutex-based locking mechanism.
*/
@Injectable({
providedIn: 'root'
})
export class storeService {
/**
* The underlying storage instance.
* Initialized using the `Initialize` method.
*/
private _storage: Storage | null = null;
/**
* Constructor to inject the storage dependency.
* @param storage - An instance of `@ionic/storage` for data persistence.
*/
constructor(private storage: Storage){
// nothing to do
}
/**
* Initializes the storage instance and generates a unique tab/session identifier.
* @returns A promise that resolves to `true` if the storage is initialized successfully, or `false` otherwise.
*/
private async Initialize(): Promise <boolean> {
this._storage = await this.storage.create();
return true;
}
/****************************/
/* STORAGE ACCESS */
/****************************/
/**
* Retrieves a value from the storage by key.
* @param key - The key associated with the stored value.
* @returns A promise that resolves to the stored value (parsed as JSON if applicable) or `null` if the key does not exist.
*/
async Get(key: string): Promise <any | null> {
Iif (!this._storage){
await this.Initialize();
Iif (!this._storage){
return null;
}
}
await this._AddCompleted(key);
const _data = await this._storage.get(key);
this._DelCompleted(key);
if (_data){
try {
return JSON.parse(_data);
}
catch {
return null; // not json encoded
}
}
return null;
}
/**
* Stores a value in the storage under the specified key.
* @param key - The key under which the value will be stored.
* @param value - The value to be stored (serialized to JSON).
* @returns A promise that resolves once the operation is complete.
* @throws An error if the operation fails.
*/
async Set(key: string, value: any): Promise <void> {
Iif (!this._storage){
await this.Initialize();
Iif (!this._storage){
return;
}
}
try {
await this._AddCompleted(key);
await this._storage.set(key, JSON.stringify(value))
this._DelCompleted(key);
}
catch(error: any){
throw new Error("[STORAGE] Cannot set value in key '" + key + "' (" + value.length + " bytes) - " + error.name + "(" + error.message + ")");
}
}
/**
* Deletes a value from the storage by key.
* @param key - The key associated with the value to be deleted.
* @returns A promise that resolves once the operation is complete.
*/
async Del(key: string): Promise <void> {
console.info("[STORAGE] removing key '" + key + "'");
Iif (!this._storage){
return; // not intialized
}
await this._AddCompleted(key);
await this._storage.remove(key);
this._DelCompleted(key);
// check that it has been removed (and clear if not)
const _data = await this.Get(key);
Iif (_data){ // not removed workarround. Clear the content
await this.Set(key, null);
}
}
/****************************/
/* SAVE ACCESS TO THE KEY */
/****************************/
/**
* A map of mutexes for managing concurrent access to keys.
*/
private _mutexes = new Map < string, Subject<void> > ();
/**
* Waits for the completion of any ongoing operation for the specified key.
* @param key - The key to check for concurrent operations.
* @returns A promise that resolves once the ongoing operation completes.
*/
private async _GetCompleted(key: string): Promise <void> {
let _observable: any | null = null;
const _subject = this._mutexes.get(key);
if (_subject){
_observable = _subject.asObservable();
}
if (_observable){ // writing in progress
const _ini = performance.now();
console.info("[STORE] waiting for write [" + key + "] to be completed..")
await _observable;
console.info("[STORE] write [" + key + "] completed in " + (performance.now() - _ini).toFixed(2) + " ms")
}
}
/**
* Adds a mutex lock for the specified key to prevent concurrent access.
* @param key - The key to lock.
* @returns A promise that resolves once the lock is added.
*/
private async _AddCompleted(key: string): Promise <void> {
await this._GetCompleted(key);
// the key is free: block next ones
this._mutexes.set(key, new Subject<void>())
}
/**
* Releases the mutex lock for the specified key.
* @param key - The key to unlock.
*/
private _DelCompleted(key: string): void {
const _subject = this._mutexes.get(key);
if (_subject){
_subject.next(); // notify
_subject.complete(); // release
}
this._mutexes.delete(key);
}
}
|