Beispiel #1
0
// handle is the per-connection i/o goroutine.
// buffers data into readBuffer and flushes data from writeBuffer.
// if the disconnect channel is signalled, breaks the main loop and closes the connection
func (s *Server) handle(netConn net.Conn) {
	conn := NewConnection(netConn, logger)
	client, err := s.handshake(conn)
	if err == nil && client != nil {
		s.registerClient(client)
		defer s.unregisterClient(client)

		defer safe.Recover(conn.Log())

		conn.Log().Info("accepted connection")

		go encodeFromWriteQueue(client)
		go decodeToReadQueue(client)

		// Block this thread until disconnect
		<-conn.DisconnectChan

		// ensure any pending data is flushed before disconnecting
		conn.flushWriteBuffer()
	}

	close(conn.Read)
	close(conn.Write)
	conn.conn.Close()
	conn.Log().Info("connection closed")
}
Beispiel #2
0
// decodeToReadQueue is the goroutine handling the read buffer
// reads from the buffer, decodes Codables using conn.decode, which can choose
// to either handle the data or place a Codable into the read queue
func decodeToReadQueue(client Client) {
	conn := client.Conn()
	defer safe.Recover(conn.Log())
	for {
		err := conn.fillReadBuffer()
		if err != nil {
			conn.Log().Debug("read error: %v", err)
			conn.Disconnect()
			break
		}

		// at this point, the only error should be because we didn't have enough data
		// todo: formalize this and check for the right error
		canTrim := false
		toRead := conn.ReadBuffer.Len()
		for toRead > 0 && err == nil {
			err = conn.ReadBuffer.Try(func(b *encoding.Buffer) error {
				return client.Decode()
			})
			if err == nil {
				canTrim = true
			}
		}

		if err != nil && err != io.EOF {
			conn.Log().Error("decode returned non EOF error: %v", err)
		}

		if canTrim {
			// We handled some data, discard it
			conn.ReadBuffer.Trim()
		}
	}
}
Beispiel #3
0
// encodeFromWriteQueue is the goroutine handling the write buffer
// picks from conn.write, encodes the Codables, and flushes the write buffer
func encodeFromWriteQueue(client Client) {
	conn := client.Conn()
	defer safe.Recover(conn.Log())
L:
	for {
		select {
		case codable, ok := <-conn.Write:
			if ok {
				select {
				case <-conn.DisconnectChan:
					ok = false
				default:
				}
			}

			if !ok {
				break L
			}

			err := client.Encode(codable)
			if err == nil {
				err = conn.flushWriteBuffer()
			}

			if err != nil {
				conn.Log().Debug("write error: %v", err)
				conn.Disconnect()
				break L
			}
		}
	}
}
Beispiel #4
0
func (l *PyListener) Notify(e *Event, args ...interface{}) {
	lock := py.NewLock()
	defer lock.Unlock()

	argsIn := []interface{}{e}
	argsIn = append(argsIn, args...)

	log := e.log.Child("py_listener", log.MapContext{"id": l.id})
	defer safe.Recover(log)

	argsOut := []py.Object{}
	for _, a := range argsIn {
		converted, err := pybind.TypeConvOut(a, "")
		if err != nil {
			panic(err)
		}
		converted.Incref()
		argsOut = append(argsOut, converted)
	}

	_, err := l.fn.Base().CallFunctionObjArgs(argsOut...)
	if err != nil {
		// Panicing with the whole py error object causes a double panic.
		// Suspect this is because python has cleaned up by the time the runtime evals the error
		panic(err.Error())
	}
}
Beispiel #5
0
func (l *Listener) Notify(e *Event, args ...interface{}) {
	log := e.log.Child("listener", log.MapContext{"id": l.id})
	defer safe.Recover(log)

	select {
	case <-l.owner.Expired():
		// If the owner expired, unregister this listener
		e.Unregister(l)
	default:
		l.fn(e, args...)
	}
}
Beispiel #6
0
Datei: task.go Projekt: gemrs/gem
func (task *Task) Tick() bool {
	defer safe.Recover(task.logger)

	task.counter = task.counter - 1
	if task.counter == 0 {
		reschedule := task.Callback(task)
		if reschedule {
			task.counter = task.Interval
		}
	}

	return task.counter == 0
}
Beispiel #7
0
func (e *Engine) run() error {
	// Start the engine ticking...
	preTask := task.NewTask(func(*task.Task) bool {
		engine_event.PreTick.NotifyObservers()
		return true
	}, task.PreTick, 1, nil)

	duringTask := task.NewTask(func(*task.Task) bool {
		engine_event.Tick.NotifyObservers()
		return true
	}, task.Tick, 1, nil)

	postTask := task.NewTask(func(*task.Task) bool {
		engine_event.PostTick.NotifyObservers()
		return true
	}, task.PostTick, 1, nil)

	task.Scheduler.Submit(preTask)
	task.Scheduler.Submit(duringTask)
	task.Scheduler.Submit(postTask)

	// Main engine loop
	c := time.Tick(EngineTick)
	for _ = range c {
		if !e.t.Alive() {
			break
		}

		func() {
			defer safe.Recover(logger)

			task.Scheduler.Tick(task.PreTick)
			task.Scheduler.Tick(task.Tick)
			task.Scheduler.Tick(task.PostTick)
		}()
	}
	return nil
}