Winsock recv() function blocking other threads
I’m writing a simple Windows TCP/IP server application, which only needs to communicate with one client at a time. My application has four threads:
- Main program which also handles transmission of data as needed.
- Receive incoming data thread.
- Listen thread to accept connection requests from the client.
- A ping thread which monitors everything else, and transmits heartbeat messages as needed. I realise that the latter shouldn’t really be necessary with TCP/IP, but the client application (over which I have no control) requires this.
I’ve confirmed in task manager that my application does indeed have four threads running.
I’m using blocking TCP/IP sockets, but my understanding is that they only block the calling thread – the other threads should still be allowed to execute without being blocked. However, I have encountered the following issues:
If the ping thread deems the connection to have died, it calls closesocket(). However, this appears to be being blocked by the call to recv() in the receive thread.
The main application is unable to transmit data while the receive thread has a call to recv() in progress.
The socket is being created via the accept() function. At this stage I’m not setting any socket options.
I've now created a simple two thread program which illustrates the problem. Without the WSA_FLAG_OVERLAPPED flag, the second thread gets blocked by the first thread, even though this would appear to be contrary to what is supposed to happen. If the WSA_FLAG_OVERLAPPED flag is set, then everything works as I would expect.
PROJECT SOURCE FILE:
====================
program Blocking;
uses
Forms,
Blocking_Test in 'Blocking_Test.pas' {Form1},
Close_Test in 'Close_Test.pas';
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end. { Blocking }
UNIT 1 SOURCE FILE:
===================
unit Blocking_Test;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, WinSock2;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Test_Socket: TSocket;
Test_Addr: TSockAddr;
wsda: TWSADATA; { used to store info returned from WSAStartup }
implementation
{$R *.dfm}
uses
Debugger, Close_Test;
procedure TForm1.FormShow(Sender: TObject);
const
Test_Port: word = 3804;
var
Buffer: array [0..127] of byte;
Bytes_Read: integer;
begin { TForm1.FormShow }
Debug('Main thread started');
assert(WSAStartup(MAKEWORD(2,2), wsda) = 0); { WinSock load version 2.2 }
Test_Socket := WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, nil, 0, 0{WSA_FLAG_OVERLAPPED});
assert(Test_Socket <> INVALID_SOCKET);
with Test_Addr do
begin
sin_family := AF_INET;
sin_port := htons(Test_Port);
sin_addr.s_addr := 0; { this will be filled in by bind }
end; { with This_PC_Address }
assert(bind(Test_Socket, @Test_Addr, SizeOf(Test_Addr)) = 0);
Close_Thread := TClose_Thread.Create(false); { start thread immediately }
Debug('B4 Rx');
Bytes_Read := recv(Test_Socket, Buffer, SizeOf(Buffer), 0);
Debug('After Rx');
end; { TForm1.FormShow }
end. { Blocking_Test }
UNIT 2 SOURCE FILE:
===================
unit Close_Test;
interface
uses
Classes;
type
TClose_Thread = class(TThread)
protected
procedure Execute; override;
end; { TClose_Thread }
var
Close_Thread: TClose_Thread;
implementation
uses
Blocking_Test, Debugger, Windows, WinSock2;
type
TThreadNameInfo = record
FType: LongWord; // must be 0x1000
FName: PChar; // pointer to name (in user address space)
FThreadID: LongWord; // thread ID (-1 indicates caller thread)
FFlags: LongWord; // reserved for future use, must be zero
end; { TThreadNameInfo }
var
ThreadNameInfo: TThreadNameInfo;
procedure TClose_Thread.Execute;
procedure SetName;
begin { SetName }
ThreadNameInfo.FType := $1000;
ThreadNameInfo.FName := 'Ping_Thread';
ThreadNameInfo.FThreadID := $FFFFFFFF;
ThreadNameInfo.FFlags := 0;
try
RaiseException( $406D1388, 0, sizeof(ThreadNameInfo) div sizeof(LongWord), @ThreadNameInfo );
except
end; { try }
end; { SetName }
begin { TClose_Thread.Execute }
Debug('Close thread started');
SetName;
sleep(10000); { wait 10 seconds }
Debug('B4 Close');
closesocket(Test_Socket);
Debug('After Close');
end; { TClose_Thread.Execute }
end. { Close_Test }
P.S. Since setting the WSA_FLAG_OVERLAPPED attribute has fixed the problem, I've posted the above for academic interest.
multithreading delphi winsock
|
show 1 more comment
I’m writing a simple Windows TCP/IP server application, which only needs to communicate with one client at a time. My application has four threads:
- Main program which also handles transmission of data as needed.
- Receive incoming data thread.
- Listen thread to accept connection requests from the client.
- A ping thread which monitors everything else, and transmits heartbeat messages as needed. I realise that the latter shouldn’t really be necessary with TCP/IP, but the client application (over which I have no control) requires this.
I’ve confirmed in task manager that my application does indeed have four threads running.
I’m using blocking TCP/IP sockets, but my understanding is that they only block the calling thread – the other threads should still be allowed to execute without being blocked. However, I have encountered the following issues:
If the ping thread deems the connection to have died, it calls closesocket(). However, this appears to be being blocked by the call to recv() in the receive thread.
The main application is unable to transmit data while the receive thread has a call to recv() in progress.
The socket is being created via the accept() function. At this stage I’m not setting any socket options.
I've now created a simple two thread program which illustrates the problem. Without the WSA_FLAG_OVERLAPPED flag, the second thread gets blocked by the first thread, even though this would appear to be contrary to what is supposed to happen. If the WSA_FLAG_OVERLAPPED flag is set, then everything works as I would expect.
PROJECT SOURCE FILE:
====================
program Blocking;
uses
Forms,
Blocking_Test in 'Blocking_Test.pas' {Form1},
Close_Test in 'Close_Test.pas';
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end. { Blocking }
UNIT 1 SOURCE FILE:
===================
unit Blocking_Test;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, WinSock2;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Test_Socket: TSocket;
Test_Addr: TSockAddr;
wsda: TWSADATA; { used to store info returned from WSAStartup }
implementation
{$R *.dfm}
uses
Debugger, Close_Test;
procedure TForm1.FormShow(Sender: TObject);
const
Test_Port: word = 3804;
var
Buffer: array [0..127] of byte;
Bytes_Read: integer;
begin { TForm1.FormShow }
Debug('Main thread started');
assert(WSAStartup(MAKEWORD(2,2), wsda) = 0); { WinSock load version 2.2 }
Test_Socket := WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, nil, 0, 0{WSA_FLAG_OVERLAPPED});
assert(Test_Socket <> INVALID_SOCKET);
with Test_Addr do
begin
sin_family := AF_INET;
sin_port := htons(Test_Port);
sin_addr.s_addr := 0; { this will be filled in by bind }
end; { with This_PC_Address }
assert(bind(Test_Socket, @Test_Addr, SizeOf(Test_Addr)) = 0);
Close_Thread := TClose_Thread.Create(false); { start thread immediately }
Debug('B4 Rx');
Bytes_Read := recv(Test_Socket, Buffer, SizeOf(Buffer), 0);
Debug('After Rx');
end; { TForm1.FormShow }
end. { Blocking_Test }
UNIT 2 SOURCE FILE:
===================
unit Close_Test;
interface
uses
Classes;
type
TClose_Thread = class(TThread)
protected
procedure Execute; override;
end; { TClose_Thread }
var
Close_Thread: TClose_Thread;
implementation
uses
Blocking_Test, Debugger, Windows, WinSock2;
type
TThreadNameInfo = record
FType: LongWord; // must be 0x1000
FName: PChar; // pointer to name (in user address space)
FThreadID: LongWord; // thread ID (-1 indicates caller thread)
FFlags: LongWord; // reserved for future use, must be zero
end; { TThreadNameInfo }
var
ThreadNameInfo: TThreadNameInfo;
procedure TClose_Thread.Execute;
procedure SetName;
begin { SetName }
ThreadNameInfo.FType := $1000;
ThreadNameInfo.FName := 'Ping_Thread';
ThreadNameInfo.FThreadID := $FFFFFFFF;
ThreadNameInfo.FFlags := 0;
try
RaiseException( $406D1388, 0, sizeof(ThreadNameInfo) div sizeof(LongWord), @ThreadNameInfo );
except
end; { try }
end; { SetName }
begin { TClose_Thread.Execute }
Debug('Close thread started');
SetName;
sleep(10000); { wait 10 seconds }
Debug('B4 Close');
closesocket(Test_Socket);
Debug('After Close');
end; { TClose_Thread.Execute }
end. { Close_Test }
P.S. Since setting the WSA_FLAG_OVERLAPPED attribute has fixed the problem, I've posted the above for academic interest.
multithreading delphi winsock
Can you please put the source code?recv
should block only a thread where it resides. Otherwise, one connected client could block another client in a separate thread by issuing slow requests/responses.
– karastojko
Jan 2 at 7:38
OK, but it'll take a little while for me to make something small enough which just contains the relevant sections.
– Chris Hubbard
Jan 2 at 8:09
I’m still working on creating a small enough test program to list here, but in the meantime another thought has occurred to me: Can threads “own” other threads, and in so doing cause one thread to block another? My application is written in Delphi, and I’m using it’s standard TThread component.
– Chris Hubbard
Jan 2 at 10:13
@MartynA on Windows, closing a socket indeed cancels blocking operations in progress in other threads.
– Remy Lebeau
Jan 2 at 17:25
@ChrisHubbard are you, by chance, using your own critical section or mutex around blocking socket operations? Without seeing your actual code, that is the only way I can think of for this to happen. By themselves, blocking sockets do not behave the way you have described.
– Remy Lebeau
Jan 2 at 17:34
|
show 1 more comment
I’m writing a simple Windows TCP/IP server application, which only needs to communicate with one client at a time. My application has four threads:
- Main program which also handles transmission of data as needed.
- Receive incoming data thread.
- Listen thread to accept connection requests from the client.
- A ping thread which monitors everything else, and transmits heartbeat messages as needed. I realise that the latter shouldn’t really be necessary with TCP/IP, but the client application (over which I have no control) requires this.
I’ve confirmed in task manager that my application does indeed have four threads running.
I’m using blocking TCP/IP sockets, but my understanding is that they only block the calling thread – the other threads should still be allowed to execute without being blocked. However, I have encountered the following issues:
If the ping thread deems the connection to have died, it calls closesocket(). However, this appears to be being blocked by the call to recv() in the receive thread.
The main application is unable to transmit data while the receive thread has a call to recv() in progress.
The socket is being created via the accept() function. At this stage I’m not setting any socket options.
I've now created a simple two thread program which illustrates the problem. Without the WSA_FLAG_OVERLAPPED flag, the second thread gets blocked by the first thread, even though this would appear to be contrary to what is supposed to happen. If the WSA_FLAG_OVERLAPPED flag is set, then everything works as I would expect.
PROJECT SOURCE FILE:
====================
program Blocking;
uses
Forms,
Blocking_Test in 'Blocking_Test.pas' {Form1},
Close_Test in 'Close_Test.pas';
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end. { Blocking }
UNIT 1 SOURCE FILE:
===================
unit Blocking_Test;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, WinSock2;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Test_Socket: TSocket;
Test_Addr: TSockAddr;
wsda: TWSADATA; { used to store info returned from WSAStartup }
implementation
{$R *.dfm}
uses
Debugger, Close_Test;
procedure TForm1.FormShow(Sender: TObject);
const
Test_Port: word = 3804;
var
Buffer: array [0..127] of byte;
Bytes_Read: integer;
begin { TForm1.FormShow }
Debug('Main thread started');
assert(WSAStartup(MAKEWORD(2,2), wsda) = 0); { WinSock load version 2.2 }
Test_Socket := WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, nil, 0, 0{WSA_FLAG_OVERLAPPED});
assert(Test_Socket <> INVALID_SOCKET);
with Test_Addr do
begin
sin_family := AF_INET;
sin_port := htons(Test_Port);
sin_addr.s_addr := 0; { this will be filled in by bind }
end; { with This_PC_Address }
assert(bind(Test_Socket, @Test_Addr, SizeOf(Test_Addr)) = 0);
Close_Thread := TClose_Thread.Create(false); { start thread immediately }
Debug('B4 Rx');
Bytes_Read := recv(Test_Socket, Buffer, SizeOf(Buffer), 0);
Debug('After Rx');
end; { TForm1.FormShow }
end. { Blocking_Test }
UNIT 2 SOURCE FILE:
===================
unit Close_Test;
interface
uses
Classes;
type
TClose_Thread = class(TThread)
protected
procedure Execute; override;
end; { TClose_Thread }
var
Close_Thread: TClose_Thread;
implementation
uses
Blocking_Test, Debugger, Windows, WinSock2;
type
TThreadNameInfo = record
FType: LongWord; // must be 0x1000
FName: PChar; // pointer to name (in user address space)
FThreadID: LongWord; // thread ID (-1 indicates caller thread)
FFlags: LongWord; // reserved for future use, must be zero
end; { TThreadNameInfo }
var
ThreadNameInfo: TThreadNameInfo;
procedure TClose_Thread.Execute;
procedure SetName;
begin { SetName }
ThreadNameInfo.FType := $1000;
ThreadNameInfo.FName := 'Ping_Thread';
ThreadNameInfo.FThreadID := $FFFFFFFF;
ThreadNameInfo.FFlags := 0;
try
RaiseException( $406D1388, 0, sizeof(ThreadNameInfo) div sizeof(LongWord), @ThreadNameInfo );
except
end; { try }
end; { SetName }
begin { TClose_Thread.Execute }
Debug('Close thread started');
SetName;
sleep(10000); { wait 10 seconds }
Debug('B4 Close');
closesocket(Test_Socket);
Debug('After Close');
end; { TClose_Thread.Execute }
end. { Close_Test }
P.S. Since setting the WSA_FLAG_OVERLAPPED attribute has fixed the problem, I've posted the above for academic interest.
multithreading delphi winsock
I’m writing a simple Windows TCP/IP server application, which only needs to communicate with one client at a time. My application has four threads:
- Main program which also handles transmission of data as needed.
- Receive incoming data thread.
- Listen thread to accept connection requests from the client.
- A ping thread which monitors everything else, and transmits heartbeat messages as needed. I realise that the latter shouldn’t really be necessary with TCP/IP, but the client application (over which I have no control) requires this.
I’ve confirmed in task manager that my application does indeed have four threads running.
I’m using blocking TCP/IP sockets, but my understanding is that they only block the calling thread – the other threads should still be allowed to execute without being blocked. However, I have encountered the following issues:
If the ping thread deems the connection to have died, it calls closesocket(). However, this appears to be being blocked by the call to recv() in the receive thread.
The main application is unable to transmit data while the receive thread has a call to recv() in progress.
The socket is being created via the accept() function. At this stage I’m not setting any socket options.
I've now created a simple two thread program which illustrates the problem. Without the WSA_FLAG_OVERLAPPED flag, the second thread gets blocked by the first thread, even though this would appear to be contrary to what is supposed to happen. If the WSA_FLAG_OVERLAPPED flag is set, then everything works as I would expect.
PROJECT SOURCE FILE:
====================
program Blocking;
uses
Forms,
Blocking_Test in 'Blocking_Test.pas' {Form1},
Close_Test in 'Close_Test.pas';
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end. { Blocking }
UNIT 1 SOURCE FILE:
===================
unit Blocking_Test;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, WinSock2;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Test_Socket: TSocket;
Test_Addr: TSockAddr;
wsda: TWSADATA; { used to store info returned from WSAStartup }
implementation
{$R *.dfm}
uses
Debugger, Close_Test;
procedure TForm1.FormShow(Sender: TObject);
const
Test_Port: word = 3804;
var
Buffer: array [0..127] of byte;
Bytes_Read: integer;
begin { TForm1.FormShow }
Debug('Main thread started');
assert(WSAStartup(MAKEWORD(2,2), wsda) = 0); { WinSock load version 2.2 }
Test_Socket := WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, nil, 0, 0{WSA_FLAG_OVERLAPPED});
assert(Test_Socket <> INVALID_SOCKET);
with Test_Addr do
begin
sin_family := AF_INET;
sin_port := htons(Test_Port);
sin_addr.s_addr := 0; { this will be filled in by bind }
end; { with This_PC_Address }
assert(bind(Test_Socket, @Test_Addr, SizeOf(Test_Addr)) = 0);
Close_Thread := TClose_Thread.Create(false); { start thread immediately }
Debug('B4 Rx');
Bytes_Read := recv(Test_Socket, Buffer, SizeOf(Buffer), 0);
Debug('After Rx');
end; { TForm1.FormShow }
end. { Blocking_Test }
UNIT 2 SOURCE FILE:
===================
unit Close_Test;
interface
uses
Classes;
type
TClose_Thread = class(TThread)
protected
procedure Execute; override;
end; { TClose_Thread }
var
Close_Thread: TClose_Thread;
implementation
uses
Blocking_Test, Debugger, Windows, WinSock2;
type
TThreadNameInfo = record
FType: LongWord; // must be 0x1000
FName: PChar; // pointer to name (in user address space)
FThreadID: LongWord; // thread ID (-1 indicates caller thread)
FFlags: LongWord; // reserved for future use, must be zero
end; { TThreadNameInfo }
var
ThreadNameInfo: TThreadNameInfo;
procedure TClose_Thread.Execute;
procedure SetName;
begin { SetName }
ThreadNameInfo.FType := $1000;
ThreadNameInfo.FName := 'Ping_Thread';
ThreadNameInfo.FThreadID := $FFFFFFFF;
ThreadNameInfo.FFlags := 0;
try
RaiseException( $406D1388, 0, sizeof(ThreadNameInfo) div sizeof(LongWord), @ThreadNameInfo );
except
end; { try }
end; { SetName }
begin { TClose_Thread.Execute }
Debug('Close thread started');
SetName;
sleep(10000); { wait 10 seconds }
Debug('B4 Close');
closesocket(Test_Socket);
Debug('After Close');
end; { TClose_Thread.Execute }
end. { Close_Test }
P.S. Since setting the WSA_FLAG_OVERLAPPED attribute has fixed the problem, I've posted the above for academic interest.
multithreading delphi winsock
multithreading delphi winsock
edited Jan 4 at 4:54
Chris Hubbard
asked Jan 2 at 7:25
Chris HubbardChris Hubbard
104
104
Can you please put the source code?recv
should block only a thread where it resides. Otherwise, one connected client could block another client in a separate thread by issuing slow requests/responses.
– karastojko
Jan 2 at 7:38
OK, but it'll take a little while for me to make something small enough which just contains the relevant sections.
– Chris Hubbard
Jan 2 at 8:09
I’m still working on creating a small enough test program to list here, but in the meantime another thought has occurred to me: Can threads “own” other threads, and in so doing cause one thread to block another? My application is written in Delphi, and I’m using it’s standard TThread component.
– Chris Hubbard
Jan 2 at 10:13
@MartynA on Windows, closing a socket indeed cancels blocking operations in progress in other threads.
– Remy Lebeau
Jan 2 at 17:25
@ChrisHubbard are you, by chance, using your own critical section or mutex around blocking socket operations? Without seeing your actual code, that is the only way I can think of for this to happen. By themselves, blocking sockets do not behave the way you have described.
– Remy Lebeau
Jan 2 at 17:34
|
show 1 more comment
Can you please put the source code?recv
should block only a thread where it resides. Otherwise, one connected client could block another client in a separate thread by issuing slow requests/responses.
– karastojko
Jan 2 at 7:38
OK, but it'll take a little while for me to make something small enough which just contains the relevant sections.
– Chris Hubbard
Jan 2 at 8:09
I’m still working on creating a small enough test program to list here, but in the meantime another thought has occurred to me: Can threads “own” other threads, and in so doing cause one thread to block another? My application is written in Delphi, and I’m using it’s standard TThread component.
– Chris Hubbard
Jan 2 at 10:13
@MartynA on Windows, closing a socket indeed cancels blocking operations in progress in other threads.
– Remy Lebeau
Jan 2 at 17:25
@ChrisHubbard are you, by chance, using your own critical section or mutex around blocking socket operations? Without seeing your actual code, that is the only way I can think of for this to happen. By themselves, blocking sockets do not behave the way you have described.
– Remy Lebeau
Jan 2 at 17:34
Can you please put the source code?
recv
should block only a thread where it resides. Otherwise, one connected client could block another client in a separate thread by issuing slow requests/responses.– karastojko
Jan 2 at 7:38
Can you please put the source code?
recv
should block only a thread where it resides. Otherwise, one connected client could block another client in a separate thread by issuing slow requests/responses.– karastojko
Jan 2 at 7:38
OK, but it'll take a little while for me to make something small enough which just contains the relevant sections.
– Chris Hubbard
Jan 2 at 8:09
OK, but it'll take a little while for me to make something small enough which just contains the relevant sections.
– Chris Hubbard
Jan 2 at 8:09
I’m still working on creating a small enough test program to list here, but in the meantime another thought has occurred to me: Can threads “own” other threads, and in so doing cause one thread to block another? My application is written in Delphi, and I’m using it’s standard TThread component.
– Chris Hubbard
Jan 2 at 10:13
I’m still working on creating a small enough test program to list here, but in the meantime another thought has occurred to me: Can threads “own” other threads, and in so doing cause one thread to block another? My application is written in Delphi, and I’m using it’s standard TThread component.
– Chris Hubbard
Jan 2 at 10:13
@MartynA on Windows, closing a socket indeed cancels blocking operations in progress in other threads.
– Remy Lebeau
Jan 2 at 17:25
@MartynA on Windows, closing a socket indeed cancels blocking operations in progress in other threads.
– Remy Lebeau
Jan 2 at 17:25
@ChrisHubbard are you, by chance, using your own critical section or mutex around blocking socket operations? Without seeing your actual code, that is the only way I can think of for this to happen. By themselves, blocking sockets do not behave the way you have described.
– Remy Lebeau
Jan 2 at 17:34
@ChrisHubbard are you, by chance, using your own critical section or mutex around blocking socket operations? Without seeing your actual code, that is the only way I can think of for this to happen. By themselves, blocking sockets do not behave the way you have described.
– Remy Lebeau
Jan 2 at 17:34
|
show 1 more comment
2 Answers
2
active
oldest
votes
If the ping thread deems the connection to have died, it calls closesocket(). However, this appears to be being blocked by the call to recv() in the receive thread.
That's just a bug in your code. You cannot free a resource in one thread while another thread is, or might be, using it. You will have to arrange some sane way to ensure that you don't create race conditions around access to the socket.
To be clear, there is no way you can know what that kind of code could possibly do. For example, consider:
- The thread actually hasn't called
recv
yet, it's about to callrecv
but the scheduler hasn't got around to it yet. - The other thread calls
closesocket
. - A thread that is part of a system library opens a new socket and happens to get the same socket descriptor you just closed.
- Your thread now gets to call
recv
, only it's receiving on the socket the library opened!
It is your responsibility to avoid these kinds of race conditions or your code will behave unpredictably. There's no way you can know what the consequence of performing random operations on random sockets could be. So you must not release a resource in one thread while another thread is, might be, or (worst of all) might be about to be, using it.
Most likely what's actually happening is that Delphi has some kind of internal synchronization that is trying to save you from disaster by blocking the thread that can't safely make forward progress.
Thank you for your answer. In the case of the Ping thread, I could eliminate the need for it to close the socket (and therefore eliminate the potential race condition) provided I was able to add a receive timeout to the socket. So far my attempts to do this have been unsuccessful, hence why I have tried to add this function to the Ping thread. This then raises a new question: how does one add a receive timeout to a blocking socket? As per my original post, the socket is being created by the accept() function.
– Chris Hubbard
Jan 2 at 11:04
3
If you have a new question to ask, ask a new question
– David Heffernan
Jan 2 at 11:32
@ChrisHubbard You could just have the ping thread shut the connection down without closing the socket.
– David Schwartz
Jan 2 at 11:35
So it’s OK for the Ping thread to call shutdown() but not closesocket()? I was under the impression that closesocket() called shutdown() anyway. BTW, in this situation there is no need for an orderly shutdown. A hard shutdown is fine.
– Chris Hubbard
Jan 2 at 12:55
1
Note that Windows does not use file descriptors for sockets, unlike how some other platforms do. Sockets are true kernel objects, so it is unlikely for a new socket to get the same value as a recently closed socket. It is also why you can't use non-socket descriptors withselect()
, and whyclosesocket()
is needed instead of `close().
– Remy Lebeau
Jan 2 at 17:30
|
show 3 more comments
UPDATE: accept() creates the new socket with the same attributes as the socket used for listening. Since I hadn’t set the WSA_FLAG_OVERLAPPED attribute for the listen socket, this attribute wasn’t being set for the new socket, and options like the receive timeout didn’t do anything.
Setting the WSA_FLAG_OVERLAPPED attribute for the listen socket seems to have fixed the problem. Thus I can now use the receive timeout, and the Ping thread no longer needs to close the socket if no data has been received.
Setting the WSA_FLAG_OVERLAPPED attribute for the listen socket also seems to have addressed the blocking other threads issue.
note that theWSASocket()
documentation states: "By default, a socket created with theWSASocket()
function will not have this overlapped attribute set. In contrast, thesocket()
function creates a socket that supports overlapped I/O operations as the default behavior." So, if you usesocket()
to create the listening socket, thenaccept()
will inherit the overlapped attribute.
– Remy Lebeau
Jan 3 at 3:19
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54002673%2fwinsock-recv-function-blocking-other-threads%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
If the ping thread deems the connection to have died, it calls closesocket(). However, this appears to be being blocked by the call to recv() in the receive thread.
That's just a bug in your code. You cannot free a resource in one thread while another thread is, or might be, using it. You will have to arrange some sane way to ensure that you don't create race conditions around access to the socket.
To be clear, there is no way you can know what that kind of code could possibly do. For example, consider:
- The thread actually hasn't called
recv
yet, it's about to callrecv
but the scheduler hasn't got around to it yet. - The other thread calls
closesocket
. - A thread that is part of a system library opens a new socket and happens to get the same socket descriptor you just closed.
- Your thread now gets to call
recv
, only it's receiving on the socket the library opened!
It is your responsibility to avoid these kinds of race conditions or your code will behave unpredictably. There's no way you can know what the consequence of performing random operations on random sockets could be. So you must not release a resource in one thread while another thread is, might be, or (worst of all) might be about to be, using it.
Most likely what's actually happening is that Delphi has some kind of internal synchronization that is trying to save you from disaster by blocking the thread that can't safely make forward progress.
Thank you for your answer. In the case of the Ping thread, I could eliminate the need for it to close the socket (and therefore eliminate the potential race condition) provided I was able to add a receive timeout to the socket. So far my attempts to do this have been unsuccessful, hence why I have tried to add this function to the Ping thread. This then raises a new question: how does one add a receive timeout to a blocking socket? As per my original post, the socket is being created by the accept() function.
– Chris Hubbard
Jan 2 at 11:04
3
If you have a new question to ask, ask a new question
– David Heffernan
Jan 2 at 11:32
@ChrisHubbard You could just have the ping thread shut the connection down without closing the socket.
– David Schwartz
Jan 2 at 11:35
So it’s OK for the Ping thread to call shutdown() but not closesocket()? I was under the impression that closesocket() called shutdown() anyway. BTW, in this situation there is no need for an orderly shutdown. A hard shutdown is fine.
– Chris Hubbard
Jan 2 at 12:55
1
Note that Windows does not use file descriptors for sockets, unlike how some other platforms do. Sockets are true kernel objects, so it is unlikely for a new socket to get the same value as a recently closed socket. It is also why you can't use non-socket descriptors withselect()
, and whyclosesocket()
is needed instead of `close().
– Remy Lebeau
Jan 2 at 17:30
|
show 3 more comments
If the ping thread deems the connection to have died, it calls closesocket(). However, this appears to be being blocked by the call to recv() in the receive thread.
That's just a bug in your code. You cannot free a resource in one thread while another thread is, or might be, using it. You will have to arrange some sane way to ensure that you don't create race conditions around access to the socket.
To be clear, there is no way you can know what that kind of code could possibly do. For example, consider:
- The thread actually hasn't called
recv
yet, it's about to callrecv
but the scheduler hasn't got around to it yet. - The other thread calls
closesocket
. - A thread that is part of a system library opens a new socket and happens to get the same socket descriptor you just closed.
- Your thread now gets to call
recv
, only it's receiving on the socket the library opened!
It is your responsibility to avoid these kinds of race conditions or your code will behave unpredictably. There's no way you can know what the consequence of performing random operations on random sockets could be. So you must not release a resource in one thread while another thread is, might be, or (worst of all) might be about to be, using it.
Most likely what's actually happening is that Delphi has some kind of internal synchronization that is trying to save you from disaster by blocking the thread that can't safely make forward progress.
Thank you for your answer. In the case of the Ping thread, I could eliminate the need for it to close the socket (and therefore eliminate the potential race condition) provided I was able to add a receive timeout to the socket. So far my attempts to do this have been unsuccessful, hence why I have tried to add this function to the Ping thread. This then raises a new question: how does one add a receive timeout to a blocking socket? As per my original post, the socket is being created by the accept() function.
– Chris Hubbard
Jan 2 at 11:04
3
If you have a new question to ask, ask a new question
– David Heffernan
Jan 2 at 11:32
@ChrisHubbard You could just have the ping thread shut the connection down without closing the socket.
– David Schwartz
Jan 2 at 11:35
So it’s OK for the Ping thread to call shutdown() but not closesocket()? I was under the impression that closesocket() called shutdown() anyway. BTW, in this situation there is no need for an orderly shutdown. A hard shutdown is fine.
– Chris Hubbard
Jan 2 at 12:55
1
Note that Windows does not use file descriptors for sockets, unlike how some other platforms do. Sockets are true kernel objects, so it is unlikely for a new socket to get the same value as a recently closed socket. It is also why you can't use non-socket descriptors withselect()
, and whyclosesocket()
is needed instead of `close().
– Remy Lebeau
Jan 2 at 17:30
|
show 3 more comments
If the ping thread deems the connection to have died, it calls closesocket(). However, this appears to be being blocked by the call to recv() in the receive thread.
That's just a bug in your code. You cannot free a resource in one thread while another thread is, or might be, using it. You will have to arrange some sane way to ensure that you don't create race conditions around access to the socket.
To be clear, there is no way you can know what that kind of code could possibly do. For example, consider:
- The thread actually hasn't called
recv
yet, it's about to callrecv
but the scheduler hasn't got around to it yet. - The other thread calls
closesocket
. - A thread that is part of a system library opens a new socket and happens to get the same socket descriptor you just closed.
- Your thread now gets to call
recv
, only it's receiving on the socket the library opened!
It is your responsibility to avoid these kinds of race conditions or your code will behave unpredictably. There's no way you can know what the consequence of performing random operations on random sockets could be. So you must not release a resource in one thread while another thread is, might be, or (worst of all) might be about to be, using it.
Most likely what's actually happening is that Delphi has some kind of internal synchronization that is trying to save you from disaster by blocking the thread that can't safely make forward progress.
If the ping thread deems the connection to have died, it calls closesocket(). However, this appears to be being blocked by the call to recv() in the receive thread.
That's just a bug in your code. You cannot free a resource in one thread while another thread is, or might be, using it. You will have to arrange some sane way to ensure that you don't create race conditions around access to the socket.
To be clear, there is no way you can know what that kind of code could possibly do. For example, consider:
- The thread actually hasn't called
recv
yet, it's about to callrecv
but the scheduler hasn't got around to it yet. - The other thread calls
closesocket
. - A thread that is part of a system library opens a new socket and happens to get the same socket descriptor you just closed.
- Your thread now gets to call
recv
, only it's receiving on the socket the library opened!
It is your responsibility to avoid these kinds of race conditions or your code will behave unpredictably. There's no way you can know what the consequence of performing random operations on random sockets could be. So you must not release a resource in one thread while another thread is, might be, or (worst of all) might be about to be, using it.
Most likely what's actually happening is that Delphi has some kind of internal synchronization that is trying to save you from disaster by blocking the thread that can't safely make forward progress.
edited Jan 2 at 10:31
answered Jan 2 at 10:17
David SchwartzDavid Schwartz
138k14145230
138k14145230
Thank you for your answer. In the case of the Ping thread, I could eliminate the need for it to close the socket (and therefore eliminate the potential race condition) provided I was able to add a receive timeout to the socket. So far my attempts to do this have been unsuccessful, hence why I have tried to add this function to the Ping thread. This then raises a new question: how does one add a receive timeout to a blocking socket? As per my original post, the socket is being created by the accept() function.
– Chris Hubbard
Jan 2 at 11:04
3
If you have a new question to ask, ask a new question
– David Heffernan
Jan 2 at 11:32
@ChrisHubbard You could just have the ping thread shut the connection down without closing the socket.
– David Schwartz
Jan 2 at 11:35
So it’s OK for the Ping thread to call shutdown() but not closesocket()? I was under the impression that closesocket() called shutdown() anyway. BTW, in this situation there is no need for an orderly shutdown. A hard shutdown is fine.
– Chris Hubbard
Jan 2 at 12:55
1
Note that Windows does not use file descriptors for sockets, unlike how some other platforms do. Sockets are true kernel objects, so it is unlikely for a new socket to get the same value as a recently closed socket. It is also why you can't use non-socket descriptors withselect()
, and whyclosesocket()
is needed instead of `close().
– Remy Lebeau
Jan 2 at 17:30
|
show 3 more comments
Thank you for your answer. In the case of the Ping thread, I could eliminate the need for it to close the socket (and therefore eliminate the potential race condition) provided I was able to add a receive timeout to the socket. So far my attempts to do this have been unsuccessful, hence why I have tried to add this function to the Ping thread. This then raises a new question: how does one add a receive timeout to a blocking socket? As per my original post, the socket is being created by the accept() function.
– Chris Hubbard
Jan 2 at 11:04
3
If you have a new question to ask, ask a new question
– David Heffernan
Jan 2 at 11:32
@ChrisHubbard You could just have the ping thread shut the connection down without closing the socket.
– David Schwartz
Jan 2 at 11:35
So it’s OK for the Ping thread to call shutdown() but not closesocket()? I was under the impression that closesocket() called shutdown() anyway. BTW, in this situation there is no need for an orderly shutdown. A hard shutdown is fine.
– Chris Hubbard
Jan 2 at 12:55
1
Note that Windows does not use file descriptors for sockets, unlike how some other platforms do. Sockets are true kernel objects, so it is unlikely for a new socket to get the same value as a recently closed socket. It is also why you can't use non-socket descriptors withselect()
, and whyclosesocket()
is needed instead of `close().
– Remy Lebeau
Jan 2 at 17:30
Thank you for your answer. In the case of the Ping thread, I could eliminate the need for it to close the socket (and therefore eliminate the potential race condition) provided I was able to add a receive timeout to the socket. So far my attempts to do this have been unsuccessful, hence why I have tried to add this function to the Ping thread. This then raises a new question: how does one add a receive timeout to a blocking socket? As per my original post, the socket is being created by the accept() function.
– Chris Hubbard
Jan 2 at 11:04
Thank you for your answer. In the case of the Ping thread, I could eliminate the need for it to close the socket (and therefore eliminate the potential race condition) provided I was able to add a receive timeout to the socket. So far my attempts to do this have been unsuccessful, hence why I have tried to add this function to the Ping thread. This then raises a new question: how does one add a receive timeout to a blocking socket? As per my original post, the socket is being created by the accept() function.
– Chris Hubbard
Jan 2 at 11:04
3
3
If you have a new question to ask, ask a new question
– David Heffernan
Jan 2 at 11:32
If you have a new question to ask, ask a new question
– David Heffernan
Jan 2 at 11:32
@ChrisHubbard You could just have the ping thread shut the connection down without closing the socket.
– David Schwartz
Jan 2 at 11:35
@ChrisHubbard You could just have the ping thread shut the connection down without closing the socket.
– David Schwartz
Jan 2 at 11:35
So it’s OK for the Ping thread to call shutdown() but not closesocket()? I was under the impression that closesocket() called shutdown() anyway. BTW, in this situation there is no need for an orderly shutdown. A hard shutdown is fine.
– Chris Hubbard
Jan 2 at 12:55
So it’s OK for the Ping thread to call shutdown() but not closesocket()? I was under the impression that closesocket() called shutdown() anyway. BTW, in this situation there is no need for an orderly shutdown. A hard shutdown is fine.
– Chris Hubbard
Jan 2 at 12:55
1
1
Note that Windows does not use file descriptors for sockets, unlike how some other platforms do. Sockets are true kernel objects, so it is unlikely for a new socket to get the same value as a recently closed socket. It is also why you can't use non-socket descriptors with
select()
, and why closesocket()
is needed instead of `close().– Remy Lebeau
Jan 2 at 17:30
Note that Windows does not use file descriptors for sockets, unlike how some other platforms do. Sockets are true kernel objects, so it is unlikely for a new socket to get the same value as a recently closed socket. It is also why you can't use non-socket descriptors with
select()
, and why closesocket()
is needed instead of `close().– Remy Lebeau
Jan 2 at 17:30
|
show 3 more comments
UPDATE: accept() creates the new socket with the same attributes as the socket used for listening. Since I hadn’t set the WSA_FLAG_OVERLAPPED attribute for the listen socket, this attribute wasn’t being set for the new socket, and options like the receive timeout didn’t do anything.
Setting the WSA_FLAG_OVERLAPPED attribute for the listen socket seems to have fixed the problem. Thus I can now use the receive timeout, and the Ping thread no longer needs to close the socket if no data has been received.
Setting the WSA_FLAG_OVERLAPPED attribute for the listen socket also seems to have addressed the blocking other threads issue.
note that theWSASocket()
documentation states: "By default, a socket created with theWSASocket()
function will not have this overlapped attribute set. In contrast, thesocket()
function creates a socket that supports overlapped I/O operations as the default behavior." So, if you usesocket()
to create the listening socket, thenaccept()
will inherit the overlapped attribute.
– Remy Lebeau
Jan 3 at 3:19
add a comment |
UPDATE: accept() creates the new socket with the same attributes as the socket used for listening. Since I hadn’t set the WSA_FLAG_OVERLAPPED attribute for the listen socket, this attribute wasn’t being set for the new socket, and options like the receive timeout didn’t do anything.
Setting the WSA_FLAG_OVERLAPPED attribute for the listen socket seems to have fixed the problem. Thus I can now use the receive timeout, and the Ping thread no longer needs to close the socket if no data has been received.
Setting the WSA_FLAG_OVERLAPPED attribute for the listen socket also seems to have addressed the blocking other threads issue.
note that theWSASocket()
documentation states: "By default, a socket created with theWSASocket()
function will not have this overlapped attribute set. In contrast, thesocket()
function creates a socket that supports overlapped I/O operations as the default behavior." So, if you usesocket()
to create the listening socket, thenaccept()
will inherit the overlapped attribute.
– Remy Lebeau
Jan 3 at 3:19
add a comment |
UPDATE: accept() creates the new socket with the same attributes as the socket used for listening. Since I hadn’t set the WSA_FLAG_OVERLAPPED attribute for the listen socket, this attribute wasn’t being set for the new socket, and options like the receive timeout didn’t do anything.
Setting the WSA_FLAG_OVERLAPPED attribute for the listen socket seems to have fixed the problem. Thus I can now use the receive timeout, and the Ping thread no longer needs to close the socket if no data has been received.
Setting the WSA_FLAG_OVERLAPPED attribute for the listen socket also seems to have addressed the blocking other threads issue.
UPDATE: accept() creates the new socket with the same attributes as the socket used for listening. Since I hadn’t set the WSA_FLAG_OVERLAPPED attribute for the listen socket, this attribute wasn’t being set for the new socket, and options like the receive timeout didn’t do anything.
Setting the WSA_FLAG_OVERLAPPED attribute for the listen socket seems to have fixed the problem. Thus I can now use the receive timeout, and the Ping thread no longer needs to close the socket if no data has been received.
Setting the WSA_FLAG_OVERLAPPED attribute for the listen socket also seems to have addressed the blocking other threads issue.
edited Jan 3 at 1:32
answered Jan 3 at 0:59
Chris HubbardChris Hubbard
104
104
note that theWSASocket()
documentation states: "By default, a socket created with theWSASocket()
function will not have this overlapped attribute set. In contrast, thesocket()
function creates a socket that supports overlapped I/O operations as the default behavior." So, if you usesocket()
to create the listening socket, thenaccept()
will inherit the overlapped attribute.
– Remy Lebeau
Jan 3 at 3:19
add a comment |
note that theWSASocket()
documentation states: "By default, a socket created with theWSASocket()
function will not have this overlapped attribute set. In contrast, thesocket()
function creates a socket that supports overlapped I/O operations as the default behavior." So, if you usesocket()
to create the listening socket, thenaccept()
will inherit the overlapped attribute.
– Remy Lebeau
Jan 3 at 3:19
note that the
WSASocket()
documentation states: "By default, a socket created with the WSASocket()
function will not have this overlapped attribute set. In contrast, the socket()
function creates a socket that supports overlapped I/O operations as the default behavior." So, if you use socket()
to create the listening socket, then accept()
will inherit the overlapped attribute.– Remy Lebeau
Jan 3 at 3:19
note that the
WSASocket()
documentation states: "By default, a socket created with the WSASocket()
function will not have this overlapped attribute set. In contrast, the socket()
function creates a socket that supports overlapped I/O operations as the default behavior." So, if you use socket()
to create the listening socket, then accept()
will inherit the overlapped attribute.– Remy Lebeau
Jan 3 at 3:19
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54002673%2fwinsock-recv-function-blocking-other-threads%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Can you please put the source code?
recv
should block only a thread where it resides. Otherwise, one connected client could block another client in a separate thread by issuing slow requests/responses.– karastojko
Jan 2 at 7:38
OK, but it'll take a little while for me to make something small enough which just contains the relevant sections.
– Chris Hubbard
Jan 2 at 8:09
I’m still working on creating a small enough test program to list here, but in the meantime another thought has occurred to me: Can threads “own” other threads, and in so doing cause one thread to block another? My application is written in Delphi, and I’m using it’s standard TThread component.
– Chris Hubbard
Jan 2 at 10:13
@MartynA on Windows, closing a socket indeed cancels blocking operations in progress in other threads.
– Remy Lebeau
Jan 2 at 17:25
@ChrisHubbard are you, by chance, using your own critical section or mutex around blocking socket operations? Without seeing your actual code, that is the only way I can think of for this to happen. By themselves, blocking sockets do not behave the way you have described.
– Remy Lebeau
Jan 2 at 17:34