io-event 1.17.0 → 1.18.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be6f12aa408dcf76d86fbac8ae598d4a6313b65275357d611eeae9dc2fd1fe75
4
- data.tar.gz: facad52118ca469a78fa08187f14265fc98b577b1e703d76e66c83fc733a3b21
3
+ metadata.gz: 7ad9ab9326ef0bb4d637833e5d9705940a7d150c0e42154ee4642fbf6f4b4b49
4
+ data.tar.gz: bb1274afaa893df427b3eb422aa5c198da08493126e8b5f3ee2daac375f1ebda
5
5
  SHA512:
6
- metadata.gz: a32f4bf15f0377d88912e799f87898787ee817e24c2b3638dca609d54ebc9ef3c97258732545be36fb90ef0a26455c210047872f686736d5fae2d6fbf8495aef
7
- data.tar.gz: 931c9b8ee443cc311a8d8823dde3128f109e8cdc739fdc421bf959c5ebfb8fea9a17a17fdc2020a65b0b970eafa91d4cba7fa6ff483165b2c4fca862061ca93c
6
+ metadata.gz: 543bdb171056e37849b7042c0d822e7421908bbd006227be92b1b2c5c12d7988c8201ce02316c9f0aa32df9746f4a6d88345c8eb9f38f9c5018f4adbaeca27b9
7
+ data.tar.gz: 65b3f2b61cf38b3c66cb2d0e9299ece198e546d0e30676f8bbd616fdccb043b47f032f0e8fd00bb5bf14bdef471f5ebb1c4769e8de86654900916fc8952c2be7
checksums.yaml.gz.sig CHANGED
Binary file
@@ -830,14 +830,33 @@ struct timespec * make_timeout(VALUE duration, struct timespec * storage) {
830
830
  }
831
831
 
832
832
  static
833
- int timeout_nonblocking(struct timespec * timespec) {
833
+ int timeout_is_nonblocking(struct timespec * timespec) {
834
834
  return timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0;
835
835
  }
836
836
 
837
+ // Return true when it is safe and useful to enter the blocking selector wait.
838
+ static
839
+ int select_blocking_allowed(struct timespec * timespec) {
840
+ // A `0` timeout is already a poll. The selector has already performed the immediate poll previously, so there is no useful blocking wait to enter here.
841
+ if (timeout_is_nonblocking(timespec)) {
842
+ return 0;
843
+ }
844
+
845
+ #ifndef RB_NOGVL_PENDING_INTERRUPT_FAIL
846
+ // On Rubies without `RB_NOGVL_PENDING_INTERRUPT_FAIL`, `rb_thread_call_without_gvl2` can enter an indefinite native wait even if a masked interrupt is already pending for this thread. This is the last safe point to avoid that wait: we still hold the GVL, so the pending interrupt queue cannot change concurrently before we decide whether to enter the blocking path.
847
+ if (IO_Event_Selector_pending_interrupt()) {
848
+ return 0;
849
+ }
850
+ #endif
851
+
852
+ return 1;
853
+ }
854
+
837
855
  struct select_arguments {
838
856
  struct IO_Event_Selector_EPoll *selector;
839
857
 
840
858
  int count;
859
+ int result;
841
860
  struct epoll_event events[EPOLL_MAX_EVENTS];
842
861
 
843
862
  struct timespec * timeout;
@@ -851,7 +870,7 @@ static int make_timeout_ms(struct timespec * timeout) {
851
870
  return -1;
852
871
  }
853
872
 
854
- if (timeout_nonblocking(timeout)) {
873
+ if (timeout_is_nonblocking(timeout)) {
855
874
  return 0;
856
875
  }
857
876
 
@@ -872,13 +891,13 @@ void * select_internal(void *_arguments) {
872
891
  struct select_arguments * arguments = (struct select_arguments *)_arguments;
873
892
 
874
893
  #if defined(HAVE_EPOLL_PWAIT2)
875
- arguments->count = epoll_pwait2(arguments->selector->descriptor, arguments->events, EPOLL_MAX_EVENTS, arguments->timeout, NULL);
894
+ arguments->result = epoll_pwait2(arguments->selector->descriptor, arguments->events, arguments->count, arguments->timeout, NULL);
876
895
 
877
896
  // Comment out the above line and enable the below lines to test ENOSYS code path.
878
- // arguments->count = -1;
897
+ // arguments->result = -1;
879
898
  // errno = ENOSYS;
880
899
 
881
- if (!enosys_error(arguments->count)) {
900
+ if (!enosys_error(arguments->result)) {
882
901
  return NULL;
883
902
  }
884
903
  else {
@@ -886,37 +905,47 @@ void * select_internal(void *_arguments) {
886
905
  }
887
906
  #endif
888
907
 
889
- arguments->count = epoll_wait(arguments->selector->descriptor, arguments->events, EPOLL_MAX_EVENTS, make_timeout_ms(arguments->timeout));
908
+ arguments->result = epoll_wait(arguments->selector->descriptor, arguments->events, arguments->count, make_timeout_ms(arguments->timeout));
890
909
 
891
910
  return NULL;
892
911
  }
893
912
 
894
913
  static
895
- void select_internal_without_gvl(struct select_arguments *arguments) {
914
+ int select_internal_without_gvl(struct select_arguments *arguments) {
915
+ arguments->result = -1;
896
916
  arguments->selector->blocked = 1;
897
- rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
917
+ #ifdef RB_NOGVL_PENDING_INTERRUPT_FAIL
918
+ rb_nogvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0, RB_NOGVL_INTR_FAIL | RB_NOGVL_PENDING_INTERRUPT_FAIL);
919
+ #else
920
+ rb_thread_call_without_gvl2(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
921
+ #endif
898
922
  arguments->selector->blocked = 0;
899
923
 
900
- if (arguments->count == -1) {
901
- if (errno != EINTR) {
924
+ if (arguments->result == -1) {
925
+ // If Ruby skips the native callback, the result sentinel can remain `-1`; `errno` may be `0` or `EINTR` depending on the Ruby implementation. Both cases mean the blocking wait did not produce any events.
926
+ if (errno != EINTR && errno != 0) {
902
927
  rb_sys_fail("select_internal_without_gvl:epoll_wait");
903
928
  } else {
904
- arguments->count = 0;
929
+ return 0;
905
930
  }
906
931
  }
932
+
933
+ return arguments->result;
907
934
  }
908
935
 
909
936
  static
910
- void select_internal_with_gvl(struct select_arguments *arguments) {
937
+ int select_internal_with_gvl(struct select_arguments *arguments) {
911
938
  select_internal((void *)arguments);
912
939
 
913
- if (arguments->count == -1) {
940
+ if (arguments->result == -1) {
914
941
  if (errno != EINTR) {
915
942
  rb_sys_fail("select_internal_with_gvl:epoll_wait");
916
943
  } else {
917
- arguments->count = 0;
944
+ return 0;
918
945
  }
919
946
  }
947
+
948
+ return arguments->result;
920
949
  }
921
950
 
922
951
  static
@@ -972,7 +1001,7 @@ VALUE select_handle_events(VALUE _arguments)
972
1001
  struct select_arguments *arguments = (struct select_arguments *)_arguments;
973
1002
  struct IO_Event_Selector_EPoll *selector = arguments->selector;
974
1003
 
975
- for (int i = 0; i < arguments->count; i += 1) {
1004
+ for (int i = 0; i < arguments->result; i += 1) {
976
1005
  const struct epoll_event *event = &arguments->events[i];
977
1006
  if (DEBUG) fprintf(stderr, "-> fd=%d events=%d\n", event->data.fd, event->events);
978
1007
 
@@ -983,7 +1012,7 @@ VALUE select_handle_events(VALUE _arguments)
983
1012
  }
984
1013
  }
985
1014
 
986
- return INT2NUM(arguments->count);
1015
+ return INT2NUM(arguments->result);
987
1016
  }
988
1017
 
989
1018
  static
@@ -1008,6 +1037,8 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
1008
1037
 
1009
1038
  struct select_arguments arguments = {
1010
1039
  .selector = selector,
1040
+ .count = EPOLL_MAX_EVENTS,
1041
+ .result = 0,
1011
1042
  .storage = {
1012
1043
  .tv_sec = 0,
1013
1044
  .tv_nsec = 0
@@ -1018,22 +1049,22 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
1018
1049
  arguments.timeout = &arguments.storage;
1019
1050
 
1020
1051
  // Process any currently pending events:
1021
- select_internal_with_gvl(&arguments);
1052
+ int result = select_internal_with_gvl(&arguments);
1022
1053
 
1023
1054
  // If we:
1024
1055
  // 1. Didn't process any ready fibers, and
1025
1056
  // 2. Didn't process any events from non-blocking select (above), and
1026
1057
  // 3. There are no items in the ready list,
1027
1058
  // then we can perform a blocking select.
1028
- if (!ready && !arguments.count && !selector->backend.ready) {
1059
+ if (!ready && !result && !selector->backend.ready) {
1029
1060
  arguments.timeout = make_timeout(duration, &arguments.storage);
1030
1061
 
1031
- if (!timeout_nonblocking(arguments.timeout)) {
1062
+ if (select_blocking_allowed(arguments.timeout)) {
1032
1063
  struct timespec start_time;
1033
1064
  IO_Event_Time_current(&start_time);
1034
1065
 
1035
1066
  // Wait for events to occur:
1036
- select_internal_without_gvl(&arguments);
1067
+ result = select_internal_without_gvl(&arguments);
1037
1068
 
1038
1069
  struct timespec end_time;
1039
1070
  IO_Event_Time_current(&end_time);
@@ -1041,7 +1072,7 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
1041
1072
  }
1042
1073
  }
1043
1074
 
1044
- if (arguments.count) {
1075
+ if (result) {
1045
1076
  return rb_ensure(select_handle_events, (VALUE)&arguments, select_handle_events_ensure, (VALUE)&arguments);
1046
1077
  } else {
1047
1078
  return RB_INT2NUM(0);
@@ -838,15 +838,29 @@ struct timespec * make_timeout(VALUE duration, struct timespec * storage) {
838
838
  return storage;
839
839
  }
840
840
 
841
+ // Return true when it is safe and useful to enter the blocking selector wait.
841
842
  static
842
- int timeout_nonblocking(struct timespec * timespec) {
843
- return timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0;
843
+ int select_blocking_allowed(struct timespec * timespec) {
844
+ // A `0` timeout is already a poll. The selector has already performed the immediate poll previously, so there is no useful blocking wait to enter here.
845
+ if (timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0) {
846
+ return 0;
847
+ }
848
+
849
+ #ifndef RB_NOGVL_PENDING_INTERRUPT_FAIL
850
+ // On Rubies without `RB_NOGVL_PENDING_INTERRUPT_FAIL`, `rb_thread_call_without_gvl2` can enter an indefinite native wait even if a masked interrupt is already pending for this thread. This is the last safe point to avoid that wait: we still hold the GVL, so the pending interrupt queue cannot change concurrently before we decide whether to enter the blocking path.
851
+ if (IO_Event_Selector_pending_interrupt()) {
852
+ return 0;
853
+ }
854
+ #endif
855
+
856
+ return 1;
844
857
  }
845
858
 
846
859
  struct select_arguments {
847
860
  struct IO_Event_Selector_KQueue *selector;
848
861
 
849
862
  int count;
863
+ int result;
850
864
  struct kevent events[KQUEUE_MAX_EVENTS];
851
865
 
852
866
  struct timespec storage;
@@ -859,38 +873,48 @@ static
859
873
  void * select_internal(void *_arguments) {
860
874
  struct select_arguments * arguments = (struct select_arguments *)_arguments;
861
875
 
862
- arguments->count = kevent(arguments->selector->descriptor, NULL, 0, arguments->events, arguments->count, arguments->timeout);
876
+ arguments->result = kevent(arguments->selector->descriptor, NULL, 0, arguments->events, arguments->count, arguments->timeout);
863
877
 
864
878
  return NULL;
865
879
  }
866
880
 
867
881
  static
868
- void select_internal_without_gvl(struct select_arguments *arguments) {
882
+ int select_internal_without_gvl(struct select_arguments *arguments) {
883
+ arguments->result = -1;
869
884
  arguments->selector->blocked = 1;
870
885
 
871
- rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
886
+ #ifdef RB_NOGVL_PENDING_INTERRUPT_FAIL
887
+ rb_nogvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0, RB_NOGVL_INTR_FAIL | RB_NOGVL_PENDING_INTERRUPT_FAIL);
888
+ #else
889
+ rb_thread_call_without_gvl2(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
890
+ #endif
872
891
  arguments->selector->blocked = 0;
873
892
 
874
- if (arguments->count == -1) {
875
- if (errno != EINTR) {
893
+ if (arguments->result == -1) {
894
+ // If Ruby skips the native callback, the result sentinel can remain `-1`; `errno` may be `0` or `EINTR` depending on the Ruby implementation. Both cases mean the blocking wait did not produce any events.
895
+ if (errno != EINTR && errno != 0) {
876
896
  rb_sys_fail("select_internal_without_gvl:kevent");
877
897
  } else {
878
- arguments->count = 0;
898
+ return 0;
879
899
  }
880
900
  }
901
+
902
+ return arguments->result;
881
903
  }
882
904
 
883
905
  static
884
- void select_internal_with_gvl(struct select_arguments *arguments) {
906
+ int select_internal_with_gvl(struct select_arguments *arguments) {
885
907
  select_internal((void *)arguments);
886
908
 
887
- if (arguments->count == -1) {
909
+ if (arguments->result == -1) {
888
910
  if (errno != EINTR) {
889
911
  rb_sys_fail("select_internal_with_gvl:kevent");
890
912
  } else {
891
- arguments->count = 0;
913
+ return 0;
892
914
  }
893
915
  }
916
+
917
+ return arguments->result;
894
918
  }
895
919
 
896
920
  static
@@ -944,14 +968,14 @@ VALUE select_handle_events(VALUE _arguments)
944
968
  struct select_arguments *arguments = (struct select_arguments *)_arguments;
945
969
  struct IO_Event_Selector_KQueue *selector = arguments->selector;
946
970
 
947
- for (int i = 0; i < arguments->count; i += 1) {
971
+ for (int i = 0; i < arguments->result; i += 1) {
948
972
  if (arguments->events[i].udata) {
949
973
  struct IO_Event_Selector_KQueue_Descriptor *kqueue_descriptor = arguments->events[i].udata;
950
974
  kqueue_descriptor->ready_events |= events_from_kevent_filter(arguments->events[i].filter);
951
975
  }
952
976
  }
953
977
 
954
- for (int i = 0; i < arguments->count; i += 1) {
978
+ for (int i = 0; i < arguments->result; i += 1) {
955
979
  if (arguments->events[i].udata) {
956
980
  struct IO_Event_Selector_KQueue_Descriptor *kqueue_descriptor = arguments->events[i].udata;
957
981
  IO_Event_Selector_KQueue_handle(selector, arguments->events[i].ident, kqueue_descriptor, &arguments->saved);
@@ -962,7 +986,7 @@ VALUE select_handle_events(VALUE _arguments)
962
986
  }
963
987
  }
964
988
 
965
- return RB_INT2NUM(arguments->count);
989
+ return RB_INT2NUM(arguments->result);
966
990
  }
967
991
 
968
992
  static
@@ -987,6 +1011,7 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
987
1011
  struct select_arguments arguments = {
988
1012
  .selector = selector,
989
1013
  .count = KQUEUE_MAX_EVENTS,
1014
+ .result = 0,
990
1015
  .storage = {
991
1016
  .tv_sec = 0,
992
1017
  .tv_nsec = 0
@@ -997,14 +1022,14 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
997
1022
  arguments.timeout = &arguments.storage;
998
1023
 
999
1024
  // We break this implementation into two parts.
1000
- // (1) count = kevent(..., timeout = 0)
1001
- // (2) without gvl: kevent(..., timeout = 0) if count == 0 and timeout != 0
1025
+ // (1) result = kevent(..., timeout = 0)
1026
+ // (2) without gvl: kevent(..., timeout = 0) if result == 0 and timeout != 0
1002
1027
  // This allows us to avoid releasing and reacquiring the GVL.
1003
1028
  // Non-comprehensive testing shows this gives a 1.5x speedup.
1004
1029
 
1005
1030
  // First do the syscall with no timeout to get any immediately available events:
1006
1031
  if (DEBUG) fprintf(stderr, "\r\nselect_internal_with_gvl timeout=" IO_EVENT_TIME_PRINTF_TIMESPEC "\r\n", IO_EVENT_TIME_PRINTF_TIMESPEC_ARGUMENTS(arguments.storage));
1007
- select_internal_with_gvl(&arguments);
1032
+ int result = select_internal_with_gvl(&arguments);
1008
1033
  if (DEBUG) fprintf(stderr, "\r\nselect_internal_with_gvl done\r\n");
1009
1034
 
1010
1035
  // If we:
@@ -1012,17 +1037,15 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
1012
1037
  // 2. Didn't process any events from non-blocking select (above), and
1013
1038
  // 3. There are no items in the ready list,
1014
1039
  // then we can perform a blocking select.
1015
- if (!ready && !arguments.count && !selector->backend.ready) {
1040
+ if (!ready && !result && !selector->backend.ready) {
1016
1041
  arguments.timeout = make_timeout(duration, &arguments.storage);
1017
1042
 
1018
- if (!timeout_nonblocking(arguments.timeout)) {
1019
- arguments.count = KQUEUE_MAX_EVENTS;
1020
-
1043
+ if (select_blocking_allowed(arguments.timeout)) {
1021
1044
  struct timespec start_time;
1022
1045
  IO_Event_Time_current(&start_time);
1023
1046
 
1024
1047
  if (DEBUG) fprintf(stderr, "IO_Event_Selector_KQueue_select timeout=" IO_EVENT_TIME_PRINTF_TIMESPEC "\n", IO_EVENT_TIME_PRINTF_TIMESPEC_ARGUMENTS(arguments.storage));
1025
- select_internal_without_gvl(&arguments);
1048
+ result = select_internal_without_gvl(&arguments);
1026
1049
 
1027
1050
  struct timespec end_time;
1028
1051
  IO_Event_Time_current(&end_time);
@@ -1030,7 +1053,7 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
1030
1053
  }
1031
1054
  }
1032
1055
 
1033
- if (arguments.count) {
1056
+ if (result) {
1034
1057
  return rb_ensure(select_handle_events, (VALUE)&arguments, select_handle_events_ensure, (VALUE)&arguments);
1035
1058
  } else {
1036
1059
  return RB_INT2NUM(0);
@@ -8,6 +8,8 @@
8
8
 
9
9
  static const int DEBUG = 0;
10
10
 
11
+ ID IO_Event_Selector_pending_interrupt_p_id;
12
+
11
13
  #ifndef HAVE_RB_IO_DESCRIPTOR
12
14
  static ID id_fileno;
13
15
 
@@ -79,6 +81,8 @@ static VALUE IO_Event_Selector_nonblock(VALUE class, VALUE io)
79
81
  }
80
82
 
81
83
  void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
84
+ IO_Event_Selector_pending_interrupt_p_id = rb_intern("pending_interrupt?");
85
+
82
86
  #ifndef HAVE_RB_IO_DESCRIPTOR
83
87
  id_fileno = rb_intern("fileno");
84
88
  #endif
@@ -40,6 +40,12 @@ static inline int IO_Event_try_again(int error) {
40
40
  return error == EAGAIN || error == EWOULDBLOCK;
41
41
  }
42
42
 
43
+ extern ID IO_Event_Selector_pending_interrupt_p_id;
44
+
45
+ static inline int IO_Event_Selector_pending_interrupt(void) {
46
+ return RTEST(rb_funcall(rb_cThread, IO_Event_Selector_pending_interrupt_p_id, 0));
47
+ }
48
+
43
49
  #ifdef HAVE_RB_IO_DESCRIPTOR
44
50
  #define IO_Event_Selector_io_descriptor(io) rb_io_descriptor(io)
45
51
  #else
@@ -1130,9 +1130,22 @@ struct __kernel_timespec * make_timeout(VALUE duration, struct __kernel_timespec
1130
1130
  return storage;
1131
1131
  }
1132
1132
 
1133
+ // Return true when it is safe and useful to enter the blocking selector wait.
1133
1134
  static
1134
- int timeout_nonblocking(struct __kernel_timespec *timespec) {
1135
- return timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0;
1135
+ int select_blocking_allowed(struct __kernel_timespec *timespec) {
1136
+ // A `0` timeout is already a poll. The selector has already performed the immediate poll previously, so there is no useful blocking wait to enter here.
1137
+ if (timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0) {
1138
+ return 0;
1139
+ }
1140
+
1141
+ #ifndef RB_NOGVL_PENDING_INTERRUPT_FAIL
1142
+ // On Rubies without `RB_NOGVL_PENDING_INTERRUPT_FAIL`, `rb_thread_call_without_gvl2` can enter an indefinite native wait even if a masked interrupt is already pending for this thread. This is the last safe point to avoid that wait: we still hold the GVL, so the pending interrupt queue cannot change concurrently before we decide whether to enter the blocking path.
1143
+ if (IO_Event_Selector_pending_interrupt()) {
1144
+ return 0;
1145
+ }
1146
+ #endif
1147
+
1148
+ return 1;
1136
1149
  }
1137
1150
 
1138
1151
  struct select_arguments {
@@ -1171,22 +1184,27 @@ int select_internal_without_gvl(struct select_arguments *arguments) {
1171
1184
 
1172
1185
  io_uring_submit_flush(selector);
1173
1186
 
1187
+ arguments->result = -EINTR;
1174
1188
  selector->blocked = 1;
1175
- rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
1189
+ #ifdef RB_NOGVL_PENDING_INTERRUPT_FAIL
1190
+ rb_nogvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0, RB_NOGVL_INTR_FAIL | RB_NOGVL_PENDING_INTERRUPT_FAIL);
1191
+ #else
1192
+ rb_thread_call_without_gvl2(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
1193
+ #endif
1176
1194
  selector->blocked = 0;
1177
1195
 
1178
1196
  if (arguments->result == -ETIME) {
1179
- arguments->result = 0;
1197
+ return 0;
1180
1198
  } else if (arguments->result == -EINTR) {
1181
- arguments->result = 0;
1199
+ return 0;
1182
1200
  } else if (arguments->result < 0) {
1183
1201
  rb_syserr_fail(-arguments->result, "select_internal_without_gvl:io_uring_wait_cqe_timeout");
1184
1202
  } else {
1185
1203
  // At least 1 event is waiting:
1186
- arguments->result = 1;
1204
+ return 1;
1187
1205
  }
1188
1206
 
1189
- return arguments->result;
1207
+ return 0;
1190
1208
  }
1191
1209
 
1192
1210
  static inline
@@ -1283,28 +1301,29 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
1283
1301
 
1284
1302
  int ready = IO_Event_Selector_ready_flush(&selector->backend);
1285
1303
 
1286
- int result = select_process_completions(selector);
1304
+ int completed = select_process_completions(selector);
1287
1305
 
1288
1306
  // If we:
1289
1307
  // 1. Didn't process any ready fibers, and
1290
1308
  // 2. Didn't process any events from non-blocking select (above), and
1291
1309
  // 3. There are no items in the ready list,
1292
1310
  // then we can perform a blocking select.
1293
- if (!ready && !result && !selector->backend.ready) {
1311
+ if (!ready && !completed && !selector->backend.ready) {
1294
1312
  // We might need to wait for events:
1295
1313
  struct select_arguments arguments = {
1296
1314
  .selector = selector,
1315
+ .result = 0,
1297
1316
  .timeout = NULL,
1298
1317
  };
1299
1318
 
1300
1319
  arguments.timeout = make_timeout(duration, &arguments.storage);
1301
1320
 
1302
- if (!selector->backend.ready && !timeout_nonblocking(arguments.timeout)) {
1321
+ if (!selector->backend.ready && select_blocking_allowed(arguments.timeout)) {
1303
1322
  struct timespec start_time;
1304
1323
  IO_Event_Time_current(&start_time);
1305
1324
 
1306
1325
  // This is a blocking operation, we wait for events:
1307
- result = select_internal_without_gvl(&arguments);
1326
+ int result = select_internal_without_gvl(&arguments);
1308
1327
 
1309
1328
  struct timespec end_time;
1310
1329
  IO_Event_Time_current(&end_time);
@@ -1312,12 +1331,12 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
1312
1331
 
1313
1332
  // After waiting/flushing the SQ, check if there are any completions:
1314
1333
  if (result > 0) {
1315
- result = select_process_completions(selector);
1334
+ completed = select_process_completions(selector);
1316
1335
  }
1317
1336
  }
1318
1337
  }
1319
1338
 
1320
- return RB_INT2NUM(result);
1339
+ return RB_INT2NUM(completed);
1321
1340
  }
1322
1341
 
1323
1342
  VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
@@ -7,6 +7,6 @@
7
7
  class IO
8
8
  # @namespace
9
9
  module Event
10
- VERSION = "1.17.0"
10
+ VERSION = "1.18.0"
11
11
  end
12
12
  end
data/readme.md CHANGED
@@ -18,6 +18,10 @@ Please see the [project documentation](https://socketry.github.io/io-event/) for
18
18
 
19
19
  Please see the [project releases](https://socketry.github.io/io-event/releases/index) for all releases.
20
20
 
21
+ ### v1.18.0
22
+
23
+ - **Fixed**: Avoid entering a blocking native selector wait when an interrupt is already pending for the current thread.
24
+
21
25
  ### v1.17.0
22
26
 
23
27
  - Report inherited selector objects as closed after fork, and avoid closing descriptors they no longer own.
@@ -60,10 +64,6 @@ Please see the [project releases](https://socketry.github.io/io-event/releases/i
60
64
 
61
65
  - Allow `epoll_pwait2` to be disabled via `--disable-epoll_pwait2`.
62
66
 
63
- ### v1.14.3
64
-
65
- - Fix several implementation bugs that could cause deadlocks on blocking writes.
66
-
67
67
  ## Contributing
68
68
 
69
69
  We welcome contributions to this project.
data/releases.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Releases
2
2
 
3
+ ## v1.18.0
4
+
5
+ - **Fixed**: Avoid entering a blocking native selector wait when an interrupt is already pending for the current thread.
6
+
3
7
  ## v1.17.0
4
8
 
5
9
  - Report inherited selector objects as closed after fork, and avoid closing descriptors they no longer own.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io-event
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.17.0
4
+ version: 1.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
metadata.gz.sig CHANGED
@@ -1,2 +1 @@
1
- N"=|�?p]�8G;6R���%�#�2�Z��˕���8 �6~1_GN�*����c�z���&�O4����9vt���E�Y>��ׁST0s_(��vɌuu_(�acƨj���y´�Ě���̔��dH����Gj��Z]od1j��� �-��1�DU��FlZ�͈��C�Ц��la\�����@˜����>PxrA�����d5���V���k���,���gh����I�ƒ��-P�zvnuhxo !(N+���r� ��0!�T*>��j��a���.Q}
2
- D�mC[ Pf�Q��F�u�Ƥ2bA�E���Oӕ�ف�Y|�z"d������0dm��ͿR:�v�ճ�'���bNc�
1
+ Z�`.a��&����׏rc���8��C|�)ex�U�@��FzP~ �]�/��E��W�Ōl RR8֌G�-; �^�F<���^�vo2o�%x����5<fl'9��Q���V[e9{V Z��lC�����c���iK8~��JsC�M=!�J8�|�L���� ���q۔�ۯ@`��gذY�B5㿂ʢ�GRm7���%f ���?�R.�f9��&i���]���e����]�6bJpk_�`z�����ʷl��KWf!f�W���s{r�����x~�jNQ'+N������d���;3 Q�������Ph������>�pa "d�D�1�iV�Cy��kC����MM�j��%&�8��������s�$@��