package server import ( "log" "time" "math" "sync" "errors" ) // this package will implement a boilerplate manager // manager connects to client on start and returns the gRPC connection to make gRPC clients type Manager struct { *Client // gives access to c.Ip c.Id etc Hb time.Duration Active active Sig chan bool Err chan error } type active struct{ sync.Mutex bool int } func NewManager(err chan error) *Manager { hb := time.Duration(1 * time.Second) //hb to m := &Manager{Hb:hb,Err:err} return m } func (m *Manager) Start(cl *Client) { // establish connection with client and start pinging at set intervals m.Client = cl if !m.Activate() { // manager already running m.Err <-errors.New("Manager already running!") } // if we get here, manager is atomically activated and we can ensure start wont run again log.Printf("%v Manager (%v) starting!\n",m.Type,m.Id) } func (m *Manager) Exit() { // exit function to eventually allow saving to configs if !m.Deactivate() { m.Err <-errors.New("Manager already disabled!") } log.Printf("%v Manager (%v) exiting!\n",m.Type,m.Id) } // reactor manager atomic operations func (m *Manager) IsActive() bool { m.Active.Lock() defer m.Active.Unlock() return m.Active.bool } func (m *Manager) Activate() bool { // slightly confusing but returns result of trying to activate m.Active.Lock() defer m.Active.Unlock() alive := m.Active.bool if alive { return false } else { m.Active.bool = true m.Active.int = 0 return m.Active.bool } } func (m *Manager) Deactivate() bool { // result of trying to deactivate m.Active.Lock() defer m.Active.Unlock() alive := m.Active.bool if alive { m.Active.bool = false return true } else { return m.Active.bool } } // connection stuff func (m *Manager) Timeout() int { // keeps track of and generates timeout [0-1.2s) over span of ~2.5s // returns 0 on TO elapse m.Active.Lock() defer m.Active.Unlock() if m.Active.int < 9 { v := int(5 * math.Pow(float64(2), float64(m.Active.int))) m.Active.int += 1 return v } else { // exceeded retries return 0 } }