eventmachine 1.0.0.beta.3-java → 1.0.0.beta.4-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/.gitignore +5 -0
  2. data/.yardopts +5 -1
  3. data/{docs/GNU → GNU} +0 -0
  4. data/Gemfile +1 -0
  5. data/{docs/COPYING → LICENSE} +0 -0
  6. data/README.md +109 -0
  7. data/Rakefile +8 -0
  8. data/docs/DocumentationGuidesIndex.md +27 -0
  9. data/docs/GettingStarted.md +521 -0
  10. data/docs/{ChangeLog → old/ChangeLog} +0 -0
  11. data/docs/{DEFERRABLES → old/DEFERRABLES} +0 -0
  12. data/docs/{EPOLL → old/EPOLL} +0 -0
  13. data/docs/{INSTALL → old/INSTALL} +0 -0
  14. data/docs/{KEYBOARD → old/KEYBOARD} +0 -0
  15. data/docs/{LEGAL → old/LEGAL} +0 -0
  16. data/docs/{LIGHTWEIGHT_CONCURRENCY → old/LIGHTWEIGHT_CONCURRENCY} +0 -0
  17. data/docs/{PURE_RUBY → old/PURE_RUBY} +0 -0
  18. data/docs/{RELEASE_NOTES → old/RELEASE_NOTES} +0 -0
  19. data/docs/{SMTP → old/SMTP} +0 -0
  20. data/docs/{SPAWNED_PROCESSES → old/SPAWNED_PROCESSES} +0 -0
  21. data/docs/{TODO → old/TODO} +0 -0
  22. data/eventmachine.gemspec +5 -2
  23. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  24. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  25. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  26. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  27. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  28. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  29. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  30. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  31. data/examples/{ex_channel.rb → old/ex_channel.rb} +3 -3
  32. data/examples/{ex_queue.rb → old/ex_queue.rb} +0 -0
  33. data/examples/{ex_tick_loop_array.rb → old/ex_tick_loop_array.rb} +0 -0
  34. data/examples/{ex_tick_loop_counter.rb → old/ex_tick_loop_counter.rb} +0 -0
  35. data/examples/{helper.rb → old/helper.rb} +0 -0
  36. data/ext/cmain.cpp +3 -3
  37. data/ext/ed.cpp +90 -15
  38. data/ext/ed.h +5 -5
  39. data/ext/em.cpp +59 -65
  40. data/ext/em.h +12 -2
  41. data/ext/extconf.rb +3 -3
  42. data/ext/pipe.cpp +2 -2
  43. data/ext/project.h +1 -1
  44. data/ext/rubymain.cpp +48 -3
  45. data/ext/ssl.cpp +5 -0
  46. data/java/src/com/rubyeventmachine/EmReactor.java +2 -2
  47. data/lib/em/buftok.rb +35 -63
  48. data/lib/em/callback.rb +43 -11
  49. data/lib/em/channel.rb +21 -14
  50. data/lib/em/completion.rb +304 -0
  51. data/lib/em/connection.rb +339 -209
  52. data/lib/em/deferrable.rb +4 -0
  53. data/lib/em/deferrable/pool.rb +2 -0
  54. data/lib/em/file_watch.rb +37 -18
  55. data/lib/em/iterator.rb +42 -42
  56. data/lib/em/pool.rb +146 -0
  57. data/lib/em/process_watch.rb +5 -4
  58. data/lib/em/processes.rb +8 -4
  59. data/lib/em/protocols/httpclient.rb +22 -11
  60. data/lib/em/protocols/httpclient2.rb +15 -5
  61. data/lib/em/protocols/line_protocol.rb +2 -1
  62. data/lib/em/protocols/memcache.rb +17 -9
  63. data/lib/em/protocols/object_protocol.rb +2 -1
  64. data/lib/em/protocols/postgres3.rb +8 -9
  65. data/lib/em/protocols/smtpclient.rb +19 -11
  66. data/lib/em/protocols/smtpserver.rb +1 -1
  67. data/lib/em/protocols/stomp.rb +8 -6
  68. data/lib/em/protocols/tcptest.rb +3 -2
  69. data/lib/em/pure_ruby.rb +212 -208
  70. data/lib/em/queue.rb +22 -13
  71. data/lib/em/resolver.rb +70 -64
  72. data/lib/em/spawnable.rb +6 -3
  73. data/lib/em/streamer.rb +33 -45
  74. data/lib/em/threaded_resource.rb +90 -0
  75. data/lib/em/timers.rb +6 -2
  76. data/lib/em/version.rb +1 -1
  77. data/lib/eventmachine.rb +538 -602
  78. data/lib/jeventmachine.rb +22 -1
  79. data/tasks/package.rake +13 -3
  80. data/tasks/test.rake +1 -0
  81. data/tests/em_test_helper.rb +12 -3
  82. data/tests/test_completion.rb +177 -0
  83. data/tests/test_epoll.rb +2 -2
  84. data/tests/test_httpclient.rb +9 -9
  85. data/tests/test_httpclient2.rb +11 -9
  86. data/tests/test_ltp.rb +2 -10
  87. data/tests/test_pool.rb +128 -0
  88. data/tests/test_processes.rb +20 -2
  89. data/tests/test_queue.rb +8 -0
  90. data/tests/test_resolver.rb +1 -1
  91. data/tests/test_set_sock_opt.rb +37 -0
  92. data/tests/test_shutdown_hooks.rb +23 -0
  93. data/tests/test_threaded_resource.rb +53 -0
  94. data/tests/test_unbind_reason.rb +31 -0
  95. metadata +87 -45
  96. data/README +0 -81
  97. data/tasks/doc.rake +0 -30
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems' # or use Bundler.setup
4
+ require 'eventmachine'
5
+
6
+ class SimpleChatServer < EM::Connection
7
+
8
+ @@connected_clients = Array.new
9
+ DM_REGEXP = /^@([a-zA-Z0-9]+)\s*:?\s+(.+)/.freeze
10
+
11
+ attr_reader :username
12
+
13
+
14
+ #
15
+ # EventMachine handlers
16
+ #
17
+
18
+ def post_init
19
+ @username = nil
20
+
21
+ puts "A client has connected..."
22
+ ask_username
23
+ end
24
+
25
+ def unbind
26
+ @@connected_clients.delete(self)
27
+ puts "[info] #{@username} has left" if entered_username?
28
+ end
29
+
30
+ def receive_data(data)
31
+ if entered_username?
32
+ handle_chat_message(data.strip)
33
+ else
34
+ handle_username(data.strip)
35
+ end
36
+ end
37
+
38
+
39
+ #
40
+ # Username handling
41
+ #
42
+
43
+ def entered_username?
44
+ !@username.nil? && !@username.empty?
45
+ end # entered_username?
46
+
47
+ def handle_username(input)
48
+ if input.empty?
49
+ send_line("Blank usernames are not allowed. Try again.")
50
+ ask_username
51
+ else
52
+ @username = input
53
+ @@connected_clients.push(self)
54
+ self.other_peers.each { |c| c.send_data("#{@username} has joined the room\n") }
55
+ puts "#{@username} has joined"
56
+
57
+ self.send_line("[info] Ohai, #{@username}")
58
+ end
59
+ end # handle_username(input)
60
+
61
+ def ask_username
62
+ self.send_line("[info] Enter your username:")
63
+ end # ask_username
64
+
65
+
66
+ #
67
+ # Message handling
68
+ #
69
+
70
+ def handle_chat_message(msg)
71
+ if command?(msg)
72
+ self.handle_command(msg)
73
+ else
74
+ if direct_message?(msg)
75
+ self.handle_direct_message(msg)
76
+ else
77
+ self.announce(msg, "#{@username}:")
78
+ end
79
+ end
80
+ end # handle_chat_message(msg)
81
+
82
+ def direct_message?(input)
83
+ input =~ DM_REGEXP
84
+ end # direct_message?(input)
85
+
86
+ def handle_direct_message(input)
87
+ username, message = parse_direct_message(input)
88
+
89
+ if connection = @@connected_clients.find { |c| c.username == username }
90
+ puts "[dm] @#{@username} => @#{username}"
91
+ connection.send_line("[dm] @#{@username}: #{message}")
92
+ else
93
+ send_line "@#{username} is not in the room. Here's who is: #{usernames.join(', ')}"
94
+ end
95
+ end # handle_direct_message(input)
96
+
97
+ def parse_direct_message(input)
98
+ return [$1, $2] if input =~ DM_REGEXP
99
+ end # parse_direct_message(input)
100
+
101
+
102
+ #
103
+ # Commands handling
104
+ #
105
+
106
+ def command?(input)
107
+ input =~ /(exit|status)$/i
108
+ end # command?(input)
109
+
110
+ def handle_command(cmd)
111
+ case cmd
112
+ when /exit$/i then self.close_connection
113
+ when /status$/i then self.send_line("[chat server] It's #{Time.now.strftime('%H:%M')} and there are #{self.number_of_connected_clients} people in the room")
114
+ end
115
+ end # handle_command(cmd)
116
+
117
+
118
+ #
119
+ # Helpers
120
+ #
121
+
122
+ def announce(msg = nil, prefix = "[chat server]")
123
+ @@connected_clients.each { |c| c.send_line("#{prefix} #{msg}") } unless msg.empty?
124
+ end # announce(msg)
125
+
126
+ def other_peers
127
+ @@connected_clients.reject { |c| self == c }
128
+ end # other_peers
129
+
130
+ def send_line(line)
131
+ self.send_data("#{line}\n")
132
+ end # send_line(line)
133
+ end
134
+
135
+ EventMachine.run do
136
+ # hit Control + C to stop
137
+ Signal.trap("INT") { EventMachine.stop }
138
+ Signal.trap("TERM") { EventMachine.stop }
139
+
140
+ EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer)
141
+ end
@@ -33,11 +33,11 @@ EM.run do
33
33
  end
34
34
  end
35
35
 
36
- # This part of the example is more fake, but imagine sleep was in fact a
36
+ # This part of the example is more fake, but imagine sleep was in fact a
37
37
  # long running calculation to achieve the value.
38
38
  40.times do
39
39
  EM.defer lambda { v = sleep(rand * 2); RandChannel << [Time.now, v] }
40
40
  end
41
-
41
+
42
42
  EM.add_timer(5) { EM.stop }
43
- end
43
+ end
File without changes
File without changes
data/ext/cmain.cpp CHANGED
@@ -220,7 +220,7 @@ evma_pause
220
220
 
221
221
  extern "C" int evma_pause (const unsigned long binding)
222
222
  {
223
- ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
223
+ EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
224
224
  if (cd)
225
225
  return cd->Pause() ? 1 : 0;
226
226
 
@@ -233,7 +233,7 @@ evma_resume
233
233
 
234
234
  extern "C" int evma_resume (const unsigned long binding)
235
235
  {
236
- ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
236
+ EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
237
237
  if (cd)
238
238
  return cd->Resume() ? 1 : 0;
239
239
 
@@ -246,7 +246,7 @@ evma_is_paused
246
246
 
247
247
  extern "C" int evma_is_paused (const unsigned long binding)
248
248
  {
249
- ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
249
+ EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
250
250
  if (cd)
251
251
  return cd->IsPaused() ? 1 : 0;
252
252
 
data/ext/ed.cpp CHANGED
@@ -63,7 +63,8 @@ EventableDescriptor::EventableDescriptor (int sd, EventMachine_t *em):
63
63
  MaxOutboundBufSize(0),
64
64
  MyEventMachine (em),
65
65
  PendingConnectTimeout(20000000),
66
- InactivityTimeout (0)
66
+ InactivityTimeout (0),
67
+ bPaused (false)
67
68
  {
68
69
  /* There are three ways to close a socket, all of which should
69
70
  * automatically signal to the event machine that this object
@@ -107,7 +108,7 @@ EventableDescriptor::~EventableDescriptor
107
108
  EventableDescriptor::~EventableDescriptor()
108
109
  {
109
110
  if (NextHeartbeat)
110
- MyEventMachine->ClearHeartbeat(NextHeartbeat);
111
+ MyEventMachine->ClearHeartbeat(NextHeartbeat, this);
111
112
  if (EventCallback && bCallbackUnbind)
112
113
  (*EventCallback)(GetBinding(), EM_CONNECTION_UNBOUND, NULL, UnbindReasonCode);
113
114
  if (ProxiedFrom) {
@@ -135,10 +136,54 @@ EventableDescriptor::Close
135
136
 
136
137
  void EventableDescriptor::Close()
137
138
  {
139
+ /* EventMachine relies on the fact that when close(fd)
140
+ * is called that the fd is removed from any
141
+ * epoll event queues.
142
+ *
143
+ * However, this is not *always* the behavior of close(fd)
144
+ *
145
+ * See man 4 epoll Q6/A6 and then consider what happens
146
+ * when using pipes with eventmachine.
147
+ * (As is often done when communicating with a subprocess)
148
+ *
149
+ * The pipes end up looking like:
150
+ *
151
+ * ls -l /proc/<pid>/fd
152
+ * ...
153
+ * lr-x------ 1 root root 64 2011-08-19 21:31 3 -> pipe:[940970]
154
+ * l-wx------ 1 root root 64 2011-08-19 21:31 4 -> pipe:[940970]
155
+ *
156
+ * This meets the critera from man 4 epoll Q6/A4 for not
157
+ * removing fds from epoll event queues until all fds
158
+ * that reference the underlying file have been removed.
159
+ *
160
+ * If the EventableDescriptor associated with fd 3 is deleted,
161
+ * its dtor will call EventableDescriptor::Close(),
162
+ * which will call ::close(int fd).
163
+ *
164
+ * However, unless the EventableDescriptor associated with fd 4 is
165
+ * also deleted before the next call to epoll_wait, events may fire
166
+ * for fd 3 that were registered with an already deleted
167
+ * EventableDescriptor.
168
+ *
169
+ * Therefore, it is necessary to notify EventMachine that
170
+ * the fd associated with this EventableDescriptor is
171
+ * closing.
172
+ *
173
+ * EventMachine also never closes fds for STDIN, STDOUT and
174
+ * STDERR (0, 1 & 2)
175
+ */
176
+
138
177
  // Close the socket right now. Intended for emergencies.
139
- if (MySocket != INVALID_SOCKET && !bWatchOnly) {
140
- shutdown (MySocket, 1);
141
- close (MySocket);
178
+ if (MySocket != INVALID_SOCKET) {
179
+ MyEventMachine->Deregister (this);
180
+
181
+ // Do not close STDIN, STDOUT, STDERR
182
+ if (MySocket > 2 && !bWatchOnly) {
183
+ shutdown (MySocket, 1);
184
+ close (MySocket);
185
+ }
186
+
142
187
  MySocket = INVALID_SOCKET;
143
188
  }
144
189
  }
@@ -294,7 +339,7 @@ EventableDescriptor::GetNextHeartbeat
294
339
  uint64_t EventableDescriptor::GetNextHeartbeat()
295
340
  {
296
341
  if (NextHeartbeat)
297
- MyEventMachine->ClearHeartbeat(NextHeartbeat);
342
+ MyEventMachine->ClearHeartbeat(NextHeartbeat, this);
298
343
 
299
344
  NextHeartbeat = 0;
300
345
 
@@ -319,7 +364,6 @@ ConnectionDescriptor::ConnectionDescriptor
319
364
 
320
365
  ConnectionDescriptor::ConnectionDescriptor (int sd, EventMachine_t *em):
321
366
  EventableDescriptor (sd, em),
322
- bPaused (false),
323
367
  bConnectPending (false),
324
368
  bNotifyReadable (false),
325
369
  bNotifyWritable (false),
@@ -703,6 +747,7 @@ void ConnectionDescriptor::Read()
703
747
 
704
748
 
705
749
  int r = read (sd, readbuffer, sizeof(readbuffer) - 1);
750
+ int e = errno;
706
751
  //cerr << "<R:" << r << ">";
707
752
 
708
753
  if (r > 0) {
@@ -721,8 +766,21 @@ void ConnectionDescriptor::Read()
721
766
  break;
722
767
  }
723
768
  else {
724
- // Basically a would-block, meaning we've read everything there is to read.
725
- break;
769
+ #ifdef OS_UNIX
770
+ if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EAGAIN) && (e != EINTR)) {
771
+ #endif
772
+ #ifdef OS_WIN32
773
+ if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK)) {
774
+ #endif
775
+ // 26Mar11: Previously, all read errors were assumed to be EWOULDBLOCK and ignored.
776
+ // Now, instead, we call Close() on errors like ECONNRESET and ENOTCONN.
777
+ UnbindReasonCode = e;
778
+ Close();
779
+ break;
780
+ } else {
781
+ // Basically a would-block, meaning we've read everything there is to read.
782
+ break;
783
+ }
726
784
  }
727
785
 
728
786
  }
@@ -831,9 +889,12 @@ void ConnectionDescriptor::Write()
831
889
  // from EventMachine_t::AttachFD as well.
832
890
  SetConnectPending (false);
833
891
  }
834
- else
892
+ else {
893
+ if (o == 0)
894
+ UnbindReasonCode = error;
835
895
  ScheduleClose (false);
836
896
  //bCloseNow = true;
897
+ }
837
898
  }
838
899
  else {
839
900
 
@@ -953,6 +1014,7 @@ void ConnectionDescriptor::_WriteOutboundData()
953
1014
  #endif
954
1015
 
955
1016
  bool err = false;
1017
+ int e = errno;
956
1018
  if (bytes_written < 0) {
957
1019
  err = true;
958
1020
  bytes_written = 0;
@@ -1003,12 +1065,14 @@ void ConnectionDescriptor::_WriteOutboundData()
1003
1065
 
1004
1066
  if (err) {
1005
1067
  #ifdef OS_UNIX
1006
- if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR))
1068
+ if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR)) {
1007
1069
  #endif
1008
1070
  #ifdef OS_WIN32
1009
- if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK))
1071
+ if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK)) {
1010
1072
  #endif
1073
+ UnbindReasonCode = e;
1011
1074
  Close();
1075
+ }
1012
1076
  }
1013
1077
  }
1014
1078
 
@@ -1019,6 +1083,10 @@ ConnectionDescriptor::ReportErrorStatus
1019
1083
 
1020
1084
  int ConnectionDescriptor::ReportErrorStatus()
1021
1085
  {
1086
+ if (MySocket == INVALID_SOCKET) {
1087
+ return -1;
1088
+ }
1089
+
1022
1090
  int error;
1023
1091
  socklen_t len;
1024
1092
  len = sizeof(error);
@@ -1030,8 +1098,10 @@ int ConnectionDescriptor::ReportErrorStatus()
1030
1098
  #endif
1031
1099
  if ((o == 0) && (error == 0))
1032
1100
  return 0;
1101
+ else if (o == 0)
1102
+ return error;
1033
1103
  else
1034
- return 1;
1104
+ return -1;
1035
1105
  }
1036
1106
 
1037
1107
 
@@ -1198,14 +1268,18 @@ void ConnectionDescriptor::Heartbeat()
1198
1268
  */
1199
1269
 
1200
1270
  if (bConnectPending) {
1201
- if ((MyEventMachine->GetCurrentLoopTime() - CreatedAt) >= PendingConnectTimeout)
1271
+ if ((MyEventMachine->GetCurrentLoopTime() - CreatedAt) >= PendingConnectTimeout) {
1272
+ UnbindReasonCode = ETIMEDOUT;
1202
1273
  ScheduleClose (false);
1203
1274
  //bCloseNow = true;
1275
+ }
1204
1276
  }
1205
1277
  else {
1206
- if (InactivityTimeout && ((MyEventMachine->GetCurrentLoopTime() - LastActivity) >= InactivityTimeout))
1278
+ if (InactivityTimeout && ((MyEventMachine->GetCurrentLoopTime() - LastActivity) >= InactivityTimeout)) {
1279
+ UnbindReasonCode = ETIMEDOUT;
1207
1280
  ScheduleClose (false);
1208
1281
  //bCloseNow = true;
1282
+ }
1209
1283
  }
1210
1284
  }
1211
1285
 
@@ -1582,6 +1656,7 @@ void DatagramDescriptor::Write()
1582
1656
  #ifdef OS_WIN32
1583
1657
  if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK)) {
1584
1658
  #endif
1659
+ UnbindReasonCode = e;
1585
1660
  Close();
1586
1661
  break;
1587
1662
  }
data/ext/ed.h CHANGED
@@ -88,10 +88,11 @@ class EventableDescriptor: public Bindable_t
88
88
  virtual void StopProxy();
89
89
  virtual void SetProxiedFrom(EventableDescriptor*, const unsigned long);
90
90
  virtual int SendOutboundData(const char*,int){ return -1; }
91
- virtual bool IsPaused(){ return false; }
92
- virtual bool Pause(){ return false; }
93
- virtual bool Resume(){ return false; }
91
+ virtual bool IsPaused(){ return bPaused; }
92
+ virtual bool Pause(){ bPaused = true; return bPaused; }
93
+ virtual bool Resume(){ bPaused = false; return bPaused; }
94
94
 
95
+ void SetUnbindReasonCode(int code){ UnbindReasonCode = code; }
95
96
  virtual int ReportErrorStatus(){ return 0; }
96
97
  virtual bool IsConnectPending(){ return false; }
97
98
  virtual uint64_t GetNextHeartbeat();
@@ -126,6 +127,7 @@ class EventableDescriptor: public Bindable_t
126
127
  uint64_t InactivityTimeout;
127
128
  uint64_t LastActivity;
128
129
  uint64_t NextHeartbeat;
130
+ bool bPaused;
129
131
  };
130
132
 
131
133
 
@@ -169,7 +171,6 @@ class ConnectionDescriptor: public EventableDescriptor
169
171
  void SetNotifyWritable (bool);
170
172
  void SetWatchOnly (bool);
171
173
 
172
- bool IsPaused(){ return bPaused; }
173
174
  bool Pause();
174
175
  bool Resume();
175
176
 
@@ -216,7 +217,6 @@ class ConnectionDescriptor: public EventableDescriptor
216
217
  };
217
218
 
218
219
  protected:
219
- bool bPaused;
220
220
  bool bConnectPending;
221
221
 
222
222
  bool bNotifyReadable;
data/ext/em.cpp CHANGED
@@ -415,9 +415,17 @@ void EventMachine_t::QueueHeartbeat(EventableDescriptor *ed)
415
415
  EventMachine_t::ClearHeartbeat
416
416
  ******************************/
417
417
 
418
- void EventMachine_t::ClearHeartbeat(uint64_t key)
418
+ void EventMachine_t::ClearHeartbeat(uint64_t key, EventableDescriptor* ed)
419
419
  {
420
- Heartbeats.erase(key);
420
+ multimap<uint64_t,EventableDescriptor*>::iterator it;
421
+ pair<multimap<uint64_t,EventableDescriptor*>::iterator,multimap<uint64_t,EventableDescriptor*>::iterator> ret;
422
+ ret = Heartbeats.equal_range (key);
423
+ for (it = ret.first; it != ret.second; ++it) {
424
+ if (it->second == ed) {
425
+ Heartbeats.erase (it);
426
+ break;
427
+ }
428
+ }
421
429
  }
422
430
 
423
431
  /*******************
@@ -465,8 +473,7 @@ void EventMachine_t::Run()
465
473
 
466
474
  while (true) {
467
475
  _UpdateTime();
468
- if (!_RunTimers())
469
- break;
476
+ _RunTimers();
470
477
 
471
478
  /* _Add must precede _Modify because the same descriptor might
472
479
  * be on both lists during the same pass through the machine,
@@ -513,7 +520,8 @@ bool EventMachine_t::_RunEpollOnce()
513
520
  assert (epfd != -1);
514
521
  int s;
515
522
 
516
- timeval tv = _TimeTilNextEvent();
523
+ timeval tv;
524
+ _TimeTilNextEvent(&tv);
517
525
 
518
526
  #ifdef BUILD_FOR_RUBY
519
527
  int ret = 0;
@@ -583,7 +591,8 @@ bool EventMachine_t::_RunKqueueOnce()
583
591
  assert (kqfd != -1);
584
592
  int k;
585
593
 
586
- timeval tv = _TimeTilNextEvent();
594
+ timeval tv;
595
+ _TimeTilNextEvent(&tv);
587
596
 
588
597
  struct timespec ts;
589
598
  ts.tv_sec = tv.tv_sec;
@@ -664,7 +673,7 @@ bool EventMachine_t::_RunKqueueOnce()
664
673
  EventMachine_t::_TimeTilNextEvent
665
674
  *********************************/
666
675
 
667
- timeval EventMachine_t::_TimeTilNextEvent()
676
+ timeval *EventMachine_t::_TimeTilNextEvent(timeval *tv)
668
677
  {
669
678
  uint64_t next_event = 0;
670
679
 
@@ -683,17 +692,17 @@ timeval EventMachine_t::_TimeTilNextEvent()
683
692
  next_event = MyCurrentLoopTime;
684
693
  }
685
694
 
686
- timeval tv;
687
-
688
695
  if (next_event == 0) {
689
- tv = Quantum;
696
+ // tv->tv_sec = Quantum->tv_sec;
697
+ // tv->tv_usec = Quantum->tv_usec;
698
+ return NULL;
690
699
  } else {
691
700
  if (next_event > MyCurrentLoopTime) {
692
701
  uint64_t duration = next_event - MyCurrentLoopTime;
693
- tv.tv_sec = duration / 1000000;
694
- tv.tv_usec = duration % 1000000;
702
+ tv->tv_sec = duration / 1000000;
703
+ tv->tv_usec = duration % 1000000;
695
704
  } else {
696
- tv.tv_sec = tv.tv_usec = 0;
705
+ tv->tv_sec = tv->tv_usec = 0;
697
706
  }
698
707
  }
699
708
 
@@ -899,7 +908,7 @@ bool EventMachine_t::_RunSelectOnce()
899
908
  { // read and write the sockets
900
909
  //timeval tv = {1, 0}; // Solaris fails if the microseconds member is >= 1000000.
901
910
  //timeval tv = Quantum;
902
- SelectData.tv = _TimeTilNextEvent();
911
+ _TimeTilNextEvent(&SelectData.tv);
903
912
  int s = SelectData._Select();
904
913
  //rb_thread_blocking_region(xxx,(void*)&SelectData,RUBY_UBF_IO,0);
905
914
  //int s = EmSelect (SelectData.maxsocket+1, &(SelectData.fdreads), &(SelectData.fdwrites), NULL, &(SelectData.tv));
@@ -1005,10 +1014,9 @@ void EventMachine_t::_ReadLoopBreaker()
1005
1014
  EventMachine_t::_RunTimers
1006
1015
  **************************/
1007
1016
 
1008
- bool EventMachine_t::_RunTimers()
1017
+ void EventMachine_t::_RunTimers()
1009
1018
  {
1010
1019
  // These are caller-defined timer handlers.
1011
- // Return T/F to indicate whether we should continue the main loop.
1012
1020
  // We rely on the fact that multimaps sort by their keys to avoid
1013
1021
  // inspecting the whole list every time we come here.
1014
1022
  // Just keep inspecting and processing the list head until we hit
@@ -1024,7 +1032,6 @@ bool EventMachine_t::_RunTimers()
1024
1032
  (*EventCallback) (0, EM_TIMER_FIRED, NULL, i->second.GetBinding());
1025
1033
  Timers.erase (i);
1026
1034
  }
1027
- return true;
1028
1035
  }
1029
1036
 
1030
1037
 
@@ -1096,34 +1103,6 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
1096
1103
  throw std::runtime_error (buf);
1097
1104
  }
1098
1105
 
1099
- /*
1100
- sockaddr_in pin;
1101
- unsigned long HostAddr;
1102
-
1103
- HostAddr = inet_addr (server);
1104
- if (HostAddr == INADDR_NONE) {
1105
- hostent *hp = gethostbyname ((char*)server); // Windows requires (char*)
1106
- if (!hp) {
1107
- // TODO: This gives the caller a fatal error. Not good.
1108
- // They can respond by catching RuntimeError (blecch).
1109
- // Possibly we need to fire an unbind event and provide
1110
- // a status code so user code can detect the cause of the
1111
- // failure.
1112
- return NULL;
1113
- }
1114
- HostAddr = ((in_addr*)(hp->h_addr))->s_addr;
1115
- }
1116
-
1117
- memset (&pin, 0, sizeof(pin));
1118
- pin.sin_family = AF_INET;
1119
- pin.sin_addr.s_addr = HostAddr;
1120
- pin.sin_port = htons (port);
1121
-
1122
- int sd = socket (AF_INET, SOCK_STREAM, 0);
1123
- if (sd == INVALID_SOCKET)
1124
- return NULL;
1125
- */
1126
-
1127
1106
  // From here on, ALL error returns must close the socket.
1128
1107
  // Set the new socket nonblocking.
1129
1108
  if (!SetSocketNonblocking (sd)) {
@@ -1150,6 +1129,7 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
1150
1129
  }
1151
1130
 
1152
1131
  unsigned long out = 0;
1132
+ int e = 0;
1153
1133
 
1154
1134
  #ifdef OS_UNIX
1155
1135
  //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
@@ -1183,7 +1163,7 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
1183
1163
  else if (errno == EINPROGRESS) {
1184
1164
  // Errno will generally always be EINPROGRESS, but on Linux
1185
1165
  // we have to look at getsockopt to be sure what really happened.
1186
- int error;
1166
+ int error = 0;
1187
1167
  socklen_t len;
1188
1168
  len = sizeof(error);
1189
1169
  int o = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len);
@@ -1197,11 +1177,15 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
1197
1177
  cd->SetConnectPending (true);
1198
1178
  Add (cd);
1199
1179
  out = cd->GetBinding();
1180
+ } else {
1181
+ // Fall through to the !out case below.
1182
+ e = error;
1200
1183
  }
1201
1184
  }
1202
1185
  else {
1203
1186
  // The error from connect was something other then EINPROGRESS (EHOSTDOWN, etc).
1204
1187
  // Fall through to the !out case below
1188
+ e = errno;
1205
1189
  }
1206
1190
 
1207
1191
  if (!out) {
@@ -1220,6 +1204,7 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
1220
1204
  ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
1221
1205
  if (!cd)
1222
1206
  throw std::runtime_error ("no connection allocated");
1207
+ cd->SetUnbindReasonCode(e);
1223
1208
  cd->ScheduleClose (false);
1224
1209
  Add (cd);
1225
1210
  out = cd->GetBinding();
@@ -1526,25 +1511,6 @@ const unsigned long EventMachine_t::CreateTcpServer (const char *server, int por
1526
1511
  goto fail;
1527
1512
  }
1528
1513
 
1529
- /*
1530
- memset (&sin, 0, sizeof(sin));
1531
- sin.sin_family = AF_INET;
1532
- sin.sin_addr.s_addr = INADDR_ANY;
1533
- sin.sin_port = htons (port);
1534
-
1535
- if (server && *server) {
1536
- sin.sin_addr.s_addr = inet_addr (server);
1537
- if (sin.sin_addr.s_addr == INADDR_NONE) {
1538
- hostent *hp = gethostbyname ((char*)server); // Windows requires the cast.
1539
- if (hp == NULL) {
1540
- //__warning ("hostname not resolved: ", server);
1541
- goto fail;
1542
- }
1543
- sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
1544
- }
1545
- }
1546
- */
1547
-
1548
1514
  { // set reuseaddr to improve performance on restarts.
1549
1515
  int oval = 1;
1550
1516
  if (setsockopt (sd_accept, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0) {
@@ -1824,6 +1790,34 @@ void EventMachine_t::Modify (EventableDescriptor *ed)
1824
1790
  }
1825
1791
 
1826
1792
 
1793
+ /***********************
1794
+ EventMachine_t::Deregister
1795
+ ***********************/
1796
+
1797
+ void EventMachine_t::Deregister (EventableDescriptor *ed)
1798
+ {
1799
+ if (!ed)
1800
+ throw std::runtime_error ("modified bad descriptor");
1801
+ #ifdef HAVE_EPOLL
1802
+ // cut/paste from _CleanupSockets(). The error handling could be
1803
+ // refactored out of there, but it is cut/paste all over the
1804
+ // file already.
1805
+ if (bEpoll) {
1806
+ assert (epfd != -1);
1807
+ assert (ed->GetSocket() != INVALID_SOCKET);
1808
+ int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
1809
+ // ENOENT or EBADF are not errors because the socket may be already closed when we get here.
1810
+ if (e && (errno != ENOENT) && (errno != EBADF) && (errno != EPERM)) {
1811
+ char buf [200];
1812
+ snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
1813
+ throw std::runtime_error (buf);
1814
+ }
1815
+ ModifiedDescriptors.erase(ed);
1816
+ }
1817
+ #endif
1818
+ }
1819
+
1820
+
1827
1821
  /**************************************
1828
1822
  EventMachine_t::CreateUnixDomainServer
1829
1823
  **************************************/