Commit 864233c7 authored by Andrei Mihu's avatar Andrei Mihu
Browse files

Improve handling of JavaScript runtime context cancellation.

parent 2369de66
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ The format is based on [keep a changelog](http://keepachangelog.com) and this pr
- Build with Go 1.18.4 release.
- Improve signature of JavaScript runtime HMAC SHA256 hash function.
- Improve signature of JavaScript runtime Base64 encode functions.
- Improve handling of JavaScript runtime context cancellation.

## [3.12.0] - 2022-05-22
### Added
+24 −0
Original line number Diff line number Diff line
@@ -48,9 +48,14 @@ type RuntimeJS struct {
	jsLoggerInst goja.Value
	env          goja.Value
	vm           *goja.Runtime
	nakamaModule *runtimeJavascriptNakamaModule
	callbacks    *RuntimeJavascriptCallbacks
}

func (r *RuntimeJS) SetContext(ctx context.Context) {
	r.nakamaModule.ctx = ctx
}

func (r *RuntimeJS) GetCallback(e RuntimeExecutionMode, key string) string {
	switch e {
	case RuntimeExecutionModeRPC:
@@ -157,7 +162,9 @@ func (rp *RuntimeProviderJS) Rpc(ctx context.Context, id string, headers, queryP
		r.logger.Error("Could not instantiate js logger.", zap.Error(err))
		return "", errors.New("Could not run Rpc function."), codes.Internal
	}
	r.SetContext(ctx)
	retValue, err, code := r.InvokeFunction(RuntimeExecutionModeRPC, id, fn, jsLogger, headers, queryParams, userID, username, vars, expiry, sessionID, clientIP, clientPort, lang, payload)
	r.SetContext(context.Background())
	rp.Put(r)
	if err != nil {
		return "", err, code
@@ -212,7 +219,9 @@ func (rp *RuntimeProviderJS) BeforeRt(ctx context.Context, id string, logger *za
		logger.Error("Could not instantiate js logger.", zap.Error(err))
		return nil, errors.New("Could not run runtime Before function.")
	}
	r.SetContext(ctx)
	result, fnErr, _ := r.InvokeFunction(RuntimeExecutionModeBefore, id, fn, jsLogger, nil, nil, userID, username, vars, expiry, sessionID, clientIP, clientPort, lang, envelopeMap)
	r.SetContext(context.Background())
	rp.Put(r)

	if fnErr != nil {
@@ -292,7 +301,9 @@ func (rp *RuntimeProviderJS) AfterRt(ctx context.Context, id string, logger *zap
		logger.Error("Could not instantiate js logger.", zap.Error(err))
		return errors.New("Could not run runtime After function.")
	}
	r.SetContext(ctx)
	_, fnErr, _ := r.InvokeFunction(RuntimeExecutionModeAfter, id, fn, jsLogger, nil, nil, userID, username, vars, expiry, sessionID, clientIP, clientPort, lang, outMap, inMap)
	r.SetContext(context.Background())
	rp.Put(r)

	if fnErr != nil {
@@ -353,7 +364,9 @@ func (rp *RuntimeProviderJS) BeforeReq(ctx context.Context, id string, logger *z
		logger.Error("Could not instantiate js logger.", zap.Error(err))
		return nil, errors.New("Could not run runtime Before function."), codes.Internal
	}
	r.SetContext(ctx)
	result, fnErr, code := r.InvokeFunction(RuntimeExecutionModeBefore, id, fn, jsLogger, nil, nil, userID, username, vars, expiry, "", clientIP, clientPort, "", reqMap)
	r.SetContext(context.Background())
	rp.Put(r)

	if fnErr != nil {
@@ -452,7 +465,9 @@ func (rp *RuntimeProviderJS) AfterReq(ctx context.Context, id string, logger *za
		logger.Error("Could not instantiate js logger.", zap.Error(err))
		return errors.New("Could not run runtime After function.")
	}
	r.SetContext(ctx)
	_, fnErr, _ := r.InvokeFunction(RuntimeExecutionModeAfter, id, fn, jsLogger, nil, nil, userID, username, vars, expiry, "", clientIP, clientPort, "", resMap, reqMap)
	r.SetContext(context.Background())
	rp.Put(r)

	if fnErr != nil {
@@ -1611,6 +1626,7 @@ func NewRuntimeProviderJS(logger, startupLogger *zap.Logger, db *sql.DB, protojs
			nkInst:       nkInst,
			node:         config.GetName(),
			vm:           runtime,
			nakamaModule: nakamaModule,
			env:          runtime.ToValue(config.GetRuntime().Environment),
			callbacks:    callbacks,
		}
@@ -1750,7 +1766,9 @@ func (rp *RuntimeProviderJS) MatchmakerMatched(ctx context.Context, entries []*M
		return "", false, errors.New("Could not run matchmaker matched hook.")
	}

	r.SetContext(ctx)
	retValue, err, _ := r.InvokeFunction(RuntimeExecutionModeMatchmaker, "matchmakerMatched", fn, jsLogger, nil, nil, "", "", nil, 0, "", "", "", "", r.vm.ToValue(entriesSlice))
	r.SetContext(context.Background())
	rp.Put(r)
	if err != nil {
		return "", false, fmt.Errorf("Error running runtime Matchmaker Matched hook: %v", err.Error())
@@ -1830,7 +1848,9 @@ func (rp *RuntimeProviderJS) TournamentEnd(ctx context.Context, tournament *api.
		return errors.New("Could not run tournament end hook.")
	}

	r.SetContext(ctx)
	retValue, err, _ := r.InvokeFunction(RuntimeExecutionModeTournamentEnd, "tournamentEnd", fn, jsLogger, nil, nil, "", "", nil, 0, "", "", "", "", tournamentObj, r.vm.ToValue(end), r.vm.ToValue(reset))
	r.SetContext(context.Background())
	rp.Put(r)
	if err != nil {
		return fmt.Errorf("Error running runtime Tournament End hook: %v", err.Error())
@@ -1895,7 +1915,9 @@ func (rp *RuntimeProviderJS) TournamentReset(ctx context.Context, tournament *ap
		return errors.New("Could not run tournament reset hook.")
	}

	r.SetContext(ctx)
	retValue, err, _ := r.InvokeFunction(RuntimeExecutionModeTournamentReset, "tournamentReset", fn, jsLogger, nil, nil, "", "", nil, 0, "", "", "", "", tournamentObj, r.vm.ToValue(end), r.vm.ToValue(reset))
	r.SetContext(context.Background())
	rp.Put(r)
	if err != nil {
		return fmt.Errorf("Error running runtime Tournament Reset hook: %v", err.Error())
@@ -1945,7 +1967,9 @@ func (rp *RuntimeProviderJS) LeaderboardReset(ctx context.Context, leaderboard *
		return errors.New("Could not run leaderboard reset hook.")
	}

	r.SetContext(ctx)
	retValue, err, _ := r.InvokeFunction(RuntimeExecutionModeLeaderboardReset, "leaderboardReset", fn, jsLogger, nil, nil, "", "", nil, 0, "", "", "", "", leaderboardObj, r.vm.ToValue(reset))
	r.SetContext(context.Background())
	rp.Put(r)
	if err != nil {
		return fmt.Errorf("Error running runtime Leaderboard Reset hook: %v", err.Error())
+14 −3
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
package server

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
@@ -64,7 +65,7 @@ type RuntimeJavaScriptMatchCore struct {
	loggerModule  goja.Value
	program       *goja.Program

	// ctxCancelFn context.CancelFunc
	ctxCancelFn context.CancelFunc
}

func NewRuntimeJavascriptMatchCore(logger *zap.Logger, module string, db *sql.DB, protojsonMarshaler *protojson.MarshalOptions, protojsonUnmarshaler *protojson.UnmarshalOptions, config Config, socialClient *social.Client, leaderboardCache LeaderboardCache, rankCache LeaderboardRankCache, localCache *RuntimeJavascriptLocalCache, leaderboardScheduler LeaderboardScheduler, sessionRegistry SessionRegistry, sessionCache SessionCache, statusRegistry *StatusRegistry, matchRegistry MatchRegistry, tracker Tracker, metrics Metrics, streamManager StreamManager, router MessageRouter, matchCreateFn RuntimeMatchCreateFunction, eventFn RuntimeEventCustomFunction, id uuid.UUID, node string, stopped *atomic.Bool, matchHandlers *jsMatchHandlers, modCache *RuntimeJSModuleCache) (RuntimeMatchCore, error) {
@@ -81,6 +82,8 @@ func NewRuntimeJavascriptMatchCore(logger *zap.Logger, module string, db *sql.DB
	if err != nil {
		logger.Fatal("Failed to initialize JavaScript runtime", zap.Error(err))
	}
	goCtx, ctxCancelFn := context.WithCancel(context.Background())
	nakamaModule.ctx = goCtx

	runtime.RunProgram(modCache.Modules[modCache.Names[0]].Program)
	freezeGlobalObject(config, runtime)
@@ -96,30 +99,37 @@ func NewRuntimeJavascriptMatchCore(logger *zap.Logger, module string, db *sql.DB

	initFn, ok := goja.AssertFunction(runtime.Get(matchHandlers.initFn))
	if !ok {
		ctxCancelFn()
		logger.Fatal("Failed to get JavaScript match loop function reference.", zap.String("fn", string(MatchInit)), zap.String("key", matchHandlers.initFn))
	}
	joinAttemptFn, ok := goja.AssertFunction(runtime.Get(matchHandlers.joinAttemptFn))
	if !ok {
		ctxCancelFn()
		logger.Fatal("Failed to get JavaScript match loop function reference.", zap.String("fn", string(MatchJoinAttempt)), zap.String("key", matchHandlers.joinAttemptFn))
	}
	joinFn, ok := goja.AssertFunction(runtime.Get(matchHandlers.joinFn))
	if !ok {
		ctxCancelFn()
		logger.Fatal("Failed to get JavaScript match loop function reference.", zap.String("fn", string(MatchJoin)), zap.String("key", matchHandlers.joinFn))
	}
	leaveFn, ok := goja.AssertFunction(runtime.Get(matchHandlers.leaveFn))
	if !ok {
		ctxCancelFn()
		logger.Fatal("Failed to get JavaScript match loop function reference.", zap.String("fn", string(MatchLeave)), zap.String("key", matchHandlers.leaveFn))
	}
	loopFn, ok := goja.AssertFunction(runtime.Get(matchHandlers.loopFn))
	if !ok {
		ctxCancelFn()
		logger.Fatal("Failed to get JavaScript match loop function reference.", zap.String("fn", string(MatchLoop)), zap.String("key", matchHandlers.loopFn))
	}
	terminateFn, ok := goja.AssertFunction(runtime.Get(matchHandlers.terminateFn))
	if !ok {
		ctxCancelFn()
		logger.Fatal("Failed to get JavaScript match loop function reference.", zap.String("fn", string(MatchTerminate)), zap.String("key", matchHandlers.terminateFn))
	}
	signalFn, ok := goja.AssertFunction(runtime.Get(matchHandlers.signalFn))
	if !ok {
		ctxCancelFn()
		logger.Fatal("Failed to get JavaScript match loop function reference.", zap.String("fn", string(MatchSignal)), zap.String("key", matchHandlers.signalFn))
	}

@@ -156,7 +166,7 @@ func NewRuntimeJavascriptMatchCore(logger *zap.Logger, module string, db *sql.DB

		loggerModule: jsLoggerInst,
		nakamaModule: nkInst,
		// ctxCancelFn: ctxCancelFn,
		ctxCancelFn:  ctxCancelFn,
	}

	dispatcher := runtime.ToValue(
@@ -174,6 +184,7 @@ func NewRuntimeJavascriptMatchCore(logger *zap.Logger, module string, db *sql.DB

	dispatcherInst, err := runtime.New(dispatcher)
	if err != nil {
		ctxCancelFn()
		logger.Fatal("Failed to initialize JavaScript runtime", zap.Error(err))
	}
	core.dispatcher = dispatcherInst
@@ -513,7 +524,7 @@ func (rm *RuntimeJavaScriptMatchCore) CreateTime() int64 {
}

func (rm *RuntimeJavaScriptMatchCore) Cancel() {
	// TODO: implement cancel
	rm.ctxCancelFn()
}

func (rm *RuntimeJavaScriptMatchCore) Cleanup() {}
+112 −110

File changed.

Preview size limit exceeded, changes collapsed.