Re: [systemd-devel] [PATCH 1/3] shared: add generic IPC barrier

2014-07-14 Thread Tom Gundersen
On Sun, Jul 13, 2014 at 5:37 PM, David Herrmann dh.herrm...@gmail.com wrote:
 Hi

 On Sun, Jul 13, 2014 at 1:30 PM, Tom Gundersen t...@jklm.no wrote:
 Couple of random nitpicks below.

 On Sun, Jul 13, 2014 at 12:37 PM, David Herrmann dh.herrm...@gmail.com 
 wrote:
 The Barrier object is a simple inter-process barrier implementation. It
 allows placing synchronization points and waiting for the other side to
 reach it. Additionally, it has an abortion-mechanism as second-layer
 synchronization to send abortion-events asynchronously to the other side.

 The API is usually used to synchronize processes during fork(). However,
 it can be extended to pass state through execve() so you could synchronize
 beyond execve().

 Usually, it's used like this (error-handling replaced by assert() for
 simplicity):

 Barrier b;

 r = barrier_init(b);
 assert_se(r = 0);

 pid = fork();
 assert_se(pid = 0);
 if (pid == 0) {
 barrier_set_role(b, BARRIER_CHILD);

 ...do child post-setup...
 if (CHILD_SETUP_FAILED)
exit(1);
 ...child setup done...

 barrier_place(b);
 if (!barrier_sync(b)) {
 /* parent setup failed */
 exit(1);
 }

 barrier_destroy(b); /* redundant as execve() and exit() imply 
 this */

 /* parent  child setup successful */
 execve(...);
 }

 barrier_set_role(b, BARRIER_PARENT);

 ...do parent post-setup...
 if (PARENT_SETUP_FAILED) {
 barrier_abort(b);  /* send abortion event */
 barrier_wait_abortion(b);  /* wait for child to abort (exit() 
 implies abortion) */
 barrier_destroy(b);
...bail out...
 }
 ...parent setup done...

 barrier_place(b);
 if (!barrier_sync(b)) {
 ...child setup failed... ;
 barrier_destroy(b);
 ...bail out...
 }

 barrier_destroy(b);

 ...child setup successfull...

 This is the most basic API. Using barrier_place() to place barriers and
 barrier_sync() to perform a full synchronization between both processes.
 barrier_abort() places an abortion barrier which superceeds any other
 barriers, exit() (or barrier_destroy()) places an abortion-barrier that
 queues behind existing barriers (thus *not* replacing existing barriers
 unlike barrier_abort()).

 This example uses hard-synchronization with wait_abortion(), sync() and
 friends. These are all optional. Barriers are highly dynamic and can be
 used for one-way synchronization or even no synchronization at all
 (postponing it for later). The sync() call performs a full two-way
 synchronization.

 The API is documented and should be fairly self-explanatory. A test-suite
 shows some special semantics regarding abortion, wait_next() and exit().

 Internally, barriers use two eventfds and a pipe. The pipe is used to
 detect exit()s of the remote side as eventfds do not allow that. The
 eventfds are used to place barriers, one for each side. Barriers itself
 are numbered, but the numbers are reused once both sides reached the same
 barrier, thus you cannot address barriers by the index. Moreover, the
 numbering is implicit and we only store a counter. This makes the
 implementation itself very lightweight, which is probably negligible
 considering that we need 3 FDs for a barrier..

 Last but not least: This barrier implementation is quite heavy. It's
 definitely not meant for fast IPC synchronization. However, it's very easy
 to use. And given the *HUGE* overhead of fork(), the barrier-overhead
 should be negligible.
 ---
  .gitignore  |   1 +
  Makefile.am |   9 +
  src/shared/barrier.c| 442 
 ++
  src/shared/barrier.h|  97 ++
  src/test/test-barrier.c | 460 
 
  5 files changed, 1009 insertions(+)
  create mode 100644 src/shared/barrier.c
  create mode 100644 src/shared/barrier.h
  create mode 100644 src/test/test-barrier.c

 diff --git a/.gitignore b/.gitignore
 index 31cd8f8..5289f0e 100644
 --- a/.gitignore
 +++ b/.gitignore
 @@ -123,6 +123,7 @@
  /tags
  /test-architecture
  /test-async
 +/test-barrier
  /test-boot-timestamp
  /test-bus-chat
  /test-bus-cleanup
 diff --git a/Makefile.am b/Makefile.am
 index 2b1484f..039a83e 100644
 --- a/Makefile.am
 +++ b/Makefile.am
 @@ -828,6 +828,8 @@ libsystemd_shared_la_SOURCES = \
 src/shared/login-shared.h \
 src/shared/ring.c \
 src/shared/ring.h \
 +   src/shared/barrier.c \
 +   src/shared/barrier.h \
 src/shared/async.c \
 src/shared/async.h \
 src/shared/eventfd-util.c \
 @@ -1238,6 +1240,7 @@ tests += \
 test-ellipsize \
 test-util \
 test-ring \
 +   test-barrier \
 test-tmpfiles \
 test-namespace \
 test-date 

[systemd-devel] [PATCH 1/3] shared: add generic IPC barrier

2014-07-13 Thread David Herrmann
The Barrier object is a simple inter-process barrier implementation. It
allows placing synchronization points and waiting for the other side to
reach it. Additionally, it has an abortion-mechanism as second-layer
synchronization to send abortion-events asynchronously to the other side.

The API is usually used to synchronize processes during fork(). However,
it can be extended to pass state through execve() so you could synchronize
beyond execve().

Usually, it's used like this (error-handling replaced by assert() for
simplicity):

Barrier b;

r = barrier_init(b);
assert_se(r = 0);

pid = fork();
assert_se(pid = 0);
if (pid == 0) {
barrier_set_role(b, BARRIER_CHILD);

...do child post-setup...
if (CHILD_SETUP_FAILED)
   exit(1);
...child setup done...

barrier_place(b);
if (!barrier_sync(b)) {
/* parent setup failed */
exit(1);
}

barrier_destroy(b); /* redundant as execve() and exit() imply this 
*/

/* parent  child setup successful */
execve(...);
}

barrier_set_role(b, BARRIER_PARENT);

...do parent post-setup...
if (PARENT_SETUP_FAILED) {
barrier_abort(b);  /* send abortion event */
barrier_wait_abortion(b);  /* wait for child to abort (exit() 
implies abortion) */
barrier_destroy(b);
   ...bail out...
}
...parent setup done...

barrier_place(b);
if (!barrier_sync(b)) {
...child setup failed... ;
barrier_destroy(b);
...bail out...
}

barrier_destroy(b);

...child setup successfull...

This is the most basic API. Using barrier_place() to place barriers and
barrier_sync() to perform a full synchronization between both processes.
barrier_abort() places an abortion barrier which superceeds any other
barriers, exit() (or barrier_destroy()) places an abortion-barrier that
queues behind existing barriers (thus *not* replacing existing barriers
unlike barrier_abort()).

This example uses hard-synchronization with wait_abortion(), sync() and
friends. These are all optional. Barriers are highly dynamic and can be
used for one-way synchronization or even no synchronization at all
(postponing it for later). The sync() call performs a full two-way
synchronization.

The API is documented and should be fairly self-explanatory. A test-suite
shows some special semantics regarding abortion, wait_next() and exit().

Internally, barriers use two eventfds and a pipe. The pipe is used to
detect exit()s of the remote side as eventfds do not allow that. The
eventfds are used to place barriers, one for each side. Barriers itself
are numbered, but the numbers are reused once both sides reached the same
barrier, thus you cannot address barriers by the index. Moreover, the
numbering is implicit and we only store a counter. This makes the
implementation itself very lightweight, which is probably negligible
considering that we need 3 FDs for a barrier..

Last but not least: This barrier implementation is quite heavy. It's
definitely not meant for fast IPC synchronization. However, it's very easy
to use. And given the *HUGE* overhead of fork(), the barrier-overhead
should be negligible.
---
 .gitignore  |   1 +
 Makefile.am |   9 +
 src/shared/barrier.c| 442 ++
 src/shared/barrier.h|  97 ++
 src/test/test-barrier.c | 460 
 5 files changed, 1009 insertions(+)
 create mode 100644 src/shared/barrier.c
 create mode 100644 src/shared/barrier.h
 create mode 100644 src/test/test-barrier.c

diff --git a/.gitignore b/.gitignore
index 31cd8f8..5289f0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,6 +123,7 @@
 /tags
 /test-architecture
 /test-async
+/test-barrier
 /test-boot-timestamp
 /test-bus-chat
 /test-bus-cleanup
diff --git a/Makefile.am b/Makefile.am
index 2b1484f..039a83e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -828,6 +828,8 @@ libsystemd_shared_la_SOURCES = \
src/shared/login-shared.h \
src/shared/ring.c \
src/shared/ring.h \
+   src/shared/barrier.c \
+   src/shared/barrier.h \
src/shared/async.c \
src/shared/async.h \
src/shared/eventfd-util.c \
@@ -1238,6 +1240,7 @@ tests += \
test-ellipsize \
test-util \
test-ring \
+   test-barrier \
test-tmpfiles \
test-namespace \
test-date \
@@ -1408,6 +1411,12 @@ test_ring_SOURCES = \
 test_ring_LDADD = \
libsystemd-core.la
 
+test_barrier_SOURCES = \
+   src/test/test-barrier.c
+
+test_barrier_LDADD = \
+   libsystemd-core.la
+
 test_tmpfiles_SOURCES = \
src/test/test-tmpfiles.c
 
diff --git a/src/shared/barrier.c b/src/shared/barrier.c
new file mode 100644
index 000..e7b4ead
---