Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
pistache
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
pistache
Commits
e673be32
Commit
e673be32
authored
Feb 09, 2016
by
octal
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improved the client architecture and fixed a data-race in async
parent
4dd9f900
Changes
7
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
612 additions
and
140 deletions
+612
-140
examples/http_client.cc
examples/http_client.cc
+9
-5
include/async.h
include/async.h
+23
-3
include/client.h
include/client.h
+80
-54
include/timer_pool.h
include/timer_pool.h
+80
-0
src/client/client.cc
src/client/client.cc
+202
-78
src/common/timer_pool.cc
src/common/timer_pool.cc
+79
-0
tests/async_test.cc
tests/async_test.cc
+139
-0
No files found.
examples/http_client.cc
View file @
e673be32
...
@@ -15,11 +15,11 @@ int main() {
...
@@ -15,11 +15,11 @@ int main() {
Http
::
Experimental
::
Client
client
(
"http://supnetwork.org:9080"
);
Http
::
Experimental
::
Client
client
(
"http://supnetwork.org:9080"
);
auto
opts
=
Http
::
Experimental
::
Client
::
options
()
auto
opts
=
Http
::
Experimental
::
Client
::
options
()
.
threads
(
1
)
.
threads
(
1
)
.
maxConnections
(
20
);
.
maxConnections
(
64
);
using
namespace
Net
::
Http
;
using
namespace
Net
::
Http
;
constexpr
size_t
Requests
=
1000
;
constexpr
size_t
Requests
=
1000
0
;
std
::
atomic
<
int
>
responsesReceived
(
0
);
std
::
atomic
<
int
>
responsesReceived
(
0
);
client
.
init
(
opts
);
client
.
init
(
opts
);
...
@@ -30,13 +30,17 @@ int main() {
...
@@ -30,13 +30,17 @@ int main() {
.
then
([
&
](
const
Http
::
Response
&
response
)
{
.
then
([
&
](
const
Http
::
Response
&
response
)
{
responsesReceived
.
fetch_add
(
1
);
responsesReceived
.
fetch_add
(
1
);
//std::cout << "code = " << response.code() << std::endl;
//std::cout << "code = " << response.code() << std::endl;
//
std::cout << "body = " << response.body() << std::endl;
//
std::cout << "body = " << response.body() << std::endl;
},
Async
::
NoExcept
);
},
Async
::
NoExcept
);
const
auto
count
=
i
+
1
;
if
(
count
%
10
==
0
)
std
::
cout
<<
"Sent "
<<
count
<<
" requests"
<<
std
::
endl
;
}
}
std
::
cout
<<
"Sent "
<<
Requests
<<
" requests"
<<
std
::
endl
;
for
(;;)
{
for
(;;)
{
std
::
this_thread
::
sleep_for
(
std
::
chrono
::
seconds
(
1
));
std
::
this_thread
::
sleep_for
(
std
::
chrono
::
seconds
(
1
));
std
::
cout
<<
"Received "
<<
responsesReceived
.
load
()
<<
" responses"
<<
std
::
endl
;
auto
count
=
responsesReceived
.
load
();
std
::
cout
<<
"Received "
<<
count
<<
" responses"
<<
std
::
endl
;
if
(
count
==
Requests
)
break
;
}
}
client
.
shutdown
();
client
.
shutdown
();
}
}
include/async.h
View file @
e673be32
...
@@ -12,6 +12,7 @@
...
@@ -12,6 +12,7 @@
#include <memory>
#include <memory>
#include <atomic>
#include <atomic>
#include <vector>
#include <vector>
#include <mutex>
#include "optional.h"
#include "optional.h"
#include "typeid.h"
#include "typeid.h"
...
@@ -170,6 +171,19 @@ namespace Async {
...
@@ -170,6 +171,19 @@ namespace Async {
State
state
;
State
state
;
std
::
exception_ptr
exc
;
std
::
exception_ptr
exc
;
/*
* We need this lock because a Promise might be resolved or rejected from a thread A
* while a continuation to the same Promise (Core) might be attached at the same from
* a thread B. If that's the case, then we need to serialize operations so that we
* avoid a race-condition.
*
* Since we have a lock, we have a blocking progress guarantee but I don't expect this
* to be a major bottleneck as I don't expect major contention on the lock
* If it ends up being a bottlenick, try @improving it by experimenting with a lock-free
* scheme
*/
std
::
mutex
mtx
;
std
::
vector
<
std
::
shared_ptr
<
Request
>>
requests
;
std
::
vector
<
std
::
shared_ptr
<
Request
>>
requests
;
TypeId
id
;
TypeId
id
;
...
@@ -536,10 +550,13 @@ namespace Async {
...
@@ -536,10 +550,13 @@ namespace Async {
* than runtime. However, since types are erased, this looks like
* than runtime. However, since types are erased, this looks like
* a difficult task
* a difficult task
*/
*/
if
(
core_
->
isVoid
())
if
(
core_
->
isVoid
())
{
throw
Error
(
"Attempt to resolve a void promise with arguments"
);
throw
Error
(
"Attempt to resolve a void promise with arguments"
);
}
std
::
unique_lock
<
std
::
mutex
>
guard
(
core_
->
mtx
);
core_
->
construct
<
Type
>
(
std
::
forward
<
Arg
>
(
arg
));
core_
->
construct
<
Type
>
(
std
::
forward
<
Arg
>
(
arg
));
for
(
const
auto
&
req
:
core_
->
requests
)
{
for
(
const
auto
&
req
:
core_
->
requests
)
{
req
->
resolve
(
core_
);
req
->
resolve
(
core_
);
}
}
...
@@ -554,6 +571,7 @@ namespace Async {
...
@@ -554,6 +571,7 @@ namespace Async {
if
(
!
core_
->
isVoid
())
if
(
!
core_
->
isVoid
())
throw
Error
(
"Attempt ro resolve a non-void promise with no argument"
);
throw
Error
(
"Attempt ro resolve a non-void promise with no argument"
);
std
::
unique_lock
<
std
::
mutex
>
guard
(
core_
->
mtx
);
core_
->
state
=
State
::
Fulfilled
;
core_
->
state
=
State
::
Fulfilled
;
for
(
const
auto
&
req
:
core_
->
requests
)
{
for
(
const
auto
&
req
:
core_
->
requests
)
{
req
->
resolve
(
core_
);
req
->
resolve
(
core_
);
...
@@ -578,6 +596,7 @@ namespace Async {
...
@@ -578,6 +596,7 @@ namespace Async {
if
(
core_
->
state
!=
State
::
Pending
)
if
(
core_
->
state
!=
State
::
Pending
)
throw
Error
(
"Attempt to reject a fulfilled promise"
);
throw
Error
(
"Attempt to reject a fulfilled promise"
);
std
::
unique_lock
<
std
::
mutex
>
guard
(
core_
->
mtx
);
core_
->
exc
=
std
::
make_exception_ptr
(
exc
);
core_
->
exc
=
std
::
make_exception_ptr
(
exc
);
core_
->
state
=
State
::
Rejected
;
core_
->
state
=
State
::
Rejected
;
for
(
const
auto
&
req
:
core_
->
requests
)
{
for
(
const
auto
&
req
:
core_
->
requests
)
{
...
@@ -716,6 +735,7 @@ namespace Async {
...
@@ -716,6 +735,7 @@ namespace Async {
std
::
forward
<
ResolveFunc
>
(
resolveFunc
),
std
::
forward
<
ResolveFunc
>
(
resolveFunc
),
std
::
forward
<
RejectFunc
>
(
rejectFunc
)));
std
::
forward
<
RejectFunc
>
(
rejectFunc
)));
std
::
unique_lock
<
std
::
mutex
>
guard
(
core_
->
mtx
);
if
(
isFulfilled
())
{
if
(
isFulfilled
())
{
req
->
resolve
(
core_
);
req
->
resolve
(
core_
);
}
}
...
...
include/client.h
View file @
e673be32
...
@@ -9,6 +9,7 @@
...
@@ -9,6 +9,7 @@
#include "os.h"
#include "os.h"
#include "http.h"
#include "http.h"
#include "io.h"
#include "io.h"
#include "timer_pool.h"
#include <atomic>
#include <atomic>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
...
@@ -23,30 +24,42 @@ namespace Experimental {
...
@@ -23,30 +24,42 @@ namespace Experimental {
class
ConnectionPool
;
class
ConnectionPool
;
class
Transport
;
class
Transport
;
struct
Connection
{
struct
Connection
:
public
std
::
enable_shared_from_this
<
Connection
>
{
friend
class
ConnectionPool
;
friend
class
ConnectionPool
;
typedef
std
::
function
<
void
()
>
OnDone
;
typedef
std
::
function
<
void
()
>
OnDone
;
Connection
()
:
fd
(
-
1
)
,
connectionState_
(
NotConnected
)
,
inflightCount
(
0
)
,
responsesReceived
(
0
)
{
state_
.
store
(
static_cast
<
uint32_t
>
(
State
::
Idle
));
}
struct
RequestData
{
struct
RequestData
{
RequestData
(
RequestData
(
Async
::
Resolver
resolve
,
Async
::
Rejection
reject
,
Async
::
Resolver
resolve
,
Async
::
Rejection
reject
,
const
Http
::
Request
&
request
,
const
Http
::
Request
&
request
,
std
::
string
host
,
std
::
string
host
,
std
::
chrono
::
milliseconds
timeout
,
OnDone
onDone
)
OnDone
onDone
)
:
resolve
(
std
::
move
(
resolve
))
:
resolve
(
std
::
move
(
resolve
))
,
reject
(
std
::
move
(
reject
))
,
reject
(
std
::
move
(
reject
))
,
request
(
request
)
,
request
(
request
)
,
host
(
std
::
move
(
host
))
,
host
(
std
::
move
(
host
))
,
timeout
(
timeout
)
,
onDone
(
std
::
move
(
onDone
))
,
onDone
(
std
::
move
(
onDone
))
{
}
{
}
Async
::
Resolver
resolve
;
Async
::
Resolver
resolve
;
Async
::
Rejection
reject
;
Async
::
Rejection
reject
;
std
::
string
host
;
Http
::
Request
request
;
Http
::
Request
request
;
std
::
string
host
;
std
::
chrono
::
milliseconds
timeout
;
OnDone
onDone
;
OnDone
onDone
;
};
};
...
@@ -61,13 +74,6 @@ struct Connection {
...
@@ -61,13 +74,6 @@ struct Connection {
Connected
Connected
};
};
Connection
()
:
fd
(
-
1
)
,
connectionState_
(
NotConnected
)
{
state_
.
store
(
static_cast
<
uint32_t
>
(
State
::
Idle
));
}
void
connect
(
Net
::
Address
addr
);
void
connect
(
Net
::
Address
addr
);
void
close
();
void
close
();
bool
isConnected
()
const
;
bool
isConnected
()
const
;
...
@@ -77,24 +83,58 @@ struct Connection {
...
@@ -77,24 +83,58 @@ struct Connection {
Async
::
Promise
<
Response
>
perform
(
Async
::
Promise
<
Response
>
perform
(
const
Http
::
Request
&
request
,
const
Http
::
Request
&
request
,
std
::
string
host
,
std
::
string
host
,
std
::
chrono
::
milliseconds
timeout
,
OnDone
onDone
);
OnDone
onDone
);
void
performImpl
(
void
performImpl
(
const
Http
::
Request
&
request
,
const
Http
::
Request
&
request
,
std
::
string
host
,
std
::
string
host
,
std
::
chrono
::
milliseconds
timeout
,
Async
::
Resolver
resolve
,
Async
::
Resolver
resolve
,
Async
::
Rejection
reject
,
Async
::
Rejection
reject
,
OnDone
onDone
);
OnDone
onDone
);
Fd
fd
;
Fd
fd
;
void
handleResponsePacket
(
const
char
*
buffer
,
size_t
totalBytes
);
void
handleTimeout
();
std
::
string
dump
()
const
;
private:
private:
std
::
atomic
<
int
>
inflightCount
;
std
::
atomic
<
int
>
responsesReceived
;
struct
sockaddr_in
saddr
;
void
processRequestQueue
();
void
processRequestQueue
();
struct
RequestEntry
{
RequestEntry
(
Async
::
Resolver
resolve
,
Async
::
Rejection
reject
,
std
::
shared_ptr
<
TimerPool
::
Entry
>
timer
,
OnDone
onDone
)
:
resolve
(
std
::
move
(
resolve
))
,
reject
(
std
::
move
(
reject
))
,
timer
(
std
::
move
(
timer
))
,
onDone
(
std
::
move
(
onDone
))
{
}
Async
::
Resolver
resolve
;
Async
::
Rejection
reject
;
std
::
shared_ptr
<
TimerPool
::
Entry
>
timer
;
OnDone
onDone
;
};
std
::
atomic
<
uint32_t
>
state_
;
std
::
atomic
<
uint32_t
>
state_
;
ConnectionState
connectionState_
;
ConnectionState
connectionState_
;
std
::
shared_ptr
<
Transport
>
transport_
;
std
::
shared_ptr
<
Transport
>
transport_
;
Queue
<
RequestData
>
requestsQueue
;
Queue
<
RequestData
>
requestsQueue
;
std
::
deque
<
RequestEntry
>
inflightRequests
;
Net
::
TimerPool
timerPool_
;
Private
::
Parser
<
Http
::
Response
>
parser_
;
};
};
struct
ConnectionPool
{
struct
ConnectionPool
{
...
@@ -121,14 +161,12 @@ public:
...
@@ -121,14 +161,12 @@ public:
void
registerPoller
(
Polling
::
Epoll
&
poller
);
void
registerPoller
(
Polling
::
Epoll
&
poller
);
Async
::
Promise
<
void
>
Async
::
Promise
<
void
>
asyncConnect
(
Fd
fd
,
const
struct
sockaddr
*
address
,
socklen_t
addr_len
);
asyncConnect
(
const
std
::
shared_ptr
<
Connection
>&
connection
,
const
struct
sockaddr
*
address
,
socklen_t
addr_len
);
void
asyncSendRequest
(
Async
::
Promise
<
ssize_t
>
asyncSendRequest
(
Fd
fd
,
const
std
::
shared_ptr
<
Connection
>&
connection
,
const
Buffer
&
buffer
,
std
::
shared_ptr
<
TimerPool
::
Entry
>
timer
,
Async
::
Resolver
resolve
,
const
Buffer
&
buffer
);
Async
::
Rejection
reject
,
OnResponseParsed
onParsed
);
private:
private:
...
@@ -137,74 +175,61 @@ private:
...
@@ -137,74 +175,61 @@ private:
Retry
Retry
};
};
struct
PendingConnection
{
struct
ConnectionEntry
{
PendingConnection
(
ConnectionEntry
(
Async
::
Resolver
resolve
,
Async
::
Rejection
reject
,
Async
::
Resolver
resolve
,
Async
::
Rejection
reject
,
Fd
fd
,
const
struct
sockaddr
*
addr
,
socklen_t
addr_len
)
std
::
shared_ptr
<
Connection
>
connection
,
const
struct
sockaddr
*
addr
,
socklen_t
addr_len
)
:
resolve
(
std
::
move
(
resolve
))
:
resolve
(
std
::
move
(
resolve
))
,
reject
(
std
::
move
(
reject
))
,
reject
(
std
::
move
(
reject
))
,
fd
(
fd
)
,
connection
(
std
::
move
(
connection
)
)
,
addr
(
addr
)
,
addr
(
addr
)
,
addr_len
(
addr_len
)
,
addr_len
(
addr_len
)
{
}
{
}
Async
::
Resolver
resolve
;
Async
::
Resolver
resolve
;
Async
::
Rejection
reject
;
Async
::
Rejection
reject
;
Fd
fd
;
std
::
shared_ptr
<
Connection
>
connection
;
const
struct
sockaddr
*
addr
;
const
struct
sockaddr
*
addr
;
socklen_t
addr_len
;
socklen_t
addr_len
;
};
};
struct
InflightRequest
{
struct
RequestEntry
{
InflightRequest
(
RequestEntry
(
Async
::
Resolver
resolve
,
Async
::
Rejection
reject
,
Async
::
Resolver
resolve
,
Async
::
Rejection
reject
,
Fd
fd
,
std
::
shared_ptr
<
Connection
>
connection
,
const
Buffer
&
buff
er
,
std
::
shared_ptr
<
TimerPool
::
Entry
>
tim
er
,
OnResponseParsed
onParsed
=
nullpt
r
)
const
Buffer
&
buffe
r
)
:
resolve
_
(
std
::
move
(
resolve
))
:
resolve
(
std
::
move
(
resolve
))
,
reject
(
std
::
move
(
reject
))
,
reject
(
std
::
move
(
reject
))
,
fd
(
fd
)
,
connection
(
std
::
move
(
connection
))
,
timer
(
std
::
move
(
timer
))
,
buffer
(
buffer
)
,
buffer
(
buffer
)
,
onParsed
(
onParsed
)
{
{
}
}
void
feed
(
const
char
*
buffer
,
size_t
totalBytes
)
{
Async
::
Resolver
resolve
;
if
(
!
parser
)
parser
.
reset
(
new
Private
::
Parser
<
Http
::
Response
>
());
parser
->
feed
(
buffer
,
totalBytes
);
}
void
resolve
(
Http
::
Response
response
)
{
if
(
onParsed
)
onParsed
();
resolve_
(
std
::
move
(
response
));
}
Async
::
Resolver
resolve_
;
Async
::
Rejection
reject
;
Async
::
Rejection
reject
;
Fd
fd
;
std
::
shared_ptr
<
Connection
>
connection
;
std
::
shared_ptr
<
TimerPool
::
Entry
>
timer
;
Buffer
buffer
;
Buffer
buffer
;
OnResponseParsed
onParsed
;
std
::
shared_ptr
<
Private
::
Parser
<
Http
::
Response
>>
parser
;
};
};
PollableQueue
<
InflightRequest
>
requestsQueue
;
PollableQueue
<
RequestEntry
>
requestsQueue
;
PollableQueue
<
PendingConnection
>
connectionsQueue
;
PollableQueue
<
ConnectionEntry
>
connectionsQueue
;
std
::
unordered_map
<
Fd
,
PendingConnection
>
pendingConnections
;
std
::
unordered_map
<
Fd
,
ConnectionEntry
>
connections
;
std
::
unordered_map
<
Fd
,
std
::
deque
<
InflightRequest
>>
inflightRequests
;
std
::
unordered_map
<
Fd
,
RequestEntry
>
requests
;
std
::
unordered_map
<
Fd
,
std
::
shared_ptr
<
Connection
>>
timeouts
;
void
asyncSendRequestImpl
(
InflightRequest
&
req
,
WriteStatus
status
=
FirstTry
);
void
asyncSendRequestImpl
(
const
RequestEntry
&
req
,
WriteStatus
status
=
FirstTry
);
void
handleRequestsQueue
();
void
handleRequestsQueue
();
void
handleConnectionQueue
();
void
handleConnectionQueue
();
void
handleIncoming
(
Fd
fd
);
void
handleIncoming
(
const
std
::
shared_ptr
<
Connection
>&
connection
);
void
handleResponsePacket
(
Fd
fd
,
const
char
*
buffer
,
size_t
totalBytes
);
void
handleResponsePacket
(
const
std
::
shared_ptr
<
Connection
>&
connection
,
const
char
*
buffer
,
size_t
totalBytes
);
void
handleTimeout
(
const
std
::
shared_ptr
<
Connection
>&
connection
);
};
};
...
@@ -260,6 +285,7 @@ public:
...
@@ -260,6 +285,7 @@ public:
void
shutdown
();
void
shutdown
();
private:
private:
Io
::
ServiceGroup
io_
;
Io
::
ServiceGroup
io_
;
std
::
string
url_
;
std
::
string
url_
;
std
::
string
host_
;
std
::
string
host_
;
...
...
include/timer_pool.h
0 → 100644
View file @
e673be32
/* timer_pool.h
Mathieu Stefani, 09 février 2016
A pool of timer fd to avoid creating fds everytime we need a timer and
thus reduce the total number of system calls.
Most operations are lock-free except resize operations needed when the
pool is empty, in which case it's blocking but we expect it to be rare.
*/
#pragma once
#include <memory>
#include <vector>
#include <mutex>
#include <atomic>
#include <unistd.h>
#include "os.h"
#include "io.h"
namespace
Net
{
namespace
Default
{
static
constexpr
size_t
InitialPoolSize
=
128
;
}
class
TimerPool
{
public:
TimerPool
(
size_t
initialSize
=
Default
::
InitialPoolSize
);
struct
Entry
{
friend
class
TimerPool
;
Fd
fd
;
Entry
()
:
fd
(
-
1
)
,
registered
(
false
)
{
state
.
store
(
static_cast
<
uint32_t
>
(
State
::
Idle
));
}
~
Entry
()
{
if
(
fd
!=
-
1
)
close
(
fd
);
}
void
initialize
();
template
<
typename
Duration
>
void
arm
(
Duration
duration
)
{
if
(
fd
==
-
1
)
return
;
armMs
(
std
::
chrono
::
duration_cast
<
std
::
chrono
::
milliseconds
>
(
duration
));
}
void
disarm
();
void
registerIo
(
Io
::
Service
*
io
)
{
if
(
!
registered
)
{
io
->
registerFd
(
fd
,
Polling
::
NotifyOn
::
Read
);
}
registered
=
true
;
}
private:
void
armMs
(
std
::
chrono
::
milliseconds
value
);
enum
class
State
:
uint32_t
{
Idle
,
Used
};
std
::
atomic
<
uint32_t
>
state
;
bool
registered
;
};
std
::
shared_ptr
<
Entry
>
pickTimer
();
void
releaseTimer
(
const
std
::
shared_ptr
<
Entry
>&
timer
);
private:
std
::
vector
<
std
::
shared_ptr
<
Entry
>>
timers
;
};
}
// namespace Net
src/client/client.cc
View file @
e673be32
This diff is collapsed.
Click to expand it.
src/common/timer_pool.cc
0 → 100644
View file @
e673be32
/* timer_pool.cc
Mathieu Stefani, 09 février 2016
Implementation of the timer pool
*/
#include "timer_pool.h"
#include <sys/timerfd.h>
namespace
Net
{
void
TimerPool
::
Entry
::
initialize
()
{
if
(
fd
==
-
1
)
{
fd
=
TRY_RET
(
timerfd_create
(
CLOCK_MONOTONIC
,
TFD_NONBLOCK
));
}
}
void
TimerPool
::
Entry
::
disarm
()
{
if
(
fd
==
-
1
)
return
;
itimerspec
spec
;
spec
.
it_interval
.
tv_sec
=
0
;
spec
.
it_interval
.
tv_nsec
=
0
;
spec
.
it_value
.
tv_sec
=
0
;
spec
.
it_value
.
tv_nsec
=
0
;
TRY
(
timerfd_settime
(
fd
,
0
,
&
spec
,
0
));
}
void
TimerPool
::
Entry
::
armMs
(
std
::
chrono
::
milliseconds
value
)
{
itimerspec
spec
;
spec
.
it_interval
.
tv_sec
=
0
;
spec
.
it_interval
.
tv_nsec
=
0
;
if
(
value
.
count
()
<
1000
)
{
spec
.
it_value
.
tv_sec
=
0
;
spec
.
it_value
.
tv_nsec
=
std
::
chrono
::
duration_cast
<
std
::
chrono
::
nanoseconds
>
(
value
).
count
();
}
else
{
spec
.
it_value
.
tv_sec
=
std
::
chrono
::
duration_cast
<
std
::
chrono
::
seconds
>
(
value
).
count
();
spec
.
it_value
.
tv_nsec
=
0
;
}
TRY
(
timerfd_settime
(
fd
,
0
,
&
spec
,
0
));
}
TimerPool
::
TimerPool
(
size_t
initialSize
)
{
for
(
size_t
i
=
0
;
i
<
initialSize
;
++
i
)
{
timers
.
push_back
(
std
::
make_shared
<
TimerPool
::
Entry
>
());
}
}
std
::
shared_ptr
<
TimerPool
::
Entry
>
TimerPool
::
pickTimer
()
{
for
(
auto
&
entry
:
timers
)
{
auto
curState
=
static_cast
<
uint32_t
>
(
TimerPool
::
Entry
::
State
::
Idle
);
auto
newState
=
static_cast
<
uint32_t
>
(
TimerPool
::
Entry
::
State
::
Used
);
if
(
entry
->
state
.
compare_exchange_strong
(
curState
,
newState
))
{
entry
->
initialize
();
return
entry
;
}
}
return
nullptr
;
}
void
TimerPool
::
releaseTimer
(
const
std
::
shared_ptr
<
Entry
>&
timer
)
{
timer
->
state
.
store
(
static_cast
<
uint32_t
>
(
TimerPool
::
Entry
::
State
::
Idle
));
}
}
// namespace Net
tests/async_test.cc
View file @
e673be32
...
@@ -2,6 +2,9 @@
...
@@ -2,6 +2,9 @@
#include "async.h"
#include "async.h"
#include <thread>
#include <thread>
#include <algorithm>
#include <algorithm>
#include <deque>
#include <mutex>
#include <condition_variable>
Async
::
Promise
<
int
>
doAsync
(
int
N
)
Async
::
Promise
<
int
>
doAsync
(
int
N
)
{
{
...
@@ -291,3 +294,139 @@ TEST(async_test, rethrow_test) {
...
@@ -291,3 +294,139 @@ TEST(async_test, rethrow_test) {
ASSERT_TRUE
(
p2
.
isRejected
());
ASSERT_TRUE
(
p2
.
isRejected
());
}
}
template
<
typename
T
>
struct
MessageQueue
{
public:
template
<
typename
U
>
void
push
(
U
&&
arg
)
{
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
q
.
push_back
(
std
::
forward
<
U
>
(
arg
));
cv
.
notify_one
();
}
T
pop
()
{
std
::
unique_lock
<
std
::
mutex
>
lock
(
mtx
);
cv
.
wait
(
lock
,
[
=
]()
{
return
!
q
.
empty
();
});
T
out
=
std
::
move
(
q
.
front
());
q
.
pop_front
();
return
out
;
}
bool
tryPop
(
T
&
out
,
std
::
chrono
::
milliseconds
timeout
)
{
std
::
unique_lock
<
std
::
mutex
>
lock
(
mtx
);
if
(
!
cv
.
wait_for
(
lock
,
timeout
,
[
=
]()
{
return
!
q
.
empty
();
}))
return
false
;
out
=
std
::
move
(
q
.
front
());
q
.
pop_front
();
return
true
;
}
private:
std
::
deque
<
T
>
q
;
std
::
mutex
mtx
;
std
::
condition_variable
cv
;
};
struct
Worker
{
public:
~
Worker
()
{
thread
->
join
();
}
void
start
()
{
shutdown
.
store
(
false
);
thread
.
reset
(
new
std
::
thread
([
=
]()
{
run
();
}));
}
void
stop
()
{
shutdown
.
store
(
true
);
}
Async
::
Promise
<
int
>
doWork
(
int
seq
)
{
return
Async
::
Promise
<
int
>
([
=
](
Async
::
Resolver
&
resolve
,
Async
::
Rejection
&
reject
)
{
queue
.
push
(
new
WorkRequest
(
std
::
move
(
resolve
),
std
::
move
(
reject
),
seq
));
});
}
private:
void
run
()
{
while
(
!
shutdown
)
{
WorkRequest
*
request
;
if
(
queue
.
tryPop
(
request
,
std
::
chrono
::
milliseconds
(
200
)))
{
request
->
resolve
(
request
->
seq
);
delete
request
;
}
}
}
struct
WorkRequest
{
WorkRequest
(
Async
::
Resolver
resolve
,
Async
::
Rejection
reject
,
int
seq
)
:
resolve
(
std
::
move
(
resolve
))
,
reject
(
std
::
move
(
reject
))
,
seq
(
seq
)
{
}
int
seq
;
Async
::
Resolver
resolve
;
Async
::
Rejection
reject
;
};
std
::
atomic
<
bool
>
shutdown
;
MessageQueue
<
WorkRequest
*>
queue
;
std
::
random_device
rd
;
std
::
unique_ptr
<
std
::
thread
>
thread
;
};
TEST
(
async_test
,
stress_multithreaded_test
)
{
static
constexpr
size_t
OpsPerThread
=
100000
;
static
constexpr
size_t
Workers
=
6
;
static
constexpr
size_t
Ops
=
OpsPerThread
*
Workers
;
std
::
cout
<<
"Starting stress testing promises, hang on, this test might take some time to complete"
<<
std
::
endl
;
std
::
cout
<<
"================================================="
<<
std
::
endl
;
std
::
cout
<<
"Parameters for the test: "
<<
std
::
endl
;
std
::
cout
<<
"Workers -> "
<<
Workers
<<
std
::
endl
;
std
::
cout
<<
"OpsPerThread -> "
<<
OpsPerThread
<<
std
::
endl
;
std
::
cout
<<
"Total Ops -> "
<<
Ops
<<
std
::
endl
;
std
::
cout
<<
"================================================="
<<
std
::
endl
;
std
::
cout
<<
std
::
endl
<<
std
::
endl
;
std
::
vector
<
std
::
unique_ptr
<
Worker
>>
workers
;
for
(
size_t
i
=
0
;
i
<
Workers
;
++
i
)
{
std
::
unique_ptr
<
Worker
>
wrk
(
new
Worker
);
wrk
->
start
();
workers
.
push_back
(
std
::
move
(
wrk
));
}
std
::
vector
<
Async
::
Promise
<
int
>>
promises
;
std
::
atomic
<
int
>
resolved
(
0
);
size_t
wrkIndex
=
0
;
for
(
size_t
i
=
0
;
i
<
Ops
;
++
i
)
{
auto
&
wrk
=
workers
[
wrkIndex
];
wrk
->
doWork
(
i
).
then
([
&
](
int
seq
)
{
++
resolved
;
},
Async
::
NoExcept
);
wrkIndex
=
(
wrkIndex
+
1
)
%
Workers
;
}
for
(;;)
{
auto
r
=
resolved
.
load
();
std
::
cout
<<
r
<<
" promises resolved"
<<
std
::
endl
;
if
(
r
==
Ops
)
break
;
std
::
this_thread
::
sleep_for
(
std
::
chrono
::
milliseconds
(
500
));
}
std
::
cout
<<
"Stopping worker"
<<
std
::
endl
;
for
(
auto
&
wrk
:
workers
)
{
wrk
->
stop
();
}
}
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