summaryrefslogtreecommitdiff
path: root/examples/Reactor/ReactorEx/README
blob: 17fa59edf255cd3ae665bd1d47088b2a00fab03f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
The ACE_ReactorEx encapsulates the Win32 WaitForMultipleObjects() API
within ACE.  The ACE_ReactorEx is similar in spirit to the
ACE_Reactor, except that (1) it is much simpler and (2) it works for
the complete range of Win32 handles (whereas the ACE_Reactor just
works for socket handles.

Here's the API for the ACE_ReactorEx:

class ACE_ReactorEx
{
public:
	// = Event loop.
	virtual int handle_events (ACE_Time_Value *);

	// = Handler registration management.
	virtual int register_handler (ACE_Event_Handler *);
	virtual int remove_handler (ACE_Event_Handler *);

	virtual int notify (void);

	// = Timer management
	virtual int schedule_timer (), etc.
	// ...
};

----------------------------------------

Here's how you might use it:

----------------------------------------

class My_Thread_Handler : public ACE_Event_Handler 
{
public:
	My_Thread_Handler (void) {
		// Create a thread that will run
		// for a time and then exit.
		this->thread_handle_ = 
			ACE_OS::thr_create (run, ......);
	}

	// Called back by the ACE_ReactorEx when the
	// event is signaled.
	virtual int handle_signal (int)
	{
		cout << "thread is done" << endl;
	}

	virtual ACE_HANDLE get_handle (void) const
	{
		return this->thread_handle_;
	}

private:
	ACE_HANDLE thread_handle_;

	static void *run (void *) {
		// Sleep for a while and then exit.
		ACE_OS::sleep (100000);
		return 0;
	}
};

----------------------------------------

The main program might look something like this:

----------------------------------------

int main (void)
{
	// ...
	ACE_ReactorEx dispatcher;
	My_Thread_Handler handler;

	// Register the thread handler.
	dispatcher.register_handler (&handler);
	
	// Block until the thread exits and the
	// handle_signal() method of the My_Thread_Handler
	// is called!
	dispatcher.handle_events ();

	// ...
}

----------------------------------------

. test_timeout --

This example application shows how to write ReactorEx and Proactor
event loops that handle events for some fixed amount of time.  It uses
ACE_Service_Config::run_reactorEx_event_loop (run_time) to handle
events for run_time seconds.  The output should be the following:

1 timeout occurred for Foo.
2 timeout occurred for Bar.
3 timeout occurred for Foo.
4 timeout occurred for Bar.
5 timeout occurred for Foo.
6 timeout occurred for Bar.
7 timeout occurred for Foo.
8 timeout occurred for Foo.
9 timeout occurred for Bar.
10 timeout occurred for Foo.
11 timeout occurred for Bar.
12 timeout occurred for Foo.

. test_remove_handler -- 

This application tests the ReactorEx's ability to handle simultaneous
events.  If you pass anything on the command-line, then each handler
requests to be removed from the ReactorEx after each event.  This has
a funky effect on the order in which handlers are serviced.  So, if no
parameters are passed in, the handlers should be serviced 1 through
MAXIMUM_WAIT_OBJECTS. If handlers to request to be removed as signals
occur, they will be serviced 1, MAX, MAX-1, ..., 2.  This is because
of a ReactorEx bookkeeping optimization.

. test_reactorEx.cpp --

This test application tests a wide range of events that can be
demultiplexed using various ACE utilities.  Events used include ^C
events, reading from STDIN, vanilla Win32 events, thread exits,
ReactorEx notifications, proactive reads, and proactive writes.

The proactive I/O events are demultiplexed by the ACE_Proactor.  The
thread exits, notications, and vanilla Win32 events are demultiplexed
by the ACE_ReactorEx.  To enable a single thread to run all these
events, the Proactor is integrated with the ReactorEx.

The test application prototypes a simple ntalk program.  Two instances
of the application connect.  Input from either console is displayed on
the others console also.  Because of the evils of Win32 STDIN, a
separate thread is used to read from STDIN.  To test the Proactor and
ReactorEx, I/O between the remote processes is performed proactively
and interactions between the STDIN thread and the main thread are
performed reactively.

The following description of the test application is in two parts.
The participants section explains the main components involved in the
application.  The collaboration section describes how the partipants
interact in response to the multiple event types which occur.

The ReactorEx test application has the following participants:

. ReactorEx -- The ReactorEx demultiplexes Win32 "waitable" events
  using WaitForMultipleObjects.

. Proactor  -- The proactor initiates and demultiplexes overlapped I/O
  operations.  The Proactor registers with the ReactorEx so that a
  single-thread can demultiplex all application events.

. STDIN_Handler -- STDIN_Handler is an Active Object which reads from
  STDIN and forwards the input to the Peer_Handler.  This runs
  in a separate thread to make the test more interesting.  However,
  STDIN is "waitable", so in general it can be waited on by the ACE
  ReactorEx, thanks MicroSlush!

. Peer_Handler -- The Peer_Handler connects to another instance of
  test_reactorEx.  It Proactively reads and writes data to the peer.
  When the STDIN_Handler gives it messages, it fowards them to the
  remote peer.  When it receives messages from the remote peer, it
  prints the output to the console.

The collaborations of the participants are as follows:

. Initialization

  Peer_Handler -- connects to the remote peer.  It then begins
  proactively reading from the remote connection.  Note that it will
  be notified by the Proactor when a read completes.  It also
  registers a new_msg_event with the ReactorEx.  Note that when the
  new_msg_event is signaled (by the STDIN_Handler),
  Peer_Handler::handle_signal will get called.

  STDIN_Handler -- STDIN_Handler registers a signal handler for
  SIGINT.  This just captures the exception so that the kernel doesn't
  kill our process; We want to exit gracefully.  It also creates an
  Exit_Hook object which registers the STDIN_Handler's thread handle
  with the ReactorEx.  The Exit_Hook will get called back when the
  STDIN_Handler thread exits.  After registering these, it blocks
  reading from STDIN.

  Proactor -- is registered with the ReactorEx.

  The main thread of control waits in the ReactorEx.

. STDIN events -- When the STDIN_Handler thread reads from STDIN, it
  puts the message on Peer_Handler's message queue and signals the
  new_msg_event.  It then returns to reading from STDIN.

. new_msg_events -- The ReactorEx thread wakes up and calls
  Peer_Handler::handle_signal.  The Peer_Handler then tries to dequeue
  a message from its message queue.  If it can, the message is
  Proactively sent to the remote peer.  Note that the Peer_Handler
  will be notified with this operation is complete.  The Peer_Handler
  then falls back into the ReactorEx event loop.

. Send complete event -- When a proactive send is complete, the
  Proactor is notified by the ReactorEx.  The Proactor, in turn,
  notifies the Peer_Handler.  The Peer_Handler then checks for more
  messages from the message queue.  If there are any, it tries to send
  them.  If there are not, it returns to the ReactorEx event loop.
  This is ok since it is notified via new_msg_event when new message
  arrive.

. Read complete event -- When a proactive read is complete (the
  Peer_Handler initiated a proactive read when it connected to the
  remote peer), the Proactor is notified by the ReactorEx.  The
  Proactor, in turn notifies the Peer_Handler.  If the read was
  successful the Peer_Handler just displays the received msg to the
  console and reinvokes a proactive read from the network connection.
  If the read failed (i.e. the remote peer exited), the Peer_Handler
  sets a flag to end the event loop and returns.  This will cause the
  application to exit.

. ^C events -- When the user types ^C at the console, the
  STDIN_Handler's signal handler will be called.  It does nothing, but
  as a result of the signal, the STDIN_Handler thread will exit.

. STDIN_Handler thread exits -- The Exit_Hook will get called back
  from the ReactorEx.  Exit_Hook::handle_signal sets a flag to end the
  event loop and returns.  This will cause the application to exit.