Compare commits

..

1 Commits

Author SHA1 Message Date
e578b961d8 Wrap the EngineCommandManager with a singleton.
There should only be one backend connection at a time, and we really
don't want concurrent instances. This will synchronize access, only
creating a new EngineCommandManager if the width/height requested
is different than the last cached version.

Signed-off-by: Paul Tagliamonte <paul@kittycad.io>
2023-09-20 17:08:00 -04:00
2 changed files with 71 additions and 46 deletions

View File

@ -1,8 +1,57 @@
import { useRef, useLayoutEffect } from 'react'
import { useLayoutEffect } from 'react'
import { _executor } from '../lang/executor'
import { useStore } from '../useStore'
import { EngineCommandManager } from '../lang/std/engineConnection'
class EngineCommandManagerSingleton {
width: number
height: number
engineCommandManager?: EngineCommandManager
constructor() {
this.width = 0
this.height = 0
}
get({
setMediaStream,
setIsStreamReady,
width,
height,
token,
}: {
setMediaStream: (stream: MediaStream) => void
setIsStreamReady: (isStreamReady: boolean) => void
width: number
height: number
token?: string
}): EngineCommandManager {
if (
this.engineCommandManager !== undefined &&
this.width == width &&
this.height == height
) {
return this.engineCommandManager
}
const ecm = new EngineCommandManager({
setMediaStream,
setIsStreamReady,
width: width,
height: height,
token,
})
this.engineCommandManager = ecm
this.width = width
this.height = height
return ecm
}
}
const engineManagerSingleton = new EngineCommandManagerSingleton()
export function useSetupEngineManager(
streamRef: React.RefObject<HTMLDivElement>,
token?: string
@ -28,11 +77,6 @@ export function useSetupEngineManager(
const quadWidth = Math.round(width / 4) * 4
const height = streamHeight ? streamHeight : 0
const quadHeight = Math.round(height / 4) * 4
const eng = useRef<{
engine: EngineCommandManager
width: number
height: number
} | null>(null)
useLayoutEffect(() => {
setStreamDimensions({
@ -40,37 +84,19 @@ export function useSetupEngineManager(
streamHeight: quadHeight,
})
if (!width || !height) return
if (eng.current) {
// Before we go further, we're going to check to see if the
// width/height is the same as the last go-around. If it is, we
// can continue as normal, but if it's different, we should be
// clearing out the manager and going again.
let c = eng.current
if (width !== c.width || height !== c.height) {
eng.current = null
}
}
if (eng.current === null) {
eng.current = {
engine: new EngineCommandManager({
setMediaStream,
setIsStreamReady,
width: quadWidth,
height: quadHeight,
token,
}),
width: width,
height: height,
}
}
setEngineCommandManager(eng.current.engine)
eng.current.engine.waitForReady.then(() => {
const eng = engineManagerSingleton.get({
setMediaStream,
setIsStreamReady,
width: quadWidth,
height: quadHeight,
token,
})
setEngineCommandManager(eng)
eng.waitForReady.then(() => {
executeCode()
})
return () => {
eng.current?.engine?.tearDown()
eng?.tearDown()
}
}, [quadWidth, quadHeight])
}

View File

@ -68,12 +68,12 @@ export class EngineConnection {
constructor({
url,
token,
onWebsocketOpen = () => { },
onNewTrack = () => { },
onEngineConnectionOpen = () => { },
onConnectionStarted = () => { },
onClose = () => { },
onDataChannelOpen = () => { },
onWebsocketOpen = () => {},
onNewTrack = () => {},
onEngineConnectionOpen = () => {},
onConnectionStarted = () => {},
onClose = () => {},
onDataChannelOpen = () => {},
}: {
url: string
token?: string
@ -364,10 +364,8 @@ export class EngineConnection {
// fix responsiveness for clients that had a weird network hiccup.
const connectionTimeoutMs = VITE_KC_CONNECTION_TIMEOUT_MS
console.log('setting timeout for connection')
setTimeout(() => {
if (this.isReady()) {
console.log('timeout fired but we were ready')
return
}
console.log('engine connection timeout on connection, retrying')
@ -533,8 +531,8 @@ export class EngineCommandManager {
outSequence = 1
inSequence = 1
engineConnection?: EngineConnection
waitForReady: Promise<void> = new Promise(() => { })
private resolveReady = () => { }
waitForReady: Promise<void> = new Promise(() => {})
private resolveReady = () => {}
subscriptions: {
[event: string]: {
@ -563,7 +561,6 @@ export class EngineCommandManager {
this.resolveReady = resolve
})
const url = `${VITE_KC_API_WS_MODELING_URL}?video_res_width=${width}&video_res_height=${height}`
console.log('new eng conn')
this.engineConnection = new EngineConnection({
url,
token,
@ -780,6 +777,7 @@ export class EngineCommandManager {
lastMessage = command.cmd.type
}
if (!this.engineConnection?.isReady()) {
console.log('socket not ready')
return Promise.resolve()
}
if (command.type !== 'modeling_cmd_req') return Promise.resolve()
@ -826,6 +824,7 @@ export class EngineCommandManager {
this.sourceRangeMap[id] = range
if (!this.engineConnection?.isReady()) {
console.log('socket not ready')
return Promise.resolve()
}
this.engineConnection?.send(command)
@ -843,7 +842,7 @@ export class EngineCommandManager {
command: Models['ModelingCmd_type'],
range?: SourceRange
) {
let resolve: (val: any) => void = () => { }
let resolve: (val: any) => void = () => {}
const promise = new Promise((_resolve, reject) => {
resolve = _resolve
})