github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/swarm/network/simulation/simulation.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package simulation 18 19 import ( 20 "context" 21 "errors" 22 "net/http" 23 "sync" 24 "time" 25 26 "github.com/ethereum/go-ethereum/log" 27 "github.com/ethereum/go-ethereum/node" 28 "github.com/ethereum/go-ethereum/p2p/enode" 29 "github.com/ethereum/go-ethereum/p2p/simulations" 30 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 31 ) 32 33 // Common errors that are returned by functions in this package. 34 var ( 35 ErrNodeNotFound = errors.New("node not found") 36 ErrNoPivotNode = errors.New("no pivot node set") 37 ) 38 39 // Simulation provides methods on network, nodes and services 40 // to manage them. 41 type Simulation struct { 42 // Net is exposed as a way to access lower level functionalities 43 // of p2p/simulations.Network. 44 Net *simulations.Network 45 46 serviceNames []string 47 cleanupFuncs []func() 48 buckets map[enode.ID]*sync.Map 49 pivotNodeID *enode.ID 50 shutdownWG sync.WaitGroup 51 done chan struct{} 52 mu sync.RWMutex 53 54 httpSrv *http.Server //attach a HTTP server via SimulationOptions 55 handler *simulations.Server //HTTP handler for the server 56 runC chan struct{} //channel where frontend signals it is ready 57 } 58 59 // ServiceFunc is used in New to declare new service constructor. 60 // The first argument provides ServiceContext from the adapters package 61 // giving for example the access to NodeID. Second argument is the sync.Map 62 // where all "global" state related to the service should be kept. 63 // All cleanups needed for constructed service and any other constructed 64 // objects should ne provided in a single returned cleanup function. 65 // Returned cleanup function will be called by Close function 66 // after network shutdown. 67 type ServiceFunc func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) 68 69 // New creates a new Simulation instance with new 70 // simulations.Network initialized with provided services. 71 func New(services map[string]ServiceFunc) (s *Simulation) { 72 s = &Simulation{ 73 buckets: make(map[enode.ID]*sync.Map), 74 done: make(chan struct{}), 75 } 76 77 adapterServices := make(map[string]adapters.ServiceFunc, len(services)) 78 for name, serviceFunc := range services { 79 s.serviceNames = append(s.serviceNames, name) 80 adapterServices[name] = func(ctx *adapters.ServiceContext) (node.Service, error) { 81 b := new(sync.Map) 82 service, cleanup, err := serviceFunc(ctx, b) 83 if err != nil { 84 return nil, err 85 } 86 s.mu.Lock() 87 defer s.mu.Unlock() 88 if cleanup != nil { 89 s.cleanupFuncs = append(s.cleanupFuncs, cleanup) 90 } 91 s.buckets[ctx.Config.ID] = b 92 return service, nil 93 } 94 } 95 96 s.Net = simulations.NewNetwork( 97 adapters.NewTCPAdapter(adapterServices), 98 &simulations.NetworkConfig{ID: "0"}, 99 ) 100 101 return s 102 } 103 104 // RunFunc is the function that will be called 105 // on Simulation.Run method call. 106 type RunFunc func(context.Context, *Simulation) error 107 108 // Result is the returned value of Simulation.Run method. 109 type Result struct { 110 Duration time.Duration 111 Error error 112 } 113 114 // Run calls the RunFunc function while taking care of 115 // cancellation provided through the Context. 116 func (s *Simulation) Run(ctx context.Context, f RunFunc) (r Result) { 117 //if the option is set to run a HTTP server with the simulation, 118 //init the server and start it 119 start := time.Now() 120 if s.httpSrv != nil { 121 log.Info("Waiting for frontend to be ready...(send POST /runsim to HTTP server)") 122 //wait for the frontend to connect 123 select { 124 case <-s.runC: 125 case <-ctx.Done(): 126 return Result{ 127 Duration: time.Since(start), 128 Error: ctx.Err(), 129 } 130 } 131 log.Info("Received signal from frontend - starting simulation run.") 132 } 133 errc := make(chan error) 134 quit := make(chan struct{}) 135 defer close(quit) 136 go func() { 137 select { 138 case errc <- f(ctx, s): 139 case <-quit: 140 } 141 }() 142 var err error 143 select { 144 case <-ctx.Done(): 145 err = ctx.Err() 146 case err = <-errc: 147 } 148 return Result{ 149 Duration: time.Since(start), 150 Error: err, 151 } 152 } 153 154 // Maximal number of parallel calls to cleanup functions on 155 // Simulation.Close. 156 var maxParallelCleanups = 10 157 158 // Close calls all cleanup functions that are returned by 159 // ServiceFunc, waits for all of them to finish and other 160 // functions that explicitly block shutdownWG 161 // (like Simulation.PeerEvents) and shuts down the network 162 // at the end. It is used to clean all resources from the 163 // simulation. 164 func (s *Simulation) Close() { 165 close(s.done) 166 167 sem := make(chan struct{}, maxParallelCleanups) 168 s.mu.RLock() 169 cleanupFuncs := make([]func(), len(s.cleanupFuncs)) 170 for i, f := range s.cleanupFuncs { 171 if f != nil { 172 cleanupFuncs[i] = f 173 } 174 } 175 s.mu.RUnlock() 176 var cleanupWG sync.WaitGroup 177 for _, cleanup := range cleanupFuncs { 178 cleanupWG.Add(1) 179 sem <- struct{}{} 180 go func(cleanup func()) { 181 defer cleanupWG.Done() 182 defer func() { <-sem }() 183 184 cleanup() 185 }(cleanup) 186 } 187 cleanupWG.Wait() 188 189 if s.httpSrv != nil { 190 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 191 defer cancel() 192 err := s.httpSrv.Shutdown(ctx) 193 if err != nil { 194 log.Error("Error shutting down HTTP server!", "err", err) 195 } 196 close(s.runC) 197 } 198 199 s.shutdownWG.Wait() 200 s.Net.Shutdown() 201 } 202 203 // Done returns a channel that is closed when the simulation 204 // is closed by Close method. It is useful for signaling termination 205 // of all possible goroutines that are created within the test. 206 func (s *Simulation) Done() <-chan struct{} { 207 return s.done 208 }