Merge lp:~thomas-voss/process-cpp/add-cross-process-sync-primitive into lp:process-cpp

Proposed by Thomas Voß
Status: Merged
Approved by: Timo Jyrinki
Approved revision: 30
Merged at revision: 28
Proposed branch: lp:~thomas-voss/process-cpp/add-cross-process-sync-primitive
Merge into: lp:process-cpp
Diff against target: 421 lines (+319/-3)
10 files modified
debian/libprocess-cpp0.symbols.amd64 (+15/-0)
debian/libprocess-cpp0.symbols.armhf (+15/-0)
debian/libprocess-cpp0.symbols.i386 (+15/-0)
doc/Doxyfile.in (+1/-1)
include/core/testing/cross_process_sync.h (+99/-0)
src/CMakeLists.txt (+1/-0)
src/core/posix/exec.cpp (+0/-2)
src/core/testing/cross_process_sync.cpp (+99/-0)
tests/CMakeLists.txt (+14/-0)
tests/cross_process_sync_test.cpp (+60/-0)
To merge this branch: bzr merge lp:~thomas-voss/process-cpp/add-cross-process-sync-primitive
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Ubuntu Phablet Team Pending
Review via email: mp+199934@code.launchpad.net

Commit message

Added a cross-process synchronization primitive.

Description of the change

Added a cross-process synchronization primitive.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
29. By Thomas Voß

Adjust symbol files.
Adjust doxygen setup to limit dot threads to 1, addressing armhf building issues.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
30. By Thomas Voß

Remove unnecessary std::cout's in core::posix::exec.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/libprocess-cpp0.symbols.amd64'
2--- debian/libprocess-cpp0.symbols.amd64 2013-12-02 14:46:29 +0000
3+++ debian/libprocess-cpp0.symbols.amd64 2014-01-08 10:16:42 +0000
4@@ -1,4 +1,19 @@
5 libprocess-cpp.so.0 libprocess-cpp0 #MINVER#
6+ (c++)"core::testing::CrossProcessSync::try_signal_ready_for(std::chrono::duration<long, std::ratio<1l, 1000l> > const&)@Base" 0replaceme
7+ (c++)"core::testing::CrossProcessSync::wait_for_signal_ready_for(std::chrono::duration<long, std::ratio<1l, 1000l> > const&)@Base" 0replaceme
8+ (c++)"core::testing::CrossProcessSync::Error::Timeout::~Timeout()@Base" 0replaceme
9+ (c++)"core::testing::CrossProcessSync::Error::Timeout::~Timeout()@Base" 0replaceme
10+ (c++)"core::testing::CrossProcessSync::Error::Timeout::~Timeout()@Base" 0replaceme
11+ (c++)"core::testing::CrossProcessSync::CrossProcessSync(core::testing::CrossProcessSync const&)@Base" 0replaceme
12+ (c++)"core::testing::CrossProcessSync::CrossProcessSync()@Base" 0replaceme
13+ (c++)"core::testing::CrossProcessSync::CrossProcessSync(core::testing::CrossProcessSync const&)@Base" 0replaceme
14+ (c++)"core::testing::CrossProcessSync::CrossProcessSync()@Base" 0replaceme
15+ (c++)"core::testing::CrossProcessSync::~CrossProcessSync()@Base" 0replaceme
16+ (c++)"core::testing::CrossProcessSync::~CrossProcessSync()@Base" 0replaceme
17+ (c++)"core::testing::CrossProcessSync::operator=(core::testing::CrossProcessSync const&)@Base" 0replaceme
18+ (c++)"typeinfo for core::testing::CrossProcessSync::Error::Timeout@Base" 0replaceme
19+ (c++)"typeinfo name for core::testing::CrossProcessSync::Error::Timeout@Base" 0replaceme
20+ (c++)"vtable for core::testing::CrossProcessSync::Error::Timeout@Base" 0replaceme
21 (c++)"core::posix::Signalable::send_signal(core::posix::Signal, std::error_code&)@Base" 0replaceme
22 (c++)"core::posix::Signalable::send_signal_or_throw(core::posix::Signal)@Base" 0replaceme
23 (c++)"core::posix::ChildProcess::invalid()@Base" 0replaceme
24
25=== modified file 'debian/libprocess-cpp0.symbols.armhf'
26--- debian/libprocess-cpp0.symbols.armhf 2013-12-02 14:46:29 +0000
27+++ debian/libprocess-cpp0.symbols.armhf 2014-01-08 10:16:42 +0000
28@@ -1,4 +1,19 @@
29 libprocess-cpp.so.0 libprocess-cpp0 #MINVER#
30+ (c++)"core::testing::CrossProcessSync::try_signal_ready_for(std::chrono::duration<long long, std::ratio<1ll, 1000ll> > const&)@Base" 0replaceme
31+ (c++)"core::testing::CrossProcessSync::wait_for_signal_ready_for(std::chrono::duration<long long, std::ratio<1ll, 1000ll> > const&)@Base" 0replaceme
32+ (c++)"core::testing::CrossProcessSync::Error::Timeout::~Timeout()@Base" 0replaceme
33+ (c++)"core::testing::CrossProcessSync::Error::Timeout::~Timeout()@Base" 0replaceme
34+ (c++)"core::testing::CrossProcessSync::Error::Timeout::~Timeout()@Base" 0replaceme
35+ (c++)"core::testing::CrossProcessSync::CrossProcessSync(core::testing::CrossProcessSync const&)@Base" 0replaceme
36+ (c++)"core::testing::CrossProcessSync::CrossProcessSync()@Base" 0replaceme
37+ (c++)"core::testing::CrossProcessSync::CrossProcessSync(core::testing::CrossProcessSync const&)@Base" 0replaceme
38+ (c++)"core::testing::CrossProcessSync::CrossProcessSync()@Base" 0replaceme
39+ (c++)"core::testing::CrossProcessSync::~CrossProcessSync()@Base" 0replaceme
40+ (c++)"core::testing::CrossProcessSync::~CrossProcessSync()@Base" 0replaceme
41+ (c++)"core::testing::CrossProcessSync::operator=(core::testing::CrossProcessSync const&)@Base" 0replaceme
42+ (c++)"typeinfo for core::testing::CrossProcessSync::Error::Timeout@Base" 0replaceme
43+ (c++)"typeinfo name for core::testing::CrossProcessSync::Error::Timeout@Base" 0replaceme
44+ (c++)"vtable for core::testing::CrossProcessSync::Error::Timeout@Base" 0replaceme
45 (c++)"core::posix::Signalable::send_signal(core::posix::Signal, std::error_code&)@Base" 0replaceme
46 (c++)"core::posix::Signalable::send_signal_or_throw(core::posix::Signal)@Base" 0replaceme
47 (c++)"core::posix::ChildProcess::invalid()@Base" 0replaceme
48
49=== modified file 'debian/libprocess-cpp0.symbols.i386'
50--- debian/libprocess-cpp0.symbols.i386 2013-12-02 14:46:29 +0000
51+++ debian/libprocess-cpp0.symbols.i386 2014-01-08 10:16:42 +0000
52@@ -1,4 +1,19 @@
53 libprocess-cpp.so.0 libprocess-cpp0 #MINVER#
54+ (c++)"core::testing::CrossProcessSync::try_signal_ready_for(std::chrono::duration<long long, std::ratio<1ll, 1000ll> > const&)@Base" 0replaceme
55+ (c++)"core::testing::CrossProcessSync::wait_for_signal_ready_for(std::chrono::duration<long long, std::ratio<1ll, 1000ll> > const&)@Base" 0replaceme
56+ (c++)"core::testing::CrossProcessSync::Error::Timeout::~Timeout()@Base" 0replaceme
57+ (c++)"core::testing::CrossProcessSync::Error::Timeout::~Timeout()@Base" 0replaceme
58+ (c++)"core::testing::CrossProcessSync::Error::Timeout::~Timeout()@Base" 0replaceme
59+ (c++)"core::testing::CrossProcessSync::CrossProcessSync(core::testing::CrossProcessSync const&)@Base" 0replaceme
60+ (c++)"core::testing::CrossProcessSync::CrossProcessSync()@Base" 0replaceme
61+ (c++)"core::testing::CrossProcessSync::CrossProcessSync(core::testing::CrossProcessSync const&)@Base" 0replaceme
62+ (c++)"core::testing::CrossProcessSync::CrossProcessSync()@Base" 0replaceme
63+ (c++)"core::testing::CrossProcessSync::~CrossProcessSync()@Base" 0replaceme
64+ (c++)"core::testing::CrossProcessSync::~CrossProcessSync()@Base" 0replaceme
65+ (c++)"core::testing::CrossProcessSync::operator=(core::testing::CrossProcessSync const&)@Base" 0replaceme
66+ (c++)"typeinfo for core::testing::CrossProcessSync::Error::Timeout@Base" 0replaceme
67+ (c++)"typeinfo name for core::testing::CrossProcessSync::Error::Timeout@Base" 0replaceme
68+ (c++)"vtable for core::testing::CrossProcessSync::Error::Timeout@Base" 0replaceme
69 (c++)"core::posix::Signalable::send_signal(core::posix::Signal, std::error_code&)@Base" 0replaceme
70 (c++)"core::posix::Signalable::send_signal_or_throw(core::posix::Signal)@Base" 0replaceme
71 (c++)"core::posix::ChildProcess::invalid()@Base" 0replaceme
72
73=== modified file 'doc/Doxyfile.in'
74--- doc/Doxyfile.in 2013-11-12 10:01:42 +0000
75+++ doc/Doxyfile.in 2014-01-08 10:16:42 +0000
76@@ -1684,7 +1684,7 @@
77 # explicitly to a value larger than 0 to get control over the balance
78 # between CPU load and processing speed.
79
80-DOT_NUM_THREADS = 0
81+DOT_NUM_THREADS = 1
82
83 # By default doxygen will use the Helvetica font for all dot files that
84 # doxygen generates. When you want a differently looking font you can specify
85
86=== added file 'include/core/testing/cross_process_sync.h'
87--- include/core/testing/cross_process_sync.h 1970-01-01 00:00:00 +0000
88+++ include/core/testing/cross_process_sync.h 2014-01-08 10:16:42 +0000
89@@ -0,0 +1,99 @@
90+/*
91+ * Copyright © 2013 Canonical Ltd.
92+ *
93+ * This program is free software: you can redistribute it and/or modify it
94+ * under the terms of the GNU General Public License version 3,
95+ * as published by the Free Software Foundation.
96+ *
97+ * This program is distributed in the hope that it will be useful,
98+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
99+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
100+ * GNU General Public License for more details.
101+ *
102+ * You should have received a copy of the GNU General Public License
103+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
104+ *
105+ * Authored by: Thomas Voss <thomas.voss@canonical.com>
106+ */
107+
108+#ifndef CORE_TESTING_CROSS_PROCESS_SYNC_H_
109+#define CORE_TESTING_CROSS_PROCESS_SYNC_H_
110+
111+#include <core/posix/visibility.h>
112+
113+#include <cstdint>
114+
115+#include <chrono>
116+#include <stdexcept>
117+
118+namespace core
119+{
120+namespace testing
121+{
122+/**
123+ * @brief A cross-process synchronization primitive that supports simple wait-condition-like scenarios.
124+ */
125+class CORE_POSIX_DLL_PUBLIC CrossProcessSync
126+{
127+ public:
128+ struct Error
129+ {
130+ Error() = delete;
131+ ~Error() = delete;
132+
133+ /**
134+ * @brief Thrown if any of the *_for functions times out.
135+ */
136+ struct Timeout : public std::runtime_error
137+ {
138+ Timeout() : std::runtime_error("Timeout while waiting for event to happen.")
139+ {
140+ }
141+ };
142+ };
143+
144+ /**
145+ * @brief Constructs a new sync element.
146+ */
147+ CrossProcessSync();
148+
149+ /**
150+ * @brief Copy c'tor, duping the underlying fds.
151+ * @param rhs The instance to copy.
152+ */
153+ CrossProcessSync(const CrossProcessSync& rhs);
154+
155+ /**
156+ * @brief Closes the underlying fds.
157+ */
158+ ~CrossProcessSync() noexcept;
159+
160+ /**
161+ * @brief operator =, dup's the underlying fds.
162+ * @param rhs The instance to assign from.
163+ * @return A mutable reference to this instance.
164+ */
165+ CrossProcessSync& operator=(const CrossProcessSync& rhs);
166+
167+ /**
168+ * @brief Try to signal the other side that we are ready for at most duration milliseconds.
169+ * @throw Error::Timeout in case of a timeout.
170+ * @throw std::system_error for problems with the underlying pipe.
171+ */
172+ void try_signal_ready_for(const std::chrono::milliseconds& duration);
173+
174+ /**
175+ * @brief Wait for the other sides to signal readiness for at most duration milliseconds.
176+ * @return The number of ready signals that have been collected since creation.
177+ * @throw Error::Timeout in case of a timeout.
178+ * @throw std::system_error for problems with the underlying pipe.
179+ */
180+ std::uint32_t wait_for_signal_ready_for(const std::chrono::milliseconds& duration);
181+
182+ private:
183+ int fds[2]; ///< The cross-process pipe.
184+ std::uint32_t counter; ///< Counts the number of times the sync has been signalled.
185+};
186+}
187+}
188+#endif // CORE_TESTING_CROSS_PROCESS_SYNC_H_
189
190=== modified file 'src/CMakeLists.txt'
191--- src/CMakeLists.txt 2013-11-29 07:38:07 +0000
192+++ src/CMakeLists.txt 2014-01-08 10:16:42 +0000
193@@ -32,6 +32,7 @@
194 core/posix/linux/proc/process/oom_score_adj.cpp
195 core/posix/linux/proc/process/stat.cpp
196
197+ core/testing/cross_process_sync.cpp
198 core/testing/fork_and_run.cpp
199 )
200
201
202=== modified file 'src/core/posix/exec.cpp'
203--- src/core/posix/exec.cpp 2013-11-29 07:38:07 +0000
204+++ src/core/posix/exec.cpp 2014-01-08 10:16:42 +0000
205@@ -44,7 +44,6 @@
206 for (auto element : argv)
207 {
208 *it = ::strdup(element.c_str());
209- std::cout << *it << std::endl;
210 it++;
211 }
212 *it = nullptr;
213@@ -53,7 +52,6 @@
214 for (auto pair : env)
215 {
216 *it = ::strdup((pair.first + "=" + pair.second).c_str());
217- std::cout << *it << std::endl;
218 it++;
219 }
220 *it = nullptr;
221
222=== added file 'src/core/testing/cross_process_sync.cpp'
223--- src/core/testing/cross_process_sync.cpp 1970-01-01 00:00:00 +0000
224+++ src/core/testing/cross_process_sync.cpp 2014-01-08 10:16:42 +0000
225@@ -0,0 +1,99 @@
226+/*
227+ * Copyright © 2013 Canonical Ltd.
228+ *
229+ * This program is free software: you can redistribute it and/or modify
230+ * it under the terms of the GNU General Public License version 3 as
231+ * published by the Free Software Foundation.
232+ *
233+ * This program is distributed in the hope that it will be useful,
234+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
235+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
236+ * GNU General Public License for more details.
237+ *
238+ * You should have received a copy of the GNU General Public License
239+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
240+ *
241+ * Authored by: Thomas Voss <thomas.voss@canonical.com>
242+ */
243+
244+#include <core/testing/cross_process_sync.h>
245+
246+#include <system_error>
247+
248+#include <poll.h>
249+#include <unistd.h>
250+
251+namespace
252+{
253+const int read_fd = 0;
254+const int write_fd = 1;
255+}
256+
257+core::testing::CrossProcessSync::CrossProcessSync() : counter(0)
258+{
259+ if (::pipe(fds) < 0)
260+ throw std::system_error(errno, std::system_category());
261+}
262+
263+core::testing::CrossProcessSync::CrossProcessSync(const CrossProcessSync& rhs) : counter(rhs.counter)
264+{
265+ fds[0] = ::dup(rhs.fds[0]);
266+ fds[1] = ::dup(rhs.fds[1]);
267+}
268+
269+core::testing::CrossProcessSync::~CrossProcessSync() noexcept
270+{
271+ ::close(fds[0]);
272+ ::close(fds[1]);
273+}
274+
275+core::testing::CrossProcessSync& core::testing::CrossProcessSync::operator=(const core::testing::CrossProcessSync& rhs)
276+{
277+ ::close(fds[0]);
278+ ::close(fds[1]);
279+ fds[0] = ::dup(rhs.fds[0]);
280+ fds[1] = ::dup(rhs.fds[1]);
281+
282+ counter = rhs.counter;
283+
284+ return *this;
285+}
286+
287+void core::testing::CrossProcessSync::try_signal_ready_for(const std::chrono::milliseconds& duration)
288+{
289+ static const short empty_revents = 0;
290+ pollfd poll_fd[1] = { { fds[write_fd], POLLOUT, empty_revents } };
291+ int rc = -1;
292+
293+ if ((rc = ::poll(poll_fd, 1, duration.count())) < 0)
294+ throw std::system_error(errno, std::system_category());
295+ else if (rc == 0)
296+ throw Error::Timeout{};
297+
298+ static const std::uint32_t value = 1;
299+ if (sizeof(value) != write(fds[write_fd], std::addressof(value), sizeof(value)))
300+ throw std::system_error(errno, std::system_category());
301+}
302+
303+std::uint32_t core::testing::CrossProcessSync::wait_for_signal_ready_for(const std::chrono::milliseconds& duration)
304+{
305+ static const short empty_revents = 0;
306+ pollfd poll_fd[1] = { { fds[read_fd], POLLIN, empty_revents } };
307+ int rc = -1;
308+
309+ if ((rc = ::poll(poll_fd, 1, duration.count())) < 0)
310+ throw std::system_error(errno, std::system_category());
311+ else if (rc == 0)
312+ throw Error::Timeout{};
313+
314+ std::uint32_t value = 0;
315+ if (sizeof(value) != read(fds[read_fd], std::addressof(value), sizeof(value)))
316+ throw std::system_error(errno, std::system_category());
317+
318+ if (value != 1)
319+ throw std::system_error(errno, std::system_category());
320+
321+ counter += value;
322+
323+ return counter;
324+}
325
326=== modified file 'tests/CMakeLists.txt'
327--- tests/CMakeLists.txt 2013-11-12 11:11:26 +0000
328+++ tests/CMakeLists.txt 2014-01-08 10:16:42 +0000
329@@ -40,6 +40,11 @@
330 fork_and_run_test.cpp
331 )
332
333+add_executable(
334+ cross_process_sync_test
335+ cross_process_sync_test.cpp
336+)
337+
338 target_link_libraries(
339 posix_process_test
340
341@@ -64,6 +69,15 @@
342 ${GTEST_BOTH_LIBRARIES}
343 )
344
345+target_link_libraries(
346+ cross_process_sync_test
347+
348+ process-cpp
349+
350+ ${GTEST_BOTH_LIBRARIES}
351+)
352+
353 add_test(posix_process_test ${CMAKE_CURRENT_BINARY_DIR}/posix_process_test)
354 add_test(linux_process_test ${CMAKE_CURRENT_BINARY_DIR}/linux_process_test)
355 add_test(fork_and_run_test ${CMAKE_CURRENT_BINARY_DIR}/fork_and_run_test)
356+add_test(cross_process_sync_test ${CMAKE_CURRENT_BINARY_DIR}/cross_process_sync_test)
357
358=== added file 'tests/cross_process_sync_test.cpp'
359--- tests/cross_process_sync_test.cpp 1970-01-01 00:00:00 +0000
360+++ tests/cross_process_sync_test.cpp 2014-01-08 10:16:42 +0000
361@@ -0,0 +1,60 @@
362+/*
363+ * Copyright © 2013 Canonical Ltd.
364+ *
365+ * This program is free software: you can redistribute it and/or modify it
366+ * under the terms of the GNU Lesser General Public License version 3,
367+ * as published by the Free Software Foundation.
368+ *
369+ * This program is distributed in the hope that it will be useful,
370+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
371+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
372+ * GNU Lesser General Public License for more details.
373+ *
374+ * You should have received a copy of the GNU Lesser General Public License
375+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
376+ *
377+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
378+ */
379+
380+#include <core/testing/cross_process_sync.h>
381+
382+#include <core/testing/fork_and_run.h>
383+
384+#include <gtest/gtest.h>
385+
386+TEST(CrossProcessSync, signalling_the_sync_object_results_in_correct_count)
387+{
388+ core::testing::CrossProcessSync cps;
389+
390+ auto service = [&cps]()
391+ {
392+ for (unsigned int i = 1; i <= 50; i++)
393+ {
394+ EXPECT_NO_THROW(cps.try_signal_ready_for(std::chrono::milliseconds{500}));
395+ }
396+ return ::testing::Test::HasFatalFailure() || ::testing::Test::HasFailure() ?
397+ core::posix::exit::Status::failure : core::posix::exit::Status::success;
398+ };
399+
400+ auto client = [&cps]()
401+ {
402+ std::uint32_t counter = 0;
403+ for (unsigned int i = 1; i <= 50; i++)
404+ {
405+ EXPECT_NO_THROW(counter = cps.wait_for_signal_ready_for(std::chrono::milliseconds{500}));
406+ EXPECT_EQ(i, counter);
407+ }
408+ return ::testing::Test::HasFatalFailure() || ::testing::Test::HasFailure() ?
409+ core::posix::exit::Status::failure : core::posix::exit::Status::success;
410+ };
411+
412+ EXPECT_EQ(core::testing::ForkAndRunResult::empty, core::testing::fork_and_run(service, client));
413+}
414+
415+TEST(CrossProcessSync, timed_out_wait_on_sync_object_throws_correct_exception)
416+{
417+ core::testing::CrossProcessSync cps;
418+
419+ EXPECT_THROW(cps.wait_for_signal_ready_for(std::chrono::milliseconds{1}),
420+ core::testing::CrossProcessSync::Error::Timeout);
421+}

Subscribers

People subscribed via source and target branches