| package mounttable |
| |
| import ( |
| "container/list" |
| "sync" |
| "time" |
| |
| "veyron.io/veyron/veyron2/naming" |
| ) |
| |
| type serverListClock interface { |
| now() time.Time |
| } |
| |
| type realTime bool |
| |
| func (t realTime) now() time.Time { |
| return time.Now() |
| } |
| |
| // TODO(caprita): Replace this with the timekeeper library. |
| var slc = serverListClock(realTime(true)) |
| |
| // server maintains the state of a single server. Unless expires is refreshed before the |
| // time is reached, the entry will be removed. |
| type server struct { |
| expires time.Time |
| oa string // object address of server |
| } |
| |
| // serverList represents an ordered list of servers. |
| type serverList struct { |
| sync.Mutex |
| l *list.List // contains entries of type *server |
| } |
| |
| // NewServerList creates a synchronized list of servers. |
| func NewServerList() *serverList { |
| return &serverList{l: list.New()} |
| } |
| |
| // set up an alternate clock. |
| func setServerListClock(x serverListClock) { |
| slc = x |
| } |
| |
| func (sl *serverList) len() int { |
| sl.Lock() |
| defer sl.Unlock() |
| return sl.l.Len() |
| } |
| |
| func (sl *serverList) Front() *server { |
| sl.Lock() |
| defer sl.Unlock() |
| return sl.l.Front().Value.(*server) |
| } |
| |
| // add to the front of the list if not already in the list, otherwise, |
| // update the expiration time and move to the front of the list. That |
| // way the most recently refreshed is always first. |
| func (sl *serverList) add(oa string, ttl time.Duration) { |
| expires := slc.now().Add(ttl) |
| sl.Lock() |
| defer sl.Unlock() |
| for e := sl.l.Front(); e != nil; e = e.Next() { |
| s := e.Value.(*server) |
| if s.oa == oa { |
| s.expires = expires |
| sl.l.MoveToFront(e) |
| return |
| } |
| } |
| s := &server{ |
| oa: oa, |
| expires: expires, |
| } |
| sl.l.PushFront(s) // innocent until proven guilty |
| } |
| |
| // remove an element from the list. Return the number of elements remaining. |
| func (sl *serverList) remove(oa string) int { |
| sl.Lock() |
| defer sl.Unlock() |
| for e := sl.l.Front(); e != nil; e = e.Next() { |
| s := e.Value.(*server) |
| if s.oa == oa { |
| sl.l.Remove(e) |
| break |
| } |
| } |
| return sl.l.Len() |
| } |
| |
| // removeExpired removes any expired servers. |
| func (sl *serverList) removeExpired() int { |
| sl.Lock() |
| defer sl.Unlock() |
| |
| now := slc.now() |
| var next *list.Element |
| for e := sl.l.Front(); e != nil; e = next { |
| s := e.Value.(*server) |
| next = e.Next() |
| if now.After(s.expires) { |
| sl.l.Remove(e) |
| } |
| } |
| return sl.l.Len() |
| } |
| |
| // copyToSlice returns the contents of the list as a slice of MountedServer. |
| func (sl *serverList) copyToSlice() []naming.VDLMountedServer { |
| sl.Lock() |
| defer sl.Unlock() |
| var slice []naming.VDLMountedServer |
| now := slc.now() |
| for e := sl.l.Front(); e != nil; e = e.Next() { |
| s := e.Value.(*server) |
| ttl := uint32(s.expires.Sub(now).Seconds()) |
| ms := naming.VDLMountedServer{Server: s.oa, TTL: ttl} |
| slice = append(slice, ms) |
| } |
| return slice |
| } |