Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
cppdap
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chen Yisong
cppdap
Commits
9003ee55
Commit
9003ee55
authored
Jun 08, 2020
by
Ben Clayton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Socket: Use the RWMutex to fix TSAN error
... about `close()`ing the socket on one thread while in a blocking `recv()` or `send()` call on another thread. Fixes #35
parent
53a62fd7
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
92 additions
and
77 deletions
+92
-77
socket.cpp
src/socket.cpp
+92
-77
No files found.
src/socket.cpp
View file @
9003ee55
...
@@ -14,6 +14,8 @@
...
@@ -14,6 +14,8 @@
#include "socket.h"
#include "socket.h"
#include "rwmutex.h"
#if defined(_WIN32)
#if defined(_WIN32)
#include <winsock2.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <ws2tcpip.h>
...
@@ -41,7 +43,7 @@ using SOCKET = int;
...
@@ -41,7 +43,7 @@ using SOCKET = int;
namespace
{
namespace
{
constexpr
SOCKET
InvalidSocket
=
static_cast
<
SOCKET
>
(
-
1
);
constexpr
SOCKET
InvalidSocket
=
static_cast
<
SOCKET
>
(
-
1
);
static
void
init
()
{
void
init
()
{
#if defined(_WIN32)
#if defined(_WIN32)
if
(
wsaInitCount
++
==
0
)
{
if
(
wsaInitCount
++
==
0
)
{
WSADATA
winsockData
;
WSADATA
winsockData
;
...
@@ -50,7 +52,7 @@ static void init() {
...
@@ -50,7 +52,7 @@ static void init() {
#endif
#endif
}
}
static
void
term
()
{
void
term
()
{
#if defined(_WIN32)
#if defined(_WIN32)
if
(
--
wsaInitCount
==
0
)
{
if
(
--
wsaInitCount
==
0
)
{
WSACleanup
();
WSACleanup
();
...
@@ -58,6 +60,30 @@ static void term() {
...
@@ -58,6 +60,30 @@ static void term() {
#endif
#endif
}
}
bool
setBlocking
(
SOCKET
s
,
bool
blocking
)
{
#if defined(_WIN32)
u_long
mode
=
blocking
?
0
:
1
;
return
ioctlsocket
(
s
,
FIONBIO
,
&
mode
)
==
NO_ERROR
;
#else
auto
arg
=
fcntl
(
s
,
F_GETFL
,
nullptr
);
if
(
arg
<
0
)
{
return
false
;
}
arg
=
blocking
?
(
arg
&
~
O_NONBLOCK
)
:
(
arg
|
O_NONBLOCK
);
return
fcntl
(
s
,
F_SETFL
,
arg
)
>=
0
;
#endif
}
bool
errored
(
SOCKET
s
)
{
if
(
s
==
InvalidSocket
)
{
return
true
;
}
char
error
=
0
;
socklen_t
len
=
sizeof
(
error
);
getsockopt
(
s
,
SOL_SOCKET
,
SO_ERROR
,
&
error
,
&
len
);
return
error
!=
0
;
}
}
// anonymous namespace
}
// anonymous namespace
class
dap
::
Socket
::
Shared
:
public
dap
::
ReaderWriter
{
class
dap
::
Socket
::
Shared
:
public
dap
::
ReaderWriter
{
...
@@ -87,8 +113,8 @@ class dap::Socket::Shared : public dap::ReaderWriter {
...
@@ -87,8 +113,8 @@ class dap::Socket::Shared : public dap::ReaderWriter {
return
nullptr
;
return
nullptr
;
}
}
Shared
(
SOCKET
socket
)
:
info
(
nullptr
),
s
ock
(
socket
)
{}
Shared
(
SOCKET
socket
)
:
info
(
nullptr
),
s
(
socket
)
{}
Shared
(
addrinfo
*
info
,
SOCKET
socket
)
:
info
(
info
),
s
ock
(
socket
)
{}
Shared
(
addrinfo
*
info
,
SOCKET
socket
)
:
info
(
info
),
s
(
socket
)
{}
~
Shared
()
{
~
Shared
()
{
freeaddrinfo
(
info
);
freeaddrinfo
(
info
);
...
@@ -96,10 +122,14 @@ class dap::Socket::Shared : public dap::ReaderWriter {
...
@@ -96,10 +122,14 @@ class dap::Socket::Shared : public dap::ReaderWriter {
term
();
term
();
}
}
SOCKET
socket
()
{
return
sock
.
load
();
}
template
<
typename
FUNCTION
>
void
lock
(
FUNCTION
&&
f
)
{
RLock
l
(
mutex
);
f
(
s
,
info
);
}
void
setOptions
()
{
void
setOptions
()
{
SOCKET
s
=
socket
(
);
RLock
l
(
mutex
);
if
(
s
==
InvalidSocket
)
{
if
(
s
==
InvalidSocket
)
{
return
;
return
;
}
}
...
@@ -125,59 +155,42 @@ class dap::Socket::Shared : public dap::ReaderWriter {
...
@@ -125,59 +155,42 @@ class dap::Socket::Shared : public dap::ReaderWriter {
setsockopt
(
s
,
IPPROTO_TCP
,
TCP_NODELAY
,
(
char
*
)
&
enable
,
sizeof
(
enable
));
setsockopt
(
s
,
IPPROTO_TCP
,
TCP_NODELAY
,
(
char
*
)
&
enable
,
sizeof
(
enable
));
}
}
bool
setBlocking
(
bool
blocking
)
{
// dap::ReaderWriter compliance
SOCKET
s
=
socket
();
bool
isOpen
()
{
if
(
s
==
InvalidSocket
)
{
{
return
false
;
RLock
l
(
mutex
);
}
if
((
s
!=
InvalidSocket
)
&&
!
errored
(
s
))
{
#if defined(_WIN32)
u_long
mode
=
blocking
?
0
:
1
;
return
ioctlsocket
(
s
,
FIONBIO
,
&
mode
)
==
NO_ERROR
;
#else
auto
arg
=
fcntl
(
s
,
F_GETFL
,
nullptr
);
if
(
arg
<
0
)
{
return
false
;
}
arg
=
blocking
?
(
arg
&
~
O_NONBLOCK
)
:
(
arg
|
O_NONBLOCK
);
return
fcntl
(
s
,
F_SETFL
,
arg
)
>=
0
;
#endif
}
bool
errored
()
{
SOCKET
s
=
socket
();
if
(
s
==
InvalidSocket
)
{
return
true
;
return
true
;
}
}
char
error
=
0
;
socklen_t
len
=
sizeof
(
error
);
getsockopt
(
s
,
SOL_SOCKET
,
SO_ERROR
,
&
error
,
&
len
);
if
(
error
!=
0
)
{
sock
.
compare_exchange_weak
(
s
,
InvalidSocket
);
return
true
;
}
}
WLock
lock
(
mutex
);
s
=
InvalidSocket
;
return
false
;
return
false
;
}
}
// dap::ReaderWriter compliance
bool
isOpen
()
{
return
!
errored
();
}
void
close
()
{
void
close
()
{
SOCKET
s
=
sock
.
exchange
(
InvalidSocket
);
#if !defined(_WIN32)
{
RLock
l
(
mutex
);
if
(
s
!=
InvalidSocket
)
{
::
shutdown
(
s
,
SHUT_RDWR
);
}
}
#endif
WLock
l
(
mutex
);
if
(
s
!=
InvalidSocket
)
{
if
(
s
!=
InvalidSocket
)
{
#if defined(_WIN32)
#if defined(_WIN32)
closesocket
(
s
);
closesocket
(
s
);
#else
#else
::
shutdown
(
s
,
SHUT_RDWR
);
::
close
(
s
);
::
close
(
s
);
#endif
#endif
s
=
InvalidSocket
;
}
}
}
}
size_t
read
(
void
*
buffer
,
size_t
bytes
)
{
size_t
read
(
void
*
buffer
,
size_t
bytes
)
{
SOCKET
s
=
socket
(
);
RLock
lock
(
mutex
);
if
(
s
==
InvalidSocket
)
{
if
(
s
==
InvalidSocket
)
{
return
0
;
return
0
;
}
}
...
@@ -187,7 +200,7 @@ class dap::Socket::Shared : public dap::ReaderWriter {
...
@@ -187,7 +200,7 @@ class dap::Socket::Shared : public dap::ReaderWriter {
}
}
bool
write
(
const
void
*
buffer
,
size_t
bytes
)
{
bool
write
(
const
void
*
buffer
,
size_t
bytes
)
{
SOCKET
s
=
socket
(
);
RLock
lock
(
mutex
);
if
(
s
==
InvalidSocket
)
{
if
(
s
==
InvalidSocket
)
{
return
false
;
return
false
;
}
}
...
@@ -198,22 +211,19 @@ class dap::Socket::Shared : public dap::ReaderWriter {
...
@@ -198,22 +211,19 @@ class dap::Socket::Shared : public dap::ReaderWriter {
static_cast
<
int
>
(
bytes
),
0
)
>
0
;
static_cast
<
int
>
(
bytes
),
0
)
>
0
;
}
}
addrinfo
*
const
info
;
private
:
private
:
std
::
atomic
<
SOCKET
>
sock
=
{
InvalidSocket
};
addrinfo
*
const
info
;
SOCKET
s
=
InvalidSocket
;
RWMutex
mutex
;
};
};
namespace
dap
{
namespace
dap
{
Socket
::
Socket
(
const
char
*
address
,
const
char
*
port
)
Socket
::
Socket
(
const
char
*
address
,
const
char
*
port
)
:
shared
(
Shared
::
create
(
address
,
port
))
{
:
shared
(
Shared
::
create
(
address
,
port
))
{
if
(
!
shared
)
{
if
(
shared
)
{
return
;
shared
->
lock
([
&
](
SOCKET
socket
,
const
addrinfo
*
info
)
{
}
if
(
bind
(
socket
,
info
->
ai_addr
,
(
int
)
info
->
ai_addrlen
)
!=
0
)
{
auto
socket
=
shared
->
socket
();
if
(
bind
(
socket
,
shared
->
info
->
ai_addr
,
(
int
)
shared
->
info
->
ai_addrlen
)
!=
0
)
{
shared
.
reset
();
shared
.
reset
();
return
;
return
;
}
}
...
@@ -222,20 +232,22 @@ Socket::Socket(const char* address, const char* port)
...
@@ -222,20 +232,22 @@ Socket::Socket(const char* address, const char* port)
shared
.
reset
();
shared
.
reset
();
return
;
return
;
}
}
});
}
}
}
std
::
shared_ptr
<
ReaderWriter
>
Socket
::
accept
()
const
{
std
::
shared_ptr
<
ReaderWriter
>
Socket
::
accept
()
const
{
std
::
shared_ptr
<
Shared
>
out
;
if
(
shared
)
{
if
(
shared
)
{
SOCKET
socket
=
shared
->
socket
();
shared
->
lock
([
&
](
SOCKET
socket
,
const
addrinfo
*
)
{
if
(
socket
!=
InvalidSocket
)
{
if
(
socket
!=
InvalidSocket
)
{
init
();
init
();
auto
out
=
std
::
make_shared
<
Shared
>
(
::
accept
(
socket
,
0
,
0
));
out
=
std
::
make_shared
<
Shared
>
(
::
accept
(
socket
,
0
,
0
));
out
->
setOptions
();
out
->
setOptions
();
return
out
;
}
}
});
}
}
return
out
;
return
{};
}
}
bool
Socket
::
isOpen
()
const
{
bool
Socket
::
isOpen
()
const
{
...
@@ -259,47 +271,50 @@ std::shared_ptr<ReaderWriter> Socket::connect(const char* address,
...
@@ -259,47 +271,50 @@ std::shared_ptr<ReaderWriter> Socket::connect(const char* address,
return
nullptr
;
return
nullptr
;
}
}
if
(
timeoutMillis
==
0
)
{
std
::
shared_ptr
<
ReaderWriter
>
out
;
if
(
::
connect
(
shared
->
socket
(),
shared
->
info
->
ai_addr
,
shared
->
lock
([
&
](
SOCKET
socket
,
const
addrinfo
*
info
)
{
(
int
)
shared
->
info
->
ai_addrlen
)
==
0
)
{
if
(
socket
==
InvalidSocket
)
{
return
shared
;
return
;
}
return
nullptr
;
}
}
auto
s
=
shared
->
socket
();
if
(
timeoutMillis
==
0
)
{
if
(
s
==
InvalidSocket
)
{
if
(
::
connect
(
socket
,
info
->
ai_addr
,
(
int
)
info
->
ai_addrlen
)
==
0
)
{
return
nullptr
;
out
=
shared
;
}
return
;
}
}
if
(
!
shared
->
setBlocking
(
false
))
{
if
(
!
setBlocking
(
socket
,
false
))
{
return
nullptr
;
return
;
}
}
auto
res
=
::
connect
(
s
,
shared
->
info
->
ai_addr
,
(
int
)
shared
->
info
->
ai_addrlen
);
auto
res
=
::
connect
(
socket
,
info
->
ai_addr
,
(
int
)
info
->
ai_addrlen
);
if
(
res
==
0
)
{
if
(
res
==
0
)
{
return
shared
->
setBlocking
(
true
)
?
shared
:
nullptr
;
if
(
setBlocking
(
socket
,
true
))
{
out
=
shared
;
}
}
}
else
{
const
auto
microseconds
=
timeoutMillis
*
1000
;
const
auto
microseconds
=
timeoutMillis
*
1000
;
fd_set
fdset
;
fd_set
fdset
;
FD_ZERO
(
&
fdset
);
FD_ZERO
(
&
fdset
);
FD_SET
(
s
,
&
fdset
);
FD_SET
(
socket
,
&
fdset
);
timeval
tv
;
timeval
tv
;
tv
.
tv_sec
=
microseconds
/
1000000
;
tv
.
tv_sec
=
microseconds
/
1000000
;
tv
.
tv_usec
=
microseconds
-
(
tv
.
tv_sec
*
1000000
);
tv
.
tv_usec
=
microseconds
-
(
tv
.
tv_sec
*
1000000
);
res
=
select
(
static_cast
<
int
>
(
s
+
1
),
nullptr
,
&
fdset
,
nullptr
,
&
tv
);
res
=
select
(
static_cast
<
int
>
(
socket
+
1
),
nullptr
,
&
fdset
,
nullptr
,
&
tv
);
if
(
res
<=
0
)
{
if
(
res
>
0
&&
!
errored
(
socket
)
&&
setBlocking
(
socket
,
true
))
{
return
nullptr
;
out
=
shared
;
}
}
}
});
if
(
shared
->
errored
()
)
{
if
(
!
out
)
{
return
nullptr
;
return
nullptr
;
}
}
return
shared
->
setBlocking
(
true
)
?
shared
:
nullptr
;
return
out
->
isOpen
()
?
out
:
nullptr
;
}
}
}
// namespace dap
}
// namespace dap
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment