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
a5cbcf4a
Commit
a5cbcf4a
authored
5 years ago
by
Dennis Jenkins
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Ran clang-format on all headers (except date.h, since its not ours).
parent
9a0f8984
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
1218 additions
and
1440 deletions
+1218
-1440
include/pistache/async.h
include/pistache/async.h
+1216
-1439
include/pistache/router.h
include/pistache/router.h
+2
-1
No files found.
include/pistache/async.h
View file @
a5cbcf4a
...
@@ -9,1595 +9,1372 @@
...
@@ -9,1595 +9,1372 @@
#include <pistache/typeid.h>
#include <pistache/typeid.h>
#include <type_traits>
#include <atomic>
#include <condition_variable>
#include <functional>
#include <functional>
#include <memory>
#include <memory>
#include <atomic>
#include <vector>
#include <mutex>
#include <mutex>
#include <condition_variable>
#include <stdexcept>
#include <stdexcept>
#include <type_traits>
#include <typeinfo>
#include <typeinfo>
#include <vector>
namespace
Pistache
{
namespace
Pistache
{
namespace
Async
{
namespace
Async
{
class
Error
:
public
std
::
runtime_error
{
class
Error
:
public
std
::
runtime_error
{
public:
public:
explicit
Error
(
const
char
*
what
)
:
std
::
runtime_error
(
what
)
{
}
explicit
Error
(
const
char
*
what
)
:
std
::
runtime_error
(
what
)
{
}
explicit
Error
(
const
std
::
string
&
what
)
:
std
::
runtime_error
(
what
)
{
}
explicit
Error
(
const
std
::
string
&
what
)
:
std
::
runtime_error
(
what
)
{
}
};
};
class
BadType
:
public
Error
{
class
BadType
:
public
Error
{
public:
public:
explicit
BadType
(
TypeId
id
)
explicit
BadType
(
TypeId
id
)
:
Error
(
"Argument type can not be used to resolve the promise "
:
Error
(
"Argument type can not be used to resolve the promise "
" (TypeId does not match)"
)
" (TypeId does not match)"
),
,
id_
(
std
::
move
(
id
))
id_
(
std
::
move
(
id
))
{}
{
}
TypeId
typeId
()
const
{
TypeId
typeId
()
const
{
return
id_
;
}
return
id_
;
}
private:
private:
TypeId
id_
;
TypeId
id_
;
};
};
class
BadAnyCast
:
public
std
::
bad_cast
{
class
BadAnyCast
:
public
std
::
bad_cast
{
public:
public:
const
char
*
what
()
const
noexcept
override
{
return
"Bad any cast"
;
}
const
char
*
what
()
const
noexcept
override
{
return
"Bad any cast"
;
}
virtual
~
BadAnyCast
()
{
}
virtual
~
BadAnyCast
()
{
}
};
};
enum
class
State
{
enum
class
State
{
Pending
,
Fulfilled
,
Rejected
};
Pending
,
Fulfilled
,
Rejected
};
template
<
typename
T
>
class
Promise
;
template
<
typename
T
>
class
Promise
;
class
PromiseBase
{
class
PromiseBase
{
public:
public:
virtual
~
PromiseBase
()
{
}
virtual
~
PromiseBase
()
{
}
virtual
bool
isPending
()
const
=
0
;
virtual
bool
isPending
()
const
=
0
;
virtual
bool
isFulfilled
()
const
=
0
;
virtual
bool
isFulfilled
()
const
=
0
;
virtual
bool
isRejected
()
const
=
0
;
virtual
bool
isRejected
()
const
=
0
;
bool
isSettled
()
const
{
return
isFulfilled
()
||
isRejected
();
}
bool
isSettled
()
const
{
return
isFulfilled
()
||
isRejected
();
}
};
};
namespace
detail
{
namespace
detail
{
template
<
typename
Func
,
typename
T
>
template
<
typename
Func
,
typename
T
>
struct
IsCallable
{
struct
IsCallable
{
template
<
typename
U
>
template
<
typename
U
>
static
auto
test
(
U
*
)
->
decltype
(
std
::
declval
<
Func
>
()(
std
::
declval
<
U
>
()),
std
::
true_type
());
static
auto
test
(
U
*
)
->
decltype
(
std
::
declval
<
Func
>
()(
std
::
declval
<
U
>
()),
std
::
true_type
());
template
<
typename
U
>
template
<
typename
U
>
static
auto
test
(...)
->
std
::
false_type
;
static
auto
test
(...)
->
std
::
false_type
;
static
constexpr
bool
value
=
std
::
is_same
<
decltype
(
test
<
T
>
(
0
)),
std
::
true_type
>::
value
;
static
constexpr
bool
value
=
};
std
::
is_same
<
decltype
(
test
<
T
>
(
0
)),
std
::
true_type
>::
value
;
};
template
<
typename
Func
>
template
<
typename
Func
>
struct
IsMoveCallable
:
public
IsMoveCallable
<
decltype
(
&
Func
::
operator
())
>
{
};
struct
IsMoveCallable
:
public
IsMoveCallable
<
decltype
(
&
Func
::
operator
())
>
{
};
template
<
typename
R
,
typename
Class
,
typename
Arg
>
template
<
typename
R
,
typename
Class
,
typename
Arg
>
struct
IsMoveCallable
<
R
(
Class
::*
)(
Arg
)
const
>
:
public
std
::
is_rvalue_reference
<
Arg
>
{
};
struct
IsMoveCallable
<
R
(
Class
::*
)(
Arg
)
const
>
:
public
std
::
is_rvalue_reference
<
Arg
>
{};
template
<
typename
Func
,
typename
Arg
>
template
<
typename
Func
,
typename
Arg
>
typename
std
::
conditional
<
typename
std
::
conditional
<
IsMoveCallable
<
Func
>::
value
,
Arg
&&
,
IsMoveCallable
<
Func
>::
value
,
const
Arg
&>::
type
Arg
&&
,
tryMove
(
Arg
&
arg
)
{
const
Arg
&
return
std
::
move
(
arg
);
>::
type
tryMove
(
Arg
&
arg
)
{
}
return
std
::
move
(
arg
);
}
template
<
typename
Func
>
struct
FunctionTrait
:
public
FunctionTrait
<
decltype
(
&
Func
::
operator
())
>
{};
template
<
typename
R
,
typename
Class
,
typename
...
Args
>
struct
FunctionTrait
<
R
(
Class
::*
)(
Args
...)
const
>
{
typedef
R
ReturnType
;
static
constexpr
size_t
ArgsCount
=
sizeof
...(
Args
);
};
template
<
typename
R
,
typename
Class
,
typename
...
Args
>
struct
FunctionTrait
<
R
(
Class
::*
)(
Args
...)
>
{
typedef
R
ReturnType
;
static
constexpr
size_t
ArgsCount
=
sizeof
...(
Args
);
};
template
<
typename
T
>
struct
RemovePromise
{
typedef
T
Type
;
};
template
<
typename
T
>
struct
RemovePromise
<
Promise
<
T
>>
{
typedef
T
Type
;
};
template
<
size_t
N
,
typename
...
T
>
struct
nth_element
;
template
<
typename
Head
,
typename
...
Tail
>
struct
nth_element
<
0
,
Head
,
Tail
...
>
{
typedef
Head
type
;
};
template
<
typename
Func
>
template
<
size_t
N
,
typename
Head
,
typename
...
Tail
>
struct
FunctionTrait
:
public
FunctionTrait
<
decltype
(
&
Func
::
operator
())
>
{
};
struct
nth_element
<
N
,
Head
,
Tail
...
>
{
typedef
typename
nth_element
<
N
-
1
,
Tail
...
>::
type
type
;
};
template
<
typename
R
,
typename
Class
,
typename
...
Args
>
}
// namespace detail
struct
FunctionTrait
<
R
(
Class
::*
)(
Args
...)
const
>
{
typedef
R
ReturnType
;
static
constexpr
size_t
ArgsCount
=
sizeof
...(
Args
);
namespace
Private
{
};
template
<
typename
R
,
typename
Class
,
typename
...
Args
>
struct
InternalRethrow
{
struct
FunctionTrait
<
R
(
Class
::*
)(
Args
...)
>
{
explicit
InternalRethrow
(
std
::
exception_ptr
_exc
)
:
exc
(
std
::
move
(
_exc
))
{}
typedef
R
ReturnType
;
static
constexpr
size_t
ArgsCount
=
sizeof
...(
Args
)
;
std
::
exception_ptr
exc
;
};
};
template
<
typename
T
>
struct
IgnoreException
{
struct
RemovePromise
{
void
operator
()(
std
::
exception_ptr
)
const
{}
typedef
T
Type
;
};
};
template
<
typename
T
>
struct
NoExcept
{
struct
RemovePromise
<
Promise
<
T
>>
{
void
operator
()(
std
::
exception_ptr
)
const
{
std
::
terminate
();
}
typedef
T
Type
;
};
};
template
<
size_t
N
,
typename
...
T
>
struct
nth_element
;
struct
Throw
{
void
operator
()(
std
::
exception_ptr
exc
)
const
{
throw
InternalRethrow
(
std
::
move
(
exc
));
}
};
template
<
typename
Head
,
typename
...
Tail
>
struct
Core
;
struct
nth_element
<
0
,
Head
,
Tail
...
>
{
typedef
Head
type
;
};
template
<
size_t
N
,
typename
Head
,
typename
...
Tail
>
class
Request
{
struct
nth_element
<
N
,
Head
,
Tail
...
>
{
public:
typedef
typename
nth_element
<
N
-
1
,
Tail
...
>::
type
type
;
virtual
void
resolve
(
const
std
::
shared_ptr
<
Core
>
&
core
)
=
0
;
};
virtual
void
reject
(
const
std
::
shared_ptr
<
Core
>
&
core
)
=
0
;
virtual
~
Request
()
{}
};
struct
Core
{
Core
(
State
_state
,
TypeId
_id
)
:
allocated
(
false
),
state
(
_state
),
exc
(),
mtx
(),
requests
(),
id
(
_id
)
{}
bool
allocated
;
std
::
atomic
<
State
>
state
;
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
;
TypeId
id
;
virtual
void
*
memory
()
=
0
;
virtual
bool
isVoid
()
const
=
0
;
template
<
typename
T
,
typename
...
Args
>
void
construct
(
Args
&&
...
args
)
{
if
(
isVoid
())
throw
Error
(
"Can not construct a void core"
);
if
(
id
!=
TypeId
::
of
<
T
>
())
{
throw
BadType
(
id
);
}
}
namespace
Private
{
void
*
mem
=
memory
();
struct
InternalRethrow
{
if
(
allocated
)
{
explicit
InternalRethrow
(
std
::
exception_ptr
_exc
)
reinterpret_cast
<
T
*>
(
mem
)
->~
T
();
:
exc
(
std
::
move
(
_exc
))
allocated
=
false
;
{
}
std
::
exception_ptr
exc
;
};
struct
IgnoreException
{
void
operator
()(
std
::
exception_ptr
)
const
{
}
};
struct
NoExcept
{
void
operator
()(
std
::
exception_ptr
)
const
{
std
::
terminate
();
}
};
struct
Throw
{
void
operator
()(
std
::
exception_ptr
exc
)
const
{
throw
InternalRethrow
(
std
::
move
(
exc
));
}
};
struct
Core
;
class
Request
{
public:
virtual
void
resolve
(
const
std
::
shared_ptr
<
Core
>&
core
)
=
0
;
virtual
void
reject
(
const
std
::
shared_ptr
<
Core
>&
core
)
=
0
;
virtual
~
Request
()
{}
};
struct
Core
{
Core
(
State
_state
,
TypeId
_id
)
:
allocated
(
false
)
,
state
(
_state
)
,
exc
()
,
mtx
()
,
requests
()
,
id
(
_id
)
{
}
bool
allocated
;
std
::
atomic
<
State
>
state
;
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
;
TypeId
id
;
virtual
void
*
memory
()
=
0
;
virtual
bool
isVoid
()
const
=
0
;
template
<
typename
T
,
typename
...
Args
>
void
construct
(
Args
&&
...
args
)
{
if
(
isVoid
())
throw
Error
(
"Can not construct a void core"
);
if
(
id
!=
TypeId
::
of
<
T
>
())
{
throw
BadType
(
id
);
}
void
*
mem
=
memory
();
if
(
allocated
)
{
reinterpret_cast
<
T
*>
(
mem
)
->~
T
();
allocated
=
false
;
}
new
(
mem
)
T
(
std
::
forward
<
Args
>
(
args
)...);
allocated
=
true
;
state
=
State
::
Fulfilled
;
}
virtual
~
Core
()
{}
};
template
<
typename
T
>
struct
CoreT
:
public
Core
{
CoreT
()
:
Core
(
State
::
Pending
,
TypeId
::
of
<
T
>
())
,
storage
()
{
}
~
CoreT
()
{
if
(
allocated
)
{
reinterpret_cast
<
T
*>
(
&
storage
)
->~
T
();
allocated
=
false
;
}
}
template
<
class
Other
>
struct
Rebind
{
typedef
CoreT
<
Other
>
Type
;
};
T
&
value
()
{
if
(
state
!=
State
::
Fulfilled
)
throw
Error
(
"Attempted to take the value of a not fulfilled promise"
);
return
*
reinterpret_cast
<
T
*>
(
&
storage
);
}
bool
isVoid
()
const
override
{
return
false
;
}
protected:
void
*
memory
()
override
{
return
&
storage
;
}
private:
typedef
typename
std
::
aligned_storage
<
sizeof
(
T
),
alignof
(
T
)
>::
type
Storage
;
Storage
storage
;
};
template
<
>
struct
CoreT
<
void
>
:
public
Core
{
CoreT
()
:
Core
(
State
::
Pending
,
TypeId
::
of
<
void
>
())
{
}
bool
isVoid
()
const
override
{
return
true
;
}
protected:
void
*
memory
()
override
{
return
nullptr
;
}
};
template
<
typename
T
>
struct
Continuable
:
public
Request
{
explicit
Continuable
(
const
std
::
shared_ptr
<
Core
>&
chain
)
:
resolveCount_
(
0
)
,
rejectCount_
(
0
)
,
chain_
(
chain
)
{
}
void
resolve
(
const
std
::
shared_ptr
<
Core
>&
core
)
override
{
if
(
resolveCount_
>=
1
)
return
;
//TODO is this the right thing?
//throw Error("Resolve must not be called more than once");
++
resolveCount_
;
doResolve
(
coreCast
(
core
));
}
void
reject
(
const
std
::
shared_ptr
<
Core
>&
core
)
override
{
if
(
rejectCount_
>=
1
)
return
;
//TODO is this the right thing?
//throw Error("Reject must not be called more than once");
++
rejectCount_
;
try
{
doReject
(
coreCast
(
core
));
}
catch
(
const
InternalRethrow
&
e
)
{
chain_
->
exc
=
std
::
move
(
e
.
exc
);
chain_
->
state
=
State
::
Rejected
;
for
(
const
auto
&
req
:
chain_
->
requests
)
{
req
->
reject
(
chain_
);
}
}
}
std
::
shared_ptr
<
CoreT
<
T
>>
coreCast
(
const
std
::
shared_ptr
<
Core
>&
core
)
const
{
return
std
::
static_pointer_cast
<
CoreT
<
T
>>
(
core
);
}
virtual
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
T
>>&
core
)
=
0
;
virtual
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
T
>>&
core
)
=
0
;
virtual
~
Continuable
()
{
}
size_t
resolveCount_
;
size_t
rejectCount_
;
std
::
shared_ptr
<
Core
>
chain_
;
};
namespace
impl
{
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
U
>
struct
Continuation
;
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
Cls
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Cls
::*
)(
Args
...)
const
>
:
public
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
{
typedef
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
Base
;
Continuation
(
const
std
::
shared_ptr
<
Core
>&
chain
,
Resolve
resolve
,
Reject
reject
)
:
Base
(
chain
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{
}
};
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
Cls
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Cls
::*
)(
Args
...)
>
:
public
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
{
typedef
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
Base
;
Continuation
(
const
std
::
shared_ptr
<
Core
>&
chain
,
Resolve
resolve
,
Reject
reject
)
:
Base
(
chain
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{
}
};
// General specialization
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
:
public
Continuable
<
T
>
{
static_assert
(
sizeof
...(
Args
)
==
1
,
"A continuation should only take one single argument"
);
typedef
typename
detail
::
nth_element
<
0
,
Args
...
>::
type
Arg
;
static_assert
(
std
::
is_same
<
T
,
Arg
>::
value
||
std
::
is_convertible
<
T
,
Arg
>::
value
,
"Incompatible types detected"
);
Continuation
(
const
std
::
shared_ptr
<
Core
>&
chain
,
Resolve
resolve
,
Reject
reject
)
:
Continuable
<
T
>
(
chain
)
,
resolve_
(
resolve
)
,
reject_
(
reject
)
{
}
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
T
>>&
core
)
override
{
finishResolve
(
resolve_
(
detail
::
tryMove
<
Resolve
>
(
core
->
value
())));
}
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
T
>>&
core
)
override
{
reject_
(
core
->
exc
);
for
(
const
auto
&
req
:
this
->
chain_
->
requests
)
{
req
->
reject
(
this
->
chain_
);
}
}
template
<
typename
Ret
>
void
finishResolve
(
Ret
&&
ret
)
const
{
typedef
typename
std
::
decay
<
Ret
>::
type
CleanRet
;
this
->
chain_
->
template
construct
<
CleanRet
>(
std
::
forward
<
Ret
>
(
ret
));
for
(
const
auto
&
req
:
this
->
chain_
->
requests
)
{
req
->
resolve
(
this
->
chain_
);
}
}
Resolve
resolve_
;
Reject
reject_
;
};
// Specialization for a void-Promise
template
<
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
...
Args
>
struct
Continuation
<
void
,
Resolve
,
Reject
,
Res
(
Args
...)
>
:
public
Continuable
<
void
>
{
Continuation
(
const
std
::
shared_ptr
<
Core
>&
chain
,
Resolve
resolve
,
Reject
reject
)
:
Continuable
<
void
>
(
chain
)
,
resolve_
(
resolve
)
,
reject_
(
reject
)
{
}
static_assert
(
sizeof
...(
Args
)
==
0
,
"Can not attach a non-void continuation to a void-Promise"
);
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
void
>>&
/*core*/
)
{
finishResolve
(
resolve_
());
}
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
void
>>&
core
)
{
reject_
(
core
->
exc
);
for
(
const
auto
&
req
:
this
->
chain_
->
requests
)
{
req
->
reject
(
this
->
chain_
);
}
}
template
<
typename
Ret
>
void
finishResolve
(
Ret
&&
ret
)
const
{
typedef
typename
std
::
remove_reference
<
Ret
>::
type
CleanRet
;
this
->
chain_
->
template
construct
<
CleanRet
>(
std
::
forward
<
Ret
>
(
ret
));
for
(
const
auto
&
req
:
this
->
chain_
->
requests
)
{
req
->
resolve
(
this
->
chain_
);
}
}
Resolve
resolve_
;
Reject
reject_
;
};
// Specialization for a callback returning void
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
void
(
Args
...)
>
:
public
Continuable
<
T
>
{
Continuation
(
const
std
::
shared_ptr
<
Core
>&
chain
,
Resolve
resolve
,
Reject
reject
)
:
Continuable
<
T
>
(
chain
)
,
resolve_
(
resolve
)
,
reject_
(
reject
)
{
}
static_assert
(
sizeof
...(
Args
)
==
1
,
"A continuation should only take one single argument"
);
typedef
typename
detail
::
nth_element
<
0
,
Args
...
>::
type
Arg
;
static_assert
(
std
::
is_same
<
T
,
Arg
>::
value
||
std
::
is_convertible
<
T
,
Arg
>::
value
,
"Incompatible types detected"
);
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
T
>>&
core
)
override
{
resolve_
(
core
->
value
());
}
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
T
>>&
core
)
override
{
reject_
(
core
->
exc
);
}
Resolve
resolve_
;
Reject
reject_
;
};
// Specialization for a void-Promise on a callback returning void
template
<
typename
Resolve
,
typename
Reject
,
typename
...
Args
>
struct
Continuation
<
void
,
Resolve
,
Reject
,
void
(
Args
...)
>
:
public
Continuable
<
void
>
{
Continuation
(
const
std
::
shared_ptr
<
Core
>&
chain
,
Resolve
resolve
,
Reject
reject
)
:
Continuable
<
void
>
(
chain
)
,
resolve_
(
resolve
)
,
reject_
(
reject
)
{
}
static_assert
(
sizeof
...(
Args
)
==
0
,
"Can not attach a non-void continuation to a void-Promise"
);
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
void
>>&
/*core*/
)
override
{
resolve_
();
}
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
void
>>&
core
)
override
{
reject_
(
core
->
exc
);
}
Resolve
resolve_
;
Reject
reject_
;
};
// Specialization for a callback returning a Promise
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
U
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Promise
<
U
>
(
Args
...)
>
:
public
Continuable
<
T
>
{
static_assert
(
sizeof
...(
Args
)
==
1
,
"A continuation should only take one single argument"
);
typedef
typename
detail
::
nth_element
<
0
,
Args
...
>::
type
Arg
;
static_assert
(
std
::
is_same
<
T
,
Arg
>::
value
||
std
::
is_convertible
<
T
,
Arg
>::
value
,
"Incompatible types detected"
);
Continuation
(
const
std
::
shared_ptr
<
Core
>&
chain
,
Resolve
resolve
,
Reject
reject
)
:
Continuable
<
T
>
(
chain
)
,
resolve_
(
resolve
)
,
reject_
(
reject
)
{
}
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
T
>>&
core
)
override
{
auto
promise
=
resolve_
(
detail
::
tryMove
<
Resolve
>
(
core
->
value
()));
finishResolve
(
promise
);
}
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
T
>>&
core
)
override
{
reject_
(
core
->
exc
);
for
(
const
auto
&
req
:
core
->
requests
)
{
req
->
reject
(
core
);
}
}
template
<
typename
PromiseType
>
struct
Chainer
{
explicit
Chainer
(
const
std
::
shared_ptr
<
Private
::
Core
>&
core
)
:
chainCore
(
core
)
{
}
void
operator
()(
const
PromiseType
&
val
)
{
chainCore
->
construct
<
PromiseType
>
(
val
);
for
(
const
auto
&
req
:
chainCore
->
requests
)
{
req
->
resolve
(
chainCore
);
}
}
std
::
shared_ptr
<
Core
>
chainCore
;
};
template
<
typename
Promise
,
typename
Type
=
typename
detail
::
RemovePromise
<
Promise
>
::
Type
>
Chainer
<
Type
>
makeChainer
(
const
Promise
&
)
{
return
Chainer
<
Type
>
(
this
->
chain_
);
}
template
<
typename
P
>
void
finishResolve
(
P
&
promise
)
{
auto
chainer
=
makeChainer
(
promise
);
std
::
weak_ptr
<
Core
>
weakPtr
=
this
->
chain_
;
promise
.
then
(
std
::
move
(
chainer
),
[
weakPtr
](
std
::
exception_ptr
exc
)
{
if
(
auto
core
=
weakPtr
.
lock
())
{
core
->
exc
=
std
::
move
(
exc
);
core
->
state
=
State
::
Rejected
;
for
(
const
auto
&
req
:
core
->
requests
)
{
req
->
reject
(
core
);
}
}
});
}
Resolve
resolve_
;
Reject
reject_
;
};
// Specialization for a void callback returning a Promise
template
<
typename
Resolve
,
typename
Reject
,
typename
U
,
typename
...
Args
>
struct
Continuation
<
void
,
Resolve
,
Reject
,
Promise
<
U
>
(
Args
...)
>
:
public
Continuable
<
void
>
{
static_assert
(
sizeof
...(
Args
)
==
0
,
"Can not attach a non-void continuation to a void-Promise"
);
Continuation
(
const
std
::
shared_ptr
<
Core
>&
chain
,
Resolve
resolve
,
Reject
reject
)
:
Continuable
<
void
>
(
chain
)
,
resolve_
(
resolve
)
,
reject_
(
reject
)
{
}
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
void
>>&
/*core*/
)
{
auto
promise
=
resolve_
();
finishResolve
(
promise
);
}
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
void
>>&
core
)
{
reject_
(
core
->
exc
);
for
(
const
auto
&
req
:
core
->
requests
)
{
req
->
reject
(
core
);
}
}
template
<
typename
PromiseType
,
typename
Dummy
=
void
>
struct
Chainer
{
explicit
Chainer
(
const
std
::
shared_ptr
<
Private
::
Core
>&
core
)
:
chainCore
(
core
)
{
}
void
operator
()(
const
PromiseType
&
val
)
{
chainCore
->
construct
<
PromiseType
>
(
val
);
for
(
const
auto
&
req
:
chainCore
->
requests
)
{
req
->
resolve
(
chainCore
);
}
}
std
::
shared_ptr
<
Core
>
chainCore
;
};
template
<
typename
Dummy
>
struct
Chainer
<
void
,
Dummy
>
{
explicit
Chainer
(
const
std
::
shared_ptr
<
Private
::
Core
>&
core
)
:
chainCore
(
core
)
{
}
void
operator
()()
{
auto
core
=
this
->
chain_
;
core
->
state
=
State
::
Fulfilled
;
for
(
const
auto
&
req
:
chainCore
->
requests
)
{
req
->
resolve
(
chainCore
);
}
}
std
::
shared_ptr
<
Core
>
chainCore
;
};
template
<
typename
Promise
,
typename
Type
=
typename
detail
::
RemovePromise
<
Promise
>
::
Type
>
Chainer
<
Type
>
makeChainer
(
const
Promise
&
)
{
return
Chainer
<
Type
>
(
this
->
chain_
);
}
template
<
typename
P
>
void
finishResolve
(
P
&
promise
)
{
auto
chainer
=
makeChainer
(
promise
);
promise
.
then
(
std
::
move
(
chainer
),
[
=
](
std
::
exception_ptr
exc
)
{
auto
core
=
this
->
chain_
;
core
->
exc
=
std
::
move
(
exc
);
core
->
state
=
State
::
Rejected
;
for
(
const
auto
&
req
:
core
->
requests
)
{
req
->
reject
(
core
);
}
});
}
Resolve
resolve_
;
Reject
reject_
;
};
}
// namespace impl
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Sig
>
struct
Continuation
:
public
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
decltype
(
&
Sig
::
operator
())
>
{
typedef
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
decltype
(
&
Sig
::
operator
())
>
Base
;
Continuation
(
const
std
::
shared_ptr
<
Core
>&
core
,
Resolve
resolve
,
Reject
reject
)
:
Base
(
core
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{
}
};
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
*
)(
Args
...)
>
:
public
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
{
typedef
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
Base
;
Continuation
(
const
std
::
shared_ptr
<
Core
>&
core
,
Resolve
resolve
,
Reject
reject
)
:
Base
(
core
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{
}
};
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
Cls
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Cls
::*
)(
Args
...)
>
:
public
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
{
typedef
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
Base
;
Continuation
(
const
std
::
shared_ptr
<
Core
>&
core
,
Resolve
resolve
,
Reject
reject
)
:
Base
(
core
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{
}
};
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
Cls
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Cls
::*
)(
Args
...)
const
>
:
public
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
{
typedef
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
Base
;
Continuation
(
const
std
::
shared_ptr
<
Core
>&
core
,
Resolve
resolve
,
Reject
reject
)
:
Base
(
core
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{
}
};
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
std
::
function
<
Res
(
Args
...)
>>
:
public
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
{
typedef
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
Base
;
Continuation
(
const
std
::
shared_ptr
<
Core
>&
core
,
Resolve
resolve
,
Reject
reject
)
:
Base
(
core
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{
}
};
}
}
class
Resolver
{
new
(
mem
)
T
(
std
::
forward
<
Args
>
(
args
)...);
public:
allocated
=
true
;
state
=
State
::
Fulfilled
;
}
explicit
Resolver
(
const
std
::
shared_ptr
<
Private
::
Core
>
&
core
)
virtual
~
Core
()
{}
:
core_
(
core
)
};
{
}
Resolver
(
const
Resolver
&
other
)
=
delete
;
template
<
typename
T
>
struct
CoreT
:
public
Core
{
Resolver
&
operator
=
(
const
Resolver
&
other
)
=
delete
;
CoreT
()
:
Core
(
State
::
Pending
,
TypeId
::
of
<
T
>
()),
storage
()
{}
Resolver
(
Resolver
&&
other
)
=
default
;
~
CoreT
()
{
Resolver
&
operator
=
(
Resolver
&&
other
)
=
default
;
if
(
allocated
)
{
reinterpret_cast
<
T
*>
(
&
storage
)
->~
T
();
allocated
=
false
;
}
}
template
<
typename
Arg
>
template
<
class
Other
>
struct
Rebind
{
typedef
CoreT
<
Other
>
Type
;
};
bool
operator
()(
Arg
&&
arg
)
const
{
if
(
!
core_
)
return
false
;
typedef
typename
std
::
remove_reference
<
Arg
>::
type
Type
;
T
&
value
()
{
if
(
state
!=
State
::
Fulfilled
)
throw
Error
(
"Attempted to take the value of a not fulfilled promise"
);
if
(
core_
->
state
!=
State
::
Pending
)
return
*
reinterpret_cast
<
T
*>
(
&
storage
);
throw
Error
(
"Attempt to resolve a fulfilled promise"
);
}
/* In a ideal world, this should be checked at compile-time rather
bool
isVoid
()
const
override
{
return
false
;
}
* than runtime. However, since types are erased, this looks like
* a difficult task
*/
if
(
core_
->
isVoid
())
{
throw
Error
(
"Attempt to resolve a void promise with arguments"
);
}
std
::
unique_lock
<
std
::
mutex
>
guard
(
core_
->
mtx
);
protected:
core_
->
construct
<
Type
>
(
std
::
forward
<
Arg
>
(
arg
));
void
*
memory
()
override
{
return
&
storage
;
}
for
(
const
auto
&
req
:
core_
->
requests
)
{
private:
req
->
resolve
(
core_
);
typedef
typename
std
::
aligned_storage
<
sizeof
(
T
),
alignof
(
T
)
>::
type
Storage
;
}
Storage
storage
;
};
return
true
;
template
<
>
struct
CoreT
<
void
>
:
public
Core
{
}
CoreT
()
:
Core
(
State
::
Pending
,
TypeId
::
of
<
void
>
())
{
}
bool
operator
()()
const
{
bool
isVoid
()
const
override
{
return
true
;
}
if
(
!
core_
)
return
false
;
if
(
core_
->
state
!=
State
::
Pending
)
protected:
throw
Error
(
"Attempt to resolve a fulfilled promise"
);
void
*
memory
()
override
{
return
nullptr
;
}
};
if
(
!
core_
->
isVoid
())
template
<
typename
T
>
struct
Continuable
:
public
Request
{
throw
Error
(
"Attempt ro resolve a non-void promise with no argument"
);
explicit
Continuable
(
const
std
::
shared_ptr
<
Core
>
&
chain
)
:
resolveCount_
(
0
),
rejectCount_
(
0
),
chain_
(
chain
)
{}
std
::
unique_lock
<
std
::
mutex
>
guard
(
core_
->
mtx
);
void
resolve
(
const
std
::
shared_ptr
<
Core
>
&
core
)
override
{
core_
->
state
=
State
::
Fulfilled
;
if
(
resolveCount_
>=
1
)
for
(
const
auto
&
req
:
core_
->
requests
)
{
return
;
// TODO is this the right thing?
req
->
resolve
(
core_
);
// throw Error("Resolve must not be called more than once");
}
return
true
;
++
resolveCount_
;
}
doResolve
(
coreCast
(
core
));
}
void
clear
()
{
void
reject
(
const
std
::
shared_ptr
<
Core
>
&
core
)
override
{
core_
=
nullptr
;
if
(
rejectCount_
>=
1
)
}
return
;
// TODO is this the right thing?
// throw Error("Reject must not be called more than once");
Resolver
clone
()
{
++
rejectCount_
;
return
Resolver
(
core_
);
try
{
}
doReject
(
coreCast
(
core
));
}
catch
(
const
InternalRethrow
&
e
)
{
chain_
->
exc
=
std
::
move
(
e
.
exc
);
chain_
->
state
=
State
::
Rejected
;
for
(
const
auto
&
req
:
chain_
->
requests
)
{
req
->
reject
(
chain_
);
}
}
}
private:
std
::
shared_ptr
<
CoreT
<
T
>>
coreCast
(
const
std
::
shared_ptr
<
Core
>
&
core
)
const
{
std
::
shared_ptr
<
Private
::
Core
>
core_
;
return
std
::
static_pointer_cast
<
CoreT
<
T
>>
(
core
)
;
};
}
class
Rejection
{
virtual
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
T
>>
&
core
)
=
0
;
public:
virtual
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
T
>>
&
core
)
=
0
;
explicit
Rejection
(
const
std
::
shared_ptr
<
Private
::
Core
>&
core
)
virtual
~
Continuable
()
{}
:
core_
(
core
)
{
}
Rejection
(
const
Rejection
&
other
)
=
delete
;
size_t
resolveCount_
;
Rejection
&
operator
=
(
const
Rejection
&
other
)
=
delete
;
size_t
rejectCount_
;
std
::
shared_ptr
<
Core
>
chain_
;
};
Rejection
(
Rejection
&&
other
)
=
default
;
namespace
impl
{
Rejection
&
operator
=
(
Rejection
&&
other
)
=
default
;
template
<
typename
Exc
>
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
U
>
bool
operator
()(
Exc
exc
)
const
{
struct
Continuation
;
if
(
!
core_
)
return
false
;
if
(
core_
->
state
!=
State
::
Pending
)
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
throw
Error
(
"Attempt to reject a fulfilled promise"
);
typename
Cls
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Cls
::*
)(
Args
...)
const
>
:
public
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
{
typedef
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
Base
;
std
::
unique_lock
<
std
::
mutex
>
guard
(
core_
->
mtx
);
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
chain
,
Resolve
resolve
,
core_
->
exc
=
std
::
make_exception_ptr
(
exc
);
Reject
reject
)
core_
->
state
=
State
::
Rejected
;
:
Base
(
chain
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{}
for
(
const
auto
&
req
:
core_
->
requests
)
{
};
req
->
reject
(
core_
);
}
return
true
;
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
}
typename
Cls
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Cls
::*
)(
Args
...)
>
:
public
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
{
typedef
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
Base
;
void
clear
()
{
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
chain
,
Resolve
resolve
,
core_
=
nullptr
;
Reject
reject
)
}
:
Base
(
chain
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{}
};
Rejection
clone
()
{
// General specialization
return
Rejection
(
core_
);
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
}
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
:
public
Continuable
<
T
>
{
private:
static_assert
(
sizeof
...(
Args
)
==
1
,
std
::
shared_ptr
<
Private
::
Core
>
core_
;
"A continuation should only take one single argument"
)
;
}
;
typedef
typename
detail
::
nth_element
<
0
,
Args
...
>::
type
Arg
;
template
<
typename
T
>
static_assert
(
std
::
is_same
<
T
,
Arg
>::
value
||
class
Deferred
{
std
::
is_convertible
<
T
,
Arg
>::
value
,
public:
"Incompatible types detected"
);
Deferred
()
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
chain
,
Resolve
resolve
,
:
resolver
(
nullptr
)
Reject
reject
)
,
rejection
(
nullptr
)
:
Continuable
<
T
>
(
chain
),
resolve_
(
resolve
),
reject_
(
reject
)
{}
{
}
Deferred
(
const
Deferred
&
other
)
=
delete
;
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
T
>>
&
core
)
override
{
Deferred
&
operator
=
(
const
Deferred
&
other
)
=
delete
;
finishResolve
(
resolve_
(
detail
::
tryMove
<
Resolve
>
(
core
->
value
())));
}
Deferred
(
Deferred
&&
other
)
=
default
;
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
T
>>
&
core
)
override
{
Deferred
&
operator
=
(
Deferred
&&
other
)
=
default
;
reject_
(
core
->
exc
);
for
(
const
auto
&
req
:
this
->
chain_
->
requests
)
{
req
->
reject
(
this
->
chain_
);
}
}
Deferred
(
Resolver
_resolver
,
Rejection
_reject
)
template
<
typename
Ret
>
void
finishResolve
(
Ret
&&
ret
)
const
{
:
resolver
(
std
::
move
(
_resolver
))
typedef
typename
std
::
decay
<
Ret
>::
type
CleanRet
;
,
rejection
(
std
::
move
(
_reject
))
this
->
chain_
->
template
construct
<
CleanRet
>(
std
::
forward
<
Ret
>
(
ret
));
{
}
for
(
const
auto
&
req
:
this
->
chain_
->
requests
)
{
req
->
resolve
(
this
->
chain_
);
}
}
template
<
typename
U
>
Resolve
resolve_
;
bool
resolve
(
U
&&
arg
)
{
Reject
reject_
;
typedef
typename
std
::
remove_reference
<
U
>::
type
CleanU
;
}
;
static_assert
(
std
::
is_same
<
T
,
CleanU
>::
value
||
std
::
is_convertible
<
U
,
T
>::
value
,
// Specialization for a void-Promise
"Types mismatch"
);
template
<
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
...
Args
>
struct
Continuation
<
void
,
Resolve
,
Reject
,
Res
(
Args
...)
>
:
public
Continuable
<
void
>
{
return
resolver
(
std
::
forward
<
U
>
(
arg
));
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
chain
,
Resolve
resolve
,
}
Reject
reject
)
:
Continuable
<
void
>
(
chain
),
resolve_
(
resolve
),
reject_
(
reject
)
{}
template
<
typename
...
Args
>
static_assert
(
sizeof
...(
Args
)
==
0
,
void
emplaceResolve
(
Args
&&
...)
{
"Can not attach a non-void continuation to a void-Promise"
);
}
template
<
typename
Exc
>
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
void
>>
&
/*core*/
)
{
bool
reject
(
Exc
exc
)
{
finishResolve
(
resolve_
());
return
rejection
(
std
::
move
(
exc
));
}
}
void
clear
()
{
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
void
>>
&
core
)
{
resolver
.
clear
();
reject_
(
core
->
exc
);
rejection
.
clear
();
for
(
const
auto
&
req
:
this
->
chain_
->
requests
)
{
}
req
->
reject
(
this
->
chain_
);
}
}
private:
template
<
typename
Ret
>
void
finishResolve
(
Ret
&&
ret
)
const
{
Resolver
resolver
;
typedef
typename
std
::
remove_reference
<
Ret
>::
type
CleanRet
;
Rejection
rejection
;
this
->
chain_
->
template
construct
<
CleanRet
>(
std
::
forward
<
Ret
>
(
ret
));
};
for
(
const
auto
&
req
:
this
->
chain_
->
requests
)
{
req
->
resolve
(
this
->
chain_
);
}
}
template
<
>
Resolve
resolve_
;
class
Deferred
<
void
>
{
Reject
reject_
;
public:
};
Deferred
()
// Specialization for a callback returning void
:
resolver
(
nullptr
)
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
...
Args
>
,
rejection
(
nullptr
)
struct
Continuation
<
T
,
Resolve
,
Reject
,
void
(
Args
...)
>
:
public
Continuable
<
T
>
{
{
}
Deferred
(
const
Deferred
&
other
)
=
delete
;
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
chain
,
Resolve
resolve
,
Deferred
&
operator
=
(
const
Deferred
&
other
)
=
delete
;
Reject
reject
)
:
Continuable
<
T
>
(
chain
),
resolve_
(
resolve
),
reject_
(
reject
)
{}
Deferred
(
Deferred
&&
other
)
=
default
;
static_assert
(
sizeof
...(
Args
)
==
1
,
Deferred
&
operator
=
(
Deferred
&&
other
)
=
default
;
"A continuation should only take one single argument"
)
;
Deferred
(
Resolver
_resolver
,
Rejection
_reject
)
typedef
typename
detail
::
nth_element
<
0
,
Args
...
>::
type
Arg
;
:
resolver
(
std
::
move
(
_resolver
))
,
rejection
(
std
::
move
(
_reject
))
{
}
void
resolve
()
{
static_assert
(
std
::
is_same
<
T
,
Arg
>::
value
||
resolver
();
std
::
is_convertible
<
T
,
Arg
>::
value
,
}
"Incompatible types detected"
);
template
<
typename
Exc
>
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
T
>>
&
core
)
override
{
void
reject
(
Exc
_exc
)
{
resolve_
(
core
->
value
());
rejection
(
std
::
move
(
_exc
));
}
}
private:
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
T
>>
&
core
)
override
{
Resolver
resolver
;
reject_
(
core
->
exc
);
Rejection
rejection
;
}
};
static
constexpr
Private
::
IgnoreException
IgnoreException
{};
Resolve
resolve_
;
static
constexpr
Private
::
NoExcept
NoExcept
{};
Reject
reject_
;
static
constexpr
Private
::
Throw
Throw
{};
};
namespace
details
{
/*
* Note that we could use std::result_of to SFINAE-out and dispatch to the right call
* However, gcc 4.7 does not correctly support std::result_of for SFINAE purposes, so we
* use a decltype SFINAE-expression instead.
*
* See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3462.html and
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56283 for reference
*/
template
<
typename
T
,
typename
Func
>
auto
callAsync
(
Func
func
,
Resolver
&
resolver
,
Rejection
&
rejection
)
->
decltype
(
std
::
declval
<
Func
>
()(
resolver
,
rejection
),
void
())
{
func
(
resolver
,
rejection
);
}
template
<
typename
T
,
typename
Func
>
// Specialization for a void-Promise on a callback returning void
auto
callAsync
(
Func
func
,
Resolver
&
resolver
,
Rejection
&
rejection
)
template
<
typename
Resolve
,
typename
Reject
,
typename
...
Args
>
->
decltype
(
std
::
declval
<
Func
>
()(
Deferred
<
T
>
()),
void
())
{
struct
Continuation
<
void
,
Resolve
,
Reject
,
void
(
Args
...)
>
func
(
Deferred
<
T
>
(
std
::
move
(
resolver
),
std
::
move
(
rejection
)));
:
public
Continuable
<
void
>
{
}
}
template
<
typename
T
>
class
Promise
:
public
PromiseBase
{
public:
template
<
typename
U
>
friend
class
Promise
;
typedef
Private
::
CoreT
<
T
>
Core
;
template
<
typename
Func
>
explicit
Promise
(
Func
func
)
:
core_
(
std
::
make_shared
<
Core
>
())
,
resolver_
(
core_
)
,
rejection_
(
core_
)
{
details
::
callAsync
<
T
>
(
func
,
resolver_
,
rejection_
);
}
Promise
(
const
Promise
<
T
>&
other
)
=
delete
;
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
chain
,
Resolve
resolve
,
Promise
&
operator
=
(
const
Promise
<
T
>&
other
)
=
delete
;
Reject
reject
)
:
Continuable
<
void
>
(
chain
),
resolve_
(
resolve
),
reject_
(
reject
)
{}
Promise
(
Promise
<
T
>&&
other
)
=
default
;
static_assert
(
sizeof
...(
Args
)
==
0
,
Promise
&
operator
=
(
Promise
<
T
>&&
other
)
=
default
;
"Can not attach a non-void continuation to a void-Promise"
)
;
virtual
~
Promise
()
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
void
>>
&
/*core*/
)
override
{
{
resolve_
();
}
}
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
void
>>
&
core
)
override
{
reject_
(
core
->
exc
);
}
template
<
typename
U
>
Resolve
resolve_
;
static
Reject
reject_
;
Promise
<
T
>
};
resolved
(
U
&&
value
)
{
static_assert
(
!
std
::
is_void
<
T
>::
value
,
"Can not resolve a void promise with parameters"
);
static_assert
(
std
::
is_same
<
T
,
U
>::
value
||
std
::
is_convertible
<
U
,
T
>::
value
,
"Incompatible value type"
);
auto
core
=
std
::
make_shared
<
Core
>
();
core
->
template
construct
<
T
>(
std
::
forward
<
U
>
(
value
));
return
Promise
<
T
>
(
std
::
move
(
core
));
}
static
// Specialization for a callback returning a Promise
Promise
<
void
>
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
U
,
resolved
()
{
typename
...
Args
>
static_assert
(
std
::
is_void
<
T
>::
value
,
struct
Continuation
<
T
,
Resolve
,
Reject
,
Promise
<
U
>
(
Args
...)
>
"Resolving a non-void promise requires parameters"
);
:
public
Continuable
<
T
>
{
auto
core
=
std
::
make_shared
<
Core
>
();
static_assert
(
sizeof
...(
Args
)
==
1
,
core
->
state
=
State
::
Fulfilled
;
"A continuation should only take one single argument"
);
return
Promise
<
T
>
(
std
::
move
(
core
));
}
typedef
typename
detail
::
nth_element
<
0
,
Args
...
>::
type
Arg
;
static_assert
(
std
::
is_same
<
T
,
Arg
>::
value
||
std
::
is_convertible
<
T
,
Arg
>::
value
,
"Incompatible types detected"
);
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
chain
,
Resolve
resolve
,
Reject
reject
)
:
Continuable
<
T
>
(
chain
),
resolve_
(
resolve
),
reject_
(
reject
)
{}
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
T
>>
&
core
)
override
{
auto
promise
=
resolve_
(
detail
::
tryMove
<
Resolve
>
(
core
->
value
()));
finishResolve
(
promise
);
}
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
T
>>
&
core
)
override
{
reject_
(
core
->
exc
);
for
(
const
auto
&
req
:
core
->
requests
)
{
req
->
reject
(
core
);
}
}
template
<
typename
PromiseType
>
struct
Chainer
{
explicit
Chainer
(
const
std
::
shared_ptr
<
Private
::
Core
>
&
core
)
:
chainCore
(
core
)
{}
void
operator
()(
const
PromiseType
&
val
)
{
chainCore
->
construct
<
PromiseType
>
(
val
);
for
(
const
auto
&
req
:
chainCore
->
requests
)
{
req
->
resolve
(
chainCore
);
}
}
template
<
typename
Exc
>
std
::
shared_ptr
<
Core
>
chainCore
;
static
Promise
<
T
>
};
rejected
(
Exc
exc
)
{
auto
core
=
std
::
make_shared
<
Core
>
();
template
<
typename
Promise
,
core
->
exc
=
std
::
make_exception_ptr
(
exc
);
typename
Type
=
typename
detail
::
RemovePromise
<
Promise
>
::
Type
>
core
->
state
=
State
::
Rejected
;
Chainer
<
Type
>
makeChainer
(
const
Promise
&
)
{
return
Promise
<
T
>
(
std
::
move
(
core
));
return
Chainer
<
Type
>
(
this
->
chain_
);
}
template
<
typename
P
>
void
finishResolve
(
P
&
promise
)
{
auto
chainer
=
makeChainer
(
promise
);
std
::
weak_ptr
<
Core
>
weakPtr
=
this
->
chain_
;
promise
.
then
(
std
::
move
(
chainer
),
[
weakPtr
](
std
::
exception_ptr
exc
)
{
if
(
auto
core
=
weakPtr
.
lock
())
{
core
->
exc
=
std
::
move
(
exc
);
core
->
state
=
State
::
Rejected
;
for
(
const
auto
&
req
:
core
->
requests
)
{
req
->
reject
(
core
);
}
}
}
});
}
Resolve
resolve_
;
Reject
reject_
;
};
// Specialization for a void callback returning a Promise
template
<
typename
Resolve
,
typename
Reject
,
typename
U
,
typename
...
Args
>
struct
Continuation
<
void
,
Resolve
,
Reject
,
Promise
<
U
>
(
Args
...)
>
:
public
Continuable
<
void
>
{
static_assert
(
sizeof
...(
Args
)
==
0
,
"Can not attach a non-void continuation to a void-Promise"
);
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
chain
,
Resolve
resolve
,
Reject
reject
)
:
Continuable
<
void
>
(
chain
),
resolve_
(
resolve
),
reject_
(
reject
)
{}
void
doResolve
(
const
std
::
shared_ptr
<
CoreT
<
void
>>
&
/*core*/
)
{
auto
promise
=
resolve_
();
finishResolve
(
promise
);
}
void
doReject
(
const
std
::
shared_ptr
<
CoreT
<
void
>>
&
core
)
{
reject_
(
core
->
exc
);
for
(
const
auto
&
req
:
core
->
requests
)
{
req
->
reject
(
core
);
}
}
bool
isPending
()
const
override
{
return
core_
->
state
==
State
::
Pending
;
}
template
<
typename
PromiseType
,
typename
Dummy
=
void
>
struct
Chainer
{
bool
isFulfilled
()
const
override
{
return
core_
->
state
==
State
::
Fulfilled
;
}
explicit
Chainer
(
const
std
::
shared_ptr
<
Private
::
Core
>
&
core
)
bool
isRejected
()
const
override
{
return
core_
->
state
==
State
::
Rejected
;
}
:
chainCore
(
core
)
{
}
template
<
typename
ResolveFunc
,
typename
RejectFunc
>
void
operator
()(
const
PromiseType
&
val
)
{
auto
chainCore
->
construct
<
PromiseType
>
(
val
);
then
(
ResolveFunc
resolveFunc
,
RejectFunc
rejectFunc
)
for
(
const
auto
&
req
:
chainCore
->
requests
)
{
->
Promise
<
req
->
resolve
(
chainCore
);
typename
detail
::
RemovePromise
<
}
typename
detail
::
FunctionTrait
<
ResolveFunc
>::
ReturnType
}
>::
Type
>
{
typedef
typename
detail
::
RemovePromise
<
std
::
shared_ptr
<
Core
>
chainCore
;
typename
detail
::
FunctionTrait
<
ResolveFunc
>::
ReturnType
};
>::
Type
RetType
;
Promise
<
RetType
>
promise
;
template
<
typename
Dummy
>
struct
Chainer
<
void
,
Dummy
>
{
explicit
Chainer
(
const
std
::
shared_ptr
<
Private
::
Core
>
&
core
)
:
chainCore
(
core
)
{}
typedef
Private
::
Continuation
<
T
,
ResolveFunc
,
RejectFunc
,
ResolveFunc
>
Continuation
;
void
operator
()()
{
std
::
shared_ptr
<
Private
::
Request
>
req
=
std
::
make_shared
<
Continuation
>
(
promise
.
core_
,
resolveFunc
,
rejectFunc
);
auto
core
=
this
->
chain_
;
core
->
state
=
State
::
Fulfilled
;
std
::
unique_lock
<
std
::
mutex
>
guard
(
core_
->
mtx
);
for
(
const
auto
&
req
:
chainCore
->
requests
)
{
if
(
isFulfilled
())
{
req
->
resolve
(
chainCore
);
req
->
resolve
(
core_
);
}
}
}
else
if
(
isRejected
())
{
req
->
reject
(
core_
);
}
core_
->
requests
.
push_back
(
req
);
std
::
shared_ptr
<
Core
>
chainCore
;
};
return
promise
;
template
<
typename
Promise
,
}
typename
Type
=
typename
detail
::
RemovePromise
<
Promise
>
::
Type
>
Chainer
<
Type
>
makeChainer
(
const
Promise
&
)
{
return
Chainer
<
Type
>
(
this
->
chain_
);
}
private:
template
<
typename
P
>
void
finishResolve
(
P
&
promise
)
{
Promise
()
auto
chainer
=
makeChainer
(
promise
);
:
core_
(
std
::
make_shared
<
Core
>
())
promise
.
then
(
std
::
move
(
chainer
),
[
=
](
std
::
exception_ptr
exc
)
{
,
resolver_
(
core_
)
auto
core
=
this
->
chain_
;
,
rejection_
(
core_
)
core
->
exc
=
std
::
move
(
exc
);
{
core
->
state
=
State
::
Rejected
;
}
explicit
Promise
(
std
::
shared_ptr
<
Core
>&&
core
)
for
(
const
auto
&
req
:
core
->
requests
)
{
:
core_
(
core
)
req
->
reject
(
core
);
,
resolver_
(
core_
)
}
,
rejection_
(
core_
)
});
{
}
}
Resolve
resolve_
;
Reject
reject_
;
};
}
// namespace impl
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Sig
>
struct
Continuation
:
public
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
decltype
(
&
Sig
::
operator
())
>
{
typedef
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
decltype
(
&
Sig
::
operator
())
>
Base
;
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
core
,
Resolve
resolve
,
Reject
reject
)
:
Base
(
core
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{}
};
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
*
)(
Args
...)
>
:
public
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
{
typedef
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
Base
;
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
core
,
Resolve
resolve
,
Reject
reject
)
:
Base
(
core
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{}
};
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
Cls
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Cls
::*
)(
Args
...)
>
:
public
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
{
typedef
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
Base
;
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
core
,
Resolve
resolve
,
Reject
reject
)
:
Base
(
core
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{}
};
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
Cls
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Cls
::*
)(
Args
...)
const
>
:
public
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
{
typedef
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
Base
;
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
core
,
Resolve
resolve
,
Reject
reject
)
:
Base
(
core
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{}
};
template
<
typename
T
,
typename
Resolve
,
typename
Reject
,
typename
Res
,
typename
...
Args
>
struct
Continuation
<
T
,
Resolve
,
Reject
,
std
::
function
<
Res
(
Args
...)
>>
:
public
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
{
typedef
impl
::
Continuation
<
T
,
Resolve
,
Reject
,
Res
(
Args
...)
>
Base
;
Continuation
(
const
std
::
shared_ptr
<
Core
>
&
core
,
Resolve
resolve
,
Reject
reject
)
:
Base
(
core
,
std
::
move
(
resolve
),
std
::
move
(
reject
))
{}
};
}
// namespace Private
class
Resolver
{
public:
explicit
Resolver
(
const
std
::
shared_ptr
<
Private
::
Core
>
&
core
)
:
core_
(
core
)
{}
Resolver
(
const
Resolver
&
other
)
=
delete
;
Resolver
&
operator
=
(
const
Resolver
&
other
)
=
delete
;
Resolver
(
Resolver
&&
other
)
=
default
;
Resolver
&
operator
=
(
Resolver
&&
other
)
=
default
;
template
<
typename
Arg
>
bool
operator
()(
Arg
&&
arg
)
const
{
if
(
!
core_
)
return
false
;
typedef
typename
std
::
remove_reference
<
Arg
>::
type
Type
;
if
(
core_
->
state
!=
State
::
Pending
)
throw
Error
(
"Attempt to resolve a fulfilled promise"
);
/* In a ideal world, this should be checked at compile-time rather
* than runtime. However, since types are erased, this looks like
* a difficult task
*/
if
(
core_
->
isVoid
())
{
throw
Error
(
"Attempt to resolve a void promise with arguments"
);
}
std
::
shared_ptr
<
Core
>
core_
;
std
::
unique_lock
<
std
::
mutex
>
guard
(
core_
->
mtx
);
Resolver
resolver_
;
core_
->
construct
<
Type
>
(
std
::
forward
<
Arg
>
(
arg
));
Rejection
rejection_
;
};
template
<
typename
T
>
for
(
const
auto
&
req
:
core_
->
requests
)
{
class
Barrier
{
req
->
resolve
(
core_
);
public:
}
explicit
Barrier
(
Promise
<
T
>&
promise
)
:
promise_
(
promise
)
{
}
void
wait
()
{
return
true
;
if
(
promise_
.
isFulfilled
()
||
promise_
.
isRejected
())
return
;
}
promise_
.
then
([
&
](
const
T
&
)
mutable
{
bool
operator
()()
const
{
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
if
(
!
core_
)
cv
.
notify_one
();
return
false
;
},
[
&
](
std
::
exception_ptr
)
mutable
{
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
cv
.
notify_one
();
});
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
if
(
core_
->
state
!=
State
::
Pending
)
cv
.
wait
(
guard
,
[
&
]
{
return
promise_
.
isFulfilled
()
||
promise_
.
isRejected
();
});
throw
Error
(
"Attempt to resolve a fulfilled promise"
);
}
template
<
class
Rep
,
class
Period
>
if
(
!
core_
->
isVoid
())
std
::
cv_status
wait_for
(
const
std
::
chrono
::
duration
<
Rep
,
Period
>&
period
)
{
throw
Error
(
"Attempt ro resolve a non-void promise with no argument"
);
if
(
promise_
.
isFulfilled
()
||
promise_
.
isRejected
())
return
std
::
cv_status
::
no_timeout
;
promise_
.
then
([
&
](
const
T
&
)
mutable
{
std
::
unique_lock
<
std
::
mutex
>
guard
(
core_
->
mtx
);
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
core_
->
state
=
State
::
Fulfilled
;
cv
.
notify_one
();
for
(
const
auto
&
req
:
core_
->
requests
)
{
},
[
&
](
std
::
exception_ptr
)
mutable
{
req
->
resolve
(
core_
);
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
}
cv
.
notify_one
();
});
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
return
true
;
return
cv
.
wait_for
(
guard
,
period
);
}
}
private:
void
clear
()
{
core_
=
nullptr
;
}
Promise
<
T
>&
promise_
;
mutable
std
::
mutex
mtx
;
Resolver
clone
()
{
return
Resolver
(
core_
);
}
std
::
condition_variable
cv
;
};
private:
std
::
shared_ptr
<
Private
::
Core
>
core_
;
};
namespace
Impl
{
class
Rejection
{
struct
Any
;
public:
explicit
Rejection
(
const
std
::
shared_ptr
<
Private
::
Core
>
&
core
)
:
core_
(
core
)
{}
Rejection
(
const
Rejection
&
other
)
=
delete
;
Rejection
&
operator
=
(
const
Rejection
&
other
)
=
delete
;
Rejection
(
Rejection
&&
other
)
=
default
;
Rejection
&
operator
=
(
Rejection
&&
other
)
=
default
;
template
<
typename
Exc
>
bool
operator
()(
Exc
exc
)
const
{
if
(
!
core_
)
return
false
;
if
(
core_
->
state
!=
State
::
Pending
)
throw
Error
(
"Attempt to reject a fulfilled promise"
);
std
::
unique_lock
<
std
::
mutex
>
guard
(
core_
->
mtx
);
core_
->
exc
=
std
::
make_exception_ptr
(
exc
);
core_
->
state
=
State
::
Rejected
;
for
(
const
auto
&
req
:
core_
->
requests
)
{
req
->
reject
(
core_
);
}
}
class
Any
{
return
true
;
public:
}
friend
struct
Impl
::
Any
;
Any
(
const
Any
&
other
)
=
default
;
void
clear
()
{
core_
=
nullptr
;
}
Any
&
operator
=
(
const
Any
&
other
)
=
default
;
Any
(
Any
&&
other
)
=
default
;
Rejection
clone
()
{
return
Rejection
(
core_
);
}
Any
&
operator
=
(
Any
&&
other
)
=
default
;
template
<
typename
T
>
private:
bool
is
()
const
{
std
::
shared_ptr
<
Private
::
Core
>
core_
;
return
core_
->
id
==
TypeId
::
of
<
T
>
();
};
}
template
<
typename
T
>
template
<
typename
T
>
class
Deferred
{
T
cast
()
const
{
public:
if
(
!
is
<
T
>
())
throw
BadAnyCast
();
Deferred
()
:
resolver
(
nullptr
),
rejection
(
nullptr
)
{}
auto
core
=
std
::
static_pointer_cast
<
Private
::
CoreT
<
T
>>
(
core_
);
Deferred
(
const
Deferred
&
other
)
=
delete
;
return
core
->
value
();
Deferred
&
operator
=
(
const
Deferred
&
other
)
=
delete
;
}
private:
Deferred
(
Deferred
&&
other
)
=
default
;
explicit
Any
(
const
std
::
shared_ptr
<
Private
::
Core
>&
core
)
Deferred
&
operator
=
(
Deferred
&&
other
)
=
default
;
:
core_
(
core
)
{
}
std
::
shared_ptr
<
Private
::
Core
>
core_
;
};
Deferred
(
Resolver
_resolver
,
Rejection
_reject
)
:
resolver
(
std
::
move
(
_resolver
)),
rejection
(
std
::
move
(
_reject
))
{}
template
<
typename
U
>
bool
resolve
(
U
&&
arg
)
{
typedef
typename
std
::
remove_reference
<
U
>::
type
CleanU
;
static_assert
(
std
::
is_same
<
T
,
CleanU
>::
value
||
std
::
is_convertible
<
U
,
T
>::
value
,
"Types mismatch"
);
return
resolver
(
std
::
forward
<
U
>
(
arg
));
}
template
<
typename
...
Args
>
void
emplaceResolve
(
Args
&&
...)
{}
template
<
typename
Exc
>
bool
reject
(
Exc
exc
)
{
return
rejection
(
std
::
move
(
exc
));
}
void
clear
()
{
resolver
.
clear
();
rejection
.
clear
();
}
private:
Resolver
resolver
;
Rejection
rejection
;
};
template
<
>
class
Deferred
<
void
>
{
public:
Deferred
()
:
resolver
(
nullptr
),
rejection
(
nullptr
)
{}
Deferred
(
const
Deferred
&
other
)
=
delete
;
Deferred
&
operator
=
(
const
Deferred
&
other
)
=
delete
;
Deferred
(
Deferred
&&
other
)
=
default
;
Deferred
&
operator
=
(
Deferred
&&
other
)
=
default
;
namespace
Impl
{
Deferred
(
Resolver
_resolver
,
Rejection
_reject
)
:
resolver
(
std
::
move
(
_resolver
)),
rejection
(
std
::
move
(
_reject
))
{}
/* Instead of duplicating the code between whenAll and whenAny functions, the main implementation
void
resolve
()
{
resolver
();
}
* is in the When class below and we configure the class with a policy instead, depending if we
* are executing an "all" or "any" operation, how cool is that ?
*/
struct
All
{
struct
Data
{
template
<
typename
Exc
>
void
reject
(
Exc
_exc
)
{
rejection
(
std
::
move
(
_exc
));
}
Data
(
const
size_t
_total
,
Resolver
_resolver
,
Rejection
_rejection
)
:
total
(
_total
)
,
resolved
(
0
)
,
rejected
(
false
)
,
mtx
()
,
resolve
(
std
::
move
(
_resolver
))
,
reject
(
std
::
move
(
_rejection
))
{
}
const
size_t
total
;
private:
size_t
resolved
;
Resolver
resolver
;
bool
rejected
;
Rejection
rejection
;
std
::
mutex
mtx
;
};
Resolver
resolve
;
Rejection
reject
;
};
template
<
size_t
Index
,
typename
T
,
typename
Data
>
static
void
resolveT
(
const
T
&
val
,
Data
&
data
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
rejected
)
return
;
// @Check thread-safety of std::get ?
std
::
get
<
Index
>
(
data
->
results
)
=
val
;
data
->
resolved
++
;
if
(
data
->
resolved
==
data
->
total
)
{
data
->
resolve
(
data
->
results
);
}
}
template
<
typename
Data
>
static
void
resolveVoid
(
Data
&
data
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
rejected
)
return
;
data
->
resolved
++
;
if
(
data
->
resolved
==
data
->
total
)
{
data
->
resolve
(
data
->
results
);
}
}
template
<
typename
Data
>
static
void
reject
(
std
::
exception_ptr
exc
,
Data
&
data
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
data
->
rejected
=
true
;
data
->
reject
(
exc
);
}
};
struct
Any
{
struct
Data
{
Data
(
size_t
,
Resolver
resolver
,
Rejection
rejection
)
:
done
(
false
)
,
mtx
()
,
resolve
(
std
::
move
(
resolver
))
,
reject
(
std
::
move
(
rejection
))
{
}
bool
done
;
std
::
mutex
mtx
;
Resolver
resolve
;
Rejection
reject
;
};
template
<
size_t
Index
,
typename
T
,
typename
Data
>
static
void
resolveT
(
const
T
&
val
,
Data
&
data
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
done
)
return
;
// Instead of allocating a new core, ideally we could share the same core as
// the relevant promise but we do not have access to the promise here is so meh
auto
core
=
std
::
make_shared
<
Private
::
CoreT
<
T
>>
();
core
->
template
construct
<
T
>(
val
);
data
->
resolve
(
Async
::
Any
(
core
));
data
->
done
=
true
;
}
template
<
typename
Data
>
static
void
resolveVoid
(
Data
&
data
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
done
)
return
;
auto
core
=
std
::
make_shared
<
Private
::
CoreT
<
void
>>
();
data
->
resolve
(
Async
::
Any
(
core
));
data
->
done
=
true
;
}
template
<
typename
Data
>
static
void
reject
(
std
::
exception_ptr
exc
,
Data
&
data
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
data
->
done
=
true
;
data
->
reject
(
exc
);
}
};
template
<
typename
ContinuationPolicy
>
struct
When
{
When
(
Resolver
resolver
,
Rejection
rejection
)
:
resolve
(
std
::
move
(
resolver
))
,
reject
(
std
::
move
(
rejection
))
{
}
template
<
typename
...
Args
>
void
operator
()(
Args
&&
...
args
)
{
whenArgs
(
std
::
forward
<
Args
>
(
args
)...);
}
private:
template
<
typename
T
,
size_t
Index
,
typename
Data
>
struct
WhenContinuation
{
explicit
WhenContinuation
(
Data
_data
)
:
data
(
std
::
move
(
_data
))
{
}
void
operator
()(
const
T
&
val
)
const
{
ContinuationPolicy
::
template
resolveT
<
Index
>(
val
,
data
);
}
Data
data
;
};
template
<
size_t
Index
,
typename
Data
>
struct
WhenContinuation
<
void
,
Index
,
Data
>
{
explicit
WhenContinuation
(
Data
_data
)
:
data
(
std
::
move
(
_data
))
{
}
void
operator
()()
const
{
ContinuationPolicy
::
resolveVoid
(
data
);
}
Data
data
;
};
template
<
typename
T
,
size_t
Index
,
typename
Data
>
WhenContinuation
<
T
,
Index
,
Data
>
makeContinuation
(
const
Data
&
data
)
{
return
WhenContinuation
<
T
,
Index
,
Data
>
(
data
);
}
template
<
size_t
Index
,
typename
Data
,
typename
T
>
void
when
(
const
Data
&
data
,
Promise
<
T
>&
promise
)
{
promise
.
then
(
makeContinuation
<
T
,
Index
>
(
data
),
[
=
](
std
::
exception_ptr
ptr
)
{
ContinuationPolicy
::
reject
(
std
::
move
(
ptr
),
data
);
});
}
template
<
size_t
Index
,
typename
Data
,
typename
T
>
void
when
(
const
Data
&
data
,
T
&&
arg
)
{
typedef
typename
std
::
remove_reference
<
T
>::
type
CleanT
;
auto
promise
=
Promise
<
CleanT
>::
resolved
(
std
::
forward
<
T
>
(
arg
));
when
<
Index
>
(
data
,
promise
);
}
template
<
typename
...
Args
>
void
whenArgs
(
Args
&&
...
args
)
{
typedef
std
::
tuple
<
typename
detail
::
RemovePromise
<
typename
std
::
remove_reference
<
Args
>::
type
>::
Type
...
>
Results
;
/* We need to keep the results alive until the last promise
* finishes its execution
*/
/* See the trick here ? Basically, we only have access to the real type of the results
* in this function. The policy classes do not have access to the full type (std::tuple),
* but, instead, take a generic template data type as a parameter. They only need to know
* that results is a tuple, they do not need to know the real type of the results.
*
* This is some sort of compile-time template type-erasing, hue
*/
struct
Data
:
public
ContinuationPolicy
::
Data
{
Data
(
size_t
total
,
Resolver
resolver
,
Rejection
rejection
)
:
ContinuationPolicy
::
Data
(
total
,
std
::
move
(
resolver
),
std
::
move
(
rejection
))
{
}
Results
results
;
};
auto
data
=
std
::
make_shared
<
Data
>
(
sizeof
...(
Args
),
std
::
move
(
resolve
),
std
::
move
(
reject
));
whenArgs
<
0
>
(
data
,
std
::
forward
<
Args
>
(
args
)...);
}
template
<
size_t
Index
,
typename
Data
,
typename
Head
,
typename
...
Rest
>
void
whenArgs
(
const
Data
&
data
,
Head
&&
head
,
Rest
&&
...
rest
)
{
when
<
Index
>
(
data
,
std
::
forward
<
Head
>
(
head
));
whenArgs
<
Index
+
1
>
(
data
,
std
::
forward
<
Rest
>
(
rest
)...);
}
template
<
size_t
Index
,
typename
Data
,
typename
Head
>
void
whenArgs
(
const
Data
&
data
,
Head
&&
head
)
{
when
<
Index
>
(
data
,
std
::
forward
<
Head
>
(
head
));
}
Resolver
resolve
;
Rejection
reject
;
};
template
<
typename
T
,
typename
Results
>
struct
WhenAllRange
{
WhenAllRange
(
Resolver
_resolve
,
Rejection
_reject
)
:
resolve
(
std
::
move
(
_resolve
))
,
reject
(
std
::
move
(
_reject
))
{
}
template
<
typename
Iterator
>
void
operator
()(
Iterator
first
,
Iterator
last
)
{
auto
data
=
std
::
make_shared
<
DataT
<
T
>>
(
static_cast
<
size_t
>
(
std
::
distance
(
first
,
last
)),
std
::
move
(
resolve
),
std
::
move
(
reject
)
);
size_t
index
=
0
;
for
(
auto
it
=
first
;
it
!=
last
;
++
it
)
{
WhenContinuation
<
T
>
cont
(
data
,
index
);
it
->
then
(
std
::
move
(
cont
),
[
=
](
std
::
exception_ptr
ptr
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
rejected
)
return
;
data
->
rejected
=
true
;
data
->
reject
(
std
::
move
(
ptr
));
});
++
index
;
}
}
private:
struct
Data
{
Data
(
size_t
_total
,
Resolver
_resolver
,
Rejection
_rejection
)
:
total
(
_total
)
,
resolved
(
0
)
,
rejected
(
false
)
,
mtx
()
,
resolve
(
std
::
move
(
_resolver
))
,
reject
(
std
::
move
(
_rejection
))
{
}
const
size_t
total
;
size_t
resolved
;
bool
rejected
;
std
::
mutex
mtx
;
Resolver
resolve
;
Rejection
reject
;
};
/* Ok so apparently I can not fully specialize a template structure
* here, so you know what, compiler ? Take that Dummy type and leave
* me alone
*/
template
<
typename
ValueType
,
typename
Dummy
=
void
>
struct
DataT
:
public
Data
{
DataT
(
size_t
total
,
Resolver
resolver
,
Rejection
rejection
)
:
Data
(
total
,
std
::
move
(
resolver
),
std
::
move
(
rejection
))
{
results
.
resize
(
total
);
}
Results
results
;
};
/* For a vector of void promises, we do not have any results, that's
* why we need a distinct specialization for the void case
*/
template
<
typename
Dummy
>
struct
DataT
<
void
,
Dummy
>
:
public
Data
{
DataT
(
size_t
total
,
Resolver
resolver
,
Rejection
rejection
)
:
Data
(
total
,
std
::
move
(
resolver
),
std
::
move
(
rejection
))
{
}
};
template
<
typename
ValueType
,
typename
Dummy
=
void
>
struct
WhenContinuation
{
using
D
=
std
::
shared_ptr
<
DataT
<
ValueType
>>
;
WhenContinuation
(
const
D
&
_data
,
size_t
_index
)
:
data
(
_data
)
,
index
(
_index
)
{
}
void
operator
()(
const
ValueType
&
val
)
const
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
rejected
)
return
;
data
->
results
[
index
]
=
val
;
data
->
resolved
++
;
if
(
data
->
resolved
==
data
->
total
)
{
data
->
resolve
(
data
->
results
);
}
}
D
data
;
size_t
index
;
};
template
<
typename
Dummy
>
struct
WhenContinuation
<
void
,
Dummy
>
{
using
D
=
std
::
shared_ptr
<
DataT
<
void
>>
;
WhenContinuation
(
const
D
&
_data
,
size_t
)
:
data
(
_data
)
{
}
void
operator
()()
const
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
rejected
)
return
;
data
->
resolved
++
;
if
(
data
->
resolved
==
data
->
total
)
{
data
->
resolve
();
}
}
D
data
;
};
Resolver
resolve
;
Rejection
reject
;
};
static
constexpr
Private
::
IgnoreException
IgnoreException
{};
static
constexpr
Private
::
NoExcept
NoExcept
{};
static
constexpr
Private
::
Throw
Throw
{};
namespace
details
{
/*
* Note that we could use std::result_of to SFINAE-out and dispatch to the right
* call However, gcc 4.7 does not correctly support std::result_of for SFINAE
* purposes, so we use a decltype SFINAE-expression instead.
*
* See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3462.html and
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56283 for reference
*/
template
<
typename
T
,
typename
Func
>
auto
callAsync
(
Func
func
,
Resolver
&
resolver
,
Rejection
&
rejection
)
->
decltype
(
std
::
declval
<
Func
>
()(
resolver
,
rejection
),
void
())
{
func
(
resolver
,
rejection
);
}
template
<
typename
T
,
typename
Func
>
auto
callAsync
(
Func
func
,
Resolver
&
resolver
,
Rejection
&
rejection
)
->
decltype
(
std
::
declval
<
Func
>
()(
Deferred
<
T
>
()),
void
())
{
func
(
Deferred
<
T
>
(
std
::
move
(
resolver
),
std
::
move
(
rejection
)));
}
}
// namespace details
template
<
typename
T
>
class
Promise
:
public
PromiseBase
{
public:
template
<
typename
U
>
friend
class
Promise
;
typedef
Private
::
CoreT
<
T
>
Core
;
template
<
typename
Func
>
explicit
Promise
(
Func
func
)
:
core_
(
std
::
make_shared
<
Core
>
()),
resolver_
(
core_
),
rejection_
(
core_
)
{
details
::
callAsync
<
T
>
(
func
,
resolver_
,
rejection_
);
}
Promise
(
const
Promise
<
T
>
&
other
)
=
delete
;
Promise
&
operator
=
(
const
Promise
<
T
>
&
other
)
=
delete
;
Promise
(
Promise
<
T
>
&&
other
)
=
default
;
Promise
&
operator
=
(
Promise
<
T
>
&&
other
)
=
default
;
virtual
~
Promise
()
{}
template
<
typename
U
>
static
Promise
<
T
>
resolved
(
U
&&
value
)
{
static_assert
(
!
std
::
is_void
<
T
>::
value
,
"Can not resolve a void promise with parameters"
);
static_assert
(
std
::
is_same
<
T
,
U
>::
value
||
std
::
is_convertible
<
U
,
T
>::
value
,
"Incompatible value type"
);
auto
core
=
std
::
make_shared
<
Core
>
();
core
->
template
construct
<
T
>(
std
::
forward
<
U
>
(
value
));
return
Promise
<
T
>
(
std
::
move
(
core
));
}
static
Promise
<
void
>
resolved
()
{
static_assert
(
std
::
is_void
<
T
>::
value
,
"Resolving a non-void promise requires parameters"
);
auto
core
=
std
::
make_shared
<
Core
>
();
core
->
state
=
State
::
Fulfilled
;
return
Promise
<
T
>
(
std
::
move
(
core
));
}
template
<
typename
Exc
>
static
Promise
<
T
>
rejected
(
Exc
exc
)
{
auto
core
=
std
::
make_shared
<
Core
>
();
core
->
exc
=
std
::
make_exception_ptr
(
exc
);
core
->
state
=
State
::
Rejected
;
return
Promise
<
T
>
(
std
::
move
(
core
));
}
bool
isPending
()
const
override
{
return
core_
->
state
==
State
::
Pending
;
}
bool
isFulfilled
()
const
override
{
return
core_
->
state
==
State
::
Fulfilled
;
}
bool
isRejected
()
const
override
{
return
core_
->
state
==
State
::
Rejected
;
}
template
<
typename
ResolveFunc
,
typename
RejectFunc
>
auto
then
(
ResolveFunc
resolveFunc
,
RejectFunc
rejectFunc
)
->
Promise
<
typename
detail
::
RemovePromise
<
typename
detail
::
FunctionTrait
<
ResolveFunc
>::
ReturnType
>::
Type
>
{
typedef
typename
detail
::
RemovePromise
<
typename
detail
::
FunctionTrait
<
ResolveFunc
>::
ReturnType
>::
Type
RetType
;
Promise
<
RetType
>
promise
;
typedef
Private
::
Continuation
<
T
,
ResolveFunc
,
RejectFunc
,
ResolveFunc
>
Continuation
;
std
::
shared_ptr
<
Private
::
Request
>
req
=
std
::
make_shared
<
Continuation
>
(
promise
.
core_
,
resolveFunc
,
rejectFunc
);
std
::
unique_lock
<
std
::
mutex
>
guard
(
core_
->
mtx
);
if
(
isFulfilled
())
{
req
->
resolve
(
core_
);
}
else
if
(
isRejected
())
{
req
->
reject
(
core_
);
}
}
template
<
core_
->
requests
.
push_back
(
req
);
typename
...
Args
,
typename
Results
=
return
promise
;
std
::
tuple
<
}
typename
detail
::
RemovePromise
<
typename
std
::
remove_reference
<
Args
>
::
type
private:
>::
Type
...
Promise
()
>
:
core_
(
std
::
make_shared
<
Core
>
()),
resolver_
(
core_
),
rejection_
(
core_
)
{}
>
Promise
<
Results
>
whenAll
(
Args
&&
...
args
)
{
explicit
Promise
(
std
::
shared_ptr
<
Core
>
&&
core
)
// As ugly as it looks, this is needed to bypass a bug of gcc < 4.9
:
core_
(
core
),
resolver_
(
core_
),
rejection_
(
core_
)
{}
// whereby template parameters pack inside a lambda expression are not
// captured correctly and can not be expanded inside the lambda.
std
::
shared_ptr
<
Core
>
core_
;
Resolver
*
resolve
;
Resolver
resolver_
;
Rejection
*
reject
;
Rejection
rejection_
;
};
Promise
<
Results
>
promise
([
&
](
Resolver
&
resolver
,
Rejection
&
rejection
)
{
resolve
=
&
resolver
;
template
<
typename
T
>
class
Barrier
{
reject
=
&
rejection
;
public:
explicit
Barrier
(
Promise
<
T
>
&
promise
)
:
promise_
(
promise
)
{}
void
wait
()
{
if
(
promise_
.
isFulfilled
()
||
promise_
.
isRejected
())
return
;
promise_
.
then
(
[
&
](
const
T
&
)
mutable
{
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
cv
.
notify_one
();
},
[
&
](
std
::
exception_ptr
)
mutable
{
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
cv
.
notify_one
();
});
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
cv
.
wait
(
guard
,
[
&
]
{
return
promise_
.
isFulfilled
()
||
promise_
.
isRejected
();
});
}
template
<
class
Rep
,
class
Period
>
std
::
cv_status
wait_for
(
const
std
::
chrono
::
duration
<
Rep
,
Period
>
&
period
)
{
if
(
promise_
.
isFulfilled
()
||
promise_
.
isRejected
())
return
std
::
cv_status
::
no_timeout
;
promise_
.
then
(
[
&
](
const
T
&
)
mutable
{
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
cv
.
notify_one
();
},
[
&
](
std
::
exception_ptr
)
mutable
{
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
cv
.
notify_one
();
});
});
Impl
::
When
<
Impl
::
All
>
impl
(
std
::
move
(
*
resolve
),
std
::
move
(
*
reject
));
std
::
unique_lock
<
std
::
mutex
>
guard
(
mtx
);
// So we capture everything we need inside the lambda and then call the
return
cv
.
wait_for
(
guard
,
period
);
// implementation and expand the parameters pack here
}
impl
(
std
::
forward
<
Args
>
(
args
)...);
private:
Promise
<
T
>
&
promise_
;
mutable
std
::
mutex
mtx
;
std
::
condition_variable
cv
;
};
namespace
Impl
{
struct
Any
;
}
class
Any
{
public:
friend
struct
Impl
::
Any
;
Any
(
const
Any
&
other
)
=
default
;
Any
&
operator
=
(
const
Any
&
other
)
=
default
;
Any
(
Any
&&
other
)
=
default
;
Any
&
operator
=
(
Any
&&
other
)
=
default
;
template
<
typename
T
>
bool
is
()
const
{
return
core_
->
id
==
TypeId
::
of
<
T
>
();
}
template
<
typename
T
>
T
cast
()
const
{
if
(
!
is
<
T
>
())
throw
BadAnyCast
();
auto
core
=
std
::
static_pointer_cast
<
Private
::
CoreT
<
T
>>
(
core_
);
return
core
->
value
();
}
private:
explicit
Any
(
const
std
::
shared_ptr
<
Private
::
Core
>
&
core
)
:
core_
(
core
)
{}
std
::
shared_ptr
<
Private
::
Core
>
core_
;
};
return
promise
;
namespace
Impl
{
/* Instead of duplicating the code between whenAll and whenAny functions, the
* main implementation is in the When class below and we configure the class
* with a policy instead, depending if we are executing an "all" or "any"
* operation, how cool is that ?
*/
struct
All
{
struct
Data
{
Data
(
const
size_t
_total
,
Resolver
_resolver
,
Rejection
_rejection
)
:
total
(
_total
),
resolved
(
0
),
rejected
(
false
),
mtx
(),
resolve
(
std
::
move
(
_resolver
)),
reject
(
std
::
move
(
_rejection
))
{}
const
size_t
total
;
size_t
resolved
;
bool
rejected
;
std
::
mutex
mtx
;
Resolver
resolve
;
Rejection
reject
;
};
template
<
size_t
Index
,
typename
T
,
typename
Data
>
static
void
resolveT
(
const
T
&
val
,
Data
&
data
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
rejected
)
return
;
// @Check thread-safety of std::get ?
std
::
get
<
Index
>
(
data
->
results
)
=
val
;
data
->
resolved
++
;
if
(
data
->
resolved
==
data
->
total
)
{
data
->
resolve
(
data
->
results
);
}
}
}
template
<
typename
...
Args
>
template
<
typename
Data
>
static
void
resolveVoid
(
Data
&
data
)
{
Promise
<
Any
>
whenAny
(
Args
&&
...
args
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
// Same trick as above;
Resolver
*
resolve
;
Rejection
*
reject
;
Promise
<
Any
>
promise
([
&
](
Resolver
&
resolver
,
Rejection
&
rejection
)
{
if
(
data
->
rejected
)
resolve
=
&
resolver
;
return
;
reject
=
&
rejection
;
});
Impl
::
When
<
Impl
::
Any
>
impl
(
std
::
move
(
*
resolve
),
std
::
move
(
*
reject
));
data
->
resolved
++
;
impl
(
std
::
forward
<
Args
>
(
args
)...);
return
promise
;
if
(
data
->
resolved
==
data
->
total
)
{
data
->
resolve
(
data
->
results
);
}
}
}
template
<
template
<
typename
Data
>
typename
Iterator
,
static
void
reject
(
std
::
exception_ptr
exc
,
Data
&
data
)
{
typename
ValueType
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
=
typename
detail
::
RemovePromise
<
typename
std
::
iterator_traits
<
Iterator
>
::
value_type
>::
Type
,
typename
Results
=
typename
std
::
conditional
<
std
::
is_same
<
void
,
ValueType
>::
value
,
void
,
std
::
vector
<
ValueType
>
>::
type
>
Promise
<
Results
>
whenAll
(
Iterator
first
,
Iterator
last
)
{
/* @Security, assert that last >= first */
return
Promise
<
Results
>
([
=
](
Resolver
&
resolve
,
Rejection
&
rejection
)
{
data
->
rejected
=
true
;
data
->
reject
(
exc
);
}
};
Impl
::
WhenAllRange
<
ValueType
,
Results
>
impl
(
struct
Any
{
std
::
move
(
resolve
),
std
::
move
(
rejection
));
impl
(
first
,
last
);
struct
Data
{
Data
(
size_t
,
Resolver
resolver
,
Rejection
rejection
)
:
done
(
false
),
mtx
(),
resolve
(
std
::
move
(
resolver
)),
reject
(
std
::
move
(
rejection
))
{}
});
bool
done
;
std
::
mutex
mtx
;
Resolver
resolve
;
Rejection
reject
;
};
template
<
size_t
Index
,
typename
T
,
typename
Data
>
static
void
resolveT
(
const
T
&
val
,
Data
&
data
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
done
)
return
;
// Instead of allocating a new core, ideally we could share the same core as
// the relevant promise but we do not have access to the promise here is so
// meh
auto
core
=
std
::
make_shared
<
Private
::
CoreT
<
T
>>
();
core
->
template
construct
<
T
>(
val
);
data
->
resolve
(
Async
::
Any
(
core
));
data
->
done
=
true
;
}
template
<
typename
Data
>
static
void
resolveVoid
(
Data
&
data
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
done
)
return
;
auto
core
=
std
::
make_shared
<
Private
::
CoreT
<
void
>>
();
data
->
resolve
(
Async
::
Any
(
core
));
data
->
done
=
true
;
}
template
<
typename
Data
>
static
void
reject
(
std
::
exception_ptr
exc
,
Data
&
data
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
data
->
done
=
true
;
data
->
reject
(
exc
);
}
};
template
<
typename
ContinuationPolicy
>
struct
When
{
When
(
Resolver
resolver
,
Rejection
rejection
)
:
resolve
(
std
::
move
(
resolver
)),
reject
(
std
::
move
(
rejection
))
{}
template
<
typename
...
Args
>
void
operator
()(
Args
&&
...
args
)
{
whenArgs
(
std
::
forward
<
Args
>
(
args
)...);
}
private:
template
<
typename
T
,
size_t
Index
,
typename
Data
>
struct
WhenContinuation
{
explicit
WhenContinuation
(
Data
_data
)
:
data
(
std
::
move
(
_data
))
{}
void
operator
()(
const
T
&
val
)
const
{
ContinuationPolicy
::
template
resolveT
<
Index
>(
val
,
data
);
}
Data
data
;
};
template
<
size_t
Index
,
typename
Data
>
struct
WhenContinuation
<
void
,
Index
,
Data
>
{
explicit
WhenContinuation
(
Data
_data
)
:
data
(
std
::
move
(
_data
))
{}
void
operator
()()
const
{
ContinuationPolicy
::
resolveVoid
(
data
);
}
Data
data
;
};
template
<
typename
T
,
size_t
Index
,
typename
Data
>
WhenContinuation
<
T
,
Index
,
Data
>
makeContinuation
(
const
Data
&
data
)
{
return
WhenContinuation
<
T
,
Index
,
Data
>
(
data
);
}
template
<
size_t
Index
,
typename
Data
,
typename
T
>
void
when
(
const
Data
&
data
,
Promise
<
T
>
&
promise
)
{
promise
.
then
(
makeContinuation
<
T
,
Index
>
(
data
),
[
=
](
std
::
exception_ptr
ptr
)
{
ContinuationPolicy
::
reject
(
std
::
move
(
ptr
),
data
);
});
}
template
<
size_t
Index
,
typename
Data
,
typename
T
>
void
when
(
const
Data
&
data
,
T
&&
arg
)
{
typedef
typename
std
::
remove_reference
<
T
>::
type
CleanT
;
auto
promise
=
Promise
<
CleanT
>::
resolved
(
std
::
forward
<
T
>
(
arg
));
when
<
Index
>
(
data
,
promise
);
}
template
<
typename
...
Args
>
void
whenArgs
(
Args
&&
...
args
)
{
typedef
std
::
tuple
<
typename
detail
::
RemovePromise
<
typename
std
::
remove_reference
<
Args
>::
type
>::
Type
...
>
Results
;
/* We need to keep the results alive until the last promise
* finishes its execution
*/
/* See the trick here ? Basically, we only have access to the real type of
* the results in this function. The policy classes do not have access to
* the full type (std::tuple), but, instead, take a generic template data
* type as a parameter. They only need to know that results is a tuple, they
* do not need to know the real type of the results.
*
* This is some sort of compile-time template type-erasing, hue
*/
struct
Data
:
public
ContinuationPolicy
::
Data
{
Data
(
size_t
total
,
Resolver
resolver
,
Rejection
rejection
)
:
ContinuationPolicy
::
Data
(
total
,
std
::
move
(
resolver
),
std
::
move
(
rejection
))
{}
Results
results
;
};
auto
data
=
std
::
make_shared
<
Data
>
(
sizeof
...(
Args
),
std
::
move
(
resolve
),
std
::
move
(
reject
));
whenArgs
<
0
>
(
data
,
std
::
forward
<
Args
>
(
args
)...);
}
template
<
size_t
Index
,
typename
Data
,
typename
Head
,
typename
...
Rest
>
void
whenArgs
(
const
Data
&
data
,
Head
&&
head
,
Rest
&&
...
rest
)
{
when
<
Index
>
(
data
,
std
::
forward
<
Head
>
(
head
));
whenArgs
<
Index
+
1
>
(
data
,
std
::
forward
<
Rest
>
(
rest
)...);
}
template
<
size_t
Index
,
typename
Data
,
typename
Head
>
void
whenArgs
(
const
Data
&
data
,
Head
&&
head
)
{
when
<
Index
>
(
data
,
std
::
forward
<
Head
>
(
head
));
}
Resolver
resolve
;
Rejection
reject
;
};
template
<
typename
T
,
typename
Results
>
struct
WhenAllRange
{
WhenAllRange
(
Resolver
_resolve
,
Rejection
_reject
)
:
resolve
(
std
::
move
(
_resolve
)),
reject
(
std
::
move
(
_reject
))
{}
template
<
typename
Iterator
>
void
operator
()(
Iterator
first
,
Iterator
last
)
{
auto
data
=
std
::
make_shared
<
DataT
<
T
>>
(
static_cast
<
size_t
>
(
std
::
distance
(
first
,
last
)),
std
::
move
(
resolve
),
std
::
move
(
reject
));
size_t
index
=
0
;
for
(
auto
it
=
first
;
it
!=
last
;
++
it
)
{
WhenContinuation
<
T
>
cont
(
data
,
index
);
it
->
then
(
std
::
move
(
cont
),
[
=
](
std
::
exception_ptr
ptr
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
rejected
)
return
;
data
->
rejected
=
true
;
data
->
reject
(
std
::
move
(
ptr
));
});
++
index
;
}
}
private:
struct
Data
{
Data
(
size_t
_total
,
Resolver
_resolver
,
Rejection
_rejection
)
:
total
(
_total
),
resolved
(
0
),
rejected
(
false
),
mtx
(),
resolve
(
std
::
move
(
_resolver
)),
reject
(
std
::
move
(
_rejection
))
{}
const
size_t
total
;
size_t
resolved
;
bool
rejected
;
std
::
mutex
mtx
;
Resolver
resolve
;
Rejection
reject
;
};
/* Ok so apparently I can not fully specialize a template structure
* here, so you know what, compiler ? Take that Dummy type and leave
* me alone
*/
template
<
typename
ValueType
,
typename
Dummy
=
void
>
struct
DataT
:
public
Data
{
DataT
(
size_t
total
,
Resolver
resolver
,
Rejection
rejection
)
:
Data
(
total
,
std
::
move
(
resolver
),
std
::
move
(
rejection
))
{
results
.
resize
(
total
);
}
Results
results
;
};
/* For a vector of void promises, we do not have any results, that's
* why we need a distinct specialization for the void case
*/
template
<
typename
Dummy
>
struct
DataT
<
void
,
Dummy
>
:
public
Data
{
DataT
(
size_t
total
,
Resolver
resolver
,
Rejection
rejection
)
:
Data
(
total
,
std
::
move
(
resolver
),
std
::
move
(
rejection
))
{}
};
template
<
typename
ValueType
,
typename
Dummy
=
void
>
struct
WhenContinuation
{
using
D
=
std
::
shared_ptr
<
DataT
<
ValueType
>>
;
WhenContinuation
(
const
D
&
_data
,
size_t
_index
)
:
data
(
_data
),
index
(
_index
)
{}
void
operator
()(
const
ValueType
&
val
)
const
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
rejected
)
return
;
data
->
results
[
index
]
=
val
;
data
->
resolved
++
;
if
(
data
->
resolved
==
data
->
total
)
{
data
->
resolve
(
data
->
results
);
}
}
D
data
;
size_t
index
;
};
template
<
typename
Dummy
>
struct
WhenContinuation
<
void
,
Dummy
>
{
using
D
=
std
::
shared_ptr
<
DataT
<
void
>>
;
WhenContinuation
(
const
D
&
_data
,
size_t
)
:
data
(
_data
)
{}
void
operator
()()
const
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
data
->
mtx
);
if
(
data
->
rejected
)
return
;
data
->
resolved
++
;
if
(
data
->
resolved
==
data
->
total
)
{
data
->
resolve
();
}
}
}
D
data
;
};
Resolver
resolve
;
Rejection
reject
;
};
}
// namespace Impl
template
<
typename
...
Args
,
typename
Results
=
std
::
tuple
<
typename
detail
::
RemovePromise
<
typename
std
::
remove_reference
<
Args
>
::
type
>::
Type
...
>>
Promise
<
Results
>
whenAll
(
Args
&&
...
args
)
{
// As ugly as it looks, this is needed to bypass a bug of gcc < 4.9
// whereby template parameters pack inside a lambda expression are not
// captured correctly and can not be expanded inside the lambda.
Resolver
*
resolve
;
Rejection
*
reject
;
Promise
<
Results
>
promise
([
&
](
Resolver
&
resolver
,
Rejection
&
rejection
)
{
resolve
=
&
resolver
;
reject
=
&
rejection
;
});
Impl
::
When
<
Impl
::
All
>
impl
(
std
::
move
(
*
resolve
),
std
::
move
(
*
reject
));
// So we capture everything we need inside the lambda and then call the
// implementation and expand the parameters pack here
impl
(
std
::
forward
<
Args
>
(
args
)...);
return
promise
;
}
template
<
typename
...
Args
>
Promise
<
Any
>
whenAny
(
Args
&&
...
args
)
{
// Same trick as above;
Resolver
*
resolve
;
Rejection
*
reject
;
Promise
<
Any
>
promise
([
&
](
Resolver
&
resolver
,
Rejection
&
rejection
)
{
resolve
=
&
resolver
;
reject
=
&
rejection
;
});
Impl
::
When
<
Impl
::
Any
>
impl
(
std
::
move
(
*
resolve
),
std
::
move
(
*
reject
));
impl
(
std
::
forward
<
Args
>
(
args
)...);
return
promise
;
}
template
<
typename
Iterator
,
typename
ValueType
=
typename
detail
::
RemovePromise
<
typename
std
::
iterator_traits
<
Iterator
>
::
value_type
>::
Type
,
typename
Results
=
typename
std
::
conditional
<
std
::
is_same
<
void
,
ValueType
>::
value
,
void
,
std
::
vector
<
ValueType
>>::
type
>
Promise
<
Results
>
whenAll
(
Iterator
first
,
Iterator
last
)
{
/* @Security, assert that last >= first */
return
Promise
<
Results
>
([
=
](
Resolver
&
resolve
,
Rejection
&
rejection
)
{
Impl
::
WhenAllRange
<
ValueType
,
Results
>
impl
(
std
::
move
(
resolve
),
std
::
move
(
rejection
));
impl
(
first
,
last
);
});
}
}
// namespace Async
}
// namespace Async
}
// namespace Pistache
}
// namespace Pistache
This diff is collapsed.
Click to expand it.
include/pistache/router.h
View file @
a5cbcf4a
...
@@ -255,7 +255,8 @@ public:
...
@@ -255,7 +255,8 @@ public:
*/
*/
explicit
RouterHandler
(
std
::
shared_ptr
<
Rest
::
Router
>
router
);
explicit
RouterHandler
(
std
::
shared_ptr
<
Rest
::
Router
>
router
);
void
onRequest
(
const
Http
::
Request
&
req
,
Http
::
ResponseWriter
response
)
override
;
void
onRequest
(
const
Http
::
Request
&
req
,
Http
::
ResponseWriter
response
)
override
;
private:
private:
std
::
shared_ptr
<
Tcp
::
Handler
>
clone
()
const
final
{
std
::
shared_ptr
<
Tcp
::
Handler
>
clone
()
const
final
{
...
...
This diff is collapsed.
Click to expand it.
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