Exemple #1
0
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))
}
Exemple #2
0
// 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()
}