// routine to poll room information at variable intervals func pollTfsRooms(tfsApi *tfs.Api, min time.Duration, max time.Duration, send *chan interface{}, recv *chan interface{}) { delay := min roomRecv := make(chan interface{}) // routines started here we'll be proxied roomQuit := make(map[int]*chan interface{}) // we'll close this to have routines we started return roomMap := make(map[int]*tfs.Room) // defer close(roomRecv) // TODO: will cause a panic if network errors cause this to be closed before other routines quit, but will block a routine if we don't timer := time.NewTimer(delay) defer timer.Stop() for { select { case <-timer.C: // tick tock delay = delay * 2 if delay > max { delay = max } rooms, err := tfsApi.GetRooms() if err != nil { trace.Log(err) for _, quit := range roomQuit { // clean up on error close(*quit) // routines should return on this being closed } *send <- fmt.Sprintf("rooms error %s", err) return } // added newRoomMap := make(map[int]*tfs.Room) for _, room := range rooms.Value { newRoomMap[room.Id] = room if _, ok := roomMap[room.Id]; ok { continue } json, err := json.Marshal(room) if err != nil { trace.Log(err) continue } *send <- fmt.Sprintf("rooms add %d %s", room.Id, json) roomMap[room.Id] = room q := make(chan interface{}) roomQuit[room.Id] = &q // http://stackoverflow.com/questions/25601802/why-does-inline-instantiation-of-variable-requires-explicitly-taking-the-address go pollTfsRoomUsers(room, tfsApi, min, max/2, &roomRecv, roomQuit[room.Id]) go pollTfsRoomMessages(room, tfsApi, min, max/4, &roomRecv, roomQuit[room.Id]) delay = min } // removed for _, room := range roomMap { if _, ok := newRoomMap[room.Id]; ok { continue } close(*roomQuit[room.Id]) delete(roomMap, room.Id) delete(roomQuit, room.Id) *send <- fmt.Sprintf("rooms remove %d", room.Id) delay = min } timer.Reset(delay) case event := <-roomRecv: // relay send or clean up from a routine that errored _, action, params := tokenizeEvent(event.(string)) if action == "error" { // routine error room, _ := strconv.Atoi(params) quit, ok := roomQuit[room] if ok { // clean up routines close(*quit) delete(roomMap, room) delete(roomQuit, room) *send <- fmt.Sprintf("rooms remove %d", room) } } else { *send <- event // relay } case <-*recv: // we need to quit for _, quit := range roomQuit { // clean up routines close(*quit) // routines should return on this being closed } return } } }