Files
Zakk 6e6a7deed9 fix(controller): reads no longer wait for a mutation, writes wait for their turn
One `updating` flag guarded thirteen handlers, reads included: while a
subscription update or a latency test ran, GET /touch answered
REQUEST_IN_PROGRESS, so refreshing the page during a test failed at
random. A touch is a database read and needs no turn.

The flag becomes a turn that only the state-changing handlers take, and
a handler that finds it taken waits up to five seconds before answering
REQUEST_IN_PROGRESS instead of refusing at once; a client that leaves
while waiting is not answered.
2026-09-20 04:47:30 +10:00

36 lines
986 B
Go

package controller
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
"github.com/v2rayA/v2rayA/common"
)
var (
// mutation serialises the requests that rewrite the store or restart
// the core. Reads do not take it: a touch during a latency test is a
// database read and needs no turn.
mutation = make(chan struct{}, 1)
mutationWait = 5 * time.Second
processingErr = common.Coded("REQUEST_IN_PROGRESS", fmt.Errorf("the last request is being processed"), nil)
)
// beginMutation waits for the turn to change state. When it returns false
// the request has been answered with REQUEST_IN_PROGRESS, or the client
// went away.
func beginMutation(ctx *gin.Context) (release func(), ok bool) {
timer := time.NewTimer(mutationWait)
defer timer.Stop()
select {
case mutation <- struct{}{}:
return func() { <-mutation }, true
case <-timer.C:
common.ResponseError(ctx, processingErr)
return nil, false
case <-ctx.Request.Context().Done():
return nil, false
}
}