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
c50dd602
Commit
c50dd602
authored
Dec 06, 2014
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use edge-triggered epoll on Linux.
parent
50b9a529
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
121 additions
and
89 deletions
+121
-89
async-io.c++
c++/src/kj/async-io.c++
+11
-12
async-unix-test.c++
c++/src/kj/async-unix-test.c++
+52
-21
async-unix.c++
c++/src/kj/async-unix.c++
+0
-0
async-unix.h
c++/src/kj/async-unix.h
+58
-56
No files found.
c++/src/kj/async-io.c++
View file @
c50dd602
...
...
@@ -110,8 +110,7 @@ class AsyncStreamFd: public OwnedFileDescriptor, public AsyncIoStream {
public
:
AsyncStreamFd
(
UnixEventPort
&
eventPort
,
int
fd
,
uint
flags
)
:
OwnedFileDescriptor
(
fd
,
flags
),
readObserver
(
eventPort
,
fd
),
writeObserver
(
eventPort
,
fd
)
{}
observer
(
eventPort
,
fd
,
UnixEventPort
::
FdObserver
::
OBSERVE_READ_WRITE
)
{}
virtual
~
AsyncStreamFd
()
noexcept
(
false
)
{}
Promise
<
size_t
>
read
(
void
*
buffer
,
size_t
minBytes
,
size_t
maxBytes
)
override
{
...
...
@@ -157,7 +156,7 @@ public:
buffer
=
reinterpret_cast
<
const
byte
*>
(
buffer
)
+
n
;
size
-=
n
;
return
writeO
bserver
.
whenBecomesWritable
().
then
([
=
]()
{
return
o
bserver
.
whenBecomesWritable
().
then
([
=
]()
{
return
write
(
buffer
,
size
);
});
}
...
...
@@ -192,7 +191,7 @@ public:
if
(
pollResult
==
0
)
{
// Not ready yet. We can safely use the edge-triggered observer.
return
readO
bserver
.
whenBecomesReadable
();
return
o
bserver
.
whenBecomesReadable
();
}
else
{
// Ready now.
return
kj
::
READY_NOW
;
...
...
@@ -200,8 +199,7 @@ public:
}
private
:
UnixEventPort
::
ReadObserver
readObserver
;
UnixEventPort
::
WriteObserver
writeObserver
;
UnixEventPort
::
FdObserver
observer
;
Promise
<
size_t
>
tryReadInternal
(
void
*
buffer
,
size_t
minBytes
,
size_t
maxBytes
,
size_t
alreadyRead
)
{
...
...
@@ -226,7 +224,7 @@ private:
if
(
n
<
0
)
{
// Read would block.
return
readO
bserver
.
whenBecomesReadable
().
then
([
=
]()
{
return
o
bserver
.
whenBecomesReadable
().
then
([
=
]()
{
return
tryReadInternal
(
buffer
,
minBytes
,
maxBytes
,
alreadyRead
);
});
}
else
if
(
n
==
0
)
{
...
...
@@ -243,7 +241,7 @@ private:
maxBytes
-=
n
;
alreadyRead
+=
n
;
KJ_IF_MAYBE
(
atEnd
,
readO
bserver
.
atEndHint
())
{
KJ_IF_MAYBE
(
atEnd
,
o
bserver
.
atEndHint
())
{
if
(
*
atEnd
)
{
// We've already received an indication that the next read() will return EOF, so there's
// nothing to wait for.
...
...
@@ -256,7 +254,7 @@ private:
// that even if it was received since then, whenBecomesReadable() will catch that. So,
// let's go ahead and skip calling read() here and instead go straight to waiting for
// more input.
return
readO
bserver
.
whenBecomesReadable
().
then
([
=
]()
{
return
o
bserver
.
whenBecomesReadable
().
then
([
=
]()
{
return
tryReadInternal
(
buffer
,
minBytes
,
maxBytes
,
alreadyRead
);
});
}
...
...
@@ -304,7 +302,7 @@ private:
if
(
n
<
firstPiece
.
size
())
{
// Only part of the first piece was consumed. Wait for buffer space and then write again.
firstPiece
=
firstPiece
.
slice
(
n
,
firstPiece
.
size
());
return
writeO
bserver
.
whenBecomesWritable
().
then
([
=
]()
{
return
o
bserver
.
whenBecomesWritable
().
then
([
=
]()
{
return
writeInternal
(
firstPiece
,
morePieces
);
});
}
else
if
(
morePieces
.
size
()
==
0
)
{
...
...
@@ -731,7 +729,8 @@ Promise<Array<SocketAddress>> SocketAddress::lookupHost(
class
FdConnectionReceiver
final
:
public
ConnectionReceiver
,
public
OwnedFileDescriptor
{
public
:
FdConnectionReceiver
(
UnixEventPort
&
eventPort
,
int
fd
,
uint
flags
)
:
OwnedFileDescriptor
(
fd
,
flags
),
eventPort
(
eventPort
),
observer
(
eventPort
,
fd
)
{}
:
OwnedFileDescriptor
(
fd
,
flags
),
eventPort
(
eventPort
),
observer
(
eventPort
,
fd
,
UnixEventPort
::
FdObserver
::
OBSERVE_READ
)
{}
Promise
<
Own
<
AsyncIoStream
>>
accept
()
override
{
int
newFd
;
...
...
@@ -785,7 +784,7 @@ public:
public
:
UnixEventPort
&
eventPort
;
UnixEventPort
::
Rea
dObserver
observer
;
UnixEventPort
::
F
dObserver
observer
;
};
class
TimerImpl
final
:
public
Timer
{
...
...
c++/src/kj/async-unix-test.c++
View file @
c50dd602
...
...
@@ -86,6 +86,27 @@ TEST_F(AsyncUnixTest, SignalWithValue) {
EXPECT_SI_CODE
(
SI_QUEUE
,
info
.
si_code
);
EXPECT_EQ
(
123
,
info
.
si_value
.
sival_int
);
}
TEST_F
(
AsyncUnixTest
,
SignalWithPointerValue
)
{
// This tests that if we use sigqueue() to attach a value to the signal, that value is received
// correctly. Note that this only works on platforms that support real-time signals -- even
// though the signal we're sending is SIGURG, the sigqueue() system call is introduced by RT
// signals. Hence this test won't run on e.g. Mac OSX.
UnixEventPort
port
;
EventLoop
loop
(
port
);
WaitScope
waitScope
(
loop
);
union
sigval
value
;
memset
(
&
value
,
0
,
sizeof
(
value
));
value
.
sival_ptr
=
&
port
;
sigqueue
(
getpid
(),
SIGURG
,
value
);
siginfo_t
info
=
port
.
onSignal
(
SIGURG
).
wait
(
waitScope
);
EXPECT_EQ
(
SIGURG
,
info
.
si_signo
);
EXPECT_SI_CODE
(
SI_QUEUE
,
info
.
si_code
);
EXPECT_EQ
(
&
port
,
info
.
si_value
.
sival_ptr
);
}
#endif
TEST_F
(
AsyncUnixTest
,
SignalsMultiListen
)
{
...
...
@@ -207,14 +228,14 @@ TEST_F(AsyncUnixTest, ReadObserver) {
KJ_SYSCALL
(
pipe
(
pipefds
));
kj
::
AutoCloseFd
infd
(
pipefds
[
0
]),
outfd
(
pipefds
[
1
]);
UnixEventPort
::
ReadObserver
readObserver
(
port
,
infd
);
UnixEventPort
::
FdObserver
observer
(
port
,
infd
,
UnixEventPort
::
FdObserver
::
OBSERVE_READ
);
KJ_SYSCALL
(
write
(
outfd
,
"foo"
,
3
));
readO
bserver
.
whenBecomesReadable
().
wait
(
waitScope
);
o
bserver
.
whenBecomesReadable
().
wait
(
waitScope
);
#if __linux__ // platform known to support POLLRDHUP
EXPECT_FALSE
(
KJ_ASSERT_NONNULL
(
readO
bserver
.
atEndHint
()));
EXPECT_FALSE
(
KJ_ASSERT_NONNULL
(
o
bserver
.
atEndHint
()));
char
buffer
[
4096
];
ssize_t
n
;
...
...
@@ -224,9 +245,9 @@ TEST_F(AsyncUnixTest, ReadObserver) {
KJ_SYSCALL
(
write
(
outfd
,
"bar"
,
3
));
outfd
=
nullptr
;
readO
bserver
.
whenBecomesReadable
().
wait
(
waitScope
);
o
bserver
.
whenBecomesReadable
().
wait
(
waitScope
);
EXPECT_TRUE
(
KJ_ASSERT_NONNULL
(
readO
bserver
.
atEndHint
()));
EXPECT_TRUE
(
KJ_ASSERT_NONNULL
(
o
bserver
.
atEndHint
()));
#endif
}
...
...
@@ -237,10 +258,12 @@ TEST_F(AsyncUnixTest, ReadObserverMultiListen) {
int
bogusPipefds
[
2
];
KJ_SYSCALL
(
pipe
(
bogusPipefds
));
UnixEventPort
::
ReadObserver
bogusReadObserver
(
port
,
bogusPipefds
[
0
]);
KJ_DEFER
({
close
(
bogusPipefds
[
1
]);
close
(
bogusPipefds
[
0
]);
});
bogusReadObserver
.
whenBecomesReadable
().
then
([]()
{
UnixEventPort
::
FdObserver
bogusObserver
(
port
,
bogusPipefds
[
0
],
UnixEventPort
::
FdObserver
::
OBSERVE_READ
);
bogusObserver
.
whenBecomesReadable
().
then
([]()
{
ADD_FAILURE
()
<<
"Received wrong poll."
;
}).
detach
([](
kj
::
Exception
&&
exception
)
{
ADD_FAILURE
()
<<
kj
::
str
(
exception
).
cStr
();
...
...
@@ -250,10 +273,11 @@ TEST_F(AsyncUnixTest, ReadObserverMultiListen) {
KJ_SYSCALL
(
pipe
(
pipefds
));
KJ_DEFER
({
close
(
pipefds
[
1
]);
close
(
pipefds
[
0
]);
});
UnixEventPort
::
ReadObserver
readObserver
(
port
,
pipefds
[
0
]);
UnixEventPort
::
FdObserver
observer
(
port
,
pipefds
[
0
],
UnixEventPort
::
FdObserver
::
OBSERVE_READ
);
KJ_SYSCALL
(
write
(
pipefds
[
1
],
"foo"
,
3
));
readO
bserver
.
whenBecomesReadable
().
wait
(
waitScope
);
o
bserver
.
whenBecomesReadable
().
wait
(
waitScope
);
}
TEST_F
(
AsyncUnixTest
,
ReadObserverMultiReceive
)
{
...
...
@@ -265,18 +289,22 @@ TEST_F(AsyncUnixTest, ReadObserverMultiReceive) {
KJ_SYSCALL
(
pipe
(
pipefds
));
KJ_DEFER
({
close
(
pipefds
[
1
]);
close
(
pipefds
[
0
]);
});
UnixEventPort
::
ReadObserver
readObserver
(
port
,
pipefds
[
0
]);
UnixEventPort
::
FdObserver
observer
(
port
,
pipefds
[
0
],
UnixEventPort
::
FdObserver
::
OBSERVE_READ
);
KJ_SYSCALL
(
write
(
pipefds
[
1
],
"foo"
,
3
));
int
pipefds2
[
2
];
KJ_SYSCALL
(
pipe
(
pipefds2
));
KJ_DEFER
({
close
(
pipefds2
[
1
]);
close
(
pipefds2
[
0
]);
});
UnixEventPort
::
ReadObserver
readObserver2
(
port
,
pipefds2
[
0
]);
UnixEventPort
::
FdObserver
observer2
(
port
,
pipefds2
[
0
],
UnixEventPort
::
FdObserver
::
OBSERVE_READ
);
KJ_SYSCALL
(
write
(
pipefds2
[
1
],
"bar"
,
3
));
readObserver
.
whenBecomesReadable
().
wait
(
waitScope
);
readObserver2
.
whenBecomesReadable
().
wait
(
waitScope
);
auto
promise1
=
observer
.
whenBecomesReadable
();
auto
promise2
=
observer2
.
whenBecomesReadable
();
promise1
.
wait
(
waitScope
);
promise2
.
wait
(
waitScope
);
}
TEST_F
(
AsyncUnixTest
,
ReadObserverAsync
)
{
...
...
@@ -288,7 +316,8 @@ TEST_F(AsyncUnixTest, ReadObserverAsync) {
int
pipefds
[
2
];
KJ_SYSCALL
(
pipe
(
pipefds
));
KJ_DEFER
({
close
(
pipefds
[
1
]);
close
(
pipefds
[
0
]);
});
UnixEventPort
::
ReadObserver
readObserver
(
port
,
pipefds
[
0
]);
UnixEventPort
::
FdObserver
observer
(
port
,
pipefds
[
0
],
UnixEventPort
::
FdObserver
::
OBSERVE_READ
);
Thread
thread
([
&
]()
{
delay
();
...
...
@@ -296,7 +325,7 @@ TEST_F(AsyncUnixTest, ReadObserverAsync) {
});
// Wait for the event in this thread.
readO
bserver
.
whenBecomesReadable
().
wait
(
waitScope
);
o
bserver
.
whenBecomesReadable
().
wait
(
waitScope
);
}
TEST_F
(
AsyncUnixTest
,
ReadObserverNoWait
)
{
...
...
@@ -309,18 +338,20 @@ TEST_F(AsyncUnixTest, ReadObserverNoWait) {
int
pipefds
[
2
];
KJ_SYSCALL
(
pipe
(
pipefds
));
KJ_DEFER
({
close
(
pipefds
[
1
]);
close
(
pipefds
[
0
]);
});
UnixEventPort
::
ReadObserver
readObserver
(
port
,
pipefds
[
0
]);
UnixEventPort
::
FdObserver
observer
(
port
,
pipefds
[
0
],
UnixEventPort
::
FdObserver
::
OBSERVE_READ
);
int
pipefds2
[
2
];
KJ_SYSCALL
(
pipe
(
pipefds2
));
KJ_DEFER
({
close
(
pipefds2
[
1
]);
close
(
pipefds2
[
0
]);
});
UnixEventPort
::
ReadObserver
readObserver2
(
port
,
pipefds2
[
0
]);
UnixEventPort
::
FdObserver
observer2
(
port
,
pipefds2
[
0
],
UnixEventPort
::
FdObserver
::
OBSERVE_READ
);
int
receivedCount
=
0
;
readO
bserver
.
whenBecomesReadable
().
then
([
&
]()
{
o
bserver
.
whenBecomesReadable
().
then
([
&
]()
{
receivedCount
++
;
}).
detach
([](
Exception
&&
e
)
{
ADD_FAILURE
()
<<
str
(
e
).
cStr
();
});
readO
bserver2
.
whenBecomesReadable
().
then
([
&
]()
{
o
bserver2
.
whenBecomesReadable
().
then
([
&
]()
{
receivedCount
++
;
}).
detach
([](
Exception
&&
e
)
{
ADD_FAILURE
()
<<
str
(
e
).
cStr
();
});
...
...
@@ -360,7 +391,7 @@ TEST_F(AsyncUnixTest, WriteObserver) {
kj
::
AutoCloseFd
infd
(
pipefds
[
0
]),
outfd
(
pipefds
[
1
]);
setNonblocking
(
outfd
);
UnixEventPort
::
WriteObserver
writeObserver
(
port
,
outfd
);
UnixEventPort
::
FdObserver
observer
(
port
,
outfd
,
UnixEventPort
::
FdObserver
::
OBSERVE_WRITE
);
// Fill buffer.
ssize_t
n
;
...
...
@@ -369,7 +400,7 @@ TEST_F(AsyncUnixTest, WriteObserver) {
}
while
(
n
>=
0
);
bool
writable
=
false
;
auto
promise
=
writeO
bserver
.
whenBecomesWritable
()
auto
promise
=
o
bserver
.
whenBecomesWritable
()
.
then
([
&
]()
{
writable
=
true
;
}).
eagerlyEvaluate
(
nullptr
);
loop
.
run
();
...
...
c++/src/kj/async-unix.c++
View file @
c50dd602
This diff is collapsed.
Click to expand it.
c++/src/kj/async-unix.h
View file @
c50dd602
...
...
@@ -29,8 +29,12 @@
#include "async.h"
#include "time.h"
#include "vector.h"
#include "io.h"
#include <signal.h>
#include <pthread.h>
#if __linux__ && !defined(KJ_USE_EPOLL)
#define KJ_USE_EPOLL 1
#endif
namespace
kj
{
...
...
@@ -51,9 +55,8 @@ public:
UnixEventPort
();
~
UnixEventPort
()
noexcept
(
false
);
class
ReadObserver
;
class
WriteObserver
;
// Classes that watch an fd for readability or writability. See definitions below.
class
FdObserver
;
// Class that watches an fd for readability or writability. See definition below.
Promise
<
siginfo_t
>
onSignal
(
int
signum
);
// When the given signal is delivered to this thread, return the corresponding siginfo_t.
...
...
@@ -91,34 +94,43 @@ public:
void
poll
()
override
;
private
:
#if KJ_USE_EPOLL
// TODO(soon): epoll implementation
#else
class
PollPromiseAdapter
;
class
SignalPromiseAdapter
;
class
TimerPromiseAdapter
;
class
PollContext
;
struct
TimerSet
;
// Defined in source file to avoid STL include.
class
TimerPromiseAdapter
;
class
SignalPromiseAdapter
;
PollPromiseAdapter
*
pollHead
=
nullptr
;
PollPromiseAdapter
**
pollTail
=
&
pollHead
;
SignalPromiseAdapter
*
signalHead
=
nullptr
;
SignalPromiseAdapter
**
signalTail
=
&
signalHead
;
Own
<
TimerSet
>
timers
;
TimePoint
frozenSteadyTime
;
void
gotSignal
(
const
siginfo_t
&
siginfo
);
SignalPromiseAdapter
*
signalHead
=
nullptr
;
SignalPromiseAdapter
**
signalTail
=
&
signalHead
;
TimePoint
currentSteadyTime
();
void
processTimers
();
void
gotSignal
(
const
siginfo_t
&
siginfo
);
friend
class
TimerPromiseAdapter
;
#if KJ_USE_EPOLL
AutoCloseFd
epollFd
;
AutoCloseFd
signalFd
;
AutoCloseFd
eventFd
;
// Used for cross-thread wakeups.
sigset_t
signalFdSigset
;
// Signal mask as currently set on the signalFd. Tracked so we can detect whether or not it
// needs updating.
void
doEpollWait
(
int
timeout
);
#else
class
PollContext
;
FdObserver
*
observersHead
=
nullptr
;
FdObserver
**
observersTail
=
&
observersHead
;
#endif
};
class
UnixEventPort
::
Rea
dObserver
{
// Object which watches a file descriptor to determine when it is readable.
class
UnixEventPort
::
F
dObserver
{
// Object which watches a file descriptor to determine when it is readable
or writable
.
//
// For listen sockets, "readable" means that there is a connection to accept(). For everything
// else, it means that read() (or recv()) will return data.
...
...
@@ -133,11 +145,19 @@ class UnixEventPort::ReadObserver {
// behavior. If at all possible, use the higher-level AsyncInputStream interface instead.
public
:
ReadObserver
(
UnixEventPort
&
eventPort
,
int
fd
)
:
eventPort
(
eventPort
),
fd
(
fd
)
{}
enum
Flags
{
OBSERVE_READ
=
1
,
OBSERVE_WRITE
=
2
,
OBSERVE_READ_WRITE
=
OBSERVE_READ
|
OBSERVE_WRITE
};
FdObserver
(
UnixEventPort
&
eventPort
,
int
fd
,
uint
flags
);
// Begin watching the given file descriptor for readability. Only one ReadObserver may exist
// for a given file descriptor at a time.
KJ_DISALLOW_COPY
(
ReadObserver
);
~
FdObserver
()
noexcept
(
false
);
KJ_DISALLOW_COPY
(
FdObserver
);
Promise
<
void
>
whenBecomesReadable
();
// Resolves the next time the file descriptor transitions from having no data to read to having
...
...
@@ -177,37 +197,6 @@ public:
//
// This hint may be useful as an optimization to avoid an unnecessary system call.
private
:
UnixEventPort
&
eventPort
;
int
fd
;
#if KJ_USE_EPOLL
// TODO(soon): epoll implementation
Own
<
PromiseFulfiller
<
void
>>
fulfiller
;
// Replaced each time `whenBecomesReadable()` is called.
#else
#endif
Maybe
<
bool
>
atEnd
;
friend
class
UnixEventPort
;
};
class
UnixEventPort
::
WriteObserver
{
// Object which watches a file descriptor to determine when it is writable.
//
// WARNING: The exact behavior of this class differs across systems, since event interfaces
// vary wildly. Be sure to read the documentation carefully and avoid depending on unspecified
// behavior. If at all possible, use the higher-level AsyncOutputStream interface instead.
public
:
WriteObserver
(
UnixEventPort
&
eventPort
,
int
fd
)
:
eventPort
(
eventPort
),
fd
(
fd
)
{}
// Begin watching the given file descriptor for writability. Only one WriteObserver may exist
// for a given file descriptor at a time.
KJ_DISALLOW_COPY
(
WriteObserver
);
Promise
<
void
>
whenBecomesWritable
();
// Resolves the next time the file descriptor transitions from having no space available in the
// write buffer to having some space available.
...
...
@@ -232,11 +221,24 @@ public:
private
:
UnixEventPort
&
eventPort
;
int
fd
;
uint
flags
;
#if KJ_USE_EPOLL
Own
<
PromiseFulfiller
<
void
>>
fulfiller
;
// Replaced each time `whenBecomesReadable()` is called.
#else
kj
::
Maybe
<
Own
<
PromiseFulfiller
<
void
>>>
readFulfiller
;
kj
::
Maybe
<
Own
<
PromiseFulfiller
<
void
>>>
writeFulfiller
;
// Replaced each time `whenBecomesReadable()` or `whenBecomesWritable()` is called. Reverted to
// null every time an event is fired.
Maybe
<
bool
>
atEnd
;
void
fire
(
short
events
);
#if !KJ_USE_EPOLL
FdObserver
*
next
;
FdObserver
**
prev
;
// Linked list of observers which currently have a non-null readFulfiller or writeFulfiller.
// If `prev` is null then the observer is not currently in the list.
short
getEventMask
();
#endif
friend
class
UnixEventPort
;
...
...
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