rbczmq 1.7.4 → 1.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. checksums.yaml +5 -13
  2. data/.travis.yml +4 -1
  3. data/CHANGELOG.rdoc +13 -0
  4. data/Gemfile +8 -1
  5. data/Gemfile.lock +211 -2
  6. data/README.rdoc +7 -4
  7. data/ext/rbczmq/beacon.c +6 -3
  8. data/ext/rbczmq/context.c +117 -8
  9. data/ext/rbczmq/context.h +5 -0
  10. data/ext/rbczmq/message.c +6 -4
  11. data/ext/rbczmq/poller.c +12 -5
  12. data/ext/rbczmq/rbczmq_ext.c +2 -0
  13. data/ext/rbczmq/rbczmq_ext.h +1 -0
  14. data/ext/rbczmq/socket.c +135 -59
  15. data/ext/rbczmq/socket.h +2 -0
  16. data/ext/zeromq/CMakeLists.txt +3 -1
  17. data/ext/zeromq/NEWS +28 -3
  18. data/ext/zeromq/doc/zmq.txt +3 -3
  19. data/ext/zeromq/doc/zmq_getsockopt.txt +3 -3
  20. data/ext/zeromq/doc/zmq_msg_get.txt +2 -1
  21. data/ext/zeromq/doc/zmq_msg_more.txt +1 -1
  22. data/ext/zeromq/doc/zmq_setsockopt.txt +18 -19
  23. data/ext/zeromq/doc/zmq_socket.txt +4 -0
  24. data/ext/zeromq/include/zmq.h +14 -18
  25. data/ext/zeromq/src/clock.cpp +57 -2
  26. data/ext/zeromq/src/ctx.cpp +11 -5
  27. data/ext/zeromq/src/devpoll.cpp +5 -0
  28. data/ext/zeromq/src/devpoll.hpp +2 -0
  29. data/ext/zeromq/src/epoll.cpp +5 -0
  30. data/ext/zeromq/src/epoll.hpp +2 -0
  31. data/ext/zeromq/src/i_engine.hpp +2 -2
  32. data/ext/zeromq/src/kqueue.cpp +5 -0
  33. data/ext/zeromq/src/kqueue.hpp +2 -0
  34. data/ext/zeromq/src/pgm_receiver.cpp +2 -2
  35. data/ext/zeromq/src/pgm_receiver.hpp +2 -2
  36. data/ext/zeromq/src/pgm_sender.cpp +2 -2
  37. data/ext/zeromq/src/pgm_sender.hpp +2 -2
  38. data/ext/zeromq/src/poll.cpp +5 -0
  39. data/ext/zeromq/src/poll.hpp +2 -0
  40. data/ext/zeromq/src/router.cpp +2 -0
  41. data/ext/zeromq/src/select.cpp +5 -0
  42. data/ext/zeromq/src/select.hpp +2 -0
  43. data/ext/zeromq/src/session_base.cpp +2 -2
  44. data/ext/zeromq/src/signaler.cpp +73 -99
  45. data/ext/zeromq/src/signaler.hpp +2 -2
  46. data/ext/zeromq/src/socket_base.cpp +42 -40
  47. data/ext/zeromq/src/stream_engine.cpp +60 -58
  48. data/ext/zeromq/src/stream_engine.hpp +7 -8
  49. data/ext/zeromq/src/zmq_utils.cpp +6 -5
  50. data/ext/zeromq/tests/Makefile.am +6 -5
  51. data/ext/zeromq/tests/test_conflate.cpp +2 -5
  52. data/ext/zeromq/tests/test_ctx_destroy.cpp +1 -1
  53. data/ext/zeromq/tests/test_ctx_options.cpp +1 -1
  54. data/ext/zeromq/tests/test_immediate.cpp +1 -2
  55. data/ext/zeromq/tests/test_inproc_connect.cpp +1 -1
  56. data/ext/zeromq/tests/test_iov.cpp +1 -1
  57. data/ext/zeromq/tests/test_many_sockets.cpp +90 -0
  58. data/ext/zeromq/tests/test_monitor.cpp +3 -3
  59. data/ext/zeromq/tests/test_req_relaxed.cpp +1 -1
  60. data/ext/zeromq/tests/test_router_raw_empty.cpp +65 -0
  61. data/ext/zeromq/tests/test_spec_req.cpp +1 -1
  62. data/ext/zeromq/tests/test_stream.cpp +6 -7
  63. data/ext/zeromq/tests/test_sub_forward.cpp +1 -1
  64. data/ext/zeromq/tests/test_term_endpoint.cpp +2 -2
  65. data/ext/zeromq/tests/testutil.hpp +18 -1
  66. data/ext/zeromq/tools/Makefile.am +1 -1
  67. data/lib/zmq/socket.rb +1 -0
  68. data/lib/zmq/socket/stream.rb +44 -0
  69. data/lib/zmq/version.rb +1 -1
  70. data/test/socket/test_dealer_socket.rb +1 -1
  71. data/test/socket/test_pair_socket.rb +1 -1
  72. data/test/socket/test_pair_sockets.rb +1 -1
  73. data/test/socket/test_pub_socket.rb +1 -1
  74. data/test/socket/test_pub_sub_sockets.rb +1 -1
  75. data/test/socket/test_pull_socket.rb +1 -1
  76. data/test/socket/test_push_pull_sockets.rb +1 -1
  77. data/test/socket/test_push_socket.rb +1 -1
  78. data/test/socket/test_rep_socket.rb +1 -1
  79. data/test/socket/test_req_rep_sockets.rb +1 -1
  80. data/test/socket/test_req_socket.rb +1 -1
  81. data/test/socket/test_router_socket.rb +1 -1
  82. data/test/socket/test_routing.rb +1 -1
  83. data/test/socket/test_stream_socket.rb +74 -0
  84. data/test/socket/test_sub_socket.rb +1 -1
  85. data/test/test_beacon.rb +4 -2
  86. data/test/test_context.rb +2 -2
  87. data/test/test_frame.rb +2 -2
  88. data/test/test_handler.rb +2 -2
  89. data/test/test_logger.rb +1 -1
  90. data/test/test_loop.rb +2 -2
  91. data/test/test_message.rb +1 -1
  92. data/test/test_monitoring.rb +15 -3
  93. data/test/test_poller.rb +2 -2
  94. data/test/test_pollitem.rb +2 -2
  95. data/test/test_socket.rb +53 -6
  96. data/test/test_threading.rb +2 -2
  97. data/test/test_timer.rb +2 -2
  98. data/test/test_zmq.rb +2 -2
  99. metadata +109 -104
@@ -56,6 +56,8 @@ namespace zmq
56
56
  void start ();
57
57
  void stop ();
58
58
 
59
+ static int max_fds ();
60
+
59
61
  private:
60
62
 
61
63
  // Main worker thread routine.
@@ -126,6 +126,11 @@ void zmq::epoll_t::stop ()
126
126
  stopping = true;
127
127
  }
128
128
 
129
+ int zmq::epoll_t::max_fds ()
130
+ {
131
+ return -1;
132
+ }
133
+
129
134
  void zmq::epoll_t::loop ()
130
135
  {
131
136
  epoll_event ev_buf [max_io_events];
@@ -58,6 +58,8 @@ namespace zmq
58
58
  void start ();
59
59
  void stop ();
60
60
 
61
+ static int max_fds ();
62
+
61
63
  private:
62
64
 
63
65
  // Main worker thread routine.
@@ -41,11 +41,11 @@ namespace zmq
41
41
 
42
42
  // This method is called by the session to signalise that more
43
43
  // messages can be written to the pipe.
44
- virtual void activate_in () = 0;
44
+ virtual void restart_input () = 0;
45
45
 
46
46
  // This method is called by the session to signalise that there
47
47
  // are messages to send available.
48
- virtual void activate_out () = 0;
48
+ virtual void restart_output () = 0;
49
49
 
50
50
  virtual void zap_msg_available () = 0;
51
51
  };
@@ -152,6 +152,11 @@ void zmq::kqueue_t::stop ()
152
152
  stopping = true;
153
153
  }
154
154
 
155
+ int zmq::kqueue_t::max_fds ()
156
+ {
157
+ return -1;
158
+ }
159
+
155
160
  void zmq::kqueue_t::loop ()
156
161
  {
157
162
  while (!stopping) {
@@ -58,6 +58,8 @@ namespace zmq
58
58
  void start ();
59
59
  void stop ();
60
60
 
61
+ static int max_fds ();
62
+
61
63
  private:
62
64
 
63
65
  // Main worker thread routine.
@@ -102,12 +102,12 @@ void zmq::pgm_receiver_t::terminate ()
102
102
  delete this;
103
103
  }
104
104
 
105
- void zmq::pgm_receiver_t::activate_out ()
105
+ void zmq::pgm_receiver_t::restart_output ()
106
106
  {
107
107
  drop_subscriptions ();
108
108
  }
109
109
 
110
- void zmq::pgm_receiver_t::activate_in ()
110
+ void zmq::pgm_receiver_t::restart_input ()
111
111
  {
112
112
  zmq_assert (session != NULL);
113
113
  zmq_assert (active_tsi != NULL);
@@ -57,8 +57,8 @@ namespace zmq
57
57
  void plug (zmq::io_thread_t *io_thread_,
58
58
  zmq::session_base_t *session_);
59
59
  void terminate ();
60
- void activate_in ();
61
- void activate_out ();
60
+ void restart_input ();
61
+ void restart_output ();
62
62
  void zap_msg_available () {}
63
63
 
64
64
  // i_poll_events interface implementation.
@@ -119,13 +119,13 @@ void zmq::pgm_sender_t::terminate ()
119
119
  delete this;
120
120
  }
121
121
 
122
- void zmq::pgm_sender_t::activate_out ()
122
+ void zmq::pgm_sender_t::restart_output ()
123
123
  {
124
124
  set_pollout (handle);
125
125
  out_event ();
126
126
  }
127
127
 
128
- void zmq::pgm_sender_t::activate_in ()
128
+ void zmq::pgm_sender_t::restart_input ()
129
129
  {
130
130
  zmq_assert (false);
131
131
  }
@@ -56,8 +56,8 @@ namespace zmq
56
56
  void plug (zmq::io_thread_t *io_thread_,
57
57
  zmq::session_base_t *session_);
58
58
  void terminate ();
59
- void activate_in ();
60
- void activate_out ();
59
+ void restart_input ();
60
+ void restart_output ();
61
61
  void zap_msg_available () {}
62
62
 
63
63
  // i_poll_events interface implementation.
@@ -114,6 +114,11 @@ void zmq::poll_t::stop ()
114
114
  stopping = true;
115
115
  }
116
116
 
117
+ int zmq::poll_t::max_fds ()
118
+ {
119
+ return -1;
120
+ }
121
+
117
122
  void zmq::poll_t::loop ()
118
123
  {
119
124
  while (!stopping) {
@@ -59,6 +59,8 @@ namespace zmq
59
59
  void start ();
60
60
  void stop ();
61
61
 
62
+ static int max_fds ();
63
+
62
64
  private:
63
65
 
64
66
  // Main worker thread routine.
@@ -225,6 +225,8 @@ int zmq::router_t::xsend (msg_t *msg_)
225
225
  current_out->terminate (false);
226
226
  int rc = msg_->close ();
227
227
  errno_assert (rc == 0);
228
+ rc = msg_->init ();
229
+ errno_assert (rc == 0);
228
230
  current_out = NULL;
229
231
  return 0;
230
232
  }
@@ -144,6 +144,11 @@ void zmq::select_t::stop ()
144
144
  stopping = true;
145
145
  }
146
146
 
147
+ int zmq::select_t::max_fds ()
148
+ {
149
+ return FD_SETSIZE;
150
+ }
151
+
147
152
  void zmq::select_t::loop ()
148
153
  {
149
154
  while (!stopping) {
@@ -69,6 +69,8 @@ namespace zmq
69
69
  void start ();
70
70
  void stop ();
71
71
 
72
+ static int max_fds ();
73
+
72
74
  private:
73
75
 
74
76
  // Main worker thread routine.
@@ -243,7 +243,7 @@ void zmq::session_base_t::read_activated (pipe_t *pipe_)
243
243
  }
244
244
 
245
245
  if (likely (pipe_ == pipe))
246
- engine->activate_out ();
246
+ engine->restart_output ();
247
247
  else
248
248
  engine->zap_msg_available ();
249
249
  }
@@ -257,7 +257,7 @@ void zmq::session_base_t::write_activated (pipe_t *pipe_)
257
257
  }
258
258
 
259
259
  if (engine)
260
- engine->activate_in ();
260
+ engine->restart_input ();
261
261
  }
262
262
 
263
263
  void zmq::session_base_t::hiccuped (pipe_t *)
@@ -80,13 +80,10 @@
80
80
  zmq::signaler_t::signaler_t ()
81
81
  {
82
82
  // Create the socketpair for signaling.
83
- int rc = make_fdpair (&r, &w);
84
- errno_assert (rc == 0);
85
-
86
- // Set both fds to non-blocking mode.
87
- unblock_socket (w);
88
- unblock_socket (r);
89
-
83
+ if (make_fdpair (&r, &w) == 0) {
84
+ unblock_socket (w);
85
+ unblock_socket (r);
86
+ }
90
87
  #ifdef HAVE_FORK
91
88
  pid = getpid();
92
89
  #endif
@@ -184,8 +181,7 @@ int zmq::signaler_t::wait (int timeout_)
184
181
  return -1;
185
182
  }
186
183
  #ifdef HAVE_FORK
187
- if (unlikely(pid != getpid()))
188
- {
184
+ if (unlikely(pid != getpid())) {
189
185
  // we have forked and the file descriptor is closed. Emulate an interupt
190
186
  // response.
191
187
  //printf("Child process %d signaler_t::wait returning simulating interrupt #2\n", getpid());
@@ -266,42 +262,30 @@ void zmq::signaler_t::recv ()
266
262
  #ifdef HAVE_FORK
267
263
  void zmq::signaler_t::forked()
268
264
  {
269
- int oldr = r;
270
- #if !defined ZMQ_HAVE_EVENTFD
271
- int oldw = w;
272
- #endif
273
-
274
- // replace the file descriptors created in the parent with new
275
- // ones, and close the inherited ones
276
- make_fdpair(&r, &w);
277
- #if defined ZMQ_HAVE_EVENTFD
278
- int rc = close (oldr);
279
- errno_assert (rc == 0);
280
- #else
281
- int rc = close (oldw);
282
- errno_assert (rc == 0);
283
- rc = close (oldr);
284
- errno_assert (rc == 0);
285
- #endif
265
+ // Close file descriptors created in the parent and create new pair
266
+ close (r);
267
+ close (w);
268
+ make_fdpair (&r, &w);
286
269
  }
287
270
  #endif
288
271
 
289
-
290
-
291
-
272
+ // Returns -1 if we could not make the socket pair successfully
292
273
  int zmq::signaler_t::make_fdpair (fd_t *r_, fd_t *w_)
293
274
  {
294
275
  #if defined ZMQ_HAVE_EVENTFD
295
-
296
- // Create eventfd object.
297
276
  fd_t fd = eventfd (0, 0);
298
- errno_assert (fd != -1);
299
- *w_ = fd;
300
- *r_ = fd;
301
- return 0;
277
+ if (fd == -1) {
278
+ errno_assert (errno == ENFILE || errno == EMFILE);
279
+ *w_ = *r_ = -1;
280
+ return -1;
281
+ }
282
+ else {
283
+ *w_ = *r_ = fd;
284
+ return 0;
285
+ }
302
286
 
303
287
  #elif defined ZMQ_HAVE_WINDOWS
304
- #if !defined _WIN32_WCE
288
+ # if !defined _WIN32_WCE
305
289
  // Windows CE does not manage security attributes
306
290
  SECURITY_DESCRIPTOR sd;
307
291
  SECURITY_ATTRIBUTES sa;
@@ -313,7 +297,7 @@ int zmq::signaler_t::make_fdpair (fd_t *r_, fd_t *w_)
313
297
 
314
298
  sa.nLength = sizeof(SECURITY_ATTRIBUTES);
315
299
  sa.lpSecurityDescriptor = &sd;
316
- #endif
300
+ # endif
317
301
 
318
302
  // This function has to be in a system-wide critical section so that
319
303
  // two instances of the library don't accidentally create signaler
@@ -322,20 +306,17 @@ int zmq::signaler_t::make_fdpair (fd_t *r_, fd_t *w_)
322
306
  // Note that if the event object already exists, the CreateEvent requests
323
307
  // EVENT_ALL_ACCESS access right. If this fails, we try to open
324
308
  // the event object asking for SYNCHRONIZE access only.
325
- #if !defined _WIN32_WCE
309
+ # if !defined _WIN32_WCE
326
310
  HANDLE sync = CreateEvent (&sa, FALSE, TRUE, TEXT ("Global\\zmq-signaler-port-sync"));
327
- #else
311
+ # else
328
312
  HANDLE sync = CreateEvent (NULL, FALSE, TRUE, TEXT ("Global\\zmq-signaler-port-sync"));
329
- #endif
313
+ # endif
330
314
  if (sync == NULL && GetLastError () == ERROR_ACCESS_DENIED)
331
- sync = OpenEvent (SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, TEXT ("Global\\zmq-signaler-port-sync"));
315
+ sync = OpenEvent (SYNCHRONIZE | EVENT_MODIFY_STATE,
316
+ FALSE, TEXT ("Global\\zmq-signaler-port-sync"));
332
317
 
333
318
  win_assert (sync != NULL);
334
319
 
335
- // Enter the critical section.
336
- DWORD dwrc = WaitForSingleObject (sync, INFINITE);
337
- zmq_assert (dwrc == WAIT_OBJECT_0);
338
-
339
320
  // Windows has no 'socketpair' function. CreatePipe is no good as pipe
340
321
  // handles cannot be polled on. Here we create the socketpair by hand.
341
322
  *w_ = INVALID_SOCKET;
@@ -356,58 +337,51 @@ int zmq::signaler_t::make_fdpair (fd_t *r_, fd_t *w_)
356
337
  (char *)&tcp_nodelay, sizeof (tcp_nodelay));
357
338
  wsa_assert (rc != SOCKET_ERROR);
358
339
 
359
- // Bind listening socket to signaler port.
340
+ // Init sockaddr to signaler port.
360
341
  struct sockaddr_in addr;
361
342
  memset (&addr, 0, sizeof (addr));
362
343
  addr.sin_family = AF_INET;
363
344
  addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
364
345
  addr.sin_port = htons (signaler_port);
365
- rc = bind (listener, (const struct sockaddr*) &addr, sizeof (addr));
366
- wsa_assert (rc != SOCKET_ERROR);
367
-
368
- // Listen for incomming connections.
369
- rc = listen (listener, 1);
370
- wsa_assert (rc != SOCKET_ERROR);
371
346
 
372
347
  // Create the writer socket.
373
- *w_ = WSASocket (AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
348
+ *w_ = open_socket (AF_INET, SOCK_STREAM, 0);
374
349
  wsa_assert (*w_ != INVALID_SOCKET);
375
350
 
376
- #if !defined _WIN32_WCE
377
- // On Windows, preventing sockets to be inherited by child processes.
378
- BOOL brc = SetHandleInformation ((HANDLE) *w_, HANDLE_FLAG_INHERIT, 0);
379
- win_assert (brc);
380
- #else
381
- BOOL brc;
382
- #endif
383
-
384
351
  // Set TCP_NODELAY on writer socket.
385
352
  rc = setsockopt (*w_, IPPROTO_TCP, TCP_NODELAY,
386
353
  (char *)&tcp_nodelay, sizeof (tcp_nodelay));
387
354
  wsa_assert (rc != SOCKET_ERROR);
388
355
 
356
+ // Enter the critical section.
357
+ DWORD dwrc = WaitForSingleObject (sync, INFINITE);
358
+ zmq_assert (dwrc == WAIT_OBJECT_0);
359
+
360
+ // Bind listening socket to signaler port.
361
+ rc = bind (listener, (const struct sockaddr*) &addr, sizeof (addr));
362
+
363
+ // Listen for incoming connections.
364
+ if (rc != SOCKET_ERROR)
365
+ rc = listen (listener, 1);
366
+
389
367
  // Connect writer to the listener.
390
- rc = connect (*w_, (struct sockaddr*) &addr, sizeof (addr));
391
-
392
- // Save errno if connection fails
393
- int conn_errno = 0;
394
- if (rc == SOCKET_ERROR) {
395
- conn_errno = WSAGetLastError ();
396
- } else {
397
- // Accept connection from writer.
368
+ if (rc != SOCKET_ERROR)
369
+ rc = connect (*w_, (struct sockaddr*) &addr, sizeof (addr));
370
+
371
+ // Accept connection from writer.
372
+ if (rc != SOCKET_ERROR)
398
373
  *r_ = accept (listener, NULL, NULL);
399
374
 
400
- if (*r_ == INVALID_SOCKET) {
401
- conn_errno = WSAGetLastError ();
402
- }
403
- }
375
+ // Save errno if error occurred in bind/listen/connect/accept.
376
+ int saved_errno = 0;
377
+ if (*r_ == INVALID_SOCKET)
378
+ saved_errno = WSAGetLastError ();
404
379
 
405
380
  // We don't need the listening socket anymore. Close it.
406
- rc = closesocket (listener);
407
- wsa_assert (rc != SOCKET_ERROR);
381
+ closesocket (listener);
408
382
 
409
383
  // Exit the critical section.
410
- brc = SetEvent (sync);
384
+ BOOL brc = SetEvent (sync);
411
385
  win_assert (brc != 0);
412
386
 
413
387
  // Release the kernel object
@@ -415,27 +389,22 @@ int zmq::signaler_t::make_fdpair (fd_t *r_, fd_t *w_)
415
389
  win_assert (brc != 0);
416
390
 
417
391
  if (*r_ != INVALID_SOCKET) {
418
- #if !defined _WIN32_WCE
392
+ # if !defined _WIN32_WCE
419
393
  // On Windows, preventing sockets to be inherited by child processes.
420
394
  brc = SetHandleInformation ((HANDLE) *r_, HANDLE_FLAG_INHERIT, 0);
421
395
  win_assert (brc);
422
- #endif
396
+ # endif
423
397
  return 0;
424
- } else {
398
+ }
399
+ else {
425
400
  // Cleanup writer if connection failed
426
- rc = closesocket (*w_);
427
- wsa_assert (rc != SOCKET_ERROR);
428
-
429
- *w_ = INVALID_SOCKET;
430
-
401
+ if (*w_ != INVALID_SOCKET) {
402
+ rc = closesocket (*w_);
403
+ wsa_assert (rc != SOCKET_ERROR);
404
+ *w_ = INVALID_SOCKET;
405
+ }
431
406
  // Set errno from saved value
432
- errno = wsa_error_to_errno (conn_errno);
433
-
434
- // Ideally, we would return errno to the caller signaler_t()
435
- // Unfortunately, it uses errno_assert() which gives "Unknown error"
436
- // We might as well assert here and print the actual error message
437
- wsa_assert_no (conn_errno);
438
-
407
+ errno = wsa_error_to_errno (saved_errno);
439
408
  return -1;
440
409
  }
441
410
 
@@ -463,7 +432,7 @@ int zmq::signaler_t::make_fdpair (fd_t *r_, fd_t *w_)
463
432
  rc = setsockopt (listener, IPPROTO_TCP, TCP_NODELACK, &on, sizeof (on));
464
433
  errno_assert (rc != -1);
465
434
 
466
- rc = bind(listener, (struct sockaddr*) &lcladdr, sizeof (lcladdr));
435
+ rc = bind (listener, (struct sockaddr*) &lcladdr, sizeof (lcladdr));
467
436
  errno_assert (rc != -1);
468
437
 
469
438
  socklen_t lcladdr_len = sizeof (lcladdr);
@@ -493,15 +462,20 @@ int zmq::signaler_t::make_fdpair (fd_t *r_, fd_t *w_)
493
462
 
494
463
  return 0;
495
464
 
496
- #else // All other implementations support socketpair()
497
-
465
+ #else
466
+ // All other implementations support socketpair()
498
467
  int sv [2];
499
468
  int rc = socketpair (AF_UNIX, SOCK_STREAM, 0, sv);
500
- errno_assert (rc == 0);
501
- *w_ = sv [0];
502
- *r_ = sv [1];
503
- return 0;
504
-
469
+ if (rc == -1) {
470
+ errno_assert (errno == ENFILE || errno == EMFILE);
471
+ *w_ = *r_ = -1;
472
+ return -1;
473
+ }
474
+ else {
475
+ *w_ = sv [0];
476
+ *r_ = sv [1];
477
+ return 0;
478
+ }
505
479
  #endif
506
480
  }
507
481