mirror of
https://github.com/v2rayA/v2rayA.git
synced 2026-10-19 23:45:11 +08:00
6e6a7deed9
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.
36 lines
986 B
Go
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
|
|
}
|
|
}
|