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
a0e2a45f
Commit
a0e2a45f
authored
Nov 02, 2013
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
UnixEventLoop supports polling fds and catching signals.
parent
e7d27780
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
284 additions
and
2 deletions
+284
-2
async-unix-test.c++
c++/src/kj/async-unix-test.c++
+182
-0
async-unix.c++
c++/src/kj/async-unix.c++
+0
-0
async-unix.h
c++/src/kj/async-unix.h
+100
-0
async.h
c++/src/kj/async.h
+2
-2
No files found.
c++/src/kj/async-unix-test.c++
0 → 100644
View file @
a0e2a45f
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "async-unix.h"
#include "thread.h"
#include "debug.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <gtest/gtest.h>
namespace
kj
{
inline
void
delay
()
{
usleep
(
10000
);
}
class
DummyErrorHandler
:
public
TaskSet
::
ErrorHandler
{
public
:
void
taskFailed
(
kj
::
Exception
&&
exception
)
override
{
kj
::
throwRecoverableException
(
kj
::
mv
(
exception
));
}
};
class
AsyncUnixTest
:
public
testing
::
Test
{
public
:
static
void
SetUpTestCase
()
{
UnixEventLoop
::
captureSignal
(
SIGUSR2
);
UnixEventLoop
::
captureSignal
(
SIGIO
);
}
};
TEST_F
(
AsyncUnixTest
,
Signals
)
{
UnixEventLoop
loop
;
union
sigval
value
;
value
.
sival_int
=
123
;
sigqueue
(
getpid
(),
SIGUSR2
,
value
);
siginfo_t
info
=
loop
.
wait
(
loop
.
onSignal
(
SIGUSR2
));
EXPECT_EQ
(
SIGUSR2
,
info
.
si_signo
);
EXPECT_EQ
(
SI_QUEUE
,
info
.
si_code
);
EXPECT_EQ
(
123
,
info
.
si_value
.
sival_int
);
}
TEST_F
(
AsyncUnixTest
,
SignalsMulti
)
{
UnixEventLoop
loop
;
DummyErrorHandler
dummyHandler
;
TaskSet
tasks
(
loop
,
dummyHandler
);
tasks
.
add
(
loop
.
onSignal
(
SIGIO
).
thenInAnyThread
([](
siginfo_t
&&
)
{
ADD_FAILURE
()
<<
"Received wrong signal."
;
}));
union
sigval
value
;
value
.
sival_int
=
123
;
sigqueue
(
getpid
(),
SIGUSR2
,
value
);
siginfo_t
info
=
loop
.
wait
(
loop
.
onSignal
(
SIGUSR2
));
EXPECT_EQ
(
SIGUSR2
,
info
.
si_signo
);
EXPECT_EQ
(
SI_QUEUE
,
info
.
si_code
);
EXPECT_EQ
(
123
,
info
.
si_value
.
sival_int
);
}
TEST_F
(
AsyncUnixTest
,
SignalsAsync
)
{
// Arrange for another thread to wait on a UnixEventLoop...
auto
exitThread
=
newPromiseAndFulfiller
<
void
>
();
UnixEventLoop
unixLoop
;
Thread
thread
([
&
]()
{
unixLoop
.
wait
(
kj
::
mv
(
exitThread
.
promise
));
});
KJ_DEFER
(
exitThread
.
fulfiller
->
fulfill
());
// Arrange to catch a signal in the other thread. But we haven't sent one yet.
bool
received
=
false
;
Promise
<
void
>
promise
=
unixLoop
.
there
(
unixLoop
.
onSignal
(
SIGUSR2
),
[
&
](
siginfo_t
&&
info
)
{
received
=
true
;
EXPECT_EQ
(
SIGUSR2
,
info
.
si_signo
);
EXPECT_EQ
(
SI_QUEUE
,
info
.
si_code
);
EXPECT_EQ
(
123
,
info
.
si_value
.
sival_int
);
});
delay
();
EXPECT_FALSE
(
received
);
union
sigval
value
;
value
.
sival_int
=
123
;
sigqueue
(
getpid
(),
SIGUSR2
,
value
);
SimpleEventLoop
mainLoop
;
mainLoop
.
wait
(
kj
::
mv
(
promise
));
EXPECT_TRUE
(
received
);
}
TEST_F
(
AsyncUnixTest
,
Poll
)
{
UnixEventLoop
loop
;
int
pipefds
[
2
];
KJ_DEFER
({
close
(
pipefds
[
1
]);
close
(
pipefds
[
0
]);
});
KJ_SYSCALL
(
pipe
(
pipefds
));
KJ_SYSCALL
(
write
(
pipefds
[
1
],
"foo"
,
3
));
EXPECT_EQ
(
POLLIN
,
loop
.
wait
(
loop
.
onFdEvent
(
pipefds
[
0
],
POLLIN
|
POLLPRI
)));
}
TEST_F
(
AsyncUnixTest
,
PollMulti
)
{
UnixEventLoop
loop
;
DummyErrorHandler
dummyHandler
;
int
bogusPipefds
[
2
];
KJ_SYSCALL
(
pipe
(
bogusPipefds
));
KJ_DEFER
({
close
(
bogusPipefds
[
1
]);
close
(
bogusPipefds
[
0
]);
});
TaskSet
tasks
(
loop
,
dummyHandler
);
tasks
.
add
(
loop
.
onFdEvent
(
bogusPipefds
[
0
],
POLLIN
|
POLLPRI
).
thenInAnyThread
([](
short
s
)
{
KJ_DBG
(
s
);
ADD_FAILURE
()
<<
"Received wrong poll."
;
}));
int
pipefds
[
2
];
KJ_SYSCALL
(
pipe
(
pipefds
));
KJ_DEFER
({
close
(
pipefds
[
1
]);
close
(
pipefds
[
0
]);
});
KJ_SYSCALL
(
write
(
pipefds
[
1
],
"foo"
,
3
));
EXPECT_EQ
(
POLLIN
,
loop
.
wait
(
loop
.
onFdEvent
(
pipefds
[
0
],
POLLIN
|
POLLPRI
)));
}
TEST_F
(
AsyncUnixTest
,
PollAsync
)
{
// Arrange for another thread to wait on a UnixEventLoop...
auto
exitThread
=
newPromiseAndFulfiller
<
void
>
();
UnixEventLoop
unixLoop
;
Thread
thread
([
&
]()
{
unixLoop
.
wait
(
kj
::
mv
(
exitThread
.
promise
));
});
KJ_DEFER
(
exitThread
.
fulfiller
->
fulfill
());
// Make a pipe and wait on its read end in another thread. But don't write to it yet.
int
pipefds
[
2
];
KJ_DEFER
({
close
(
pipefds
[
1
]);
close
(
pipefds
[
0
]);
});
KJ_SYSCALL
(
pipe
(
pipefds
));
bool
received
=
false
;
Promise
<
void
>
promise
=
unixLoop
.
there
(
unixLoop
.
onFdEvent
(
pipefds
[
0
],
POLLIN
|
POLLPRI
),
[
&
](
short
events
)
{
received
=
true
;
EXPECT_EQ
(
POLLIN
,
events
);
});
delay
();
EXPECT_FALSE
(
received
);
KJ_SYSCALL
(
write
(
pipefds
[
1
],
"foo"
,
3
));
SimpleEventLoop
mainLoop
;
mainLoop
.
wait
(
kj
::
mv
(
promise
));
EXPECT_TRUE
(
received
);
}
}
// namespace kj
c++/src/kj/async-unix.c++
0 → 100644
View file @
a0e2a45f
This diff is collapsed.
Click to expand it.
c++/src/kj/async-unix.h
0 → 100644
View file @
a0e2a45f
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef KJ_ASYNC_UNIX_H_
#define KJ_ASYNC_UNIX_H_
#include "async.h"
#include "vector.h"
#include <signal.h>
#include <poll.h>
#include <pthread.h>
namespace
kj
{
class
UnixEventLoop
:
public
EventLoop
{
// An EventLoop implementation which can wait for events on file descriptors as well as signals.
// This API only makes sense on Unix.
//
// The implementation uses `poll()` or possibly a platform-specific API (e.g. epoll, kqueue).
// To also wait on signals without race conditions, the implementation may block signals until
// just before `poll()` while using a signal handler which `siglongjmp()`s back to just before
// the signal was unblocked, or it may use a nicer platform-specific API like signalfd.
//
// The implementation uses SIGUSR1. The application must avoid using this signal for its own
// purposes.
public
:
UnixEventLoop
();
~
UnixEventLoop
();
Promise
<
short
>
onFdEvent
(
int
fd
,
short
eventMask
)
const
;
// `eventMask` is a bitwise-OR of poll events (e.g. `POLLIN`, `POLLOUT`, etc.). The next time
// one or more of the given events occurs on `fd`, the set of events that occurred are returned.
//
// The result of waiting on the same FD twice at once is undefined.
Promise
<
siginfo_t
>
onSignal
(
int
signum
)
const
;
// When the given signal is delivered to this thread, return the corresponding siginfo_t.
// The signal must have been captured using `captureSignal()`.
//
// If `onSignal()` has not been called, the signal will remain blocked in this thread.
// Therefore, a signal which arrives before `onSignal()` was called will not be "missed" -- the
// next call to 'onSignal()' will receive it. Also, you can control which thread receives a
// process-wide signal by only calling `onSignal()` on that thread's event loop.
//
// The result of waiting on the same signal twice at once is undefined.
static
void
captureSignal
(
int
signum
);
// Arranges for the given signal to be captured and handled via UnixEventLoop, so that you may
// then pass it to `onSignal()`. This method is static because it registers a signal handler
// which applies process-wide. If any other threads exist in the process when `captureSignal()`
// is called, you *must* set the signal mask in those threads to block this signal, otherwise
// terrible things will happen if the signal happens to be delivered to those threads. If at
// all possible, call `captureSignal()` *before* creating threads, so that threads you create in
// the future will inherit the proper signal mask.
//
// To un-capture a signal, simply install a different signal handler and then un-block it from
// the signal mask.
protected
:
void
prepareToSleep
()
noexcept
override
;
void
sleep
()
override
;
void
wake
()
const
override
;
private
:
class
PollItem
;
class
PollPromiseAdapter
;
class
SignalItem
;
class
SignalPromiseAdapter
;
struct
Impl
;
Own
<
Impl
>
impl
;
pthread_t
waitThread
;
bool
isSleeping
=
false
;
};
}
// namespace kj
#endif // KJ_ASYNC_UNIX_H_
c++/src/kj/async.h
View file @
a0e2a45f
...
...
@@ -1481,8 +1481,8 @@ private:
template
<
typename
T
,
typename
Adapter
,
typename
...
Params
>
Promise
<
T
>
newAdaptedPromise
(
Params
&&
...
adapterConstructorParams
)
{
return
Promise
<
T
>
(
Own
<
_
::
PromiseNode
>
(
heap
<
_
::
AdapterPromiseNode
<
_
::
FixVoid
<
T
>
,
Adapter
>>
(
kj
::
fwd
<
Params
>
(
adapterConstructorParams
)...))
)
;
return
Promise
<
T
>
(
false
,
heap
<
_
::
AdapterPromiseNode
<
_
::
FixVoid
<
T
>
,
Adapter
>>
(
kj
::
fwd
<
Params
>
(
adapterConstructorParams
)...));
}
template
<
typename
T
>
...
...
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