Exemple #1
0
// 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
		}
	}
}