test_zmq_ppoll_signals.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MPL-2.0 */
2 
3 // author: E. G. Patrick Bos, Netherlands eScience Center, 2021
4 
5 #include "testutil.hpp"
6 #include "testutil_unity.hpp"
7 
8 #include <string.h> // memset
9 // types.h and wait.h for waitpid:
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 
13 static bool sigterm_received = false;
14 
15 void handle_sigterm (int /*signum*/)
16 {
17  sigterm_received = true;
18 }
19 
21  const char *str_,
22  int flags_)
23 {
24  const size_t len = str_ ? strlen (str_) : 0;
25  char buffer[255];
27  "recv_string_expect_success cannot be "
28  "used for strings longer than 255 "
29  "characters");
30 
31  const int rc = zmq_recv (socket_, buffer, sizeof (buffer), flags_);
32  if (rc < 0) {
33  if (errno == EAGAIN) {
34  printf ("got EAGAIN\n");
35  return;
36  } else {
38  }
39  } else {
40  TEST_ASSERT_EQUAL_INT ((int) len, rc);
41  if (str_)
43  }
44 }
45 
47 {
48 #ifdef ZMQ_HAVE_PPOLL
49  size_t len = MAX_SOCKET_STRING;
51  pid_t child_pid;
52 
53  /* Get a random TCP port first */
55  void *sb = test_context_socket (ZMQ_REP);
59 
60  do {
61  child_pid = fork ();
62  } while (child_pid == -1); // retry if fork fails
63 
64  if (child_pid > 0) { // parent
66  void *socket = test_context_socket (ZMQ_REQ);
67  // to make sure we don't hang when the child has already exited at the end, we set a receive timeout of five seconds
68  int recv_timeout = 5000;
70  socket, ZMQ_RCVTIMEO, &recv_timeout, sizeof (recv_timeout)));
72  // bind is on the master process to avoid zombie children to hold on to binds
73 
74  // first send a test message to check whether the signal mask is setup in the child process
75  send_string_expect_success (socket, "breaker breaker", 0);
76  recv_string_expect_success (socket, "one-niner", 0);
77 
78  // then send the signal
79  kill (child_pid, SIGTERM);
80 
81  // for good measure, and to make sure everything went as expected, close off with another handshake, which will trigger the second poll call on the other side
82  send_string_expect_success (socket, "breaker breaker", 0);
83  // in case the 1 second sleep was not enough on the child side, we are also fine with an EAGAIN here
84  recv_string_expect_success_or_eagain (socket, "one-niner", 0);
85 
86  // finish
89 
90  // wait for child
91  int status = 0;
92  pid_t pid;
93  do {
94  pid = waitpid (child_pid, &status, 0);
95  } while (-1 == pid
96  && EINTR == errno); // retry on interrupted system call
97 
98  if (0 != status) {
99  if (WIFEXITED (status)) {
100  printf ("exited, status=%d\n", WEXITSTATUS (status));
101  } else if (WIFSIGNALED (status)) {
102  printf ("killed by signal %d\n", WTERMSIG (status));
103  } else if (WIFSTOPPED (status)) {
104  printf ("stopped by signal %d\n", WSTOPSIG (status));
105  } else if (WIFCONTINUED (status)) {
106  printf ("continued\n");
107  }
108  }
109 
110  if (-1 == pid) {
111  printf ("waitpid returned -1, with errno %s\n", strerror (errno));
112  }
113  } else { // child
115  // set up signal mask and install handler for SIGTERM
116  sigset_t sigmask, sigmask_without_sigterm;
117  sigemptyset (&sigmask);
118  sigaddset (&sigmask, SIGTERM);
119  sigprocmask (SIG_BLOCK, &sigmask, &sigmask_without_sigterm);
120  struct sigaction sa;
121  memset (&sa, '\0', sizeof (sa));
122  sa.sa_handler = handle_sigterm;
123  TEST_ASSERT_SUCCESS_ERRNO (sigaction (SIGTERM, &sa, NULL));
124 
125  void *socket = test_context_socket (ZMQ_REP);
127 
128  zmq_pollitem_t pollitems[] = {
129  {socket, 0, ZMQ_POLLIN, 0},
130  };
131 
132  // first receive test message and send back handshake
133  recv_string_expect_success (socket, "breaker breaker", 0);
134  send_string_expect_success (socket, "one-niner", 0);
135 
136  // now start ppolling, which should exit with EINTR because of the SIGTERM
138  EINTR, zmq_ppoll (pollitems, 1, -1, &sigmask_without_sigterm));
140 
141  // poll again for the final handshake
143  zmq_ppoll (pollitems, 1, -1, &sigmask_without_sigterm));
144  TEST_ASSERT_BITS_HIGH (ZMQ_POLLIN, pollitems[0].revents);
145  // receive and send back handshake
146  recv_string_expect_success (socket, "breaker breaker", 0);
147  send_string_expect_success (socket, "one-niner", 0);
148 
149  // finish
150  // wait before closing socket, so that parent has time to receive
151  sleep (1);
152  test_context_socket_close (socket);
154  _Exit (0);
155  }
156 #else
157  TEST_IGNORE_MESSAGE ("libzmq without zmq_ppoll, ignoring test");
158 #endif // ZMQ_HAVE_PPOLL
159 }
160 
161 // We note that using zmq_poll instead of zmq_ppoll in the test above, while
162 // also not using the sigmask, will fail most of the time, because it is
163 // impossible to predict during which call the signal will be handled. Of
164 // course, every call could be surrounded with an EINTR check and a subsequent
165 // check of sigterm_received's value, but even then a race condition can occur,
166 // see the explanation given here: https://250bpm.com/blog:12/
167 
168 int main ()
169 {
170  UNITY_BEGIN ();
172  return UNITY_END ();
173 }
TEST_ASSERT_EQUAL_STRING_LEN
#define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len)
Definition: unity.h:236
NULL
NULL
Definition: test_security_zap.cpp:405
UNITY_END
return UNITY_END()
EINTR
#define EINTR
Definition: errno.hpp:7
TEST_ASSERT_TRUE
#define TEST_ASSERT_TRUE(condition)
Definition: unity.h:121
test_ppoll_signals
void test_ppoll_signals()
Definition: test_zmq_ppoll_signals.cpp:46
EAGAIN
#define EAGAIN
Definition: errno.hpp:14
RUN_TEST
#define RUN_TEST(func)
Definition: unity_internals.h:615
setup_test_context
void setup_test_context()
Definition: testutil_unity.cpp:179
errno
int errno
zmq_pollitem_t
Definition: zmq.h:487
teardown_test_context
void teardown_test_context()
Definition: testutil_unity.cpp:189
ZMQ_REQ
#define ZMQ_REQ
Definition: zmq.h:261
zmq_connect
ZMQ_EXPORT int zmq_connect(void *s_, const char *addr_)
Definition: zmq.cpp:307
main
int main()
Definition: test_zmq_ppoll_signals.cpp:168
ZMQ_POLLIN
#define ZMQ_POLLIN
Definition: zmq.h:482
zmq_ppoll
int zmq_ppoll(zmq_pollitem_t *items_, int nitems_, long timeout_, const sigset_t *sigmask_)
Definition: zmq.cpp:1355
testutil_unity.hpp
sigterm_received
static bool sigterm_received
Definition: test_zmq_ppoll_signals.cpp:13
TEST_ASSERT_LESS_OR_EQUAL_MESSAGE
#define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message)
Definition: unity.h:392
zmq_setsockopt
ZMQ_EXPORT int zmq_setsockopt(void *s_, int option_, const void *optval_, size_t optvallen_)
Definition: zmq.cpp:250
TEST_ASSERT_BITS_HIGH
#define TEST_ASSERT_BITS_HIGH(mask, actual)
Definition: unity.h:146
sb
void * sb
Definition: test_channel.cpp:8
testutil.hpp
ZMQ_REP
#define ZMQ_REP
Definition: zmq.h:262
my_endpoint
char my_endpoint[MAX_SOCKET_STRING]
Definition: test_security_curve.cpp:31
MAX_SOCKET_STRING
#define MAX_SOCKET_STRING
Definition: libzmq/tests/testutil.hpp:35
zmq_bind
ZMQ_EXPORT int zmq_bind(void *s_, const char *addr_)
Definition: zmq.cpp:299
bind_loopback
void bind_loopback(void *socket_, int ipv6_, char *my_endpoint_, size_t len_)
Definition: testutil_unity.cpp:233
buffer
Definition: buffer_processor.h:43
test_context_socket
void * test_context_socket(int type_)
Definition: testutil_unity.cpp:200
recv_string_expect_success_or_eagain
void recv_string_expect_success_or_eagain(void *socket_, const char *str_, int flags_)
Definition: test_zmq_ppoll_signals.cpp:20
TEST_ASSERT_EQUAL_INT
#define TEST_ASSERT_EQUAL_INT(expected, actual)
Definition: unity.h:128
TEST_IGNORE_MESSAGE
#define TEST_IGNORE_MESSAGE(message)
Definition: unity.h:103
send_string_expect_success
void send_string_expect_success(void *socket_, const char *str_, int flags_)
Definition: testutil_unity.cpp:94
len
int len
Definition: php/ext/google/protobuf/map.c:206
ZMQ_RCVTIMEO
#define ZMQ_RCVTIMEO
Definition: zmq.h:296
zmq_recv
ZMQ_EXPORT int zmq_recv(void *s_, void *buf_, size_t len_, int flags_)
Definition: zmq.cpp:487
recv_string_expect_success
void recv_string_expect_success(void *socket_, const char *str_, int flags_)
Definition: testutil_unity.cpp:101
UNITY_BEGIN
UNITY_BEGIN()
TEST_ASSERT_FAILURE_ERRNO
#define TEST_ASSERT_FAILURE_ERRNO(error_code, expr)
Definition: testutil_unity.hpp:95
strerror
char * strerror(int errno)
test_context_socket_close
void * test_context_socket_close(void *socket_)
Definition: testutil_unity.cpp:208
handle_sigterm
void handle_sigterm(int)
Definition: test_zmq_ppoll_signals.cpp:15
TEST_ASSERT_SUCCESS_ERRNO
#define TEST_ASSERT_SUCCESS_ERRNO(expr)
Definition: proxy_thr.cpp:47


libaditof
Author(s):
autogenerated on Wed May 21 2025 02:06:59