func (bs *Bitswap) startWorkers(px process.Process, ctx context.Context) { // Start up a worker to handle block requests this node is making px.Go(func(px process.Process) { bs.providerConnector(ctx) }) // Start up workers to handle requests from other nodes for the data on this node for i := 0; i < TaskWorkerCount; i++ { i := i px.Go(func(px process.Process) { bs.taskWorker(ctx, i) }) } // Start up a worker to manage periodically resending our wantlist out to peers px.Go(func(px process.Process) { bs.rebroadcastWorker(ctx) }) // Start up a worker to manage sending out provides messages px.Go(func(px process.Process) { bs.provideCollector(ctx) }) // Spawn up multiple workers to handle incoming blocks // consider increasing number if providing blocks bottlenecks // file transfers for i := 0; i < provideWorkers; i++ { i := i px.Go(func(px process.Process) { bs.provideWorker(ctx, i) }) } }
// WithProcessClosing returns a context.Context derived from ctx that // is cancelled as p is Closing (after: <-p.Closing()). It is simply: // // func WithProcessClosing(ctx context.Context, p goprocess.Process) context.Context { // ctx, cancel := context.WithCancel(ctx) // go func() { // <-p.Closing() // cancel() // }() // return ctx // } // func WithProcessClosing(ctx context.Context, p goprocess.Process) context.Context { ctx, cancel := context.WithCancel(ctx) go func() { <-p.Closing() cancel() }() return ctx }
// CloseAfterContext schedules the process to close after the given // context is done. It is the equivalent of: // // func CloseAfterContext(p goprocess.Process, ctx context.Context) { // go func() { // <-ctx.Done() // p.Close() // }() // } // func CloseAfterContext(p goprocess.Process, ctx context.Context) { if p == nil { panic("nil Process") } if ctx == nil { panic("nil Context") } go func() { <-ctx.Done() p.Close() }() }
// transport will grab message arrival times, wait until that time, and // then write the message out when it is scheduled to arrive func (s *stream) transport(proc process.Process) { bufsize := 256 buf := new(bytes.Buffer) ticker := time.NewTicker(time.Millisecond * 4) // writeBuf writes the contents of buf through to the s.Writer. // done only when arrival time makes sense. drainBuf := func() { if buf.Len() > 0 { _, err := s.Writer.Write(buf.Bytes()) if err != nil { return } buf.Reset() } } // deliverOrWait is a helper func that processes // an incoming packet. it waits until the arrival time, // and then writes things out. deliverOrWait := func(o *transportObject) { buffered := len(o.msg) + buf.Len() now := time.Now() if now.Before(o.arrivalTime) { if buffered < bufsize { buf.Write(o.msg) return } // we do not buffer + return here, instead hanging the // call (i.e. not accepting any more transportObjects) // so that we apply back-pressure to the sender. // this sleep should wake up same time as ticker. time.Sleep(o.arrivalTime.Sub(now)) } // ok, we waited our due time. now rite the buf + msg. // drainBuf first, before we write this message. drainBuf() // write this message. _, err := s.Writer.Write(o.msg) if err != nil { log.Error("mock_stream", err) } } for { select { case <-proc.Closing(): return // bail out of here. case o, ok := <-s.toDeliver: if !ok { return } deliverOrWait(o) case <-ticker.C: // ok, due to write it out. drainBuf() } } }
// WaitForContext makes p WaitFor ctx. When Closing, p waits for // ctx.Done(), before being Closed(). It is simply: // // p.WaitFor(goprocess.WithContext(ctx)) // func WaitForContext(ctx context.Context, p goprocess.Process) { p.WaitFor(WithContext(ctx)) }