mbserver icon indicating copy to clipboard operation
mbserver copied to clipboard

No timeout in RTU Server causes some sort of deadlock

Open thegreatco opened this issue 1 year ago • 1 comments

I'm trying to test out the RTU Server.

If I specify a timeout, the server is unstable: it either times out before the client connects or closes if requests are not made often enough.

If I don't specify a timeout, the server seems to work great, but calling Close seems to block forever and I can't cleanly shut it down.

With timeout speciffied

package main

import (
	"os"
	"os/signal"
	"time"

	"github.com/goburrow/serial"
	"github.com/tbrandon/mbserver"
)

func main() {
	serv := mbserver.NewServer()
	err := serv.ListenRTU(&serial.Config{
		Address:  "/dev/ttyUSB0",
		BaudRate: 115200,
		DataBits: 8,
		StopBits: 1,
		Parity:   "N",
		Timeout:  10 * time.Second})
	if err != nil {
		panic(err)
	}
	println("Server is running...")
	defer func() {
		println("Stopping server...")
		serv.Close()
		println("Server stopped")
		os.Exit(0)
	}()
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	<-c
}

Output

$ go run main.go
Server is running...
2024/09/10 16:52:10 serial read error serial: timeout
^CStopping server...
Server stopped

Note that I was able to read/write several coils with no problem. But if I wait longer than the timeout period to issue another command, I get the serial read error, and all subsequent requests to the server fail. However, I am able to close the server down cleanly.

Without timeout

package main

import (
	"os"
	"os/signal"

	"github.com/goburrow/serial"
	"github.com/tbrandon/mbserver"
)

func main() {
	serv := mbserver.NewServer()
	err := serv.ListenRTU(&serial.Config{
		Address:  "/dev/ttyUSB0",
		BaudRate: 115200,
		DataBits: 8,
		StopBits: 1,
		Parity:   "N"})
	if err != nil {
		panic(err)
	}
	println("Server is running...")
	defer func() {
		println("Stopping server...")
		serv.Close()
		println("Server stopped")
		os.Exit(0)
	}()
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	<-c
}
$ go run main.go
Server is running...
^CStopping server...
signal: terminated

I am able to read/write many times, with large gaps in between with no issues. Note that the server only shuts down cleanly now when a kill <pid> is issued. The program hangs on serv.Close().

It seems there is some sort of deadlock here. I noticed in the docs a section on Race Conditions, however I'm not doing anything with goroutines here and the version with a timeout works just fine.

thegreatco avatar Sep 10 '24 21:09 thegreatco

It seems that serv.Close() waits for the specified timeout to complete. So when you set it to 10 seconds, it waits 10 seconds before closing. If you don’t specify a timeout, it waits indefinitely to complete.

dushaoshuai avatar Jun 04 '25 02:06 dushaoshuai