nio4r 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7b58cda1a26e30cb81ff1b0f6febb43413a4c11f
4
- data.tar.gz: 319495e598529456785aa74289742cb87d7d2470
3
+ metadata.gz: 339fc3cc040e41d266a9f83ab155d903294fcdc5
4
+ data.tar.gz: 8872185b3bfadc884b36b65ea1a17a2c082d7090
5
5
  SHA512:
6
- metadata.gz: de267fdf599880196fa3a3431cd20a1ce2d4e6dcec16f57f4161b7b6d05d9f8e2c97651a1a959e16f2cea6de965e30b60842635aa954685666a4a4d5c99402fc
7
- data.tar.gz: a580ce6b7edf0e7923f0725e5ae9ebe5a194cbd235fa320b2b04a1c8f794ada1e93200a2fb8c9dedbee1961d73e3767e39ec1bdc6b727939281e5ac687e1ec8a
6
+ metadata.gz: 73083b74bdcb581449b5b0534209fdd9b1c08059ee946eaf9f8155d72605030c127046a7819d4ed325e0bf3e525c4aec81a41c00b55077f9e5b4629dd147d931
7
+ data.tar.gz: 44eb8368129b34cfb8780b98334ff8a67029d1751fdfc756ee761cb19c975f55a95c5166a251dba88ea7f587a3441f19650dcb13d936d88c60f847bda9c20d1d
@@ -24,6 +24,9 @@ Metrics/CyclomaticComplexity:
24
24
  Style/GlobalVars:
25
25
  Enabled: false
26
26
 
27
+ Style/TrivialAccessors:
28
+ Enabled: false
29
+
27
30
  #
28
31
  # Auto-generated overrides
29
32
  #
@@ -1,3 +1,6 @@
1
+ language: ruby
2
+ sudo: false
3
+
1
4
  rvm:
2
5
  - 2.0.0
3
6
  - 2.1.1
data/CHANGES.md CHANGED
@@ -1,3 +1,9 @@
1
+ 1.2.0 (2015-12-22)
2
+ ------------------
3
+ * Add NIO::Monitor#interests= API for changing interests. Contributed by
4
+ @UpeksheJay as a Google Summer of Code project.
5
+ * Update to libev 4.22
6
+
1
7
  1.1.1 (2015-07-17)
2
8
  ------------------
3
9
  * Update to libev 4.20
@@ -14,6 +14,16 @@ TODO: embed watchers need updating when fd changes
14
14
  TODO: document portability requirements for atomic pointer access
15
15
  TODO: document requirements for function pointers and calling conventions.
16
16
 
17
+ 4.22 Sun Dec 20 22:11:50 CET 2015
18
+ - when epoll detects unremovable fds in the fd set, rebuild
19
+ only the epoll descriptor, not the signal pipe, to avoid
20
+ SIGPIPE in ev_async_send. This doesn't solve it on fork,
21
+ so document what needs to be done in ev_loop_fork
22
+ (analyzed by Benjamin Mahler).
23
+ - remove superfluous sys/timeb.h include on win32
24
+ (analyzed by Jason Madden).
25
+ - updated libecb.
26
+
17
27
  4.20 Sat Jun 20 13:01:43 CEST 2015
18
28
  - prefer noexcept over throw () with C++ 11.
19
29
  - update ecb.h due to incompatibilities with c11.
@@ -538,7 +538,7 @@ struct signalfd_siginfo
538
538
  #define ECB_H
539
539
 
540
540
  /* 16 bits major, 16 bits minor */
541
- #define ECB_VERSION 0x00010004
541
+ #define ECB_VERSION 0x00010005
542
542
 
543
543
  #ifdef _WIN32
544
544
  typedef signed char int8_t;
@@ -565,7 +565,7 @@ struct signalfd_siginfo
565
565
  #endif
566
566
  #else
567
567
  #include <inttypes.h>
568
- #if UINTMAX_MAX > 0xffffffffU
568
+ #if (defined INTPTR_MAX ? INTPTR_MAX : ULONG_MAX) > 0xffffffffU
569
569
  #define ECB_PTRSIZE 8
570
570
  #else
571
571
  #define ECB_PTRSIZE 4
@@ -653,6 +653,10 @@ struct signalfd_siginfo
653
653
  #include <builtins.h>
654
654
  #endif
655
655
 
656
+ #if 1400 <= _MSC_VER
657
+ #include <intrin.h> /* fence functions _ReadBarrier, also bit search functions _BitScanReverse */
658
+ #endif
659
+
656
660
  #ifndef ECB_MEMORY_FENCE
657
661
  #if ECB_GCC_VERSION(2,5) || defined __INTEL_COMPILER || (__llvm__ && __GNUC__) || __SUNPRO_C >= 0x5110 || __SUNPRO_CC >= 0x5110
658
662
  #if __i386 || __i386__
@@ -665,15 +669,23 @@ struct signalfd_siginfo
665
669
  #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("")
666
670
  #elif __powerpc__ || __ppc__ || __powerpc64__ || __ppc64__
667
671
  #define ECB_MEMORY_FENCE __asm__ __volatile__ ("sync" : : : "memory")
672
+ #elif defined __ARM_ARCH_2__ \
673
+ || defined __ARM_ARCH_3__ || defined __ARM_ARCH_3M__ \
674
+ || defined __ARM_ARCH_4__ || defined __ARM_ARCH_4T__ \
675
+ || defined __ARM_ARCH_5__ || defined __ARM_ARCH_5E__ \
676
+ || defined __ARM_ARCH_5T__ || defined __ARM_ARCH_5TE__ \
677
+ || defined __ARM_ARCH_5TEJ__
678
+ /* should not need any, unless running old code on newer cpu - arm doesn't support that */
668
679
  #elif defined __ARM_ARCH_6__ || defined __ARM_ARCH_6J__ \
669
- || defined __ARM_ARCH_6K__ || defined __ARM_ARCH_6ZK__
680
+ || defined __ARM_ARCH_6K__ || defined __ARM_ARCH_6ZK__ \
681
+ || defined __ARM_ARCH_6T2__
670
682
  #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mcr p15,0,%0,c7,c10,5" : : "r" (0) : "memory")
671
683
  #elif defined __ARM_ARCH_7__ || defined __ARM_ARCH_7A__ \
672
- || defined __ARM_ARCH_7M__ || defined __ARM_ARCH_7R__
684
+ || defined __ARM_ARCH_7R__ || defined __ARM_ARCH_7M__
673
685
  #define ECB_MEMORY_FENCE __asm__ __volatile__ ("dmb" : : : "memory")
674
686
  #elif __aarch64__
675
687
  #define ECB_MEMORY_FENCE __asm__ __volatile__ ("dmb ish" : : : "memory")
676
- #elif (__sparc || __sparc__) && !__sparcv8
688
+ #elif (__sparc || __sparc__) && !(__sparc_v8__ || defined __sparcv8)
677
689
  #define ECB_MEMORY_FENCE __asm__ __volatile__ ("membar #LoadStore | #LoadLoad | #StoreStore | #StoreLoad" : : : "memory")
678
690
  #define ECB_MEMORY_FENCE_ACQUIRE __asm__ __volatile__ ("membar #LoadStore | #LoadLoad" : : : "memory")
679
691
  #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("membar #LoadStore | #StoreStore")
@@ -921,6 +933,11 @@ typedef int ecb_bool;
921
933
  ecb_function_ ecb_const int
922
934
  ecb_ctz32 (uint32_t x)
923
935
  {
936
+ #if 1400 <= _MSC_VER && (_M_IX86 || _M_X64 || _M_IA64 || _M_ARM)
937
+ unsigned long r;
938
+ _BitScanForward (&r, x);
939
+ return (int)r;
940
+ #else
924
941
  int r = 0;
925
942
 
926
943
  x &= ~x + 1; /* this isolates the lowest bit */
@@ -940,14 +957,21 @@ typedef int ecb_bool;
940
957
  #endif
941
958
 
942
959
  return r;
960
+ #endif
943
961
  }
944
962
 
945
963
  ecb_function_ ecb_const int ecb_ctz64 (uint64_t x);
946
964
  ecb_function_ ecb_const int
947
965
  ecb_ctz64 (uint64_t x)
948
966
  {
949
- int shift = x & 0xffffffffU ? 0 : 32;
967
+ #if 1400 <= _MSC_VER && (_M_X64 || _M_IA64 || _M_ARM)
968
+ unsigned long r;
969
+ _BitScanForward64 (&r, x);
970
+ return (int)r;
971
+ #else
972
+ int shift = x & 0xffffffff ? 0 : 32;
950
973
  return ecb_ctz32 (x >> shift) + shift;
974
+ #endif
951
975
  }
952
976
 
953
977
  ecb_function_ ecb_const int ecb_popcount32 (uint32_t x);
@@ -965,6 +989,11 @@ typedef int ecb_bool;
965
989
  ecb_function_ ecb_const int ecb_ld32 (uint32_t x);
966
990
  ecb_function_ ecb_const int ecb_ld32 (uint32_t x)
967
991
  {
992
+ #if 1400 <= _MSC_VER && (_M_IX86 || _M_X64 || _M_IA64 || _M_ARM)
993
+ unsigned long r;
994
+ _BitScanReverse (&r, x);
995
+ return (int)r;
996
+ #else
968
997
  int r = 0;
969
998
 
970
999
  if (x >> 16) { x >>= 16; r += 16; }
@@ -974,16 +1003,23 @@ typedef int ecb_bool;
974
1003
  if (x >> 1) { r += 1; }
975
1004
 
976
1005
  return r;
1006
+ #endif
977
1007
  }
978
1008
 
979
1009
  ecb_function_ ecb_const int ecb_ld64 (uint64_t x);
980
1010
  ecb_function_ ecb_const int ecb_ld64 (uint64_t x)
981
1011
  {
1012
+ #if 1400 <= _MSC_VER && (_M_X64 || _M_IA64 || _M_ARM)
1013
+ unsigned long r;
1014
+ _BitScanReverse64 (&r, x);
1015
+ return (int)r;
1016
+ #else
982
1017
  int r = 0;
983
1018
 
984
1019
  if (x >> 32) { x >>= 32; r += 32; }
985
1020
 
986
1021
  return r + ecb_ld32 (x);
1022
+ #endif
987
1023
  }
988
1024
  #endif
989
1025
 
@@ -1096,8 +1132,8 @@ ecb_inline ecb_const uint64_t ecb_rotr64 (uint64_t x, unsigned int count) { retu
1096
1132
  /* try to tell the compiler that some condition is definitely true */
1097
1133
  #define ecb_assume(cond) if (!(cond)) ecb_unreachable (); else 0
1098
1134
 
1099
- ecb_inline ecb_const unsigned char ecb_byteorder_helper (void);
1100
- ecb_inline ecb_const unsigned char
1135
+ ecb_inline ecb_const uint32_t ecb_byteorder_helper (void);
1136
+ ecb_inline ecb_const uint32_t
1101
1137
  ecb_byteorder_helper (void)
1102
1138
  {
1103
1139
  /* the union code still generates code under pressure in gcc, */
@@ -1106,26 +1142,28 @@ ecb_byteorder_helper (void)
1106
1142
  /* the reason why we have this horrible preprocessor mess */
1107
1143
  /* is to avoid it in all cases, at least on common architectures */
1108
1144
  /* or when using a recent enough gcc version (>= 4.6) */
1109
- #if ((__i386 || __i386__) && !__VOS__) || _M_IX86 || ECB_GCC_AMD64 || ECB_MSVC_AMD64
1110
- return 0x44;
1111
- #elif __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1112
- return 0x44;
1113
- #elif __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1114
- return 0x11;
1145
+ #if (defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \
1146
+ || ((__i386 || __i386__ || _M_IX86 || ECB_GCC_AMD64 || ECB_MSVC_AMD64) && !__VOS__)
1147
+ #define ECB_LITTLE_ENDIAN 1
1148
+ return 0x44332211;
1149
+ #elif (defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) \
1150
+ || ((__AARCH64EB__ || __MIPSEB__ || __ARMEB__) && !__VOS__)
1151
+ #define ECB_BIG_ENDIAN 1
1152
+ return 0x11223344;
1115
1153
  #else
1116
1154
  union
1117
1155
  {
1118
- uint32_t i;
1119
- uint8_t c;
1120
- } u = { 0x11223344 };
1121
- return u.c;
1156
+ uint8_t c[4];
1157
+ uint32_t u;
1158
+ } u = { 0x11, 0x22, 0x33, 0x44 };
1159
+ return u.u;
1122
1160
  #endif
1123
1161
  }
1124
1162
 
1125
1163
  ecb_inline ecb_const ecb_bool ecb_big_endian (void);
1126
- ecb_inline ecb_const ecb_bool ecb_big_endian (void) { return ecb_byteorder_helper () == 0x11; }
1164
+ ecb_inline ecb_const ecb_bool ecb_big_endian (void) { return ecb_byteorder_helper () == 0x11223344; }
1127
1165
  ecb_inline ecb_const ecb_bool ecb_little_endian (void);
1128
- ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_helper () == 0x44; }
1166
+ ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_helper () == 0x44332211; }
1129
1167
 
1130
1168
  #if ECB_GCC_VERSION(3,0) || ECB_C99
1131
1169
  #define ecb_mod(m,n) ((m) % (n) + ((m) % (n) < 0 ? (n) : 0))
@@ -1160,6 +1198,102 @@ ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_he
1160
1198
  #define ecb_array_length(name) (sizeof (name) / sizeof (name [0]))
1161
1199
  #endif
1162
1200
 
1201
+ ecb_function_ ecb_const uint32_t ecb_binary16_to_binary32 (uint32_t x);
1202
+ ecb_function_ ecb_const uint32_t
1203
+ ecb_binary16_to_binary32 (uint32_t x)
1204
+ {
1205
+ unsigned int s = (x & 0x8000) << (31 - 15);
1206
+ int e = (x >> 10) & 0x001f;
1207
+ unsigned int m = x & 0x03ff;
1208
+
1209
+ if (ecb_expect_false (e == 31))
1210
+ /* infinity or NaN */
1211
+ e = 255 - (127 - 15);
1212
+ else if (ecb_expect_false (!e))
1213
+ {
1214
+ if (ecb_expect_true (!m))
1215
+ /* zero, handled by code below by forcing e to 0 */
1216
+ e = 0 - (127 - 15);
1217
+ else
1218
+ {
1219
+ /* subnormal, renormalise */
1220
+ unsigned int s = 10 - ecb_ld32 (m);
1221
+
1222
+ m = (m << s) & 0x3ff; /* mask implicit bit */
1223
+ e -= s - 1;
1224
+ }
1225
+ }
1226
+
1227
+ /* e and m now are normalised, or zero, (or inf or nan) */
1228
+ e += 127 - 15;
1229
+
1230
+ return s | (e << 23) | (m << (23 - 10));
1231
+ }
1232
+
1233
+ ecb_function_ ecb_const uint16_t ecb_binary32_to_binary16 (uint32_t x);
1234
+ ecb_function_ ecb_const uint16_t
1235
+ ecb_binary32_to_binary16 (uint32_t x)
1236
+ {
1237
+ unsigned int s = (x >> 16) & 0x00008000; /* sign bit, the easy part */
1238
+ unsigned int e = ((x >> 23) & 0x000000ff) - (127 - 15); /* the desired exponent */
1239
+ unsigned int m = x & 0x007fffff;
1240
+
1241
+ x &= 0x7fffffff;
1242
+
1243
+ /* if it's within range of binary16 normals, use fast path */
1244
+ if (ecb_expect_true (0x38800000 <= x && x <= 0x477fefff))
1245
+ {
1246
+ /* mantissa round-to-even */
1247
+ m += 0x00000fff + ((m >> (23 - 10)) & 1);
1248
+
1249
+ /* handle overflow */
1250
+ if (ecb_expect_false (m >= 0x00800000))
1251
+ {
1252
+ m >>= 1;
1253
+ e += 1;
1254
+ }
1255
+
1256
+ return s | (e << 10) | (m >> (23 - 10));
1257
+ }
1258
+
1259
+ /* handle large numbers and infinity */
1260
+ if (ecb_expect_true (0x477fefff < x && x <= 0x7f800000))
1261
+ return s | 0x7c00;
1262
+
1263
+ /* handle zero, subnormals and small numbers */
1264
+ if (ecb_expect_true (x < 0x38800000))
1265
+ {
1266
+ /* zero */
1267
+ if (ecb_expect_true (!x))
1268
+ return s;
1269
+
1270
+ /* handle subnormals */
1271
+
1272
+ /* too small, will be zero */
1273
+ if (e < (14 - 24)) /* might not be sharp, but is good enough */
1274
+ return s;
1275
+
1276
+ m |= 0x00800000; /* make implicit bit explicit */
1277
+
1278
+ /* very tricky - we need to round to the nearest e (+10) bit value */
1279
+ {
1280
+ unsigned int bits = 14 - e;
1281
+ unsigned int half = (1 << (bits - 1)) - 1;
1282
+ unsigned int even = (m >> bits) & 1;
1283
+
1284
+ /* if this overflows, we will end up with a normalised number */
1285
+ m = (m + half + even) >> bits;
1286
+ }
1287
+
1288
+ return s | m;
1289
+ }
1290
+
1291
+ /* handle NaNs, preserve leftmost nan bits, but make sure we don't turn them into infinities */
1292
+ m >>= 13;
1293
+
1294
+ return s | 0x7c00 | m | !m;
1295
+ }
1296
+
1163
1297
  /*******************************************************************************/
1164
1298
  /* floating point stuff, can be disabled by defining ECB_NO_LIBM */
1165
1299
 
@@ -1211,23 +1345,6 @@ ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_he
1211
1345
  #define ecb_frexpf(x,e) (float) frexp ((double) (x), (e))
1212
1346
  #endif
1213
1347
 
1214
- /* converts an ieee half/binary16 to a float */
1215
- ecb_function_ ecb_const float ecb_binary16_to_float (uint16_t x);
1216
- ecb_function_ ecb_const float
1217
- ecb_binary16_to_float (uint16_t x)
1218
- {
1219
- int e = (x >> 10) & 0x1f;
1220
- int m = x & 0x3ff;
1221
- float r;
1222
-
1223
- if (!e ) r = ecb_ldexpf (m , -24);
1224
- else if (e != 31) r = ecb_ldexpf (m + 0x400, e - 25);
1225
- else if (m ) r = ECB_NAN;
1226
- else r = ECB_INFINITY;
1227
-
1228
- return x & 0x8000 ? -r : r;
1229
- }
1230
-
1231
1348
  /* convert a float to ieee single/binary32 */
1232
1349
  ecb_function_ ecb_const uint32_t ecb_float_to_binary32 (float x);
1233
1350
  ecb_function_ ecb_const uint32_t
@@ -1368,6 +1485,22 @@ ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_he
1368
1485
  return r;
1369
1486
  }
1370
1487
 
1488
+ /* convert a float to ieee half/binary16 */
1489
+ ecb_function_ ecb_const uint16_t ecb_float_to_binary16 (float x);
1490
+ ecb_function_ ecb_const uint16_t
1491
+ ecb_float_to_binary16 (float x)
1492
+ {
1493
+ return ecb_binary32_to_binary16 (ecb_float_to_binary32 (x));
1494
+ }
1495
+
1496
+ /* convert an ieee half/binary16 to float */
1497
+ ecb_function_ ecb_const float ecb_binary16_to_float (uint16_t x);
1498
+ ecb_function_ ecb_const float
1499
+ ecb_binary16_to_float (uint16_t x)
1500
+ {
1501
+ return ecb_binary32_to_float (ecb_binary16_to_binary32 (x));
1502
+ }
1503
+
1371
1504
  #endif
1372
1505
 
1373
1506
  #endif
@@ -2922,7 +3055,7 @@ loop_fork (EV_P)
2922
3055
  #endif
2923
3056
 
2924
3057
  #if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE
2925
- if (ev_is_active (&pipe_w))
3058
+ if (ev_is_active (&pipe_w) && postfork != 2)
2926
3059
  {
2927
3060
  /* pipe_write_wanted must be false now, so modifying fd vars should be safe */
2928
3061
 
@@ -211,7 +211,7 @@ struct ev_loop;
211
211
  /*****************************************************************************/
212
212
 
213
213
  #define EV_VERSION_MAJOR 4
214
- #define EV_VERSION_MINOR 20
214
+ #define EV_VERSION_MINOR 22
215
215
 
216
216
  /* eventmask, revents, events... */
217
217
  enum {
@@ -179,7 +179,7 @@ epoll_poll (EV_P_ ev_tstamp timeout)
179
179
  if (expect_false ((uint32_t)anfds [fd].egen != (uint32_t)(ev->data.u64 >> 32)))
180
180
  {
181
181
  /* recreate kernel state */
182
- postfork = 1;
182
+ postfork |= 2;
183
183
  continue;
184
184
  }
185
185
 
@@ -203,7 +203,7 @@ epoll_poll (EV_P_ ev_tstamp timeout)
203
203
  /* which is fortunately easy to do for us. */
204
204
  if (epoll_ctl (backend_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev))
205
205
  {
206
- postfork = 1; /* an error occurred, recreate kernel state */
206
+ postfork |= 2; /* an error occurred, recreate kernel state */
207
207
  continue;
208
208
  }
209
209
  }
@@ -39,9 +39,6 @@
39
39
 
40
40
  #ifdef _WIN32
41
41
 
42
- /* timeb.h is actually xsi legacy functionality */
43
- #include <sys/timeb.h>
44
-
45
42
  /* note: the comment below could not be substantiated, but what would I care */
46
43
  /* MSDN says this is required to handle SIGFPE */
47
44
  /* my wild guess would be that using something floating-pointy is required */
@@ -91,6 +88,8 @@ ev_pipe (int filedes [2])
91
88
  if (connect (sock [0], (struct sockaddr *)&addr, addr_size))
92
89
  goto fail;
93
90
 
91
+ /* TODO: returns INVALID_SOCKET on winsock accept, not < 0. fix it */
92
+ /* when convenient, probably by just removing error checking altogether? */
94
93
  if ((sock [1] = accept (listener, 0, 0)) < 0)
95
94
  goto fail;
96
95
 
@@ -15,6 +15,8 @@ static void NIO_Monitor_free(struct NIO_Monitor *monitor);
15
15
 
16
16
  /* Methods */
17
17
  static VALUE NIO_Monitor_initialize(VALUE self, VALUE selector, VALUE io, VALUE interests);
18
+ static VALUE NIO_Monitor_set_interests(VALUE self, VALUE interests);
19
+
18
20
  static VALUE NIO_Monitor_close(int argc, VALUE *argv, VALUE self);
19
21
  static VALUE NIO_Monitor_is_closed(VALUE self);
20
22
  static VALUE NIO_Monitor_io(VALUE self);
@@ -40,6 +42,7 @@ void Init_NIO_Monitor()
40
42
  rb_define_alloc_func(cNIO_Monitor, NIO_Monitor_allocate);
41
43
 
42
44
  rb_define_method(cNIO_Monitor, "initialize", NIO_Monitor_initialize, 3);
45
+ rb_define_method(cNIO_Monitor, "interests=", NIO_Monitor_set_interests, 1);
43
46
  rb_define_method(cNIO_Monitor, "close", NIO_Monitor_close, -1);
44
47
  rb_define_method(cNIO_Monitor, "closed?", NIO_Monitor_is_closed, 0);
45
48
  rb_define_method(cNIO_Monitor, "io", NIO_Monitor_io, 0);
@@ -117,6 +120,38 @@ static VALUE NIO_Monitor_initialize(VALUE self, VALUE io, VALUE interests, VALUE
117
120
  return Qnil;
118
121
  }
119
122
 
123
+ static VALUE NIO_Monitor_set_interests(VALUE self, VALUE interests)
124
+ {
125
+ struct NIO_Monitor *monitor;
126
+ ID interests_id;
127
+
128
+ if(NIO_Monitor_is_closed(self) == Qtrue) {
129
+ rb_raise(rb_eTypeError, "monitor is already closed");
130
+ }
131
+
132
+ interests_id = SYM2ID(interests);
133
+ Data_Get_Struct(self, struct NIO_Monitor, monitor);
134
+
135
+ if(interests_id == rb_intern("r")) {
136
+ monitor->interests = EV_READ;
137
+ } else if(interests_id == rb_intern("w")) {
138
+ monitor->interests = EV_WRITE;
139
+ } else if(interests_id == rb_intern("rw")) {
140
+ monitor->interests = EV_READ | EV_WRITE;
141
+ } else {
142
+ rb_raise(rb_eArgError, "invalid interest type %s (must be :r, :w, or :rw)",
143
+ RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0, 0)));
144
+ }
145
+
146
+ ev_io_stop(monitor->selector->ev_loop, &monitor->ev_io);
147
+ ev_io_set(&monitor->ev_io, monitor->ev_io.fd, monitor->interests);
148
+ ev_io_start(monitor->selector->ev_loop, &monitor->ev_io);
149
+
150
+ rb_ivar_set(self, rb_intern("interests"), interests);
151
+
152
+ return interests;
153
+ }
154
+
120
155
  static VALUE NIO_Monitor_close(int argc, VALUE *argv, VALUE self)
121
156
  {
122
157
  VALUE deregister, selector;
@@ -127,10 +162,11 @@ static VALUE NIO_Monitor_close(int argc, VALUE *argv, VALUE self)
127
162
  selector = rb_ivar_get(self, rb_intern("selector"));
128
163
 
129
164
  if(selector != Qnil) {
130
- /* if ev_loop is 0, it means that the loop has been stopped already (see NIO_Selector_shutdown)*/
165
+ /* if ev_loop is 0, it means that the loop has been stopped already (see NIO_Selector_shutdown) */
131
166
  if(monitor->selector->ev_loop != 0) {
132
167
  ev_io_stop(monitor->selector->ev_loop, &monitor->ev_io);
133
168
  }
169
+
134
170
  monitor->selector = 0;
135
171
  rb_ivar_set(self, rb_intern("selector"), Qnil);
136
172
 
@@ -148,7 +184,7 @@ static VALUE NIO_Monitor_is_closed(VALUE self)
148
184
  struct NIO_Monitor *monitor;
149
185
  Data_Get_Struct(self, struct NIO_Monitor, monitor);
150
186
 
151
- return !monitor->selector;
187
+ return monitor->selector == 0 ? Qtrue : Qfalse;
152
188
  }
153
189
 
154
190
  static VALUE NIO_Monitor_io(VALUE self)
@@ -235,6 +235,11 @@ public class Nio4r implements Library {
235
235
  @JRubyMethod
236
236
  public synchronized IRubyObject select(ThreadContext context, IRubyObject timeout, Block block) {
237
237
  Ruby runtime = context.getRuntime();
238
+
239
+ if(!this.selector.isOpen()) {
240
+ throw context.getRuntime().newIOError("selector is closed");
241
+ }
242
+
238
243
  int ready = doSelect(runtime, context, timeout);
239
244
 
240
245
  /* Timeout or wakeup */
@@ -353,6 +358,36 @@ public class Nio4r implements Library {
353
358
  key.attach(this);
354
359
  }
355
360
 
361
+ @JRubyMethod(name = "interests=")
362
+ public IRubyObject setInterests(ThreadContext context, IRubyObject interests) {
363
+ if(this.closed == context.getRuntime().getTrue()) {
364
+ throw context.getRuntime().newTypeError("monitor is already closed");
365
+ }
366
+
367
+ int interestOps = 0;
368
+ Ruby runtime = context.getRuntime();
369
+ Channel rawChannel = io.getChannel();
370
+ SelectableChannel channel = (SelectableChannel)rawChannel;
371
+
372
+ this.interests = interests;
373
+
374
+ if(interests == ruby.newSymbol("r")) {
375
+ interestOps = SelectionKey.OP_READ;
376
+ } else if(interests == ruby.newSymbol("w")) {
377
+ interestOps = SelectionKey.OP_WRITE;
378
+ } else if(interests == ruby.newSymbol("rw")) {
379
+ interestOps = SelectionKey.OP_READ|SelectionKey.OP_WRITE;
380
+ }
381
+
382
+ if((interestOps & ~(channel.validOps())) == 0) {
383
+ key.interestOps(interestOps);
384
+ } else {
385
+ throw context.getRuntime().newArgumentError("given interests not supported for this IO object");
386
+ }
387
+
388
+ return this.interests;
389
+ }
390
+
356
391
  @JRubyMethod
357
392
  public IRubyObject io(ThreadContext context) {
358
393
  return io;
@@ -297,6 +297,11 @@ static VALUE NIO_Selector_select_synchronized(VALUE *args)
297
297
  struct NIO_Selector *selector;
298
298
 
299
299
  Data_Get_Struct(args[0], struct NIO_Selector, selector);
300
+
301
+ if(selector->closed) {
302
+ rb_raise(rb_eIOError, "selector is closed");
303
+ }
304
+
300
305
  if(!rb_block_given_p()) {
301
306
  selector->ready_array = rb_ary_new();
302
307
  }
@@ -22,6 +22,14 @@ module NIO
22
22
  @closed = false
23
23
  end
24
24
 
25
+ # set the interests set
26
+ def interests=(interests)
27
+ fail TypeError, "monitor is already closed" if closed?
28
+ fail ArgumentError, "bad interests: #{interests}" unless [:r, :w, :rw].include?(interests)
29
+
30
+ @interests = interests
31
+ end
32
+
25
33
  # Is the IO object readable?
26
34
  def readable?
27
35
  readiness == :r || readiness == :rw
@@ -78,11 +78,10 @@ module NIO
78
78
 
79
79
  ready_writers.each do |io|
80
80
  monitor = @selectables[io]
81
- monitor.readiness = case monitor.readiness
82
- when :r
83
- :rw
81
+ if monitor.readiness == :r
82
+ monitor.readiness = :rw
84
83
  else
85
- :w
84
+ monitor.readiness = :w
86
85
  end
87
86
  selected_monitors << monitor
88
87
  end
@@ -1,3 +1,3 @@
1
1
  module NIO
2
- VERSION = "1.1.1"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -1,20 +1,49 @@
1
1
  require "spec_helper"
2
+ require "socket"
2
3
 
3
4
  RSpec.describe NIO::Monitor do
4
- let(:pipes) { IO.pipe }
5
- let(:reader) { pipes.first }
6
- let(:writer) { pipes.last }
5
+ let(:example_peers) do
6
+ address = "127.0.0.1"
7
+ base_port = 12_345
8
+ tries = 10
9
+
10
+ server = tries.times do |n|
11
+ begin
12
+ break TCPServer.new(address, base_port + n)
13
+ rescue Errno::EADDRINUSE
14
+ retry
15
+ end
16
+ end
17
+
18
+ fail Errno::EADDRINUSE, "couldn't find an open port" unless server
19
+ client = TCPSocket.new(address, server.addr[1])
20
+ [server, client]
21
+ end
22
+
23
+ let(:reader) { example_peers.first }
24
+ let(:writer) { example_peers.last }
25
+
7
26
  let(:selector) { NIO::Selector.new }
8
27
 
9
28
  subject { selector.register(reader, :r) }
10
29
  let(:peer) { selector.register(writer, :rw) }
11
30
  after { selector.close }
12
31
 
32
+ before { example_peers } # open server and client automatically
33
+ after { reader.close }
34
+ after { writer.close }
35
+
13
36
  it "knows its interests" do
14
37
  expect(subject.interests).to eq(:r)
15
38
  expect(peer.interests).to eq(:rw)
16
39
  end
17
40
 
41
+ it "changes the interest set" do
42
+ expect(peer.interests).not_to eq(:w)
43
+ peer.interests = :w
44
+ expect(peer.interests).to eq(:w)
45
+ end
46
+
18
47
  it "knows its IO object" do
19
48
  expect(subject.io).to eq(reader)
20
49
  end
@@ -34,14 +63,13 @@ RSpec.describe NIO::Monitor do
34
63
  writer_monitor = peer
35
64
 
36
65
  selected = selector.select(0)
37
- expect(selected).not_to include(reader_monitor)
38
66
  expect(selected).to include(writer_monitor)
39
67
 
40
68
  expect(writer_monitor.readiness).to eq(:w)
41
69
  expect(writer_monitor).not_to be_readable
42
70
  expect(writer_monitor).to be_writable
43
71
 
44
- writer << "loldata"
72
+ writer << "testing 1 2 3"
45
73
 
46
74
  selected = selector.select(0)
47
75
  expect(selected).to include(reader_monitor)
@@ -51,6 +79,14 @@ RSpec.describe NIO::Monitor do
51
79
  expect(reader_monitor).not_to be_writable
52
80
  end
53
81
 
82
+ it "changes current interests with #interests=" do
83
+ client = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
84
+ monitor = selector.register(client, :r)
85
+ expect(monitor.interests).to eq(:r)
86
+ monitor.interests = :w
87
+ expect(monitor.interests).to eq(:w)
88
+ end
89
+
54
90
  it "closes" do
55
91
  expect(subject).not_to be_closed
56
92
  expect(selector.registered?(reader)).to be_truthy
@@ -67,4 +103,11 @@ RSpec.describe NIO::Monitor do
67
103
  subject.close
68
104
  expect(subject).to be_closed
69
105
  end
106
+
107
+ it "changes the interest set after monitor closed" do
108
+ # check for changing the interests on the go after closed expected to fail
109
+ expect(subject.interests).not_to eq(:rw)
110
+ subject.close # forced shutdown
111
+ expect { subject.interests = :rw }.to raise_error(TypeError)
112
+ end
70
113
  end
@@ -80,7 +80,7 @@ RSpec.describe TCPSocket do
80
80
  client.connect_nonblock Socket.sockaddr_in(tcp_port, "127.0.0.1")
81
81
  end.to raise_exception Errno::EINPROGRESS
82
82
 
83
- expect(selector.select(0)).to include monitor
83
+ expect(selector.select(0.001)).to include monitor
84
84
  result = client.getsockopt(::Socket::SOL_SOCKET, ::Socket::SO_ERROR)
85
85
  expect(result.unpack("i").first).to be_zero
86
86
  end
@@ -161,6 +161,12 @@ RSpec.describe NIO::Selector do
161
161
  expect(readables).to include monitor2
162
162
  expect(readables).not_to include monitor3
163
163
  end
164
+
165
+ it "raises IOError if asked to select on a closed selector" do
166
+ subject.close
167
+
168
+ expect { subject.select(0) }.to raise_exception IOError
169
+ end
164
170
  end
165
171
 
166
172
  it "closes" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nio4r
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Arcieri
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-18 00:00:00.000000000 Z
11
+ date: 2015-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -145,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
145
  version: '0'
146
146
  requirements: []
147
147
  rubyforge_project:
148
- rubygems_version: 2.4.6
148
+ rubygems_version: 2.4.8
149
149
  signing_key:
150
150
  specification_version: 4
151
151
  summary: NIO provides a high performance selector API for monitoring IO objects