eventmachine 1.0.0.beta.3-x86-mswin32-60 → 1.0.0.beta.4.1-x86-mswin32-60
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.
- data/.gitignore +5 -0
- data/.yardopts +5 -1
- data/{docs/GNU → GNU} +0 -0
- data/Gemfile +1 -0
- data/{docs/COPYING → LICENSE} +0 -0
- data/README.md +109 -0
- data/Rakefile +8 -0
- data/docs/DocumentationGuidesIndex.md +27 -0
- data/docs/GettingStarted.md +521 -0
- data/docs/{ChangeLog → old/ChangeLog} +0 -0
- data/docs/{DEFERRABLES → old/DEFERRABLES} +0 -0
- data/docs/{EPOLL → old/EPOLL} +0 -0
- data/docs/{INSTALL → old/INSTALL} +0 -0
- data/docs/{KEYBOARD → old/KEYBOARD} +0 -0
- data/docs/{LEGAL → old/LEGAL} +0 -0
- data/docs/{LIGHTWEIGHT_CONCURRENCY → old/LIGHTWEIGHT_CONCURRENCY} +0 -0
- data/docs/{PURE_RUBY → old/PURE_RUBY} +0 -0
- data/docs/{RELEASE_NOTES → old/RELEASE_NOTES} +0 -0
- data/docs/{SMTP → old/SMTP} +0 -0
- data/docs/{SPAWNED_PROCESSES → old/SPAWNED_PROCESSES} +0 -0
- data/docs/{TODO → old/TODO} +0 -0
- data/eventmachine.gemspec +5 -2
- data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
- data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
- data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
- data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
- data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
- data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
- data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
- data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
- data/examples/{ex_channel.rb → old/ex_channel.rb} +3 -3
- data/examples/{ex_queue.rb → old/ex_queue.rb} +0 -0
- data/examples/{ex_tick_loop_array.rb → old/ex_tick_loop_array.rb} +0 -0
- data/examples/{ex_tick_loop_counter.rb → old/ex_tick_loop_counter.rb} +0 -0
- data/examples/{helper.rb → old/helper.rb} +0 -0
- data/ext/cmain.cpp +3 -3
- data/ext/ed.cpp +90 -15
- data/ext/ed.h +5 -5
- data/ext/em.cpp +48 -56
- data/ext/em.h +12 -2
- data/ext/extconf.rb +3 -3
- data/ext/fastfilereader/extconf.rb +1 -1
- data/ext/pipe.cpp +2 -2
- data/ext/project.h +1 -1
- data/ext/rubymain.cpp +48 -3
- data/ext/ssl.cpp +5 -0
- data/java/src/com/rubyeventmachine/EmReactor.java +2 -2
- data/lib/em/buftok.rb +35 -63
- data/lib/em/callback.rb +43 -11
- data/lib/em/channel.rb +21 -14
- data/lib/em/completion.rb +304 -0
- data/lib/em/connection.rb +339 -209
- data/lib/em/deferrable.rb +4 -0
- data/lib/em/deferrable/pool.rb +2 -0
- data/lib/em/file_watch.rb +37 -18
- data/lib/em/iterator.rb +42 -42
- data/lib/em/pool.rb +146 -0
- data/lib/em/process_watch.rb +5 -4
- data/lib/em/processes.rb +8 -4
- data/lib/em/protocols/httpclient.rb +22 -11
- data/lib/em/protocols/httpclient2.rb +15 -5
- data/lib/em/protocols/line_protocol.rb +2 -1
- data/lib/em/protocols/memcache.rb +17 -9
- data/lib/em/protocols/object_protocol.rb +2 -1
- data/lib/em/protocols/postgres3.rb +8 -9
- data/lib/em/protocols/smtpclient.rb +19 -11
- data/lib/em/protocols/smtpserver.rb +1 -1
- data/lib/em/protocols/stomp.rb +8 -6
- data/lib/em/protocols/tcptest.rb +3 -2
- data/lib/em/pure_ruby.rb +212 -208
- data/lib/em/queue.rb +22 -13
- data/lib/em/resolver.rb +70 -64
- data/lib/em/spawnable.rb +6 -3
- data/lib/em/streamer.rb +33 -45
- data/lib/em/threaded_resource.rb +90 -0
- data/lib/em/timers.rb +6 -2
- data/lib/em/version.rb +1 -1
- data/lib/eventmachine.rb +538 -602
- data/lib/jeventmachine.rb +22 -1
- data/tasks/package.rake +12 -2
- data/tasks/test.rake +1 -0
- data/tests/em_test_helper.rb +12 -3
- data/tests/test_completion.rb +177 -0
- data/tests/test_epoll.rb +2 -2
- data/tests/test_httpclient.rb +9 -9
- data/tests/test_httpclient2.rb +11 -9
- data/tests/test_ltp.rb +2 -10
- data/tests/test_pool.rb +128 -0
- data/tests/test_processes.rb +20 -2
- data/tests/test_queue.rb +8 -0
- data/tests/test_resolver.rb +1 -1
- data/tests/test_set_sock_opt.rb +37 -0
- data/tests/test_shutdown_hooks.rb +23 -0
- data/tests/test_threaded_resource.rb +53 -0
- data/tests/test_unbind_reason.rb +31 -0
- metadata +96 -32
- data/README +0 -81
- 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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
140
|
-
|
141
|
-
|
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
|
-
|
725
|
-
|
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 ((
|
1068
|
+
if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR)) {
|
1007
1069
|
#endif
|
1008
1070
|
#ifdef OS_WIN32
|
1009
|
-
if ((
|
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
|
92
|
-
virtual bool Pause(){ return
|
93
|
-
virtual bool Resume(){
|
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
|
-
|
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
|
-
|
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,
|
@@ -1005,10 +1012,9 @@ void EventMachine_t::_ReadLoopBreaker()
|
|
1005
1012
|
EventMachine_t::_RunTimers
|
1006
1013
|
**************************/
|
1007
1014
|
|
1008
|
-
|
1015
|
+
void EventMachine_t::_RunTimers()
|
1009
1016
|
{
|
1010
1017
|
// These are caller-defined timer handlers.
|
1011
|
-
// Return T/F to indicate whether we should continue the main loop.
|
1012
1018
|
// We rely on the fact that multimaps sort by their keys to avoid
|
1013
1019
|
// inspecting the whole list every time we come here.
|
1014
1020
|
// Just keep inspecting and processing the list head until we hit
|
@@ -1024,7 +1030,6 @@ bool EventMachine_t::_RunTimers()
|
|
1024
1030
|
(*EventCallback) (0, EM_TIMER_FIRED, NULL, i->second.GetBinding());
|
1025
1031
|
Timers.erase (i);
|
1026
1032
|
}
|
1027
|
-
return true;
|
1028
1033
|
}
|
1029
1034
|
|
1030
1035
|
|
@@ -1096,34 +1101,6 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
|
|
1096
1101
|
throw std::runtime_error (buf);
|
1097
1102
|
}
|
1098
1103
|
|
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
1104
|
// From here on, ALL error returns must close the socket.
|
1128
1105
|
// Set the new socket nonblocking.
|
1129
1106
|
if (!SetSocketNonblocking (sd)) {
|
@@ -1150,6 +1127,7 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
|
|
1150
1127
|
}
|
1151
1128
|
|
1152
1129
|
unsigned long out = 0;
|
1130
|
+
int e = 0;
|
1153
1131
|
|
1154
1132
|
#ifdef OS_UNIX
|
1155
1133
|
//if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
|
@@ -1183,7 +1161,7 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
|
|
1183
1161
|
else if (errno == EINPROGRESS) {
|
1184
1162
|
// Errno will generally always be EINPROGRESS, but on Linux
|
1185
1163
|
// we have to look at getsockopt to be sure what really happened.
|
1186
|
-
int error;
|
1164
|
+
int error = 0;
|
1187
1165
|
socklen_t len;
|
1188
1166
|
len = sizeof(error);
|
1189
1167
|
int o = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len);
|
@@ -1197,11 +1175,15 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
|
|
1197
1175
|
cd->SetConnectPending (true);
|
1198
1176
|
Add (cd);
|
1199
1177
|
out = cd->GetBinding();
|
1178
|
+
} else {
|
1179
|
+
// Fall through to the !out case below.
|
1180
|
+
e = error;
|
1200
1181
|
}
|
1201
1182
|
}
|
1202
1183
|
else {
|
1203
1184
|
// The error from connect was something other then EINPROGRESS (EHOSTDOWN, etc).
|
1204
1185
|
// Fall through to the !out case below
|
1186
|
+
e = errno;
|
1205
1187
|
}
|
1206
1188
|
|
1207
1189
|
if (!out) {
|
@@ -1220,6 +1202,7 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
|
|
1220
1202
|
ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
|
1221
1203
|
if (!cd)
|
1222
1204
|
throw std::runtime_error ("no connection allocated");
|
1205
|
+
cd->SetUnbindReasonCode(e);
|
1223
1206
|
cd->ScheduleClose (false);
|
1224
1207
|
Add (cd);
|
1225
1208
|
out = cd->GetBinding();
|
@@ -1274,7 +1257,7 @@ const unsigned long EventMachine_t::ConnectToUnixServer (const char *server)
|
|
1274
1257
|
|
1275
1258
|
#ifdef OS_WIN32
|
1276
1259
|
throw std::runtime_error ("unix-domain connection unavailable on this platform");
|
1277
|
-
return
|
1260
|
+
return 0;
|
1278
1261
|
#endif
|
1279
1262
|
|
1280
1263
|
// The whole rest of this function is only compiled on Unix systems.
|
@@ -1526,25 +1509,6 @@ const unsigned long EventMachine_t::CreateTcpServer (const char *server, int por
|
|
1526
1509
|
goto fail;
|
1527
1510
|
}
|
1528
1511
|
|
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
1512
|
{ // set reuseaddr to improve performance on restarts.
|
1549
1513
|
int oval = 1;
|
1550
1514
|
if (setsockopt (sd_accept, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0) {
|
@@ -1824,6 +1788,34 @@ void EventMachine_t::Modify (EventableDescriptor *ed)
|
|
1824
1788
|
}
|
1825
1789
|
|
1826
1790
|
|
1791
|
+
/***********************
|
1792
|
+
EventMachine_t::Deregister
|
1793
|
+
***********************/
|
1794
|
+
|
1795
|
+
void EventMachine_t::Deregister (EventableDescriptor *ed)
|
1796
|
+
{
|
1797
|
+
if (!ed)
|
1798
|
+
throw std::runtime_error ("modified bad descriptor");
|
1799
|
+
#ifdef HAVE_EPOLL
|
1800
|
+
// cut/paste from _CleanupSockets(). The error handling could be
|
1801
|
+
// refactored out of there, but it is cut/paste all over the
|
1802
|
+
// file already.
|
1803
|
+
if (bEpoll) {
|
1804
|
+
assert (epfd != -1);
|
1805
|
+
assert (ed->GetSocket() != INVALID_SOCKET);
|
1806
|
+
int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
|
1807
|
+
// ENOENT or EBADF are not errors because the socket may be already closed when we get here.
|
1808
|
+
if (e && (errno != ENOENT) && (errno != EBADF) && (errno != EPERM)) {
|
1809
|
+
char buf [200];
|
1810
|
+
snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
|
1811
|
+
throw std::runtime_error (buf);
|
1812
|
+
}
|
1813
|
+
ModifiedDescriptors.erase(ed);
|
1814
|
+
}
|
1815
|
+
#endif
|
1816
|
+
}
|
1817
|
+
|
1818
|
+
|
1827
1819
|
/**************************************
|
1828
1820
|
EventMachine_t::CreateUnixDomainServer
|
1829
1821
|
**************************************/
|