eventmachine 0.3.1 → 0.4.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.
- data/RELEASE_NOTES +5 -1
- data/ext/binder.cpp +125 -0
- data/ext/binder.h +54 -0
- data/ext/ed.cpp +528 -0
- data/ext/ed.h +152 -0
- data/ext/em.cpp +475 -0
- data/ext/em.h +90 -0
- data/ext/extconf.rb +31 -0
- data/ext/libmain.cpp +312 -0
- data/ext/project.h +63 -0
- data/ext/sigs.cpp +60 -0
- data/ext/sigs.h +35 -0
- data/lib/eventmachine.rb +39 -1
- metadata +18 -80
- data/doc/classes/Echo.html +0 -180
- data/doc/classes/Echo.src/M000033.html +0 -23
- data/doc/classes/Echo.src/M000034.html +0 -19
- data/doc/classes/Echo.src/M000035.html +0 -19
- data/doc/classes/Echo.src/M000036.html +0 -18
- data/doc/classes/EventMachine.html +0 -369
- data/doc/classes/EventMachine.src/M000002.html +0 -24
- data/doc/classes/EventMachine.src/M000003.html +0 -24
- data/doc/classes/EventMachine.src/M000004.html +0 -26
- data/doc/classes/EventMachine.src/M000005.html +0 -21
- data/doc/classes/EventMachine.src/M000006.html +0 -23
- data/doc/classes/EventMachine.src/M000007.html +0 -28
- data/doc/classes/EventMachine.src/M000008.html +0 -20
- data/doc/classes/EventMachine.src/M000009.html +0 -19
- data/doc/classes/EventMachine.src/M000010.html +0 -18
- data/doc/classes/EventMachine.src/M000011.html +0 -18
- data/doc/classes/EventMachine/Connection.html +0 -341
- data/doc/classes/EventMachine/Connection.src/M000020.html +0 -19
- data/doc/classes/EventMachine/Connection.src/M000021.html +0 -17
- data/doc/classes/EventMachine/Connection.src/M000022.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000023.html +0 -17
- data/doc/classes/EventMachine/Connection.src/M000024.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000025.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000026.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000027.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000028.html +0 -17
- data/doc/classes/EventMachine/Connection.src/M000029.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000030.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000031.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000032.html +0 -18
- data/doc/classes/EventMachine/ConnectionAlreadyBound.html +0 -111
- data/doc/classes/EventMachine/ConnectionNotBound.html +0 -111
- data/doc/classes/EventMachine/Connections.html +0 -292
- data/doc/classes/EventMachine/Connections.src/M000012.html +0 -20
- data/doc/classes/EventMachine/Connections.src/M000013.html +0 -23
- data/doc/classes/EventMachine/Connections.src/M000014.html +0 -23
- data/doc/classes/EventMachine/Connections.src/M000015.html +0 -24
- data/doc/classes/EventMachine/Connections.src/M000016.html +0 -19
- data/doc/classes/EventMachine/Connections.src/M000017.html +0 -21
- data/doc/classes/EventMachine/Connections.src/M000018.html +0 -19
- data/doc/classes/EventMachine/Connections.src/M000019.html +0 -20
- data/doc/classes/EventMachine/EventCodes.html +0 -133
- data/doc/classes/EventMachine/NoConnectionMade.html +0 -111
- data/doc/classes/EventMachine/NoHandlerForAcceptedConnection.html +0 -111
- data/doc/classes/EventMachine/NoServerCreated.html +0 -111
- data/doc/classes/EventMachine/TimerNotInstalled.html +0 -111
- data/doc/classes/EventMachine/TooManyAcceptors.html +0 -111
- data/doc/classes/EventMachine/TooManyTimersPending.html +0 -111
- data/doc/classes/EventMachine/UnknownTimerFired.html +0 -111
- data/doc/classes/Zzz.html +0 -131
- data/doc/classes/Zzz.src/M000001.html +0 -18
- data/doc/created.rid +0 -1
- data/doc/files/binder_cpp.html +0 -101
- data/doc/files/ed_cpp.html +0 -101
- data/doc/files/em_cpp.html +0 -101
- data/doc/files/event_machine_rb.html +0 -118
- data/doc/files/g_rb.html +0 -108
- data/doc/files/lib/eventmachine_rb.html +0 -114
- data/doc/files/libmain_cpp.html +0 -101
- data/doc/files/sigs_cpp.html +0 -101
- data/doc/files/tests/testem_rb.html +0 -110
- data/doc/fr_class_index.html +0 -41
- data/doc/fr_file_index.html +0 -35
- data/doc/fr_method_index.html +0 -62
- data/doc/index.html +0 -24
- data/ext/libeventmachine.so +0 -0
data/ext/ed.h
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/*****************************************************************************
|
|
2
|
+
|
|
3
|
+
$Id: ed.h 2291 2006-04-14 03:56:18Z francis $
|
|
4
|
+
|
|
5
|
+
File: ed.h
|
|
6
|
+
Date: 06Apr06
|
|
7
|
+
|
|
8
|
+
Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
|
9
|
+
Gmail: garbagecat20
|
|
10
|
+
|
|
11
|
+
This program is free software; you can redistribute it and/or modify
|
|
12
|
+
it under the terms of the GNU General Public License as published by
|
|
13
|
+
the Free Software Foundation; either version 2 of the License, or
|
|
14
|
+
(at your option) any later version.
|
|
15
|
+
|
|
16
|
+
This program is distributed in the hope that it will be useful,
|
|
17
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19
|
+
GNU General Public License for more details.
|
|
20
|
+
|
|
21
|
+
You should have received a copy of the GNU General Public License
|
|
22
|
+
along with this program; if not, write to the Free Software
|
|
23
|
+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
24
|
+
|
|
25
|
+
*****************************************************************************/
|
|
26
|
+
|
|
27
|
+
#ifndef __EventableDescriptor__H_
|
|
28
|
+
#define __EventableDescriptor__H_
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class EventMachine_t; // forward reference
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/*************************
|
|
35
|
+
class EventableDescriptor
|
|
36
|
+
*************************/
|
|
37
|
+
|
|
38
|
+
class EventableDescriptor: public Bindable_t
|
|
39
|
+
{
|
|
40
|
+
public:
|
|
41
|
+
EventableDescriptor (int);
|
|
42
|
+
virtual ~EventableDescriptor();
|
|
43
|
+
|
|
44
|
+
int GetSocket() {return MySocket;}
|
|
45
|
+
void Close();
|
|
46
|
+
|
|
47
|
+
virtual void Read() = 0;
|
|
48
|
+
virtual void Write() = 0;
|
|
49
|
+
virtual void Heartbeat() = 0;
|
|
50
|
+
|
|
51
|
+
// These methods tell us whether the descriptor
|
|
52
|
+
// should be selected or polled for read/write.
|
|
53
|
+
virtual bool SelectForRead() = 0;
|
|
54
|
+
virtual bool SelectForWrite() = 0;
|
|
55
|
+
|
|
56
|
+
// are we scheduled for a close, or in an error state, or already closed?
|
|
57
|
+
bool ShouldDelete();
|
|
58
|
+
// Do we have any data to write? This is used by ShouldDelete.
|
|
59
|
+
virtual int GetOutboundDataSize() {return 0;}
|
|
60
|
+
|
|
61
|
+
void SetEventCallback (void (*cb)(const char*, int, const char*, int));
|
|
62
|
+
|
|
63
|
+
protected:
|
|
64
|
+
enum {
|
|
65
|
+
PendingConnectTimeout = 4 // can easily be made an instance variable
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
void (*EventCallback)(const char*, int, const char*, int);
|
|
69
|
+
|
|
70
|
+
time_t CreatedAt;
|
|
71
|
+
time_t LastRead;
|
|
72
|
+
time_t LastWritten;
|
|
73
|
+
int MySocket;
|
|
74
|
+
bool bCloseNow;
|
|
75
|
+
bool bCloseAfterWriting;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
/**************************
|
|
81
|
+
class ConnectionDescriptor
|
|
82
|
+
**************************/
|
|
83
|
+
|
|
84
|
+
class ConnectionDescriptor: public EventableDescriptor
|
|
85
|
+
{
|
|
86
|
+
public:
|
|
87
|
+
ConnectionDescriptor (int);
|
|
88
|
+
virtual ~ConnectionDescriptor();
|
|
89
|
+
|
|
90
|
+
static int SendDataToConnection (const char*, const char*, int);
|
|
91
|
+
static void CloseConnection (const char*, bool);
|
|
92
|
+
|
|
93
|
+
int SendOutboundData (const char*, int);
|
|
94
|
+
|
|
95
|
+
void SetConnectPending (bool f) { bConnectPending = f; }
|
|
96
|
+
|
|
97
|
+
virtual void Read();
|
|
98
|
+
virtual void Write();
|
|
99
|
+
virtual void Heartbeat();
|
|
100
|
+
|
|
101
|
+
virtual bool SelectForRead();
|
|
102
|
+
virtual bool SelectForWrite();
|
|
103
|
+
|
|
104
|
+
// Do we have any data to write? This is used by ShouldDelete.
|
|
105
|
+
virtual int GetOutboundDataSize() {return OutboundDataSize;}
|
|
106
|
+
|
|
107
|
+
protected:
|
|
108
|
+
struct OutboundPage {
|
|
109
|
+
OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
|
|
110
|
+
void Free() {if (Buffer) free ((char*)Buffer); }
|
|
111
|
+
const char *Buffer;
|
|
112
|
+
int Length;
|
|
113
|
+
int Offset;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
protected:
|
|
117
|
+
bool bConnectPending;
|
|
118
|
+
|
|
119
|
+
deque<OutboundPage> OutboundPages;
|
|
120
|
+
int OutboundDataSize;
|
|
121
|
+
|
|
122
|
+
private:
|
|
123
|
+
void _WriteOutboundData();
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
/************************
|
|
128
|
+
class AcceptorDescriptor
|
|
129
|
+
************************/
|
|
130
|
+
|
|
131
|
+
class AcceptorDescriptor: public EventableDescriptor
|
|
132
|
+
{
|
|
133
|
+
public:
|
|
134
|
+
AcceptorDescriptor (EventMachine_t*, int);
|
|
135
|
+
virtual ~AcceptorDescriptor();
|
|
136
|
+
|
|
137
|
+
virtual void Read();
|
|
138
|
+
virtual void Write();
|
|
139
|
+
virtual void Heartbeat();
|
|
140
|
+
|
|
141
|
+
virtual bool SelectForRead() {return true;}
|
|
142
|
+
virtual bool SelectForWrite() {return false;}
|
|
143
|
+
|
|
144
|
+
protected:
|
|
145
|
+
EventMachine_t *MyEventMachine;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
#endif // __EventableDescriptor__H_
|
|
151
|
+
|
|
152
|
+
|
data/ext/em.cpp
ADDED
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
/*****************************************************************************
|
|
2
|
+
|
|
3
|
+
$Id: em.cpp 2291 2006-04-14 03:56:18Z francis $
|
|
4
|
+
|
|
5
|
+
File: ed.cpp
|
|
6
|
+
Date: 06Apr06
|
|
7
|
+
|
|
8
|
+
Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
|
9
|
+
Gmail: garbagecat20
|
|
10
|
+
|
|
11
|
+
This program is free software; you can redistribute it and/or modify
|
|
12
|
+
it under the terms of the GNU General Public License as published by
|
|
13
|
+
the Free Software Foundation; either version 2 of the License, or
|
|
14
|
+
(at your option) any later version.
|
|
15
|
+
|
|
16
|
+
This program is distributed in the hope that it will be useful,
|
|
17
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19
|
+
GNU General Public License for more details.
|
|
20
|
+
|
|
21
|
+
You should have received a copy of the GNU General Public License
|
|
22
|
+
along with this program; if not, write to the Free Software
|
|
23
|
+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
24
|
+
|
|
25
|
+
*****************************************************************************/
|
|
26
|
+
|
|
27
|
+
#include "project.h"
|
|
28
|
+
|
|
29
|
+
// Keep a global variable floating around
|
|
30
|
+
// with the current loop time as set by the Event Machine.
|
|
31
|
+
// This avoids the need for frequent expensive calls to time(NULL);
|
|
32
|
+
time_t gCurrentLoopTime;
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
/******************************
|
|
36
|
+
EventMachine_t::EventMachine_t
|
|
37
|
+
******************************/
|
|
38
|
+
|
|
39
|
+
EventMachine_t::EventMachine_t (void (*event_callback)(const char*, int, const char*, int)):
|
|
40
|
+
EventCallback (event_callback),
|
|
41
|
+
NextHeartbeatTime (0)
|
|
42
|
+
{
|
|
43
|
+
gTerminateSignalReceived = false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
/*******************************
|
|
48
|
+
EventMachine_t::~EventMachine_t
|
|
49
|
+
*******************************/
|
|
50
|
+
|
|
51
|
+
EventMachine_t::~EventMachine_t()
|
|
52
|
+
{
|
|
53
|
+
// Run down descriptors
|
|
54
|
+
size_t i;
|
|
55
|
+
for (i = 0; i < NewDescriptors.size(); i++)
|
|
56
|
+
delete NewDescriptors[i];
|
|
57
|
+
for (i = 0; i < Descriptors.size(); i++)
|
|
58
|
+
delete Descriptors[i];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
/****************************
|
|
64
|
+
EventMachine_t::ScheduleHalt
|
|
65
|
+
****************************/
|
|
66
|
+
|
|
67
|
+
void EventMachine_t::ScheduleHalt()
|
|
68
|
+
{
|
|
69
|
+
/* This is how we stop the machine.
|
|
70
|
+
* This can be called by clients. Signal handlers will probably
|
|
71
|
+
* set the global flag.
|
|
72
|
+
* For now this means there can only be one EventMachine ever running at a time.
|
|
73
|
+
*/
|
|
74
|
+
gTerminateSignalReceived = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
/*******************
|
|
80
|
+
EventMachine_t::Run
|
|
81
|
+
*******************/
|
|
82
|
+
|
|
83
|
+
void EventMachine_t::Run()
|
|
84
|
+
{
|
|
85
|
+
while (true) {
|
|
86
|
+
gCurrentLoopTime = time(NULL);
|
|
87
|
+
if (!_RunTimers())
|
|
88
|
+
break;
|
|
89
|
+
_AddNewDescriptors();
|
|
90
|
+
if (!_RunOnce())
|
|
91
|
+
break;
|
|
92
|
+
if (gTerminateSignalReceived)
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
/************************
|
|
100
|
+
EventMachine_t::_RunOnce
|
|
101
|
+
************************/
|
|
102
|
+
|
|
103
|
+
bool EventMachine_t::_RunOnce()
|
|
104
|
+
{
|
|
105
|
+
// Crank the event machine once.
|
|
106
|
+
// If there are no descriptors to process, then sleep
|
|
107
|
+
// for a few hundred mills to avoid busy-looping.
|
|
108
|
+
// Return T/F to indicate whether we should continue.
|
|
109
|
+
// This is based on a select loop. Alternately provide epoll
|
|
110
|
+
// if we know we're running on a 2.6 kernel.
|
|
111
|
+
|
|
112
|
+
//cerr << "X";
|
|
113
|
+
if (Descriptors.size() == 0) {
|
|
114
|
+
timeval tv = {0, 200 * 1000};
|
|
115
|
+
select (0, NULL, NULL, NULL, &tv);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
fd_set fdreads, fdwrites;
|
|
120
|
+
FD_ZERO (&fdreads);
|
|
121
|
+
FD_ZERO (&fdwrites);
|
|
122
|
+
|
|
123
|
+
int maxsocket = 0;
|
|
124
|
+
|
|
125
|
+
// prepare the sockets for reading and writing
|
|
126
|
+
size_t i;
|
|
127
|
+
for (i = 0; i < Descriptors.size(); i++) {
|
|
128
|
+
EventableDescriptor *ed = Descriptors[i];
|
|
129
|
+
assert (ed);
|
|
130
|
+
int sd = ed->GetSocket();
|
|
131
|
+
assert (sd != -1);
|
|
132
|
+
|
|
133
|
+
if (ed->SelectForRead())
|
|
134
|
+
FD_SET (sd, &fdreads);
|
|
135
|
+
if (ed->SelectForWrite())
|
|
136
|
+
FD_SET (sd, &fdwrites);
|
|
137
|
+
|
|
138
|
+
if (maxsocket < sd)
|
|
139
|
+
maxsocket = sd;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
{ // read and write the sockets
|
|
144
|
+
timeval tv = {1, 0}; // Solaris fails if the microseconds member is >= 1000000.
|
|
145
|
+
int s = select (maxsocket+1, &fdreads, &fdwrites, NULL, &tv);
|
|
146
|
+
if (s > 0) {
|
|
147
|
+
for (i=0; i < Descriptors.size(); i++) {
|
|
148
|
+
EventableDescriptor *ed = Descriptors[i];
|
|
149
|
+
assert (ed);
|
|
150
|
+
int sd = ed->GetSocket();
|
|
151
|
+
assert (sd != -1);
|
|
152
|
+
|
|
153
|
+
if (FD_ISSET (sd, &fdwrites))
|
|
154
|
+
ed->Write();
|
|
155
|
+
if (FD_ISSET (sd, &fdreads))
|
|
156
|
+
ed->Read();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else if (s < 0) {
|
|
160
|
+
// select can fail on error in a handful of ways.
|
|
161
|
+
// If this happens, then wait for a little while to avoid busy-looping.
|
|
162
|
+
// If the error was EINTR, we probaby caught SIGCHLD or something,
|
|
163
|
+
// so keep the wait short.
|
|
164
|
+
timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000};
|
|
165
|
+
select (0, NULL, NULL, NULL, &tv);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
{ // dispatch heartbeats
|
|
171
|
+
if (gCurrentLoopTime >= NextHeartbeatTime) {
|
|
172
|
+
NextHeartbeatTime = gCurrentLoopTime + HeartbeatInterval;
|
|
173
|
+
|
|
174
|
+
for (i=0; i < Descriptors.size(); i++) {
|
|
175
|
+
EventableDescriptor *ed = Descriptors[i];
|
|
176
|
+
assert (ed);
|
|
177
|
+
ed->Heartbeat();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
{ // cleanup dying sockets
|
|
183
|
+
// vector::pop_back works in constant time.
|
|
184
|
+
int i, j;
|
|
185
|
+
int nSockets = Descriptors.size();
|
|
186
|
+
for (i=0, j=0; i < nSockets; i++) {
|
|
187
|
+
EventableDescriptor *ed = Descriptors[i];
|
|
188
|
+
assert (ed);
|
|
189
|
+
if (ed->ShouldDelete())
|
|
190
|
+
delete ed;
|
|
191
|
+
else
|
|
192
|
+
Descriptors [j++] = ed;
|
|
193
|
+
}
|
|
194
|
+
while ((size_t)j < Descriptors.size())
|
|
195
|
+
Descriptors.pop_back();
|
|
196
|
+
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
/**************************
|
|
204
|
+
EventMachine_t::_RunTimers
|
|
205
|
+
**************************/
|
|
206
|
+
|
|
207
|
+
bool EventMachine_t::_RunTimers()
|
|
208
|
+
{
|
|
209
|
+
// These are caller-defined timer handlers.
|
|
210
|
+
// Return T/F to indicate whether we should continue the main loop.
|
|
211
|
+
// We rely on the fact that multimaps sort by their keys to avoid
|
|
212
|
+
// inspecting the whole list every time we come here.
|
|
213
|
+
// Just keep inspecting and processing the list head until we hit
|
|
214
|
+
// one that hasn't expired yet.
|
|
215
|
+
|
|
216
|
+
while (true) {
|
|
217
|
+
multimap<time_t,Timer_t>::iterator i = Timers.begin();
|
|
218
|
+
if (i == Timers.end())
|
|
219
|
+
break;
|
|
220
|
+
if (i->first > gCurrentLoopTime)
|
|
221
|
+
break;
|
|
222
|
+
if (EventCallback)
|
|
223
|
+
(*EventCallback) ("", TIMER_FIRED, i->second.GetBinding().c_str(), i->second.GetBinding().length());
|
|
224
|
+
Timers.erase (i);
|
|
225
|
+
}
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
/***********************************
|
|
232
|
+
EventMachine_t::InstallOneshotTimer
|
|
233
|
+
***********************************/
|
|
234
|
+
|
|
235
|
+
const char *EventMachine_t::InstallOneshotTimer (int seconds)
|
|
236
|
+
{
|
|
237
|
+
if (Timers.size() > MaxOutstandingTimers)
|
|
238
|
+
return false;
|
|
239
|
+
// Don't use the global loop-time variable here, because we might
|
|
240
|
+
// get called before the main event machine is running.
|
|
241
|
+
|
|
242
|
+
Timer_t t;
|
|
243
|
+
Timers.insert (make_pair (time(NULL) + seconds, t));
|
|
244
|
+
return t.GetBinding().c_str();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
/*******************************
|
|
249
|
+
EventMachine_t::ConnectToServer
|
|
250
|
+
*******************************/
|
|
251
|
+
|
|
252
|
+
const char *EventMachine_t::ConnectToServer (const char *server, int port)
|
|
253
|
+
{
|
|
254
|
+
/* We want to spend no more than a few seconds waiting for a connection
|
|
255
|
+
* to a remote host. So we use a nonblocking connect.
|
|
256
|
+
* Linux disobeys the usual rules for nonblocking connects.
|
|
257
|
+
* Per Stevens (UNP p.410), you expect a nonblocking connect to select
|
|
258
|
+
* both readable and writable on error, and not to return EINPROGRESS
|
|
259
|
+
* if the connect can be fulfilled immediately. Linux violates both
|
|
260
|
+
* of these expectations.
|
|
261
|
+
* Any kind of nonblocking connect on Linux returns EINPROGRESS.
|
|
262
|
+
* The socket will then return writable when the disposition of the
|
|
263
|
+
* connect is known, but it will not also be readable in case of
|
|
264
|
+
* error! Weirdly, it will be readable in case there is data to read!!!
|
|
265
|
+
* (Which can happen with protocols like SSH and SMTP.)
|
|
266
|
+
* I suppose if you were so inclined you could consider this logical,
|
|
267
|
+
* but it's not the way Unix has historically done it.
|
|
268
|
+
* So we ignore the readable flag and read getsockopt to see if there
|
|
269
|
+
* was an error connecting. A select timeout works as expected.
|
|
270
|
+
* In regard to getsockopt: Linux does the Berkeley-style thing,
|
|
271
|
+
* not the Solaris-style, and returns zero with the error code in
|
|
272
|
+
* the error parameter.
|
|
273
|
+
* Return the binding-text of the newly-created pending connection,
|
|
274
|
+
* or NULL if there was a problem.
|
|
275
|
+
*/
|
|
276
|
+
|
|
277
|
+
if (!server || !*server || !port)
|
|
278
|
+
return NULL;
|
|
279
|
+
|
|
280
|
+
sockaddr_in pin;
|
|
281
|
+
unsigned long HostAddr;
|
|
282
|
+
|
|
283
|
+
HostAddr = inet_addr (server);
|
|
284
|
+
if (HostAddr == -1) {
|
|
285
|
+
hostent *hp = gethostbyname (server);
|
|
286
|
+
if (!hp)
|
|
287
|
+
return NULL;
|
|
288
|
+
HostAddr = ((in_addr*)(hp->h_addr))->s_addr;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
memset (&pin, 0, sizeof(pin));
|
|
292
|
+
pin.sin_family = AF_INET;
|
|
293
|
+
pin.sin_addr.s_addr = HostAddr;
|
|
294
|
+
pin.sin_port = htons (port);
|
|
295
|
+
|
|
296
|
+
int sd = socket (AF_INET, SOCK_STREAM, 0);
|
|
297
|
+
if (sd == -1)
|
|
298
|
+
return NULL;
|
|
299
|
+
|
|
300
|
+
// From here on, ALL error returns must close the socket.
|
|
301
|
+
// Set the new socket nonblocking.
|
|
302
|
+
int val = fcntl (sd, F_GETFL, 0);
|
|
303
|
+
if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) {
|
|
304
|
+
close (sd);
|
|
305
|
+
return NULL;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const char *out = NULL;
|
|
309
|
+
if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
|
|
310
|
+
// This is a connect success, which Linux appears
|
|
311
|
+
// never to give when the socket is nonblocking,
|
|
312
|
+
// even if the connection is intramachine or to
|
|
313
|
+
// localhost.
|
|
314
|
+
throw std::runtime_error ("unimplemented");
|
|
315
|
+
}
|
|
316
|
+
else if (errno == EINPROGRESS) {
|
|
317
|
+
// Errno will generally always be EINPROGRESS, but on Linux
|
|
318
|
+
// we have to look at getsockopt to be sure what really happened.
|
|
319
|
+
int error;
|
|
320
|
+
socklen_t len;
|
|
321
|
+
len = sizeof(error);
|
|
322
|
+
int o = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len);
|
|
323
|
+
if ((o == 0) && (error == 0)) {
|
|
324
|
+
// Here, there's no disposition.
|
|
325
|
+
// Put the connection on the stack and wait for it to complete
|
|
326
|
+
// or time out.
|
|
327
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (sd);
|
|
328
|
+
if (!cd)
|
|
329
|
+
throw std::runtime_error ("no connection allocated");
|
|
330
|
+
cd->SetConnectPending (true);
|
|
331
|
+
Add (cd);
|
|
332
|
+
out = cd->GetBinding().c_str();
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
// This could be connection refused or something such thing.
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
// The error from connect was something other then EINPROGRESS.
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (out == NULL)
|
|
343
|
+
close (sd);
|
|
344
|
+
return out;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
/*******************************
|
|
349
|
+
EventMachine_t::CreateTcpServer
|
|
350
|
+
*******************************/
|
|
351
|
+
|
|
352
|
+
const char *EventMachine_t::CreateTcpServer (const char *server, int port)
|
|
353
|
+
{
|
|
354
|
+
/* Create a TCP-acceptor (server) socket and add it to the event machine.
|
|
355
|
+
* Return the binding of the new acceptor to the caller.
|
|
356
|
+
* This binding will be referenced when the new acceptor sends events
|
|
357
|
+
* to indicate accepted connections.
|
|
358
|
+
*/
|
|
359
|
+
|
|
360
|
+
const char *output_binding = NULL;
|
|
361
|
+
|
|
362
|
+
struct sockaddr_in sin;
|
|
363
|
+
|
|
364
|
+
int sd_accept = socket (AF_INET, SOCK_STREAM, 0);
|
|
365
|
+
if (sd_accept == -1) {
|
|
366
|
+
goto fail;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
memset (&sin, 0, sizeof(sin));
|
|
370
|
+
sin.sin_family = AF_INET;
|
|
371
|
+
sin.sin_addr.s_addr = INADDR_ANY;
|
|
372
|
+
sin.sin_port = htons (port);
|
|
373
|
+
|
|
374
|
+
if (server && *server) {
|
|
375
|
+
sin.sin_addr.s_addr = inet_addr (server);
|
|
376
|
+
if (sin.sin_addr.s_addr == -1) {
|
|
377
|
+
hostent *hp = gethostbyname (server);
|
|
378
|
+
if (hp == NULL) {
|
|
379
|
+
//__warning ("hostname not resolved: ", server);
|
|
380
|
+
goto fail;
|
|
381
|
+
}
|
|
382
|
+
sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
{ // set reuseaddr to improve performance on restarts.
|
|
387
|
+
int oval = 1;
|
|
388
|
+
if (setsockopt (sd_accept, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0) {
|
|
389
|
+
//__warning ("setsockopt failed while creating listener","");
|
|
390
|
+
goto fail;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
{ // set CLOEXEC.
|
|
395
|
+
int cloexec = fcntl (sd_accept, F_GETFD, 0);
|
|
396
|
+
assert (cloexec >= 0);
|
|
397
|
+
cloexec |= FD_CLOEXEC;
|
|
398
|
+
fcntl (sd_accept, F_SETFD, cloexec);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
if (bind (sd_accept, (struct sockaddr*)&sin, sizeof(sin))) {
|
|
403
|
+
//__warning ("binding failed");
|
|
404
|
+
goto fail;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (listen (sd_accept, 100)) {
|
|
408
|
+
//__warning ("listen failed");
|
|
409
|
+
goto fail;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
{
|
|
413
|
+
// Set the acceptor non-blocking.
|
|
414
|
+
// THIS IS CRUCIALLY IMPORTANT because we read it in a select loop.
|
|
415
|
+
int val = fcntl (sd_accept, F_GETFL, 0);
|
|
416
|
+
if (fcntl (sd_accept, F_SETFL, val | O_NONBLOCK) == -1) {
|
|
417
|
+
goto fail;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
{ // Looking good.
|
|
422
|
+
AcceptorDescriptor *ad = new AcceptorDescriptor (this, sd_accept);
|
|
423
|
+
if (!ad)
|
|
424
|
+
throw std::runtime_error ("unable to allocate acceptor");
|
|
425
|
+
Add (ad);
|
|
426
|
+
output_binding = ad->GetBinding().c_str();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return output_binding;
|
|
430
|
+
|
|
431
|
+
fail:
|
|
432
|
+
if (sd_accept != -1)
|
|
433
|
+
close (sd_accept);
|
|
434
|
+
return NULL;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
/*******************
|
|
441
|
+
EventMachine_t::Add
|
|
442
|
+
*******************/
|
|
443
|
+
|
|
444
|
+
void EventMachine_t::Add (EventableDescriptor *ed)
|
|
445
|
+
{
|
|
446
|
+
if (!ed)
|
|
447
|
+
throw std::runtime_error ("added bad descriptor");
|
|
448
|
+
ed->SetEventCallback (EventCallback);
|
|
449
|
+
NewDescriptors.push_back (ed);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
/**********************************
|
|
454
|
+
EventMachine_t::_AddNewDescriptors
|
|
455
|
+
**********************************/
|
|
456
|
+
|
|
457
|
+
void EventMachine_t::_AddNewDescriptors()
|
|
458
|
+
{
|
|
459
|
+
/* Avoid adding descriptors to the main descriptor list
|
|
460
|
+
* while we're actually traversing the list.
|
|
461
|
+
* Any descriptors that are added as a result of processing timers
|
|
462
|
+
* or acceptors should go on a temporary queue and then added
|
|
463
|
+
* while we're not traversing the main list.
|
|
464
|
+
*/
|
|
465
|
+
|
|
466
|
+
for (size_t i = 0; i < NewDescriptors.size(); i++) {
|
|
467
|
+
EventableDescriptor *ed = NewDescriptors[i];
|
|
468
|
+
if (ed == NULL)
|
|
469
|
+
throw std::runtime_error ("adding bad descriptor");
|
|
470
|
+
Descriptors.push_back (ed);
|
|
471
|
+
}
|
|
472
|
+
NewDescriptors.clear();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
|