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 | |