fix: Fix initialization race condition that occurs in certain circumstances (#1545)

* fix: Fix initialization race condition in certain circumstances

* Minor improvements and camera iris logo
This commit is contained in:
Dermot Duffy
2024-09-19 19:42:38 -07:00
committed by GitHub
parent af4666e023
commit 650d99ec00
26 changed files with 611 additions and 461 deletions
+12 -7
View File
@@ -11,6 +11,7 @@ import {
CardHelpers,
ExtendedHomeAssistant,
FrigateCardError,
LovelaceCardWithEditor,
SignedPath,
signedPathSchema,
StateParameters,
@@ -316,17 +317,21 @@ export const sideLoadHomeAssistantElements = async (): Promise<boolean> => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const helpers: CardHelpers = await (window as any).loadCardHelpers();
// The picture-glance editor loads everything this card needs.
const pictureGlance = await helpers.createCardElement({
// This bizarre combination of hacks creates a dummy picture glance card, then
// waits for it to be fully loaded/upgraded as a custom element, so it will
// have the getConfigElement() method which is necessary to load all the
// elements this card requires.
await helpers.createCardElement({
type: 'picture-glance',
entities: [],
camera_image: 'dummy-to-load-editor-components',
});
if (pictureGlance.constructor.getConfigElement) {
await pictureGlance.constructor.getConfigElement();
return true;
}
return false;
const pgcConstructor = await customElements.whenDefined('hui-picture-glance-card');
const pgc = new pgcConstructor() as LovelaceCardWithEditor;
await pgc.constructor.getConfigElement();
return true;
};
/**
+18 -38
View File
@@ -1,21 +1,14 @@
import { allPromises } from '../basic';
enum InitializationState {
INITIALIZING = 'initializing',
INITIALIZED = 'initialized',
}
type InitializationCallback = () => Promise<boolean>;
/**
* Manages initialization state & calling initializers.
* Manages initialization state & calling initializers. There is no guarantee
* something will not be initialized twice unless there are concurrency controls
* applied to the usage of this class.
*/
export class Initializer {
protected _state: Map<string, InitializationState>;
constructor() {
this._state = new Map();
}
protected _initialized: Set<string> = new Set();
public async initializeMultipleIfNecessary(
aspects: Record<string, InitializationCallback>,
@@ -27,43 +20,30 @@ export class Initializer {
return results.every(Boolean);
}
/**
*
* @param aspect The aspect to initialize.
* @param initializer The initializer to call.
* @returns `true` if the state is confirmed as initialized, `false`
* otherwise (i.e. initializing).
*/
public async initializeIfNecessary(
aspect: string,
initializer?: InitializationCallback,
): Promise<boolean> {
const state = this._state.get(aspect);
if (state !== InitializationState.INITIALIZED) {
if (state !== InitializationState.INITIALIZING) {
if (initializer) {
this._state.set(aspect, InitializationState.INITIALIZING);
if (await initializer()) {
this._state.set(aspect, InitializationState.INITIALIZED);
} else {
this.uninitialize(aspect);
}
} else {
this._state.set(aspect, InitializationState.INITIALIZED);
}
return true;
}
return false;
if (this._initialized.has(aspect)) {
return true;
}
return true;
if (!initializer) {
this._initialized.add(aspect);
return true;
}
if (await initializer()) {
this._initialized.add(aspect);
return true;
}
return false;
}
public uninitialize(aspect: string) {
return this._state.delete(aspect);
public uninitialize(aspect: string): void {
this._initialized.delete(aspect);
}
public isInitialized(aspect: string): boolean {
return this._state.get(aspect) == InitializationState.INITIALIZED;
return this._initialized.has(aspect);
}
public isInitializedMultiple(aspects: string[]): boolean {