SendMessage() freezes on Linux

Report any Hollywood bugs here
Post Reply
Flinx
Posts: 362
Joined: Sun Feb 14, 2021 9:54 am
Location: Germany

SendMessage() freezes on Linux

Post by Flinx »

The service component of my player on Linux has been unstable since I started using the IPC functions that were added a year ago. Since this occurs especially when a large number of IPC messages are transmitted, and an attempt to use DebugOutput() did hang at SendMessage(), I wrote a test script that sends data back and forth and counts the errors.
The script comes in two versions, both of which do the same thing and differ only in the names of the ports.
Three boxes are displayed: the left one for sending, the middle one for receiving, and the button on the right can be used to re-create the port.
The left box turns blue immediately before SendMessage() and then turns green again if successful, so it flickers, but this way you can see when SendMessage() freezes. The middle one turns red if no messages arrive for 0.5 seconds.
The script runs without issues on Windows; on 32-bit ARM (Raspberry) it mostly freezes after a few seconds, and on Linux Mint (x64) after a few minutes.

Code: Select all

@DISPLAY {Width = 400, Height = 150, Color = 0x666666, Layers=True}

; create/recreate port
Function p_IPCInit()
	CreatePort("x") ; replace port
	Sleep(1000) ; trigger error color of receiver
	Local err= ?CreatePort(gMPortName$) ; restore own port
	If err<>#ERR_NONE
		NPrint("CreatePort failed:\n"..GetErrorName(err))
		SetLayerStyle("box1", {color=#BLACK})
		SetLayerStyle("box3", {color=#BLACK})
	Else
		InstallEventHandler({OnUserMessage = p_EventIPCMessage})
		SetLayerStyle("box3", {color=0x00ee22})
		Sleep(500)
		SetLayerStyle("box3", {color=#GRAY})
	EndIf
EndFunction

; ipc message
Function p_EventIPCMessage(msg)
	SetLayerStyle("box2", {color=#GREEN}) ; message received
	p_InitReceiveTimeout()
	Switch msg.action
	Case "OnUserMessage"
		Local argstable=SplitStr(msg.args, "\0")
		Switch msg.command
		Case "TEST"
			If ToNumber(argstable[0])<>gReceiveCounter
				; no match
				gReceiveErrors=gReceiveErrors+1
				gReceiveCounter=argstable[0] ; sync again
			EndIf
			gReceiveCounter=gReceiveCounter+1
		Default
			; should not come
			gReceiveErrors=gReceiveErrors+1
		EndSwitch
	EndSwitch
	SetLayerStyle("ReceiveCounter", {Text=gReceiveCounter})
	SetLayerStyle("ReceiveErrors", {Text=gReceiveErrors})
EndFunction

; set receive timeout
Function p_InitReceiveTimeout()
	If HaveObject(#TIMEOUT, 1) Then ClearTimeout(1)
	SetTimeout(1,	Function()
				SetLayerStyle("box2", {color=#RED}) ; no message for 500ms
			EndFunction,
			500)
EndFunction

; button event
Function p_EventButton(bmsg)
	Switch bmsg.action
	Case "OnMouseOver":
		SetLayerStyle("box3", {color=0xaaaaaa})	
	Case "OnMouseOut":
		SetLayerStyle("box3", {color=#GRAY})	
	Case "OnMouseDown":
		SetLayerStyle("box3", {color=#YELLOW})	
	Case "OnMouseUp":
		SetLayerStyle("box3", {color=#RED})
		p_IPCInit()						
	EndSwitch
EndFunction

; interval function (try to send)
Function p_EventCheckConnection()
	SetLayerStyle("box1", {color=#BLUE})
	Local err= ?SendMessage(gSPortName$, "TEST", ToString(gSendCounter))
	If err=#ERR_NONE
		gSendCounter=gSendCounter+1
		SetLayerStyle("box1", {color=#GREEN})
	Else
		SetLayerStyle("box1", {color=#RED})
		If gSendCounter>0 ; wait for running receiver
			gSendErrors=gSendErrors+1
		EndIf
	EndIf
	SetLayerStyle("SendCounter", {Text=gSendCounter})
	SetLayerStyle("SendErrors", {Text=gSendErrors})
EndFunction

SetFillStyle(#FILLCOLOR)
Local left=20
Box(left+50, 50, 50, 50, 0x888888, {Name="box1"}) ; send state
SetInterval(1, p_EventCheckConnection, 50)

Box(left+175, 50, 50, 50, 0x888888, {Name="box2"}) ; receive state
p_InitReceiveTimeout()

Box(left+300, 50, 50, 50, 0x888888, {Name="box3"}) ; port reset button
MakeButton(1, #LAYERBUTTON, "box3", {OnMouseOver=p_EventButton, OnMouseOut=p_EventButton, OnMouseDown=p_EventButton, OnMouseUp=p_EventButton})

gSPortName$="IPCS"
gMPortName$="IPCM"
gSendCounter=0
gSendErrors=0
gReceiveCounter=0
gReceiveErrors=0
TextOut(left+50,  20, "send")
TextOut(left+175, 20, "receive")
TextOut(0, 130, "errors")
TextOut(0, 120, "counter")
TextOut(left+50, 120, gSendCounter, {Name="SendCounter", Color=#WHITE})
TextOut(left+50, 130, gSendErrors, {Name="SendErrors", Color=#WHITE})
TextOut(left+175, 120, gReceiveCounter, {Name="ReceiveCounter", Color=#WHITE})
TextOut(left+175, 130, gReceiveErrors, {Name="ReceiveErrors", Color=#WHITE})

p_IPCInit()

Repeat
  WaitEvent 
Forever

Code: Select all

@DISPLAY {Width = 400, Height = 150, Color = 0x222222, Layers=True}

; create/recreate port
Function p_IPCInit()
	CreatePort("x") ; replace port
	Sleep(1000) ; trigger error color of receiver
	Local err= ?CreatePort(gSPortName$) ; restore own port
	If err<>#ERR_NONE
		NPrint("CreatePort failed:\n"..GetErrorName(err))
		SetLayerStyle("box1", {color=#BLACK})
		SetLayerStyle("box3", {color=#BLACK})
	Else
		InstallEventHandler({OnUserMessage = p_EventIPCMessage})
		SetLayerStyle("box3", {color=0x00ee22})
		Sleep(500)
		SetLayerStyle("box3", {color=#GRAY})
	EndIf
EndFunction

; ipc message
Function p_EventIPCMessage(msg)
	SetLayerStyle("box2", {color=#GREEN}) ; message received
	p_InitReceiveTimeout()
	Switch msg.action
	Case "OnUserMessage"
		Local argstable=SplitStr(msg.args, "\0")
		Switch msg.command
		Case "TEST"
			If ToNumber(argstable[0])<>gReceiveCounter
				; no match
				gReceiveErrors=gReceiveErrors+1
				gReceiveCounter=argstable[0] ; sync again
			EndIf
			gReceiveCounter=gReceiveCounter+1
		Default
			; should not come
			gReceiveErrors=gReceiveErrors+1
		EndSwitch
	EndSwitch
	SetLayerStyle("ReceiveCounter", {Text=gReceiveCounter})
	SetLayerStyle("ReceiveErrors", {Text=gReceiveErrors})
EndFunction

; set receive timeout
Function p_InitReceiveTimeout()
	If HaveObject(#TIMEOUT, 1) Then ClearTimeout(1)
	SetTimeout(1,	Function()
				SetLayerStyle("box2", {color=#RED}) ; no message for 500ms
			EndFunction,
			500)
EndFunction

; button event
Function p_EventButton(bmsg)
	Switch bmsg.action
	Case "OnMouseOver":
		SetLayerStyle("box3", {color=0xaaaaaa})	
	Case "OnMouseOut":
		SetLayerStyle("box3", {color=#GRAY})	
	Case "OnMouseDown":
		SetLayerStyle("box3", {color=#YELLOW})	
	Case "OnMouseUp":
		SetLayerStyle("box3", {color=#RED})
		p_IPCInit()						
	EndSwitch
EndFunction

; interval function (try to send)
Function p_EventCheckConnection()
	SetLayerStyle("box1", {color=#BLUE})
	Local err= ?SendMessage(gMPortName$, "TEST", ToString(gSendCounter))
	If err=#ERR_NONE
		gSendCounter=gSendCounter+1
		SetLayerStyle("box1", {color=#GREEN})
	Else
		SetLayerStyle("box1", {color=#RED})
		If gSendCounter>0 ; wait for running receiver
			gSendErrors=gSendErrors+1
		EndIf
	EndIf
	SetLayerStyle("SendCounter", {Text=gSendCounter})
	SetLayerStyle("SendErrors", {Text=gSendErrors})
EndFunction

SetFillStyle(#FILLCOLOR)
Local left=20
Box(left+50, 50, 50, 50, 0x888888, {Name="box1"}) ; send state
SetInterval(1, p_EventCheckConnection, 50)

Box(left+175, 50, 50, 50, 0x888888, {Name="box2"}) ; receive state
p_InitReceiveTimeout()

Box(left+300, 50, 50, 50, 0x888888, {Name="box3"}) ; port reset button
MakeButton(1, #LAYERBUTTON, "box3", {OnMouseOver=p_EventButton, OnMouseOut=p_EventButton, OnMouseDown=p_EventButton, OnMouseUp=p_EventButton})

gSPortName$="IPCS"
gMPortName$="IPCM"
gSendCounter=0
gSendErrors=0
gReceiveCounter=0
gReceiveErrors=0
TextOut(left+50,  20, "send")
TextOut(left+175, 20, "receive")
TextOut(0, 130, "errors")
TextOut(0, 120, "counter")
TextOut(left+50, 120, gSendCounter, {Name="SendCounter", Color=#WHITE})
TextOut(left+50, 130, gSendErrors, {Name="SendErrors", Color=#WHITE})
TextOut(left+175, 120, gReceiveCounter, {Name="ReceiveCounter", Color=#WHITE})
TextOut(left+175, 130, gReceiveErrors, {Name="ReceiveErrors", Color=#WHITE})

p_IPCInit()

Repeat
  WaitEvent 
Forever
Post Reply