func (eng *Engine) dispatch() bool { var needTick bool // Set if we need to tick the transport. for { if cevent := C.pn_connection_engine_dispatch(&eng.engine); cevent != nil { event := makeEvent(cevent, eng) for _, h := range eng.handlers { switch event.Type() { case ETransport: needTick = true } h.HandleEvent(event) } } else { break } } if needTick { eng.tick() } return !bool(C.pn_connection_engine_finished(&eng.engine)) }
// Run the engine. Engine.Run() will exit when the engine is closed or // disconnected. You can check for errors after exit with Engine.Error(). // func (eng *Engine) Run() error { // Channels for read and write buffers going in and out of the read/write goroutines. // The channels are unbuffered: we want to exchange buffers in seuquence. readsIn, writesIn := make(chan []byte), make(chan []byte) readsOut, writesOut := make(chan []byte), make(chan []byte) wait := sync.WaitGroup{} wait.Add(2) // Read and write goroutines go func() { // Read goroutine defer wait.Done() for { rbuf, ok := <-readsIn if !ok { return } n, err := eng.conn.Read(rbuf) if n > 0 { readsOut <- rbuf[:n] } else if err != nil { eng.inject <- func() { eng.Transport().Condition().SetError(err) C.pn_connection_engine_read_close(&eng.engine) } return } } }() go func() { // Write goroutine defer wait.Done() for { wbuf, ok := <-writesIn if !ok { return } n, err := eng.conn.Write(wbuf) if n > 0 { writesOut <- wbuf[:n] } else if err != nil { eng.inject <- func() { eng.Transport().Condition().SetError(err) C.pn_connection_engine_write_close(&eng.engine) } return } } }() for !C.pn_connection_engine_finished(&eng.engine) { // Enable readIn/writeIn channles only if we have a buffer. readBuf, writeBuf := eng.buffers() var sendReads, sendWrites chan []byte if readBuf != nil { sendReads = readsIn } if writeBuf != nil { sendWrites = writesIn } // Send buffers to the read/write goroutines if we have them. // Get buffers from the read/write goroutines and process them select { case sendReads <- readBuf: case sendWrites <- writeBuf: case buf := <-readsOut: if len(buf) > 0 { C.pn_connection_engine_read_done(&eng.engine, C.size_t(len(buf))) } else { panic(fmt.Sprintf("read buf %v", buf)) } case buf := <-writesOut: if len(buf) > 0 { C.pn_connection_engine_write_done(&eng.engine, C.size_t(len(buf))) } else { panic(fmt.Sprintf("write buf %v", buf)) } case f, ok := <-eng.inject: // Function injected from another goroutine if ok { f() } } for { cevent := C.pn_connection_engine_dispatch(&eng.engine) if cevent == nil { break } event := makeEvent(cevent, eng) for _, h := range eng.handlers { h.HandleEvent(event) } } } eng.err.Set(EndpointError(eng.Connection())) eng.err.Set(eng.Transport().Condition().Error()) close(readsIn) close(writesIn) eng.conn.Close() // Make sure connection is closed wait.Wait() // Wait for goroutines close(eng.running) // Signal goroutines have exited and Error is set. C.pn_connection_engine_final(&eng.engine) for _, h := range eng.handlers { switch h := h.(type) { case cHandler: C.pn_handler_free(h.pn) } } // FIXME aconway 2016-06-21: consistent error handling return eng.err.Get() }