github.com/uber/kraken@v0.1.4/lib/torrent/scheduler/state.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package scheduler 15 16 import ( 17 "errors" 18 "fmt" 19 20 "github.com/uber/kraken/core" 21 "github.com/uber/kraken/lib/torrent/networkevent" 22 "github.com/uber/kraken/lib/torrent/scheduler/announcequeue" 23 "github.com/uber/kraken/lib/torrent/scheduler/conn" 24 "github.com/uber/kraken/lib/torrent/scheduler/connstate" 25 "github.com/uber/kraken/lib/torrent/scheduler/dispatch" 26 "github.com/uber/kraken/lib/torrent/storage" 27 "go.uber.org/zap" 28 29 "github.com/willf/bitset" 30 ) 31 32 // torrentControl bundles torrent control structures. 33 type torrentControl struct { 34 namespace string 35 dispatcher *dispatch.Dispatcher 36 errors []chan error 37 localRequest bool 38 } 39 40 // state is a superset of scheduler, which includes protected state which can 41 // only be accessed from the event loop. state is free to access scheduler fields 42 // and methods, however scheduler has no reference to state. 43 // 44 // Any network I/O, such as opening connections, does not belong at the state 45 // level. These operations should be defined as scheduler methods, and executed 46 // from a separate goroutine when calling from the event loop. Results from I/O 47 // may transform state by sending events into the event loop. 48 type state struct { 49 sched *scheduler 50 51 // Protected state. 52 torrentControls map[core.InfoHash]*torrentControl 53 conns *connstate.State 54 announceQueue announcequeue.Queue 55 } 56 57 func newState(s *scheduler, aq announcequeue.Queue) *state { 58 return &state{ 59 sched: s, 60 torrentControls: make(map[core.InfoHash]*torrentControl), 61 conns: connstate.New( 62 s.config.ConnState, s.clock, s.pctx.PeerID, s.netevents, s.logger), 63 announceQueue: aq, 64 } 65 } 66 67 // addTorrent initializes a new torrentControl for t. Overwrites any existing 68 // torrentControl for t, so callers should check if one exists first. 69 func (s *state) addTorrent( 70 namespace string, t storage.Torrent, localRequest bool) (*torrentControl, error) { 71 72 d, err := dispatch.New( 73 s.sched.config.Dispatch, 74 s.sched.stats, 75 s.sched.clock, 76 s.sched.netevents, 77 s.sched.eventLoop, 78 s.sched.pctx.PeerID, 79 t, 80 s.sched.logger, 81 s.sched.torrentlog) 82 if err != nil { 83 return nil, fmt.Errorf("new dispatcher: %s", err) 84 } 85 ctrl := &torrentControl{ 86 namespace: namespace, 87 dispatcher: d, 88 localRequest: localRequest, 89 } 90 s.announceQueue.Add(t.InfoHash()) 91 s.sched.netevents.Produce(networkevent.AddTorrentEvent( 92 t.InfoHash(), 93 s.sched.pctx.PeerID, 94 t.Bitfield(), 95 s.sched.config.ConnState.MaxOpenConnectionsPerTorrent)) 96 s.torrentControls[t.InfoHash()] = ctrl 97 return ctrl, nil 98 } 99 100 // removeTorrent tears down the torrentControl associated with h, sending err to 101 // all clients waiting on this torrent. 102 func (s *state) removeTorrent(h core.InfoHash, err error) { 103 ctrl, ok := s.torrentControls[h] 104 if !ok { 105 return 106 } 107 if !ctrl.dispatcher.Complete() { 108 ctrl.dispatcher.TearDown() 109 s.announceQueue.Eject(h) 110 for _, errc := range ctrl.errors { 111 errc <- err 112 } 113 s.sched.netevents.Produce(networkevent.TorrentCancelledEvent(h, s.sched.pctx.PeerID)) 114 s.sched.torrentArchive.DeleteTorrent(ctrl.dispatcher.Digest()) 115 } 116 delete(s.torrentControls, h) 117 } 118 119 // addOutgoingConn adds a conn, initialized by us, to state. The conn must already 120 // be in a pending state, and the torrent control must already be initialized. 121 func (s *state) addOutgoingConn(c *conn.Conn, b *bitset.BitSet, info *storage.TorrentInfo) error { 122 if err := s.conns.MovePendingToActive(c); err != nil { 123 return fmt.Errorf("move pending to active: %s", err) 124 } 125 c.Start() 126 ctrl, ok := s.torrentControls[info.InfoHash()] 127 if !ok { 128 return errors.New("torrent controls must be created before sending handshake") 129 } 130 if err := ctrl.dispatcher.AddPeer(c.PeerID(), b, c); err != nil { 131 return fmt.Errorf("add conn to dispatcher: %s", err) 132 } 133 return nil 134 } 135 136 // addIncomingConn adds a conn, initialized by a remote peer, to state. The conn 137 // must already be in a pending state. Initializes a torrent control if not 138 // present. 139 func (s *state) addIncomingConn( 140 namespace string, c *conn.Conn, b *bitset.BitSet, info *storage.TorrentInfo) error { 141 142 if err := s.conns.MovePendingToActive(c); err != nil { 143 return fmt.Errorf("move pending to active: %s", err) 144 } 145 c.Start() 146 ctrl, ok := s.torrentControls[info.InfoHash()] 147 if !ok { 148 t, err := s.sched.torrentArchive.GetTorrent(namespace, info.Digest()) 149 if err != nil { 150 return fmt.Errorf("get torrent: %s", err) 151 } 152 ctrl, err = s.addTorrent(namespace, t, false) 153 if err != nil { 154 return err 155 } 156 } 157 if err := ctrl.dispatcher.AddPeer(c.PeerID(), b, c); err != nil { 158 return fmt.Errorf("add conn to dispatcher: %s", err) 159 } 160 return nil 161 } 162 163 func (s *state) log(args ...interface{}) *zap.SugaredLogger { 164 return s.sched.log(args...) 165 }