github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/swarm/network/simulation/simulation.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:48</date>
    10  //</624342674442293248>
    11  
    12  //
    13  //
    14  //
    15  //
    16  //
    17  //
    18  //
    19  //
    20  //
    21  //
    22  //
    23  //
    24  //
    25  //
    26  //
    27  
    28  package simulation
    29  
    30  import (
    31  	"context"
    32  	"errors"
    33  	"net/http"
    34  	"sync"
    35  	"time"
    36  
    37  	"github.com/ethereum/go-ethereum/log"
    38  	"github.com/ethereum/go-ethereum/node"
    39  	"github.com/ethereum/go-ethereum/p2p/discover"
    40  	"github.com/ethereum/go-ethereum/p2p/simulations"
    41  	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
    42  )
    43  
    44  //
    45  var (
    46  	ErrNodeNotFound = errors.New("node not found")
    47  	ErrNoPivotNode  = errors.New("no pivot node set")
    48  )
    49  
    50  //
    51  //
    52  type Simulation struct {
    53  //
    54  //
    55  	Net *simulations.Network
    56  
    57  	serviceNames []string
    58  	cleanupFuncs []func()
    59  	buckets      map[discover.NodeID]*sync.Map
    60  	pivotNodeID  *discover.NodeID
    61  	shutdownWG   sync.WaitGroup
    62  	done         chan struct{}
    63  	mu           sync.RWMutex
    64  
    65  httpSrv *http.Server        //
    66  handler *simulations.Server //
    67  runC    chan struct{}       //
    68  }
    69  
    70  //
    71  //
    72  //
    73  //
    74  //
    75  //
    76  //
    77  //
    78  type ServiceFunc func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error)
    79  
    80  //
    81  //
    82  func New(services map[string]ServiceFunc) (s *Simulation) {
    83  	s = &Simulation{
    84  		buckets: make(map[discover.NodeID]*sync.Map),
    85  		done:    make(chan struct{}),
    86  	}
    87  
    88  	adapterServices := make(map[string]adapters.ServiceFunc, len(services))
    89  	for name, serviceFunc := range services {
    90  		s.serviceNames = append(s.serviceNames, name)
    91  		adapterServices[name] = func(ctx *adapters.ServiceContext) (node.Service, error) {
    92  			b := new(sync.Map)
    93  			service, cleanup, err := serviceFunc(ctx, b)
    94  			if err != nil {
    95  				return nil, err
    96  			}
    97  			s.mu.Lock()
    98  			defer s.mu.Unlock()
    99  			if cleanup != nil {
   100  				s.cleanupFuncs = append(s.cleanupFuncs, cleanup)
   101  			}
   102  			s.buckets[ctx.Config.ID] = b
   103  			return service, nil
   104  		}
   105  	}
   106  
   107  	s.Net = simulations.NewNetwork(
   108  		adapters.NewSimAdapter(adapterServices),
   109  		&simulations.NetworkConfig{ID: "0"},
   110  	)
   111  
   112  	return s
   113  }
   114  
   115  //
   116  //
   117  type RunFunc func(context.Context, *Simulation) error
   118  
   119  //
   120  type Result struct {
   121  	Duration time.Duration
   122  	Error    error
   123  }
   124  
   125  //
   126  //
   127  func (s *Simulation) Run(ctx context.Context, f RunFunc) (r Result) {
   128  //
   129  //
   130  	start := time.Now()
   131  	if s.httpSrv != nil {
   132  		log.Info("Waiting for frontend to be ready...(send POST /runsim to HTTP server)")
   133  //
   134  		select {
   135  		case <-s.runC:
   136  		case <-ctx.Done():
   137  			return Result{
   138  				Duration: time.Since(start),
   139  				Error:    ctx.Err(),
   140  			}
   141  		}
   142  		log.Info("Received signal from frontend - starting simulation run.")
   143  	}
   144  	errc := make(chan error)
   145  	quit := make(chan struct{})
   146  	defer close(quit)
   147  	go func() {
   148  		select {
   149  		case errc <- f(ctx, s):
   150  		case <-quit:
   151  		}
   152  	}()
   153  	var err error
   154  	select {
   155  	case <-ctx.Done():
   156  		err = ctx.Err()
   157  	case err = <-errc:
   158  	}
   159  	return Result{
   160  		Duration: time.Since(start),
   161  		Error:    err,
   162  	}
   163  }
   164  
   165  //
   166  //
   167  var maxParallelCleanups = 10
   168  
   169  //
   170  //
   171  //
   172  //
   173  //
   174  //
   175  func (s *Simulation) Close() {
   176  	close(s.done)
   177  
   178  //
   179  //
   180  //
   181  	for _, c := range s.Net.Conns {
   182  		if c.Up {
   183  			s.Net.Disconnect(c.One, c.Other)
   184  		}
   185  	}
   186  	s.shutdownWG.Wait()
   187  	s.Net.Shutdown()
   188  
   189  	sem := make(chan struct{}, maxParallelCleanups)
   190  	s.mu.RLock()
   191  	cleanupFuncs := make([]func(), len(s.cleanupFuncs))
   192  	for i, f := range s.cleanupFuncs {
   193  		if f != nil {
   194  			cleanupFuncs[i] = f
   195  		}
   196  	}
   197  	s.mu.RUnlock()
   198  	var cleanupWG sync.WaitGroup
   199  	for _, cleanup := range cleanupFuncs {
   200  		cleanupWG.Add(1)
   201  		sem <- struct{}{}
   202  		go func(cleanup func()) {
   203  			defer cleanupWG.Done()
   204  			defer func() { <-sem }()
   205  
   206  			cleanup()
   207  		}(cleanup)
   208  	}
   209  	cleanupWG.Wait()
   210  
   211  	if s.httpSrv != nil {
   212  		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
   213  		defer cancel()
   214  		err := s.httpSrv.Shutdown(ctx)
   215  		if err != nil {
   216  			log.Error("Error shutting down HTTP server!", "err", err)
   217  		}
   218  		close(s.runC)
   219  	}
   220  }
   221  
   222  //
   223  //
   224  //
   225  func (s *Simulation) Done() <-chan struct{} {
   226  	return s.done
   227  }
   228