base_service draft and some samples

This commit is contained in:
Jae Kwon 2015-07-19 17:42:01 -07:00
parent 111f001767
commit c30d38270c
4 changed files with 152 additions and 86 deletions

View File

@ -53,7 +53,12 @@ func (t *RepeatTimer) Reset() {
go t.fireRoutine(t.ticker)
}
// For ease of .Stop()'ing services before .Start()'ing them,
// we ignore .Stop()'s on nil RepeatTimers.
func (t *RepeatTimer) Stop() bool {
if t == nil {
return false
}
t.mtx.Lock() // Lock
defer t.mtx.Unlock()

63
common/service.go Normal file
View File

@ -0,0 +1,63 @@
package common
import "sync/atomic"
// BaseService represents a service that can be started then stopped,
// but cannot be restarted.
// .Start() calls the onStart callback function, and .Stop() calls onStop.
// It is meant to be embedded into service structs.
// The user must ensure that Start() and Stop() are not called concurrently.
// It is ok to call Stop() without calling Start() first -- the onStop
// callback will be called, and the service will never start.
type BaseService struct {
name string
service interface{} // for log statements.
started uint32 // atomic
stopped uint32 // atomic
onStart func()
onStop func()
}
func NewBaseService(name string, service interface{}, onStart, onStop func()) *BaseService {
return &BaseService{
name: name,
service: service,
onStart: onStart,
onStop: onStop,
}
}
func (bs *BaseService) Start() bool {
if atomic.CompareAndSwapUint32(&bs.started, 0, 1) {
if atomic.LoadUint32(&bs.stopped) == 1 {
log.Warn(Fmt("Not starting %v -- already stopped", bs.name), "service", bs.service)
return false
} else {
log.Notice(Fmt("Starting %v", bs.name), "service", bs.service)
}
if bs.onStart != nil {
bs.onStart()
}
return true
} else {
log.Info(Fmt("Not starting %v -- already started", bs.name), "service", bs.service)
return false
}
}
func (bs *BaseService) Stop() bool {
if atomic.CompareAndSwapUint32(&bs.stopped, 0, 1) {
log.Notice(Fmt("Stopping %v", bs.name), "service", bs.service)
if bs.onStop != nil {
bs.onStop()
}
return true
} else {
log.Notice(Fmt("Not stopping %v", bs.name), "service", bs.service)
return false
}
}
func (bs *BaseService) IsRunning() bool {
return atomic.LoadUint32(&bs.started) == 1 && atomic.LoadUint32(&bs.stopped) == 0
}

View File

@ -59,26 +59,27 @@ queue is full.
Inbound message bytes are handled with an onReceive callback function.
*/
type MConnection struct {
conn net.Conn
bufReader *bufio.Reader
bufWriter *bufio.Writer
sendMonitor *flow.Monitor
recvMonitor *flow.Monitor
sendRate int64
recvRate int64
flushTimer *ThrottleTimer // flush writes as necessary but throttled.
send chan struct{}
BaseService
conn net.Conn
bufReader *bufio.Reader
bufWriter *bufio.Writer
sendMonitor *flow.Monitor
recvMonitor *flow.Monitor
sendRate int64
recvRate int64
send chan struct{}
pong chan struct{}
channels []*Channel
channelsIdx map[byte]*Channel
onReceive receiveCbFunc
onError errorCbFunc
errored uint32
quit chan struct{}
pingTimer *RepeatTimer // send pings periodically
pong chan struct{}
chStatsTimer *RepeatTimer // update channel stats periodically
channels []*Channel
channelsIdx map[byte]*Channel
onReceive receiveCbFunc
onError errorCbFunc
started uint32
stopped uint32
errored uint32
flushTimer *ThrottleTimer // flush writes as necessary but throttled.
pingTimer *RepeatTimer // send pings periodically
chStatsTimer *RepeatTimer // update channel stats periodically
LocalAddress *NetAddress
RemoteAddress *NetAddress
@ -87,21 +88,24 @@ type MConnection struct {
func NewMConnection(conn net.Conn, chDescs []*ChannelDescriptor, onReceive receiveCbFunc, onError errorCbFunc) *MConnection {
mconn := &MConnection{
conn: conn,
bufReader: bufio.NewReaderSize(conn, minReadBufferSize),
bufWriter: bufio.NewWriterSize(conn, minWriteBufferSize),
sendMonitor: flow.New(0, 0),
recvMonitor: flow.New(0, 0),
sendRate: defaultSendRate,
recvRate: defaultRecvRate,
flushTimer: NewThrottleTimer("flush", flushThrottleMS*time.Millisecond),
send: make(chan struct{}, 1),
quit: make(chan struct{}),
pingTimer: NewRepeatTimer("ping", pingTimeoutSeconds*time.Second),
pong: make(chan struct{}),
chStatsTimer: NewRepeatTimer("chStats", updateStatsSeconds*time.Second),
onReceive: onReceive,
onError: onError,
conn: conn,
bufReader: bufio.NewReaderSize(conn, minReadBufferSize),
bufWriter: bufio.NewWriterSize(conn, minWriteBufferSize),
sendMonitor: flow.New(0, 0),
recvMonitor: flow.New(0, 0),
sendRate: defaultSendRate,
recvRate: defaultRecvRate,
send: make(chan struct{}, 1),
pong: make(chan struct{}),
onReceive: onReceive,
onError: onError,
// Initialized in Start()
quit: nil,
flushTimer: nil,
pingTimer: nil,
chStatsTimer: nil,
LocalAddress: NewNetAddress(conn.LocalAddr()),
RemoteAddress: NewNetAddress(conn.RemoteAddr()),
}
@ -118,32 +122,33 @@ func NewMConnection(conn net.Conn, chDescs []*ChannelDescriptor, onReceive recei
mconn.channels = channels
mconn.channelsIdx = channelsIdx
mconn.BaseService = *NewBaseService("MConnection", mconn, mconn.onStart, mconn.onStop)
return mconn
}
// .Start() begins multiplexing packets to and from "channels".
func (c *MConnection) Start() {
if atomic.CompareAndSwapUint32(&c.started, 0, 1) {
log.Info("Starting MConnection", "connection", c)
go c.sendRoutine()
go c.recvRoutine()
}
func (c *MConnection) onStart() {
c.quit = make(chan struct{})
go c.sendRoutine()
go c.recvRoutine()
c.flushTimer = NewThrottleTimer("flush", flushThrottleMS*time.Millisecond)
c.pingTimer = NewRepeatTimer("ping", pingTimeoutSeconds*time.Second)
c.chStatsTimer = NewRepeatTimer("chStats", updateStatsSeconds*time.Second)
}
func (c *MConnection) Stop() {
if atomic.CompareAndSwapUint32(&c.stopped, 0, 1) {
log.Info("Stopping MConnection", "connection", c)
func (c *MConnection) onStop() {
c.flushTimer.Stop()
c.pingTimer.Stop()
c.chStatsTimer.Stop()
if c.quit != nil {
close(c.quit)
c.conn.Close()
c.flushTimer.Stop()
c.chStatsTimer.Stop()
c.pingTimer.Stop()
// We can't close pong safely here because
// recvRoutine may write to it after we've stopped.
// Though it doesn't need to get closed at all,
// we close it @ recvRoutine.
// close(c.pong)
}
c.conn.Close()
// We can't close pong safely here because
// recvRoutine may write to it after we've stopped.
// Though it doesn't need to get closed at all,
// we close it @ recvRoutine.
// close(c.pong)
}
func (c *MConnection) String() string {
@ -178,11 +183,11 @@ func (c *MConnection) stopForError(r interface{}) {
// Queues a message to be sent to channel.
func (c *MConnection) Send(chId byte, msg interface{}) bool {
if atomic.LoadUint32(&c.stopped) == 1 {
if !c.IsRunning() {
return false
}
log.Info("Send", "channel", chId, "connection", c, "msg", msg) //, "bytes", binary.BinaryBytes(msg))
log.Info("Send", "channel", chId, "conn", c, "msg", msg) //, "bytes", binary.BinaryBytes(msg))
// Send message to channel.
channel, ok := c.channelsIdx[chId]
@ -199,7 +204,7 @@ func (c *MConnection) Send(chId byte, msg interface{}) bool {
default:
}
} else {
log.Warn("Send failed", "channel", chId, "connection", c, "msg", msg)
log.Warn("Send failed", "channel", chId, "conn", c, "msg", msg)
}
return success
}
@ -207,11 +212,11 @@ func (c *MConnection) Send(chId byte, msg interface{}) bool {
// Queues a message to be sent to channel.
// Nonblocking, returns true if successful.
func (c *MConnection) TrySend(chId byte, msg interface{}) bool {
if atomic.LoadUint32(&c.stopped) == 1 {
if !c.IsRunning() {
return false
}
log.Info("TrySend", "channel", chId, "connection", c, "msg", msg)
log.Info("TrySend", "channel", chId, "conn", c, "msg", msg)
// Send message to channel.
channel, ok := c.channelsIdx[chId]
@ -233,7 +238,7 @@ func (c *MConnection) TrySend(chId byte, msg interface{}) bool {
}
func (c *MConnection) CanSend(chId byte) bool {
if atomic.LoadUint32(&c.stopped) == 1 {
if !c.IsRunning() {
return false
}
@ -286,11 +291,11 @@ FOR_LOOP:
}
}
if atomic.LoadUint32(&c.stopped) == 1 {
if !c.IsRunning() {
break FOR_LOOP
}
if err != nil {
log.Warn("Connection failed @ sendRoutine", "connection", c, "error", err)
log.Warn("Connection failed @ sendRoutine", "conn", c, "error", err)
c.stopForError(err)
break FOR_LOOP
}
@ -386,8 +391,8 @@ FOR_LOOP:
pktType := binary.ReadByte(c.bufReader, &n, &err)
c.recvMonitor.Update(int(n))
if err != nil {
if atomic.LoadUint32(&c.stopped) != 1 {
log.Warn("Connection failed @ recvRoutine (reading byte)", "connection", c, "error", err)
if !c.IsRunning() {
log.Warn("Connection failed @ recvRoutine (reading byte)", "conn", c, "error", err)
c.stopForError(err)
}
break FOR_LOOP
@ -407,8 +412,9 @@ FOR_LOOP:
binary.ReadBinaryPtr(&pkt, c.bufReader, &n, &err)
c.recvMonitor.Update(int(n))
if err != nil {
if atomic.LoadUint32(&c.stopped) != 1 {
log.Warn("Connection failed @ recvRoutine", "connection", c, "error", err)
if !c.IsRunning() {
log.Warn("Connection failed @ recvRoutine", "conn", c, "error", err)
c.stopForError(err)
}
break FOR_LOOP
@ -419,8 +425,9 @@ FOR_LOOP:
}
msgBytes, err := channel.recvMsgPacket(pkt)
if err != nil {
if atomic.LoadUint32(&c.stopped) != 1 {
log.Warn("Connection failed @ recvRoutine", "connection", c, "error", err)
if !c.IsRunning() {
log.Warn("Connection failed @ recvRoutine", "conn", c, "error", err)
c.stopForError(err)
}
break FOR_LOOP

View File

@ -12,9 +12,10 @@ import (
)
type Peer struct {
BaseService
outbound bool
mconn *MConnection
running uint32
*types.NodeInfo
Key string
@ -64,30 +65,20 @@ func newPeer(conn net.Conn, peerNodeInfo *types.NodeInfo, outbound bool, reactor
p = &Peer{
outbound: outbound,
mconn: mconn,
running: 0,
NodeInfo: peerNodeInfo,
Key: peerNodeInfo.PubKey.KeyString(),
Data: NewCMap(),
}
p.BaseService = *NewBaseService("Peer", p, p.onStart, p.onStop)
return p
}
func (p *Peer) start() {
if atomic.CompareAndSwapUint32(&p.running, 0, 1) {
log.Info("Starting Peer", "peer", p)
p.mconn.Start()
}
func (p *Peer) onStart() {
p.mconn.Start()
}
func (p *Peer) stop() {
if atomic.CompareAndSwapUint32(&p.running, 1, 0) {
log.Info("Stopping Peer", "peer", p)
p.mconn.Stop()
}
}
func (p *Peer) IsRunning() bool {
return atomic.LoadUint32(&p.running) == 1
func (p *Peer) onStop() {
p.mconn.Stop()
}
func (p *Peer) Connection() *MConnection {
@ -99,21 +90,21 @@ func (p *Peer) IsOutbound() bool {
}
func (p *Peer) Send(chId byte, msg interface{}) bool {
if atomic.LoadUint32(&p.running) == 0 {
if !p.IsRunning() {
return false
}
return p.mconn.Send(chId, msg)
}
func (p *Peer) TrySend(chId byte, msg interface{}) bool {
if atomic.LoadUint32(&p.running) == 0 {
if !p.IsRunning() {
return false
}
return p.mconn.TrySend(chId, msg)
}
func (p *Peer) CanSend(chId byte) bool {
if atomic.LoadUint32(&p.running) == 0 {
if !p.IsRunning() {
return false
}
return p.mconn.CanSend(chId)