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
cb274c8e
Commit
cb274c8e
authored
Feb 11, 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
e703fc45
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
423 additions
and
160 deletions
+423
-160
examples/http_client.cc
examples/http_client.cc
+10
-6
include/async.h
include/async.h
+23
-3
include/client.h
include/client.h
+68
-56
include/timer_pool.h
include/timer_pool.h
+10
-0
src/client/client.cc
src/client/client.cc
+173
-95
tests/async_test.cc
tests/async_test.cc
+139
-0
No files found.
examples/http_client.cc
View file @
cb274c8e
...
@@ -15,28 +15,32 @@ int main() {
...
@@ -15,28 +15,32 @@ 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
=
5
000
;
constexpr
size_t
Requests
=
10
000
;
std
::
atomic
<
int
>
responsesReceived
(
0
);
std
::
atomic
<
int
>
responsesReceived
(
0
);
client
.
init
(
opts
);
client
.
init
(
opts
);
for
(
int
i
=
0
;
i
<
Requests
;
++
i
)
{
for
(
int
i
=
0
;
i
<
Requests
;
++
i
)
{
client
.
get
(
client
client
.
get
(
client
.
request
(
"/ping"
)
.
request
(
"/ping"
)
.
cookie
(
Cookie
(
"FOO"
,
"bar"
))
,
std
::
chrono
::
milliseconds
(
1000
)
)
.
cookie
(
Cookie
(
"FOO"
,
"bar"
)))
.
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 @
cb274c8e
...
@@ -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_
);
}
}
...
@@ -723,9 +743,9 @@ namespace Async {
...
@@ -723,9 +743,9 @@ namespace Async {
req
->
reject
(
core_
);
req
->
reject
(
core_
);
}
}
core_
->
requests
.
push_back
(
req
);
core_
->
requests
.
push_back
(
req
);
return
promise
;
return
promise
;
}
}
private:
private:
...
...
include/client.h
View file @
cb274c8e
...
@@ -24,12 +24,21 @@ namespace Experimental {
...
@@ -24,12 +24,21 @@ 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
(
...
@@ -48,9 +57,9 @@ struct Connection {
...
@@ -48,9 +57,9 @@ struct Connection {
Async
::
Resolver
resolve
;
Async
::
Resolver
resolve
;
Async
::
Rejection
reject
;
Async
::
Rejection
reject
;
Http
::
Request
request
;
std
::
string
host
;
std
::
string
host
;
std
::
chrono
::
milliseconds
timeout
;
std
::
chrono
::
milliseconds
timeout
;
Http
::
Request
request
;
OnDone
onDone
;
OnDone
onDone
;
};
};
...
@@ -65,13 +74,6 @@ struct Connection {
...
@@ -65,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
;
...
@@ -94,15 +96,45 @@ struct Connection {
...
@@ -94,15 +96,45 @@ struct Connection {
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_
;
Net
::
TimerPool
timerPool_
;
Private
::
Parser
<
Http
::
Response
>
parser_
;
};
};
struct
ConnectionPool
{
struct
ConnectionPool
{
...
@@ -129,15 +161,12 @@ public:
...
@@ -129,15 +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
,
std
::
shared_ptr
<
TimerPool
::
Entry
>
timer
,
std
::
shared_ptr
<
TimerPool
::
Entry
>
timer
,
const
Buffer
&
buffer
,
const
Buffer
&
buffer
);
Async
::
Resolver
resolve
,
Async
::
Rejection
reject
,
OnResponseParsed
onParsed
);
private:
private:
...
@@ -146,79 +175,61 @@ private:
...
@@ -146,79 +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
,
std
::
shared_ptr
<
TimerPool
::
Entry
>
timer
,
std
::
shared_ptr
<
TimerPool
::
Entry
>
timer
,
const
Buffer
&
buffer
,
const
Buffer
&
buffer
)
OnResponseParsed
onParsed
=
nullptr
)
:
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
))
,
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
;
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
>
pendingC
onnections
;
std
::
unordered_map
<
Fd
,
ConnectionEntry
>
c
onnections
;
std
::
unordered_map
<
Fd
,
std
::
deque
<
InflightRequest
>>
inflightR
equests
;
std
::
unordered_map
<
Fd
,
RequestEntry
>
r
equests
;
std
::
unordered_map
<
Fd
,
Fd
>
timeouts
;
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
(
Fd
fd
);
void
handleTimeout
(
const
std
::
shared_ptr
<
Connection
>&
connection
);
};
};
...
@@ -274,6 +285,7 @@ public:
...
@@ -274,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
View file @
cb274c8e
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
#include <atomic>
#include <atomic>
#include <unistd.h>
#include <unistd.h>
#include "os.h"
#include "os.h"
#include "io.h"
namespace
Net
{
namespace
Net
{
...
@@ -35,6 +36,7 @@ public:
...
@@ -35,6 +36,7 @@ public:
Entry
()
Entry
()
:
fd
(
-
1
)
:
fd
(
-
1
)
,
registered
(
false
)
{
{
state
.
store
(
static_cast
<
uint32_t
>
(
State
::
Idle
));
state
.
store
(
static_cast
<
uint32_t
>
(
State
::
Idle
));
}
}
...
@@ -53,11 +55,19 @@ public:
...
@@ -53,11 +55,19 @@ public:
}
}
void
disarm
();
void
disarm
();
void
registerIo
(
Io
::
Service
*
io
)
{
if
(
!
registered
)
{
io
->
registerFd
(
fd
,
Polling
::
NotifyOn
::
Read
);
}
registered
=
true
;
}
private:
private:
void
armMs
(
std
::
chrono
::
milliseconds
value
);
void
armMs
(
std
::
chrono
::
milliseconds
value
);
enum
class
State
:
uint32_t
{
Idle
,
Used
};
enum
class
State
:
uint32_t
{
Idle
,
Used
};
std
::
atomic
<
uint32_t
>
state
;
std
::
atomic
<
uint32_t
>
state
;
bool
registered
;
};
};
std
::
shared_ptr
<
Entry
>
pickTimer
();
std
::
shared_ptr
<
Entry
>
pickTimer
();
...
...
src/client/client.cc
View file @
cb274c8e
This diff is collapsed.
Click to expand it.
tests/async_test.cc
View file @
cb274c8e
...
@@ -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