| 1 | /* |
| 2 | Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file |
| 3 | |
| 4 | This file is part of libzmq, the ZeroMQ core engine in C++. |
| 5 | |
| 6 | libzmq is free software; you can redistribute it and/or modify it under |
| 7 | the terms of the GNU Lesser General Public License (LGPL) as published |
| 8 | by the Free Software Foundation; either version 3 of the License, or |
| 9 | (at your option) any later version. |
| 10 | |
| 11 | As a special exception, the Contributors give you permission to link |
| 12 | this library with independent modules to produce an executable, |
| 13 | regardless of the license terms of these independent modules, and to |
| 14 | copy and distribute the resulting executable under terms of your choice, |
| 15 | provided that you also meet, for each linked independent module, the |
| 16 | terms and conditions of the license of that module. An independent |
| 17 | module is a module which is not derived from or based on this library. |
| 18 | If you modify this library, you must extend this exception to your |
| 19 | version of the library. |
| 20 | |
| 21 | libzmq is distributed in the hope that it will be useful, but WITHOUT |
| 22 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 23 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
| 24 | License for more details. |
| 25 | |
| 26 | You should have received a copy of the GNU Lesser General Public License |
| 27 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 28 | */ |
| 29 | |
| 30 | #include <limits> |
| 31 | #include "testutil.hpp" |
| 32 | #include "testutil_unity.hpp" |
| 33 | |
| 34 | SETUP_TEARDOWN_TESTCONTEXT |
| 35 | |
| 36 | #define WAIT_FOR_BACKGROUND_THREAD_INSPECTION (0) |
| 37 | |
| 38 | #ifdef ZMQ_HAVE_LINUX |
| 39 | #include <sys/time.h> |
| 40 | #include <sys/resource.h> |
| 41 | #include <unistd.h> // for sleep() |
| 42 | #include <sched.h> |
| 43 | |
| 44 | #define TEST_POLICY \ |
| 45 | (SCHED_OTHER) // NOTE: SCHED_OTHER is the default Linux scheduler |
| 46 | |
| 47 | bool is_allowed_to_raise_priority () |
| 48 | { |
| 49 | // NOTE1: if setrlimit() fails with EPERM, this means that current user has not enough permissions. |
| 50 | // NOTE2: even for privileged users (e.g., root) getrlimit() would usually return 0 as nice limit; the only way to |
| 51 | // discover if the user is able to increase the nice value is to actually try to change the rlimit: |
| 52 | struct rlimit rlim; |
| 53 | rlim.rlim_cur = 40; |
| 54 | rlim.rlim_max = 40; |
| 55 | if (setrlimit (RLIMIT_NICE, &rlim) == 0) { |
| 56 | // rlim_cur == 40 means that this process is allowed to set a nice value of -20 |
| 57 | if (WAIT_FOR_BACKGROUND_THREAD_INSPECTION) |
| 58 | printf ("This process has enough permissions to raise ZMQ " |
| 59 | "background thread priority!\n" ); |
| 60 | return true; |
| 61 | } |
| 62 | |
| 63 | if (WAIT_FOR_BACKGROUND_THREAD_INSPECTION) |
| 64 | printf ("This process has NOT enough permissions to raise ZMQ " |
| 65 | "background thread priority.\n" ); |
| 66 | return false; |
| 67 | } |
| 68 | |
| 69 | #else |
| 70 | |
| 71 | #define TEST_POLICY (0) |
| 72 | |
| 73 | bool is_allowed_to_raise_priority () |
| 74 | { |
| 75 | return false; |
| 76 | } |
| 77 | |
| 78 | #endif |
| 79 | |
| 80 | |
| 81 | void test_ctx_thread_opts () |
| 82 | { |
| 83 | // verify that setting negative values (e.g., default values) fail: |
| 84 | TEST_ASSERT_FAILURE_ERRNO ( |
| 85 | EINVAL, zmq_ctx_set (get_test_context (), ZMQ_THREAD_SCHED_POLICY, |
| 86 | ZMQ_THREAD_SCHED_POLICY_DFLT)); |
| 87 | TEST_ASSERT_FAILURE_ERRNO (EINVAL, zmq_ctx_set (get_test_context (), |
| 88 | ZMQ_THREAD_PRIORITY, |
| 89 | ZMQ_THREAD_PRIORITY_DFLT)); |
| 90 | |
| 91 | |
| 92 | // test scheduling policy: |
| 93 | |
| 94 | // set context options that alter the background thread CPU scheduling/affinity settings; |
| 95 | // as of ZMQ 4.2.3 this has an effect only on POSIX systems (nothing happens on Windows, but still it should return success): |
| 96 | TEST_ASSERT_SUCCESS_ERRNO ( |
| 97 | zmq_ctx_set (get_test_context (), ZMQ_THREAD_SCHED_POLICY, TEST_POLICY)); |
| 98 | TEST_ASSERT_EQUAL_INT ( |
| 99 | TEST_POLICY, zmq_ctx_get (get_test_context (), ZMQ_THREAD_SCHED_POLICY)); |
| 100 | |
| 101 | // test priority: |
| 102 | |
| 103 | // in theory SCHED_OTHER supports only the static priority 0 but quoting the docs |
| 104 | // http://man7.org/linux/man-pages/man7/sched.7.html |
| 105 | // "The thread to run is chosen from the static priority 0 list based on |
| 106 | // a dynamic priority that is determined only inside this list. The |
| 107 | // dynamic priority is based on the nice value [...] |
| 108 | // The nice value can be modified using nice(2), setpriority(2), or sched_setattr(2)." |
| 109 | // ZMQ will internally use nice(2) to set the nice value when using SCHED_OTHER. |
| 110 | // However changing the nice value of a process requires appropriate permissions... |
| 111 | // check that the current effective user is able to do that: |
| 112 | if (is_allowed_to_raise_priority ()) { |
| 113 | TEST_ASSERT_SUCCESS_ERRNO (zmq_ctx_set ( |
| 114 | get_test_context (), ZMQ_THREAD_PRIORITY, |
| 115 | 1 /* any positive value different than the default will be ok */)); |
| 116 | } |
| 117 | |
| 118 | |
| 119 | // test affinity: |
| 120 | |
| 121 | // this should result in background threads being placed only on the |
| 122 | // first CPU available on this system; try experimenting with other values |
| 123 | // (e.g., 5 to use CPU index 5) and use "top -H" or "taskset -pc" to see the result |
| 124 | |
| 125 | int cpus_add[] = {0, 1}; |
| 126 | for (unsigned int idx = 0; idx < sizeof (cpus_add) / sizeof (cpus_add[0]); |
| 127 | idx++) { |
| 128 | TEST_ASSERT_SUCCESS_ERRNO (zmq_ctx_set ( |
| 129 | get_test_context (), ZMQ_THREAD_AFFINITY_CPU_ADD, cpus_add[idx])); |
| 130 | } |
| 131 | |
| 132 | // you can also remove CPUs from list of affinities: |
| 133 | int cpus_remove[] = {1}; |
| 134 | for (unsigned int idx = 0; |
| 135 | idx < sizeof (cpus_remove) / sizeof (cpus_remove[0]); idx++) { |
| 136 | TEST_ASSERT_SUCCESS_ERRNO (zmq_ctx_set (get_test_context (), |
| 137 | ZMQ_THREAD_AFFINITY_CPU_REMOVE, |
| 138 | cpus_remove[idx])); |
| 139 | } |
| 140 | |
| 141 | |
| 142 | // test INTEGER thread name prefix: |
| 143 | |
| 144 | TEST_ASSERT_SUCCESS_ERRNO ( |
| 145 | zmq_ctx_set (get_test_context (), ZMQ_THREAD_NAME_PREFIX, 1234)); |
| 146 | TEST_ASSERT_EQUAL_INT ( |
| 147 | 1234, zmq_ctx_get (get_test_context (), ZMQ_THREAD_NAME_PREFIX)); |
| 148 | |
| 149 | #ifdef ZMQ_BUILD_DRAFT_API |
| 150 | // test STRING thread name prefix: |
| 151 | |
| 152 | const char prefix[] = "MyPrefix9012345" ; // max len is 16 chars |
| 153 | |
| 154 | TEST_ASSERT_SUCCESS_ERRNO ( |
| 155 | zmq_ctx_set_ext (get_test_context (), ZMQ_THREAD_NAME_PREFIX, prefix, |
| 156 | sizeof (prefix) / sizeof (char))); |
| 157 | |
| 158 | char buf[16]; |
| 159 | size_t buflen = sizeof (buf) / sizeof (char); |
| 160 | zmq_ctx_get_ext (get_test_context (), ZMQ_THREAD_NAME_PREFIX, buf, &buflen); |
| 161 | TEST_ASSERT_EQUAL_STRING (prefix, buf); |
| 162 | #endif |
| 163 | } |
| 164 | |
| 165 | void test_ctx_zero_copy () |
| 166 | { |
| 167 | #ifdef ZMQ_ZERO_COPY_RECV |
| 168 | int zero_copy; |
| 169 | // Default value is 1. |
| 170 | zero_copy = zmq_ctx_get (get_test_context (), ZMQ_ZERO_COPY_RECV); |
| 171 | TEST_ASSERT_EQUAL_INT (1, zero_copy); |
| 172 | |
| 173 | // Test we can set it to 0. |
| 174 | TEST_ASSERT_SUCCESS_ERRNO ( |
| 175 | zmq_ctx_set (get_test_context (), ZMQ_ZERO_COPY_RECV, 0)); |
| 176 | zero_copy = zmq_ctx_get (get_test_context (), ZMQ_ZERO_COPY_RECV); |
| 177 | TEST_ASSERT_EQUAL_INT (0, zero_copy); |
| 178 | |
| 179 | // Create a TCP socket pair using the context and test that messages can be |
| 180 | // received. Note that inproc sockets cannot be used for this test. |
| 181 | void *pull = zmq_socket (get_test_context (), ZMQ_PULL); |
| 182 | char endpoint[MAX_SOCKET_STRING]; |
| 183 | bind_loopback_ipv4 (pull, endpoint, sizeof endpoint); |
| 184 | |
| 185 | void *push = zmq_socket (get_test_context (), ZMQ_PUSH); |
| 186 | TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (push, endpoint)); |
| 187 | |
| 188 | const char *small_str = "abcd" ; |
| 189 | const char *large_str = |
| 190 | "01234567890123456789012345678901234567890123456789" ; |
| 191 | |
| 192 | send_string_expect_success (push, small_str, 0); |
| 193 | send_string_expect_success (push, large_str, 0); |
| 194 | |
| 195 | recv_string_expect_success (pull, small_str, 0); |
| 196 | recv_string_expect_success (pull, large_str, 0); |
| 197 | |
| 198 | // Clean up. |
| 199 | TEST_ASSERT_SUCCESS_ERRNO (zmq_close (push)); |
| 200 | TEST_ASSERT_SUCCESS_ERRNO (zmq_close (pull)); |
| 201 | TEST_ASSERT_SUCCESS_ERRNO ( |
| 202 | zmq_ctx_set (get_test_context (), ZMQ_ZERO_COPY_RECV, 1)); |
| 203 | TEST_ASSERT_EQUAL_INT ( |
| 204 | 1, zmq_ctx_get (get_test_context (), ZMQ_ZERO_COPY_RECV)); |
| 205 | #endif |
| 206 | } |
| 207 | |
| 208 | void test_ctx_option_max_sockets () |
| 209 | { |
| 210 | TEST_ASSERT_EQUAL_INT (ZMQ_MAX_SOCKETS_DFLT, |
| 211 | zmq_ctx_get (get_test_context (), ZMQ_MAX_SOCKETS)); |
| 212 | } |
| 213 | |
| 214 | void test_ctx_option_socket_limit () |
| 215 | { |
| 216 | #if defined(ZMQ_USE_SELECT) |
| 217 | TEST_ASSERT_EQUAL_INT (FD_SETSIZE - 1, zmq_ctx_get (ctx, ZMQ_SOCKET_LIMIT)); |
| 218 | #elif defined(ZMQ_USE_POLL) || defined(ZMQ_USE_EPOLL) \ |
| 219 | || defined(ZMQ_USE_DEVPOLL) || defined(ZMQ_USE_KQUEUE) |
| 220 | TEST_ASSERT_EQUAL_INT (65535, zmq_ctx_get (ctx, ZMQ_SOCKET_LIMIT)); |
| 221 | #endif |
| 222 | } |
| 223 | |
| 224 | void test_ctx_option_io_threads () |
| 225 | { |
| 226 | TEST_ASSERT_EQUAL_INT (ZMQ_IO_THREADS_DFLT, |
| 227 | zmq_ctx_get (get_test_context (), ZMQ_IO_THREADS)); |
| 228 | } |
| 229 | |
| 230 | void test_ctx_option_ipv6 () |
| 231 | { |
| 232 | TEST_ASSERT_EQUAL_INT (0, zmq_ctx_get (get_test_context (), ZMQ_IPV6)); |
| 233 | } |
| 234 | |
| 235 | void test_ctx_option_msg_t_size () |
| 236 | { |
| 237 | #if defined(ZMQ_MSG_T_SIZE) |
| 238 | TEST_ASSERT_EQUAL_INT (sizeof (zmq_msg_t), |
| 239 | zmq_ctx_get (get_test_context (), ZMQ_MSG_T_SIZE)); |
| 240 | #endif |
| 241 | } |
| 242 | |
| 243 | void test_ctx_option_ipv6_set () |
| 244 | { |
| 245 | TEST_ASSERT_SUCCESS_ERRNO ( |
| 246 | zmq_ctx_set (get_test_context (), ZMQ_IPV6, true)); |
| 247 | TEST_ASSERT_EQUAL_INT (1, zmq_ctx_get (get_test_context (), ZMQ_IPV6)); |
| 248 | } |
| 249 | |
| 250 | void test_ctx_option_blocky () |
| 251 | { |
| 252 | TEST_ASSERT_SUCCESS_ERRNO ( |
| 253 | zmq_ctx_set (get_test_context (), ZMQ_IPV6, true)); |
| 254 | |
| 255 | void *router = test_context_socket (ZMQ_ROUTER); |
| 256 | int value; |
| 257 | size_t optsize = sizeof (int); |
| 258 | TEST_ASSERT_SUCCESS_ERRNO ( |
| 259 | zmq_getsockopt (router, ZMQ_IPV6, &value, &optsize)); |
| 260 | TEST_ASSERT_EQUAL_INT (1, value); |
| 261 | TEST_ASSERT_SUCCESS_ERRNO ( |
| 262 | zmq_getsockopt (router, ZMQ_LINGER, &value, &optsize)); |
| 263 | TEST_ASSERT_EQUAL_INT (-1, value); |
| 264 | test_context_socket_close (router); |
| 265 | |
| 266 | #if WAIT_FOR_BACKGROUND_THREAD_INSPECTION |
| 267 | // this is useful when you want to use an external tool (like top or taskset) to view |
| 268 | // properties of the background threads |
| 269 | printf ("Sleeping for 100sec. You can now use 'top -H -p $(pgrep -f " |
| 270 | "test_ctx_options)' and 'taskset -pc <ZMQ background thread PID>' " |
| 271 | "to view ZMQ background thread properties.\n" ); |
| 272 | sleep (100); |
| 273 | #endif |
| 274 | |
| 275 | TEST_ASSERT_SUCCESS_ERRNO ( |
| 276 | zmq_ctx_set (get_test_context (), ZMQ_BLOCKY, false)); |
| 277 | TEST_ASSERT_EQUAL_INT (0, TEST_ASSERT_SUCCESS_ERRNO ((zmq_ctx_get ( |
| 278 | get_test_context (), ZMQ_BLOCKY)))); |
| 279 | router = test_context_socket (ZMQ_ROUTER); |
| 280 | TEST_ASSERT_SUCCESS_ERRNO ( |
| 281 | zmq_getsockopt (router, ZMQ_LINGER, &value, &optsize)); |
| 282 | TEST_ASSERT_EQUAL_INT (0, value); |
| 283 | test_context_socket_close (router); |
| 284 | } |
| 285 | |
| 286 | int main (void) |
| 287 | { |
| 288 | setup_test_environment (); |
| 289 | |
| 290 | UNITY_BEGIN (); |
| 291 | RUN_TEST (test_ctx_option_max_sockets); |
| 292 | RUN_TEST (test_ctx_option_socket_limit); |
| 293 | RUN_TEST (test_ctx_option_io_threads); |
| 294 | RUN_TEST (test_ctx_option_ipv6); |
| 295 | RUN_TEST (test_ctx_option_msg_t_size); |
| 296 | RUN_TEST (test_ctx_option_ipv6_set); |
| 297 | RUN_TEST (test_ctx_thread_opts); |
| 298 | RUN_TEST (test_ctx_zero_copy); |
| 299 | RUN_TEST (test_ctx_option_blocky); |
| 300 | return UNITY_END (); |
| 301 | } |
| 302 | |