Reactors can be stopped or started at any time.

This commit is contained in:
Jae Kwon
2015-03-25 00:15:18 -07:00
parent 612f8bab9d
commit 08a83aa9fb
13 changed files with 340 additions and 96 deletions

View File

@@ -29,89 +29,100 @@ or more `Channels`. So while sending outgoing messages is typically performed o
incoming messages are received on the reactor.
*/
type Switch struct {
reactors []Reactor
chainId string
reactors map[string]Reactor
chDescs []*ChannelDescriptor
reactorsByCh map[byte]Reactor
peers *PeerSet
dialing *CMap
listeners *CMap // listenerName -> chan interface{}
quit chan struct{}
started uint32
stopped uint32
chainId string
listeners *CMap // listenerName -> chan interface{}
running uint32 // atomic
}
var (
ErrSwitchStopped = errors.New("Switch already stopped")
ErrSwitchDuplicatePeer = errors.New("Duplicate peer")
ErrSwitchStopped = errors.New("Switch stopped")
)
const (
peerDialTimeoutSeconds = 3
)
func NewSwitch(reactors []Reactor) *Switch {
// Validate the reactors. no two reactors can share the same channel.
chDescs := []*ChannelDescriptor{}
reactorsByCh := make(map[byte]Reactor)
for _, reactor := range reactors {
reactorChannels := reactor.GetChannels()
for _, chDesc := range reactorChannels {
chId := chDesc.Id
if reactorsByCh[chId] != nil {
panic(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chId, reactorsByCh[chId], reactor))
}
chDescs = append(chDescs, chDesc)
reactorsByCh[chId] = reactor
}
}
func NewSwitch() *Switch {
sw := &Switch{
reactors: reactors,
chDescs: chDescs,
reactorsByCh: reactorsByCh,
chainId: "",
reactors: make(map[string]Reactor),
chDescs: make([]*ChannelDescriptor, 0),
reactorsByCh: make(map[byte]Reactor),
peers: NewPeerSet(),
dialing: NewCMap(),
listeners: NewCMap(),
quit: make(chan struct{}),
stopped: 0,
running: 0,
}
return sw
}
func (sw *Switch) Start() {
if atomic.CompareAndSwapUint32(&sw.started, 0, 1) {
log.Info("Starting Switch")
for _, reactor := range sw.reactors {
reactor.Start(sw)
func (sw *Switch) SetChainId(hash []byte, network string) {
sw.chainId = hex.EncodeToString(hash) + "-" + network
}
func (sw *Switch) AddReactor(name string, reactor Reactor) {
// Validate the reactor.
// No two reactors can share the same channel.
reactorChannels := reactor.GetChannels()
for _, chDesc := range reactorChannels {
chId := chDesc.Id
if sw.reactorsByCh[chId] != nil {
panic(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chId, sw.reactorsByCh[chId], reactor))
}
sw.chDescs = append(sw.chDescs, chDesc)
sw.reactorsByCh[chId] = reactor
}
sw.reactors[name] = reactor
time.Sleep(1 * time.Second)
}
func (sw *Switch) StartReactor(name string) {
atomic.StoreUint32(&sw.running, 1)
sw.reactors[name].Start(sw)
}
// Convenience function
func (sw *Switch) StartAll() {
atomic.StoreUint32(&sw.running, 1)
for _, reactor := range sw.reactors {
reactor.Start(sw)
}
}
func (sw *Switch) Stop() {
if atomic.CompareAndSwapUint32(&sw.stopped, 0, 1) {
log.Info("Stopping Switch")
close(sw.quit)
// Stop each peer.
for _, peer := range sw.peers.List() {
peer.stop()
}
sw.peers = NewPeerSet()
// Stop all reactors.
for _, reactor := range sw.reactors {
reactor.Stop()
}
func (sw *Switch) StopReactor(name string) {
sw.reactors[name].Stop()
}
// Convenience function
// Not goroutine safe
func (sw *Switch) StopAll() {
atomic.StoreUint32(&sw.running, 0)
// Stop each peer.
for _, peer := range sw.peers.List() {
peer.stop()
}
sw.peers = NewPeerSet()
// Stop all reactors.
for _, reactor := range sw.reactors {
reactor.Stop()
}
}
func (sw *Switch) Reactors() []Reactor {
// Not goroutine safe
func (sw *Switch) Reactors() map[string]Reactor {
return sw.reactors
}
func (sw *Switch) AddPeerWithConnection(conn net.Conn, outbound bool) (*Peer, error) {
if atomic.LoadUint32(&sw.stopped) == 1 {
if atomic.LoadUint32(&sw.running) == 0 {
return nil, ErrSwitchStopped
}
@@ -125,12 +136,12 @@ func (sw *Switch) AddPeerWithConnection(conn net.Conn, outbound bool) (*Peer, er
return nil, ErrSwitchDuplicatePeer
}
// Start the peer
go peer.start()
// Notify listeners.
sw.doAddPeer(peer)
// Start the peer
go peer.start()
// Send handshake
msg := &pexHandshakeMessage{ChainId: sw.chainId}
peer.Send(PexChannel, msg)
@@ -139,7 +150,7 @@ func (sw *Switch) AddPeerWithConnection(conn net.Conn, outbound bool) (*Peer, er
}
func (sw *Switch) DialPeerWithAddress(addr *NetAddress) (*Peer, error) {
if atomic.LoadUint32(&sw.stopped) == 1 {
if atomic.LoadUint32(&sw.running) == 0 {
return nil, ErrSwitchStopped
}
@@ -168,7 +179,7 @@ func (sw *Switch) IsDialing(addr *NetAddress) bool {
// trying to send for defaultSendTimeoutSeconds. Returns a channel
// which receives success values for each attempted send (false if times out)
func (sw *Switch) Broadcast(chId byte, msg interface{}) chan bool {
if atomic.LoadUint32(&sw.stopped) == 1 {
if atomic.LoadUint32(&sw.running) == 0 {
return nil
}
successChan := make(chan bool, len(sw.peers.List()))
@@ -223,16 +234,12 @@ func (sw *Switch) StopPeerGracefully(peer *Peer) {
sw.doRemovePeer(peer, nil)
}
func (sw *Switch) SetChainId(hash []byte, network string) {
sw.chainId = hex.EncodeToString(hash) + "-" + network
}
func (sw *Switch) IsListening() bool {
return sw.listeners.Size() > 0
}
func (sw *Switch) doAddPeer(peer *Peer) {
for _, reactor := range sw.reactors {
for name, reactor := range sw.reactors {
reactor.AddPeer(peer)
}
}