예제 #1
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 {
	wait := sync.WaitGroup{}
	wait.Add(2) // Read and write goroutines

	readErr := make(chan error, 1) // Don't block
	go func() {                    // Read goroutine
		defer wait.Done()
		for {
			rbuf := eng.read.buffer()
			n, err := eng.conn.Read(rbuf)
			if n > 0 {
				eng.read.buffers <- rbuf[:n]
			}
			if err != nil {
				readErr <- err
				close(readErr)
				close(eng.read.buffers)
				return
			}
		}
	}()

	writeErr := make(chan error, 1) // Don't block
	go func() {                     // Write goroutine
		defer wait.Done()
		for {
			wbuf, ok := <-eng.write.buffers
			if !ok {
				return
			}
			_, err := eng.conn.Write(wbuf)
			if err != nil {
				writeErr <- err
				close(writeErr)
				return
			}
		}
	}()

	wbuf := eng.write.buffer()[:0]

	for !eng.transport.Closed() {
		if len(wbuf) == 0 {
			eng.pop(&wbuf)
		}
		// Don't set wchan unless there is something to write.
		var wchan chan []byte
		if len(wbuf) > 0 {
			wchan = eng.write.buffers
		}

		select {
		case buf, ok := <-eng.read.buffers: // Read a buffer
			if ok {
				eng.push(buf)
			}
		case wchan <- wbuf: // Write a buffer
			wbuf = eng.write.buffer()[:0]
		case f, ok := <-eng.inject: // Function injected from another goroutine
			if ok {
				f()
			}
		case err := <-readErr:
			eng.transport.Condition().SetError(err)
			eng.transport.CloseTail()
		case err := <-writeErr:
			eng.transport.Condition().SetError(err)
			eng.transport.CloseHead()
		}
		eng.dispatch()
		if eng.connection.State().RemoteClosed() && eng.connection.State().LocalClosed() {
			eng.disconnect()
		}
	}
	eng.err.Set(EndpointError(eng.connection))
	eng.err.Set(eng.transport.Condition().Error())
	close(eng.write.buffers)
	eng.conn.Close() // Make sure connection is closed
	wait.Wait()
	close(eng.running) // Signal goroutines have exited and Error is set.

	if !eng.connection.IsNil() {
		eng.connection.Free()
	}
	if !eng.transport.IsNil() {
		eng.transport.Free()
	}
	if eng.collector != nil {
		C.pn_collector_free(eng.collector)
	}
	for _, h := range eng.handlers {
		switch h := h.(type) {
		case cHandler:
			C.pn_handler_free(h.pn)
		}
	}
	return eng.err.Get()
}
예제 #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 {
	C.pn_connection_engine_start(&eng.engine)
	// 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 eng.dispatch() {
		readBuf := eng.readBuffer()
		writeBuf := eng.writeBuffer()
		// Note that getting the buffers can generate events (eg. SASL events) that
		// might close the transport. Check if we are already finished before
		// blocking for IO.
		if !eng.dispatch() {
			break
		}

		// sendReads/sendWrites are nil (not sendable in select) unless we have a
		// buffer to read/write
		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
		// Check for injected functions
		select {

		case sendReads <- readBuf:

		case sendWrites <- writeBuf:

		case buf := <-readsOut:
			C.pn_connection_engine_read_done(&eng.engine, C.size_t(len(buf)))

		case buf := <-writesOut:
			C.pn_connection_engine_write_done(&eng.engine, C.size_t(len(buf)))

		case f, ok := <-eng.inject: // Function injected from another goroutine
			if ok {
				f()
			}

		case <-eng.timer.C:
			eng.tick()
		}
	}

	eng.err.Set(EndpointError(eng.Connection()))
	eng.err.Set(eng.Transport().Condition().Error())
	close(readsIn)
	close(writesIn)
	close(eng.running)   // Signal goroutines have exited and Error is set, disable Inject()
	_ = eng.conn.Close() // Close conn, force read/write goroutines to exit (they will Inject)
	wait.Wait()          // Wait for goroutines

	for _, h := range eng.handlers {
		switch h := h.(type) {
		case cHandler:
			C.pn_handler_free(h.pn)
		}
	}
	C.pn_connection_engine_final(&eng.engine)
	return eng.err.Get()
}
예제 #3
0
파일: engine.go 프로젝트: ChugR/qpid-proton
// 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()
}