From 1904a5fc1bb5dfcfe9bd2cd4bf060f53ecf80655 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Fri, 10 Dec 2021 12:20:42 -0800 Subject: [PATCH] server: better mixin invalidation to prevent weird bugs like settings not loading, etc --- server/src/plugin/plugin-device.ts | 5 ++- server/src/plugin/plugin-host.ts | 17 +-------- server/src/runtime.ts | 60 +++++++++++++++++++++--------- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/server/src/plugin/plugin-device.ts b/server/src/plugin/plugin-device.ts index 83cf6aa4b..005403ae2 100644 --- a/server/src/plugin/plugin-device.ts +++ b/server/src/plugin/plugin-device.ts @@ -35,7 +35,7 @@ export class PluginDeviceProxyHandler implements ProxyHandler, ScryptedDevi } invalidateEntry(mixinEntry: MixinTable) { - if (!mixinEntry.mixinProviderId) + if (!mixinEntry?.mixinProviderId) return; (async () => { const mixinProvider = this.scrypted.getDevice(mixinEntry.mixinProviderId) as ScryptedDevice & MixinProvider; @@ -53,6 +53,9 @@ export class PluginDeviceProxyHandler implements ProxyHandler, ScryptedDevi } } + /** + * Rebuild the mixin table with any currently missing mixins. + */ rebuildMixinTable() { if (!this.mixinTable) return this.invalidate(); diff --git a/server/src/plugin/plugin-host.ts b/server/src/plugin/plugin-host.ts index dcfd6dd1e..628715d3a 100644 --- a/server/src/plugin/plugin-host.ts +++ b/server/src/plugin/plugin-host.ts @@ -60,21 +60,8 @@ export class PluginHost { } this.ws = {}; - // const pluginDevices = new Set(Object.values(this.scrypted.pluginDevices).filter(d => d.pluginId === this.pluginId).map(d => d._id)); - // this.scrypted.invalidateMixins(pluginDevices); - - for (const device of Object.values(this.scrypted.devices)) { - const pluginDevice = this.scrypted.pluginDevices[device.handler.id]; - if (!pluginDevice) { - console.warn('PluginDevice missing?', device.handler.id); - continue; - } - for (const mixin of getState(pluginDevice, ScryptedInterfaceProperty.mixins) || []) { - if (this.scrypted.findPluginDeviceById(mixin)?.pluginId === this.pluginId) { - device.handler.invalidate(); - } - } - } + const deviceIds = new Set(Object.values(this.scrypted.pluginDevices).filter(d => d.pluginId === this.pluginId).map(d => d._id)); + this.scrypted.invalidateMixins(deviceIds); this.consoleServer?.then(server => { server.readServer.close(); diff --git a/server/src/runtime.ts b/server/src/runtime.ts index f476a4b2f..3d7583a2b 100644 --- a/server/src/runtime.ts +++ b/server/src/runtime.ts @@ -27,7 +27,7 @@ import { ServiceControl } from './services/service-control'; import { Alerts } from './services/alerts'; import { Info } from './services/info'; import io from 'engine.io'; -import {spawn as ptySpawn} from 'node-pty'; +import { spawn as ptySpawn } from 'node-pty'; import rimraf from 'rimraf'; import { getPluginVolume } from './plugin/plugin-volume'; @@ -435,25 +435,49 @@ export class ScryptedRuntime { return proxyPair; } - invalidateMixins(mixinIds: Set) { - // const ret = new Set(mixinIds); + invalidateMixins(ids: Set) { + const ret = new Set(); + const remaining = [...ids]; - // for (const device of Object.values(this.devices)) { - // const pluginDevice = this.pluginDevices[device.handler.id]; - // if (ret.has(pluginDevice._id)) - // continue; - // if (!pluginDevice) { - // console.warn('PluginDevice missing?', device.handler.id); - // continue; - // } - // for (const mixin of getState(pluginDevice, ScryptedInterfaceProperty.mixins) || []) { - // if (this.scrypted.findPluginDeviceById(mixin)?.pluginId === this.pluginId) { - // device.handler.invalidate(); - // } - // } - // } + // first pass: + // for every id, find anything it is acting as a mixin, and clear out the entry. + while (remaining.length) { + const id = remaining.pop(); - // return ret; + for (const device of Object.values(this.devices)) { + const foundIndex = device.handler?.mixinTable?.findIndex(mt => mt.mixinProviderId === id); + if (foundIndex === -1 || foundIndex === undefined) + continue; + + const did = device.handler.id; + if (!ret.has(did)) { + // add this to the list of mixin providers that need to be rebuilt + ret.add(did); + remaining.push(did); + } + + // if it is the last entry, that means it is the device itself. + // can this happen? i don't think it is possible. mixin provider id would be undefined. + if (foundIndex === device.handler.mixinTable.length - 1) { + console.warn('attempt to invalidate mixin on actual device?'); + continue; + } + + const removed = device.handler.mixinTable.splice(0, foundIndex + 1); + for (const entry of removed) { + device.handler.invalidateEntry(entry); + } + } + } + + // second pass: + // rebuild the mixin tables. + for (const id of ret) { + const device = this.devices[id]; + device.handler.rebuildMixinTable(); + } + + return ret; } async installNpm(pkg: string, version?: string): Promise {