rbczmq 1.7.4 → 1.7.5

Sign up to get free protection for your applications and to get access to all the features.
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