Page 1 of 1

[Done] Crash with SendNetworkData

Posted: Sun Oct 27, 2019 11:29 am
by mk-soft
The network functions are as described ThreadSafe.

If you outsource the sending of data to another thread and the connection to the partner fails, SendNetworkData crashed.
With Windows and Linux, SendNetworkData returns a result of less than zero and can be aborted.

Server

Code: Select all

;-TOP

CompilerIf #PB_Compiler_Thread = 0
  CompilerError "Use Compiler Option ThreadSafe!"
CompilerEndIf

InitNetwork()

Enumeration Windows
  #Main
EndEnumeration

Global ExitApplication
Global MutexServer = CreateMutex()

Procedure thServer(id)
  Protected ServerID, Event, ConnectionID, cnt
  Protected *buffer = AllocateMemory($FFFF)
  
  ServerID = CreateNetworkServer(#PB_Any, 6037)
  If ServerID = 0
    Debug "Error: Create NetworkServer!"
    ProcedureReturn 0
  Else
    Debug "Create NetworkServer"
  EndIf
  
  Repeat
    LockMutex(MutexServer)
    Event = NetworkServerEvent(ServerID)
    ConnectionID = EventClient()
    UnlockMutex(MutexServer)
    Select Event
      Case #PB_NetworkEvent_Connect
        Debug "Client Conneceted ID " + ConnectionID
      Case #PB_NetworkEvent_Data
        cnt = ReceiveNetworkData(ConnectionID, *buffer, $FFFF)
        Debug "Receive Data ID " + ConnectionID + " / Len " + cnt
      Case #PB_NetworkEvent_Disconnect
        Debug "Client Disconneceted ID " + ConnectionID
      Default
        Delay(10)
    EndSelect
    
  Until ExitApplication 
  
  Debug "Close NetworkServer"
  CloseNetworkServer(ServerID)
  Debug "Exit Thread"
  
EndProcedure

Procedure Main()
  Protected thServer
  
  If OpenWindow(#Main, 80, 80, 200, 60, "Server" , #PB_Window_SystemMenu)
    
    thServer = CreateThread(@thServer(), 0)
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          ExitApplication = #True
          If WaitThread(thServer, 1000) = 0
            KillThread(thServer)
          EndIf
          Break
      EndSelect
    ForEver
    
  EndIf
  
EndProcedure : Main()
[/size]

Client

Code: Select all

;-TOP

CompilerIf #PB_Compiler_Thread = 0
  CompilerError "Use Compiler Option ThreadSafe!"
CompilerEndIf

InitNetwork()

Enumeration Windows
  #Main
EndEnumeration

Enumeration Gadgets
  
EndEnumeration

Enumeration Status
  #MainStatusBar
EndEnumeration

Global ExitApplication, ExitSendData
Global ConnectionID

Procedure thClient(id)
  Protected Event, cnt
  Protected *buffer = AllocateMemory($FFFF)
  
  ConnectionID = OpenNetworkConnection("127.0.0.1", 6037)
  If ConnectionID = 0
    Debug "Error: Create NetworkClient!"
    ProcedureReturn 0
  Else
    Debug "Create NetworkClient"
  EndIf
  
  Repeat
    Event = NetworkClientEvent(ConnectionID)
    Select Event
      Case #PB_NetworkEvent_Data
        cnt = ReceiveNetworkData(ConnectionID, *buffer, $FFFF)
        Debug "Receive Data ID " + ConnectionID + " / Len " + cnt
      Case #PB_NetworkEvent_Disconnect
        Debug "Server Disconneceted"
        ExitSendData = #True
        Break
      Default
        Delay(100)
    EndSelect
    
  Until ExitApplication 
  
  Debug "Close NetworkClient"
  CloseNetworkConnection(ConnectionID)
  ConnectionID = 0
  Debug "Exit Thread Client"
  
EndProcedure

Procedure thSendData(ConnectionID)
  Protected cnt
  Repeat
    cnt = SendNetworkData(ConnectionID, @"Hello World!", 12)
    Debug "Send " + cnt
    If cnt < 0
      Debug "Send Error"
      Break
    EndIf
    Delay(40)
  Until ExitApplication ; Or ExitSendData
  
  Debug "Exit Thread SendData"
  
EndProcedure

Procedure Main()
  Protected thClient, thSend
  
  If OpenWindow(#Main, 80, 180, 200, 60, "Client" , #PB_Window_SystemMenu)
    ButtonGadget(0, 10, 10, 180, 30, "Start Send")
    
    thClient = CreateThread(@thClient(), 0)
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          ExitApplication = #True
          If thClient
            If WaitThread(thClient, 1000) = 0
              KillThread(thClient)
            EndIf
          EndIf
          If thSend
            If WaitThread(thSend, 1000) = 0
              KillThread(thSend)
            EndIf
          EndIf
          Break
        Case #PB_Event_Gadget
          Select EventGadget()
            Case 0
              If ConnectionID And Not IsThread(thSend)
                thSend = CreateThread(@thSendData(), ConnectionID)
              EndIf
          EndSelect
      EndSelect
    ForEver
    
  EndIf
  
EndProcedure : Main()
[/size]

Current solution:
Check the client port before sending the data.

Code: Select all

; ***************************************************************************************
; Bugfix MacOS SendNetworkData over Threads. mk-soft, 27.10.2019, Version 1.01
; Update 07.05.2020 Version 1.02

CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
  
  Import ""
    CFSocketIsValid(Socket) ; -> Boolean
  EndImport
  
  Procedure FixSendNetworkData(ClientID, MemoryBufer, Length)
    If CFSocketIsValid(ClientID)
      ProcedureReturn SendNetworkData(ClientID, MemoryBufer, Length)
    Else
      ProcedureReturn -2
    EndIf
  EndProcedure
  
  Macro SendNetworkData(ClientID, MemoryBufer, Length)
    FixSendNetworkData(ClientID, MemoryBufer, Length)
  EndMacro
  
CompilerEndIf

; ***************************************************************************************
[/size]

Re: Crash with SendNetworkData

Posted: Sun Oct 27, 2019 2:54 pm
by Josh
Wiso sind deine Codes immer so klein geschrieben? Bei mir hier ist das nicht lesbar.

Re: Crash with SendNetworkData

Posted: Sun Oct 27, 2019 4:02 pm
by mk-soft
In diesen fall nur, damit man nicht so viel scrollen muss...

In this case only so that you don't have to scroll so much...

Re: Crash with SendNetworkData

Posted: Tue May 05, 2020 10:03 pm
by mk-soft
My help solution work not every time :(

Re: Crash with SendNetworkData

Posted: Thu May 07, 2020 4:49 pm
by mk-soft
There seems to be a problem with the socket event loop and the 'SendNetworkData' if they run in different threads.
The thread with the Network Event Loop (Function 'NetworkClientEvent') must be called at least twice as often as the thread with the data send. It also helps to check the connection before sending (CFSocketIsValid).

Runs more stable ...

Code: Select all

; ***************************************************************************************
; Bugfix MacOS SendNetworkData over Threads. mk-soft, 27.10.2019, Version 1.01
; Update 07.05.2020 Version 1.02

CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
  
  Import ""
    CFSocketIsValid(Socket) ; -> Boolean
  EndImport
  
  Procedure FixSendNetworkData(ClientID, MemoryBufer, Length)
    If CFSocketIsValid(ClientID)
      ProcedureReturn SendNetworkData(ClientID, MemoryBufer, Length)
    Else
      ProcedureReturn -2
    EndIf
  EndProcedure
  
  Macro SendNetworkData(ClientID, MemoryBufer, Length)
    FixSendNetworkData(ClientID, MemoryBufer, Length)
  EndMacro
  
  Procedure FixSendNetworkString(ClientID, String.s, Format = #PB_UTF8)
    If CFSocketIsValid(ClientID)
      ProcedureReturn SendNetworkString(ClientID, String, Format)
    Else
      ProcedureReturn -2
    EndIf
  EndProcedure
  
  Macro SendNetworkString(ClientID, String, Format = #PB_UTF8)
    FixSendNetworkString(ClientID, String, Format)
  EndMacro
  
CompilerEndIf

; ***************************************************************************************

Re: Crash with SendNetworkData

Posted: Sun Feb 18, 2024 9:43 am
by Fred
This report isn't clear to me, do you try to still use SendNetworkData() after a disconnect event ? If yes
it's not supported and can lead to crash

Re: Crash with SendNetworkData

Posted: Sun Feb 18, 2024 1:48 pm
by mk-soft
Fred wrote: Sun Feb 18, 2024 9:43 am This report isn't clear to me, do you try to still use SendNetworkData() after a disconnect event ? If yes
it's not supported and can lead to crash
Hello Fred,
It's exactly as you wrote it. The thread continues to try to send, even though the partner has ended the connection or was interrupted by an error.

With Linux and Window, SendNetworkData returns an error (-1). On macOS it crashes because macOS probably destroys the resource for the connection immediately.

The solution I am currently using is to check the connection for errors before sending.

Code: Select all

Procedure _SendNetworkData(ClientID, *MemoryBuffer, Length)
    Protected r1
    Protected error_code
    Protected error_code_size = SizeOf(error_code)
    
    r1 = getsockopt_(ConnectionID(ClientID), #SOL_SOCKET, #SO_ERROR, @error_code, @error_code_size)
    If r1 <= 0
      r1 = error_code
    EndIf
    
    If r1
      ProcedureReturn -1
    Else
      ProcedureReturn SendNetworkData(ClientID, *MemoryBuffer, Length)
    EndIf
  EndProcedure

Re: Crash with SendNetworkData

Posted: Sun Feb 18, 2024 2:58 pm
by Fred
You are right, something was different on OS X (!). I added the SO_NOSIGPIPE option to the socket: https://forums.developer.apple.com/forums/thread/107307

As I can't reproduce it, may be you can try it with the next beta !

Re: Crash with SendNetworkData

Posted: Sun Feb 18, 2024 3:08 pm
by mk-soft
Ok, Thanks ;)
I will perform a stress test with the next beta.

Re: Crash with SendNetworkData

Posted: Fri Mar 29, 2024 9:56 am
by Fred
Could you try it ? :)

Re: Crash with SendNetworkData

Posted: Fri Mar 29, 2024 1:28 pm
by mk-soft
Sorry, I had forgotten.

A quick test with my TCP module adapted that is sent further in the thread when the connection is closed.
SendNetworkData now comes back with an error ;)

Looks good so far.

Re: [Done] Crash with SendNetworkData

Posted: Fri Mar 29, 2024 1:35 pm
by Fred
Thanks !