Home > OS >  I cannot escape a for loop with a ticker
I cannot escape a for loop with a ticker

Time:10-31

I have this API that scans for drivers' locations and send them via web-socket every 1 second. The issue is that the loop cannot be escaped when client disconnects. It seems it is alive for ever. I am using Gin with nhooyr websocket library.

var GetDriverLocations = func(c *gin.Context) {

    wsoptions := websocket.AcceptOptions{InsecureSkipVerify: true}
    wsconn, err := websocket.Accept(c.Writer, c.Request, &wsoptions)
    if err != nil {
        return
    }

    defer wsconn.Close(websocket.StatusInternalError, "the sky is falling")

    driverLocation := &models.DriverLocation{}

    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
        case <-c.Request.Context().Done():
            fmt.Println("done") //this never gets printed
            return

        }
        coords, err := driverLocation.GetDrivers()
        if err != nil {
            break
        }
        err = wsjson.Write(c.Request.Context(), wsconn, &coords)

        if websocket.CloseStatus(err) == websocket.StatusNormalClosure {
            break
        }
        if err != nil {
            break
        }
    }

    fmt.Println("conn ended") //this never gets printed

}

I also tried this loop but also has the same issue:

for range ticker.C{

    coords, err := driverLocation.GetDrivers()
    if err != nil {
        break
    }
    err = wsjson.Write(c.Request.Context(), wsconn, &coords)

    if websocket.CloseStatus(err) == websocket.StatusNormalClosure {
        break
    }
    if err != nil {
        break
    }
}

CodePudding user response:

Because the network connection is hijacked from the net/http server by the nhooyr websocket library, the context c.Request.Context() is not canceled until handler returns.

Call CloseRead to get a context that's canceled when the connection is closed. Use that context in the loop.

var GetDriverLocations = func(c *gin.Context) {

    wsoptions := websocket.AcceptOptions{InsecureSkipVerify: true}
    wsconn, err := websocket.Accept(c.Writer, c.Request, &wsoptions)
    if err != nil {
        return
    }
    defer wsconn.Close(websocket.StatusInternalError, "")
    ctx := wsconn.CloseRead(c.Request.Context())

    driverLocation := &models.DriverLocation{}

    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-ticker.C:
        case <-ctx.Done():
            return
        }
        coords, err := driverLocation.GetDrivers()
        if err != nil {
            break
        }
        err = wsjson.Write(c.Request.Context(), wsconn, &coords)
        if err != nil {
            break
        }
    }
}
  •  Tags:  
  • go
  • Related