mirror of https://github.com/jetkvm/kvm.git
feat: simplify failsafe mode for native proxy
This commit is contained in:
parent
21641ffda5
commit
953b9ded30
|
|
@ -77,10 +77,12 @@ func checkFailsafeReason() {
|
|||
_ = os.Remove(lastCrashPath)
|
||||
|
||||
// TODO: read the goroutine stack trace and check which goroutine is panicking
|
||||
failsafeModeActive = true
|
||||
if strings.Contains(failsafeCrashLog, "runtime.cgocall") {
|
||||
failsafeModeActive = true
|
||||
failsafeModeReason = "video"
|
||||
return
|
||||
} else {
|
||||
failsafeModeReason = "unknown"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,17 +50,9 @@ static inline void jetkvm_cgo_setup_rpc_handler() {
|
|||
import "C"
|
||||
|
||||
var (
|
||||
cgoLock sync.Mutex
|
||||
cgoDisabled bool
|
||||
cgoLock sync.Mutex
|
||||
)
|
||||
|
||||
func setCgoDisabled(disabled bool) {
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
cgoDisabled = disabled
|
||||
}
|
||||
|
||||
//export jetkvm_go_video_state_handler
|
||||
func jetkvm_go_video_state_handler(state *C.jetkvm_video_state_t) {
|
||||
videoState := VideoState{
|
||||
|
|
@ -104,10 +96,6 @@ func jetkvm_go_rpc_handler(method *C.cchar_t, params *C.cchar_t) {
|
|||
var eventCodeToNameMap = map[int]string{}
|
||||
|
||||
func uiEventCodeToName(code int) string {
|
||||
if cgoDisabled {
|
||||
return ""
|
||||
}
|
||||
|
||||
name, ok := eventCodeToNameMap[code]
|
||||
if !ok {
|
||||
cCode := C.int(code)
|
||||
|
|
@ -120,10 +108,6 @@ func uiEventCodeToName(code int) string {
|
|||
}
|
||||
|
||||
func setUpNativeHandlers() {
|
||||
if cgoDisabled {
|
||||
return
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -135,10 +119,6 @@ func setUpNativeHandlers() {
|
|||
}
|
||||
|
||||
func uiInit(rotation uint16) {
|
||||
if cgoDisabled {
|
||||
return
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -148,10 +128,6 @@ func uiInit(rotation uint16) {
|
|||
}
|
||||
|
||||
func uiTick() {
|
||||
if cgoDisabled {
|
||||
return
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -159,10 +135,6 @@ func uiTick() {
|
|||
}
|
||||
|
||||
func videoInit(factor float64) error {
|
||||
if cgoDisabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -176,10 +148,6 @@ func videoInit(factor float64) error {
|
|||
}
|
||||
|
||||
func videoShutdown() {
|
||||
if cgoDisabled {
|
||||
return
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -187,10 +155,6 @@ func videoShutdown() {
|
|||
}
|
||||
|
||||
func videoStart() {
|
||||
if cgoDisabled {
|
||||
return
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -198,10 +162,6 @@ func videoStart() {
|
|||
}
|
||||
|
||||
func videoStop() {
|
||||
if cgoDisabled {
|
||||
return
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -209,10 +169,6 @@ func videoStop() {
|
|||
}
|
||||
|
||||
func videoLogStatus() string {
|
||||
if cgoDisabled {
|
||||
return ""
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -223,10 +179,6 @@ func videoLogStatus() string {
|
|||
}
|
||||
|
||||
func uiSetVar(name string, value string) {
|
||||
if cgoDisabled {
|
||||
return
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -240,10 +192,6 @@ func uiSetVar(name string, value string) {
|
|||
}
|
||||
|
||||
func uiGetVar(name string) string {
|
||||
if cgoDisabled {
|
||||
return ""
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -254,10 +202,6 @@ func uiGetVar(name string) string {
|
|||
}
|
||||
|
||||
func uiSwitchToScreen(screen string) {
|
||||
if cgoDisabled {
|
||||
return
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -267,10 +211,6 @@ func uiSwitchToScreen(screen string) {
|
|||
}
|
||||
|
||||
func uiGetCurrentScreen() string {
|
||||
if cgoDisabled {
|
||||
return ""
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -279,10 +219,6 @@ func uiGetCurrentScreen() string {
|
|||
}
|
||||
|
||||
func uiObjAddState(objName string, state string) (bool, error) {
|
||||
if cgoDisabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -295,10 +231,6 @@ func uiObjAddState(objName string, state string) (bool, error) {
|
|||
}
|
||||
|
||||
func uiObjClearState(objName string, state string) (bool, error) {
|
||||
if cgoDisabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -311,10 +243,6 @@ func uiObjClearState(objName string, state string) (bool, error) {
|
|||
}
|
||||
|
||||
func uiGetLVGLVersion() string {
|
||||
if cgoDisabled {
|
||||
return ""
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -323,10 +251,6 @@ func uiGetLVGLVersion() string {
|
|||
|
||||
// TODO: use Enum instead of string but it's not a hot path and performance is not a concern now
|
||||
func uiObjAddFlag(objName string, flag string) (bool, error) {
|
||||
if cgoDisabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -339,10 +263,6 @@ func uiObjAddFlag(objName string, flag string) (bool, error) {
|
|||
}
|
||||
|
||||
func uiObjClearFlag(objName string, flag string) (bool, error) {
|
||||
if cgoDisabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -363,10 +283,6 @@ func uiObjShow(objName string) (bool, error) {
|
|||
}
|
||||
|
||||
func uiObjSetOpacity(objName string, opacity int) (bool, error) {
|
||||
if cgoDisabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -378,10 +294,6 @@ func uiObjSetOpacity(objName string, opacity int) (bool, error) {
|
|||
}
|
||||
|
||||
func uiObjFadeIn(objName string, duration uint32) (bool, error) {
|
||||
if cgoDisabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -394,10 +306,6 @@ func uiObjFadeIn(objName string, duration uint32) (bool, error) {
|
|||
}
|
||||
|
||||
func uiObjFadeOut(objName string, duration uint32) (bool, error) {
|
||||
if cgoDisabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -410,10 +318,6 @@ func uiObjFadeOut(objName string, duration uint32) (bool, error) {
|
|||
}
|
||||
|
||||
func uiLabelSetText(objName string, text string) (bool, error) {
|
||||
if cgoDisabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -431,10 +335,6 @@ func uiLabelSetText(objName string, text string) (bool, error) {
|
|||
}
|
||||
|
||||
func uiImgSetSrc(objName string, src string) (bool, error) {
|
||||
if cgoDisabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -450,10 +350,6 @@ func uiImgSetSrc(objName string, src string) (bool, error) {
|
|||
}
|
||||
|
||||
func uiDispSetRotation(rotation uint16) (bool, error) {
|
||||
if cgoDisabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -466,10 +362,6 @@ func uiDispSetRotation(rotation uint16) (bool, error) {
|
|||
}
|
||||
|
||||
func videoGetStreamQualityFactor() (float64, error) {
|
||||
if cgoDisabled {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -478,10 +370,6 @@ func videoGetStreamQualityFactor() (float64, error) {
|
|||
}
|
||||
|
||||
func videoSetStreamQualityFactor(factor float64) error {
|
||||
if cgoDisabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -490,10 +378,6 @@ func videoSetStreamQualityFactor(factor float64) error {
|
|||
}
|
||||
|
||||
func videoGetEDID() (string, error) {
|
||||
if cgoDisabled {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
@ -502,10 +386,6 @@ func videoGetEDID() (string, error) {
|
|||
}
|
||||
|
||||
func videoSetEDID(edid string) error {
|
||||
if cgoDisabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
cgoLock.Lock()
|
||||
defer cgoLock.Unlock()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,111 @@
|
|||
package native
|
||||
|
||||
type EmptyNativeInterface struct {
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) Start() error { return nil }
|
||||
|
||||
func (e *EmptyNativeInterface) VideoSetSleepMode(enabled bool) error { return nil }
|
||||
|
||||
func (e *EmptyNativeInterface) VideoGetSleepMode() (bool, error) { return false, nil }
|
||||
|
||||
func (e *EmptyNativeInterface) VideoSleepModeSupported() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) VideoSetQualityFactor(factor float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) VideoGetQualityFactor() (float64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) VideoSetEDID(edid string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) VideoGetEDID() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) VideoLogStatus() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) VideoStop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) VideoStart() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) GetLVGLVersion() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UIObjHide(objName string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UIObjShow(objName string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UISetVar(name string, value string) {
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UIGetVar(name string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UIObjAddState(objName string, state string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UIObjClearState(objName string, state string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UIObjAddFlag(objName string, flag string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UIObjClearFlag(objName string, flag string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UIObjSetOpacity(objName string, opacity int) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UIObjFadeIn(objName string, duration uint32) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UIObjFadeOut(objName string, duration uint32) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UIObjSetLabelText(objName string, text string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UIObjSetImageSrc(objName string, image string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) DisplaySetRotation(rotation uint16) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *EmptyNativeInterface) UpdateLabelIfChanged(objName string, newText string) {}
|
||||
|
||||
func (e *EmptyNativeInterface) UpdateLabelAndChangeVisibility(objName string, newText string) {}
|
||||
|
||||
func (e *EmptyNativeInterface) SwitchToScreenIf(screenName string, shouldSwitch []string) {}
|
||||
|
||||
func (e *EmptyNativeInterface) SwitchToScreenIfDifferent(screenName string) {}
|
||||
|
||||
func (e *EmptyNativeInterface) DoNotUseThisIsForCrashTestingOnly() {}
|
||||
|
|
@ -131,7 +131,7 @@ func (c *GRPCClient) startEventStream() {
|
|||
stream, err := c.client.StreamEvents(ctx, &pb.Empty{})
|
||||
if err != nil {
|
||||
c.logger.Warn().Err(err).Msg("failed to start event stream, retrying ...")
|
||||
time.Sleep(1 * time.Second)
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
)
|
||||
|
||||
type Native struct {
|
||||
disable bool
|
||||
ready chan struct{}
|
||||
l *zerolog.Logger
|
||||
lD *zerolog.Logger
|
||||
|
|
@ -28,7 +27,6 @@ type Native struct {
|
|||
}
|
||||
|
||||
type NativeOptions struct {
|
||||
Disable bool
|
||||
SystemVersion *semver.Version
|
||||
AppVersion *semver.Version
|
||||
DisplayRotation uint16
|
||||
|
|
@ -81,7 +79,6 @@ func NewNative(opts NativeOptions) *Native {
|
|||
}
|
||||
|
||||
return &Native{
|
||||
disable: opts.Disable,
|
||||
ready: make(chan struct{}),
|
||||
l: &nativeSubLogger,
|
||||
lD: &displaySubLogger,
|
||||
|
|
@ -100,12 +97,6 @@ func NewNative(opts NativeOptions) *Native {
|
|||
}
|
||||
|
||||
func (n *Native) Start() error {
|
||||
if n.disable {
|
||||
nativeLogger.Warn().Msg("native is disabled, skipping initialization")
|
||||
setCgoDisabled(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
// set up singleton
|
||||
setInstance(n)
|
||||
setUpNativeHandlers()
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ func (n *NativeOptions) toProxyOptions() *nativeProxyOptions {
|
|||
maxRestartAttempts = n.MaxRestartAttempts
|
||||
}
|
||||
return &nativeProxyOptions{
|
||||
Disable: n.Disable,
|
||||
SystemVersion: n.SystemVersion,
|
||||
AppVersion: n.AppVersion,
|
||||
DisplayRotation: n.DisplayRotation,
|
||||
|
|
@ -78,7 +77,6 @@ func (n *NativeOptions) toProxyOptions() *nativeProxyOptions {
|
|||
|
||||
func (p *nativeProxyOptions) toNativeOptions() *NativeOptions {
|
||||
return &NativeOptions{
|
||||
Disable: p.Disable,
|
||||
SystemVersion: p.SystemVersion,
|
||||
AppVersion: p.AppVersion,
|
||||
DisplayRotation: p.DisplayRotation,
|
||||
|
|
@ -135,7 +133,6 @@ type NativeProxy struct {
|
|||
// NewNativeProxy creates a new NativeProxy that spawns a separate process
|
||||
func NewNativeProxy(opts NativeOptions) (*NativeProxy, error) {
|
||||
proxyOptions := opts.toProxyOptions()
|
||||
proxyOptions.CtrlUnixSocket = fmt.Sprintf("jetkvm/native/grpc/%s", randomId(4))
|
||||
proxyOptions.VideoStreamUnixSocket = fmt.Sprintf("@jetkvm/native/video-stream/%s", randomId(4))
|
||||
|
||||
// Get the current executable path to spawn itself
|
||||
|
|
@ -211,6 +208,12 @@ func (w *nativeProxyStdoutHandler) Write(p []byte) (n int, err error) {
|
|||
}
|
||||
|
||||
func (p *NativeProxy) toProcessCommand() (*cmdWrapper, error) {
|
||||
// generate a new random ID for the gRPC socket on each restart
|
||||
// sometimes the socket is not closed properly when the process exits
|
||||
// this is a workaround to avoid the issue
|
||||
p.nativeUnixSocket = fmt.Sprintf("jetkvm/native/grpc/%s", randomId(4))
|
||||
p.options.CtrlUnixSocket = p.nativeUnixSocket
|
||||
|
||||
envArgs, err := utils.MarshalEnv(p.options)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal environment variables: %w", err)
|
||||
|
|
@ -407,7 +410,10 @@ func (p *NativeProxy) restartProcess() error {
|
|||
|
||||
// Close old client
|
||||
if p.client != nil {
|
||||
_ = p.client.Close()
|
||||
if err := p.client.Close(); err != nil {
|
||||
p.logger.Warn().Err(err).Msg("failed to close gRPC client")
|
||||
}
|
||||
p.client = nil // set to nil to avoid closing it again
|
||||
}
|
||||
|
||||
p.restarts++
|
||||
|
|
|
|||
|
|
@ -85,6 +85,19 @@ func RunNativeProcess(binaryName string) {
|
|||
logger.Fatal().Err(err).Msg("failed to write handshake message to stdout")
|
||||
}
|
||||
|
||||
go func() {
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGHUP)
|
||||
|
||||
// non-blocking receive
|
||||
select {
|
||||
case <-sigChan:
|
||||
logger.Info().Msg("received SIGHUP signal, emulating crash")
|
||||
nativeInstance.DoNotUseThisIsForCrashTestingOnly()
|
||||
default:
|
||||
}
|
||||
}()
|
||||
|
||||
// Set up signal handling
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
|
||||
|
|
|
|||
|
|
@ -16,10 +16,15 @@ var (
|
|||
)
|
||||
|
||||
func initNative(systemVersion *semver.Version, appVersion *semver.Version) {
|
||||
if failsafeModeActive {
|
||||
nativeInstance = &native.EmptyNativeInterface{}
|
||||
nativeLogger.Warn().Msg("failsafe mode active, using empty native interface")
|
||||
return
|
||||
}
|
||||
|
||||
nativeLogger.Info().Msg("initializing native proxy")
|
||||
var err error
|
||||
nativeInstance, err = native.NewNativeProxy(native.NativeOptions{
|
||||
Disable: failsafeModeActive,
|
||||
SystemVersion: systemVersion,
|
||||
AppVersion: appVersion,
|
||||
DisplayRotation: config.GetDisplayRotation(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue