Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
c0750662
Commit
c0750662
authored
Sep 12, 2013
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Reduce generated code for Promises by making most of the node implementations mostly non-templated.
parent
6ff9769f
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
224 additions
and
8 deletions
+224
-8
async.c++
c++/src/kj/async.c++
+216
-5
async.h
c++/src/kj/async.h
+0
-0
debug.h
c++/src/kj/debug.h
+3
-3
memory.h
c++/src/kj/memory.h
+5
-0
No files found.
c++/src/kj/async.c++
View file @
c0750662
...
...
@@ -40,7 +40,9 @@ namespace {
thread_local
EventLoop
*
threadLocalEventLoop
=
nullptr
;
class
YieldPromiseNode
final
:
public
_
::
PromiseNode
<
_
::
Void
>
,
public
EventLoop
::
Event
{
#define _kJ_ALREADY_READY reinterpret_cast< ::kj::EventLoop::Event*>(1)
class
YieldPromiseNode
final
:
public
_
::
PromiseNode
,
public
EventLoop
::
Event
{
// A PromiseNode used to implement EventLoop::yield().
public
:
...
...
@@ -57,8 +59,8 @@ public:
return
false
;
}
}
_
::
ExceptionOr
<
_
::
Void
>
get
(
)
noexcept
override
{
return
_
::
Void
();
void
get
(
_
::
ExceptionOrValue
&
output
)
noexcept
override
{
output
.
as
<
_
::
Void
>
()
=
_
::
Void
();
}
Maybe
<
const
EventLoop
&>
getSafeEventLoop
()
noexcept
override
{
return
getEventLoop
();
...
...
@@ -74,6 +76,18 @@ private:
EventLoop
::
Event
*
onReadyEvent
=
nullptr
;
};
class
BoolEvent
:
public
EventLoop
::
Event
{
public
:
BoolEvent
(
const
EventLoop
&
loop
)
:
Event
(
loop
)
{}
~
BoolEvent
()
{
disarm
();
}
bool
fired
=
false
;
void
fire
()
override
{
fired
=
true
;
}
};
}
// namespace
EventLoop
&
EventLoop
::
current
()
{
...
...
@@ -91,12 +105,15 @@ EventLoop::EventLoop(): queue(*this) {
queue
.
prev
=
&
queue
;
}
void
EventLoop
::
loopWhile
(
bool
&
keepGoing
)
{
void
EventLoop
::
waitImpl
(
Own
<
_
::
PromiseNode
>
node
,
_
::
ExceptionOrValue
&
result
)
{
EventLoop
*
oldEventLoop
=
threadLocalEventLoop
;
threadLocalEventLoop
=
this
;
KJ_DEFER
(
threadLocalEventLoop
=
oldEventLoop
);
while
(
keepGoing
)
{
BoolEvent
event
(
*
this
);
event
.
fired
=
node
->
onReady
(
event
);
while
(
!
event
.
fired
)
{
queue
.
mutex
.
lock
(
_
::
Mutex
::
EXCLUSIVE
);
// Get the first event in the queue.
...
...
@@ -125,6 +142,9 @@ void EventLoop::loopWhile(bool& keepGoing) {
KJ_DEFER
(
event
->
mutex
.
unlock
(
_
::
Mutex
::
EXCLUSIVE
));
event
->
fire
();
}
KJ_DBG
(
&
result
);
node
->
get
(
result
);
}
Promise
<
void
>
EventLoop
::
yield
()
{
...
...
@@ -211,4 +231,195 @@ void SimpleEventLoop::wake() const {
}
}
// =======================================================================================
void
PromiseBase
::
absolve
()
{
runCatchingExceptions
([
this
]()
{
auto
deleteMe
=
kj
::
mv
(
node
);
});
}
namespace
_
{
// private
bool
PromiseNode
::
atomicOnReady
(
EventLoop
::
Event
*&
onReadyEvent
,
EventLoop
::
Event
&
newEvent
)
{
// If onReadyEvent is null, atomically set it to point at newEvent and return false.
// If onReadyEvent is _kJ_ALREADY_READY, return true.
// Useful for implementing onReady() thread-safely.
EventLoop
::
Event
*
oldEvent
=
nullptr
;
if
(
__atomic_compare_exchange_n
(
&
onReadyEvent
,
&
oldEvent
,
&
newEvent
,
false
,
__ATOMIC_ACQ_REL
,
__ATOMIC_ACQUIRE
))
{
// Event was swapped in and will be called later.
return
false
;
}
else
{
// `onReadyEvent` is not null. If it is _kJ_ALREADY_READY then this promise was fulfilled
// before any dependent existed, otherwise there is already a different dependent.
KJ_IREQUIRE
(
oldEvent
==
_kJ_ALREADY_READY
,
"onReady() can only be called once."
);
return
true
;
}
}
void
PromiseNode
::
atomicReady
(
EventLoop
::
Event
*&
onReadyEvent
)
{
// If onReadyEvent is null, atomically set it to _kJ_ALREADY_READY.
// Otherwise, arm whatever it points at.
// Useful for firing events in conjuction with atomicOnReady().
EventLoop
::
Event
*
oldEvent
=
nullptr
;
if
(
!
__atomic_compare_exchange_n
(
&
onReadyEvent
,
&
oldEvent
,
_kJ_ALREADY_READY
,
false
,
__ATOMIC_ACQ_REL
,
__ATOMIC_ACQUIRE
))
{
oldEvent
->
arm
();
}
}
bool
ImmediatePromiseNodeBase
::
onReady
(
EventLoop
::
Event
&
event
)
noexcept
{
return
true
;
}
Maybe
<
const
EventLoop
&>
ImmediatePromiseNodeBase
::
getSafeEventLoop
()
noexcept
{
return
nullptr
;
}
ImmediateBrokenPromiseNode
::
ImmediateBrokenPromiseNode
(
Exception
&&
exception
)
:
exception
(
kj
::
mv
(
exception
))
{}
void
ImmediateBrokenPromiseNode
::
get
(
ExceptionOrValue
&
output
)
noexcept
{
output
.
exception
=
kj
::
mv
(
exception
);
}
TransformPromiseNodeBase
::
TransformPromiseNodeBase
(
const
EventLoop
&
loop
,
Own
<
PromiseNode
>&&
dependency
)
:
loop
(
loop
),
dependency
(
kj
::
mv
(
dependency
))
{}
bool
TransformPromiseNodeBase
::
onReady
(
EventLoop
::
Event
&
event
)
noexcept
{
return
dependency
->
onReady
(
event
);
}
void
TransformPromiseNodeBase
::
get
(
ExceptionOrValue
&
output
)
noexcept
{
KJ_IF_MAYBE
(
exception
,
kj
::
runCatchingExceptions
([
&
]()
{
getImpl
(
output
);
}))
{
output
.
addException
(
kj
::
mv
(
*
exception
));
}
}
Maybe
<
const
EventLoop
&>
TransformPromiseNodeBase
::
getSafeEventLoop
()
noexcept
{
return
loop
;
}
ChainPromiseNode
::
ChainPromiseNode
(
const
EventLoop
&
loop
,
Own
<
PromiseNode
>
inner
)
:
Event
(
loop
),
state
(
PRE_STEP1
),
inner
(
kj
::
mv
(
inner
))
{
KJ_IREQUIRE
(
this
->
inner
->
isSafeEventLoop
(
loop
));
arm
();
}
ChainPromiseNode
::~
ChainPromiseNode
()
noexcept
(
false
)
{
disarm
();
}
bool
ChainPromiseNode
::
onReady
(
EventLoop
::
Event
&
event
)
noexcept
{
switch
(
state
)
{
case
PRE_STEP1
:
case
STEP1
:
KJ_IREQUIRE
(
onReadyEvent
==
nullptr
,
"onReady() can only be called once."
);
onReadyEvent
=
&
event
;
return
false
;
case
STEP2
:
return
inner
->
onReady
(
event
);
}
KJ_UNREACHABLE
;
}
void
ChainPromiseNode
::
get
(
ExceptionOrValue
&
output
)
noexcept
{
KJ_IREQUIRE
(
state
==
STEP2
);
return
inner
->
get
(
output
);
}
Maybe
<
const
EventLoop
&>
ChainPromiseNode
::
getSafeEventLoop
()
noexcept
{
return
getEventLoop
();
}
void
ChainPromiseNode
::
fire
()
{
if
(
state
==
PRE_STEP1
&&
!
inner
->
onReady
(
*
this
))
{
state
=
STEP1
;
return
;
}
KJ_IREQUIRE
(
state
!=
STEP2
);
static_assert
(
sizeof
(
Promise
<
int
>
)
==
sizeof
(
PromiseBase
),
"This code assumes Promise<T> does not add any new members to PromiseBase."
);
ExceptionOr
<
PromiseBase
>
intermediate
;
inner
->
get
(
intermediate
);
KJ_IF_MAYBE
(
exception
,
kj
::
runCatchingExceptions
([
this
]()
{
inner
=
nullptr
;
}))
{
intermediate
.
addException
(
kj
::
mv
(
*
exception
));
}
KJ_IF_MAYBE
(
exception
,
intermediate
.
exception
)
{
// There is an exception. If there is also a value, delete it.
kj
::
runCatchingExceptions
([
&
,
this
]()
{
intermediate
.
value
=
nullptr
;
});
// Now set step2 to a rejected promise.
inner
=
heap
<
ImmediateBrokenPromiseNode
>
(
kj
::
mv
(
*
exception
));
}
else
KJ_IF_MAYBE
(
value
,
intermediate
.
value
)
{
// There is a value and no exception. The value is itself a promise. Adopt it as our
// step2.
inner
=
kj
::
mv
(
value
->
node
);
}
else
{
// We can only get here if inner->get() returned neither an exception nor a
// value, which never actually happens.
KJ_IASSERT
(
false
,
"Inner node returned empty value."
);
}
state
=
STEP2
;
if
(
onReadyEvent
!=
nullptr
)
{
if
(
inner
->
onReady
(
*
onReadyEvent
))
{
onReadyEvent
->
arm
();
}
}
}
CrossThreadPromiseNodeBase
::
CrossThreadPromiseNodeBase
(
const
EventLoop
&
loop
,
Own
<
PromiseNode
>&&
dependent
,
ExceptionOrValue
&
resultRef
)
:
Event
(
loop
),
dependent
(
kj
::
mv
(
dependent
)),
resultRef
(
resultRef
)
{
KJ_IREQUIRE
(
this
->
dependent
->
isSafeEventLoop
(
loop
));
// The constructor may be called from any thread, so before we can even call onReady() we need
// to switch threads.
arm
();
}
CrossThreadPromiseNodeBase
::~
CrossThreadPromiseNodeBase
()
noexcept
(
false
)
{
disarm
();
}
bool
CrossThreadPromiseNodeBase
::
onReady
(
EventLoop
::
Event
&
event
)
noexcept
{
return
PromiseNode
::
atomicOnReady
(
onReadyEvent
,
event
);
}
Maybe
<
const
EventLoop
&>
CrossThreadPromiseNodeBase
::
getSafeEventLoop
()
noexcept
{
return
nullptr
;
}
void
CrossThreadPromiseNodeBase
::
fire
()
{
if
(
!
isWaiting
&&
!
this
->
dependent
->
onReady
(
*
this
))
{
isWaiting
=
true
;
}
else
{
dependent
->
get
(
resultRef
);
KJ_IF_MAYBE
(
exception
,
kj
::
runCatchingExceptions
([
this
]()
{
auto
deleteMe
=
kj
::
mv
(
dependent
);
}))
{
resultRef
.
addException
(
kj
::
mv
(
*
exception
));
}
// If onReadyEvent is null, set it to _kJ_ALREADY_READY. Otherwise, arm it.
PromiseNode
::
atomicReady
(
onReadyEvent
);
}
}
bool
AdapterPromiseNodeBase
::
onReady
(
EventLoop
::
Event
&
event
)
noexcept
{
return
PromiseNode
::
atomicOnReady
(
onReadyEvent
,
event
);
}
Maybe
<
const
EventLoop
&>
AdapterPromiseNodeBase
::
getSafeEventLoop
()
noexcept
{
// We're careful to be thread-safe so any thread is OK.
return
nullptr
;
}
}
// namespace _ (private)
}
// namespace kj
c++/src/kj/async.h
View file @
c0750662
This diff is collapsed.
Click to expand it.
c++/src/kj/debug.h
View file @
c0750662
...
...
@@ -149,12 +149,12 @@ namespace kj {
#define _kJ_NONNULL(nature, value, ...) \
(*({ \
auto result = ::kj::_::readMaybe(value); \
if (KJ_UNLIKELY(!result)) { \
auto
_kj_
result = ::kj::_::readMaybe(value); \
if (KJ_UNLIKELY(!
_kj_
result)) { \
::kj::_::Debug::Fault(__FILE__, __LINE__, ::kj::Exception::Nature::nature, 0, \
#value " != nullptr", #__VA_ARGS__, ##__VA_ARGS__).fatal(); \
} \
result; \
_kj_
result; \
}))
#define KJ_ASSERT_NONNULL(value, ...) _kJ_NONNULL(LOCAL_BUG, value, ##__VA_ARGS__)
#define KJ_REQUIRE_NONNULL(value, ...) _kJ_NONNULL(PRECONDITION, value, ##__VA_ARGS__)
...
...
c++/src/kj/memory.h
View file @
c0750662
...
...
@@ -126,6 +126,11 @@ public:
return
*
this
;
}
inline
Own
&
operator
=
(
decltype
(
nullptr
))
{
dispose
();
return
*
this
;
}
inline
T
*
operator
->
()
{
return
ptr
;
}
inline
const
T
*
operator
->
()
const
{
return
ptr
;
}
inline
T
&
operator
*
()
{
return
*
ptr
;
}
...
...
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