summaryrefslogtreecommitdiff
path: root/examples/Reactor/Misc/test_signals_2.cpp
blob: 466ab58482f4216743174fae28cc27952ae33b54 (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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
// $Id$

// Test the ability of the Reactor/Signal_Handler to register multiple
// handler per-signal.

/* This test works as follows:

	1. To test the "original" semantics of ACE (i.e., only one
	   ACE_Event_Handler can be registered per signal), you don't
	   need to do anything special.  Existing programs work the
	   same since giving the Reactor's constructor a 0 value
	   (which is the default argument, BTW) instructs it to behave
	   as before.  When a 0 is given, the ACE_Reactor's
	   constructor/open method creates an instance of
	   ACE_Sig_Handler and assigns this to an internal pointer.
	   This pointer is then used to dispatch all signal-related
	   methods within the Reactor.  The default ACE_Sig_Handler
	   only allows *one* ACE_Event_Handler to be registered
	   per-signal.

	   To run this version of the test do the following:

	   % ./test-signal
	   ./test_signals
	   waiting for SIGINT or SIGQUIT
	   ^C
	   signal Interrupt occurred in Sig_Handler_2 (fruity, 0, 0) with count = 1
	   waiting for SIGINT or SIGQUIT
	   ^\
	   signal Quit occurred in Sig_Handler_2 (fruity, 0, 0) with count = 2
	   shutting down SIGQUIT in Sig_Handler_2 (fruity, 0, 0)
	   waiting for SIGINT or SIGQUIT
	   ^C
	   signal Interrupt occurred in Sig_Handler_2 (fruity, 0, 0) with count = 3
	   waiting for SIGINT or SIGQUIT
	   ^\Quit (core dumped)

           Note that in this test only one handler (the last one --
	   "Sig_Handler_2 (fruity)") is actually registered.  BTW, the
	   core dump is the expected behavior since the default
	   disposition is restored when there are no more handlers
	   (see the code below).

	2. To test the "multiple handlers per-signal semantics", you
	   need to pass the constructor/open method of the ACE_Reactor
	   a pointer to a an instance of ACE_Sig_Handlers (note the
	   plural "s").  ACE_Sig_Handlers is a class that derives from
	   ACE_Sig_Handler.  The difference between these two classes
	   is that (1) ACE_Sig_Handlers::register_signal allows
	   multiple ACE_Event_Handlers to be registered per-signal and
	   (2) it enables SA_RESTART by default.  This class also
	   implements Detlef Becker's algorithm for integrating ACE
	   signal handling with 3rd party libraries.

	   To run this version of the test do the following:

	   % ./test_signals 1

	   waiting for SIGINT or SIGQUIT
	   ^C
	   signal Interrupt occurred in external handler!
	   signal Interrupt occurred in Sig_Handler_1 (howdy, 3, 1) with count = 1
	   shutting down SIGINT in Sig_Handler_1 (howdy, 3, 1)
	   signal Interrupt occurred in Sig_Handler_1 (doody, 5, 4) with count = 1
	   shutting down SIGINT in Sig_Handler_1 (doody, 5, 4)
	   signal Interrupt occurred in Sig_Handler_2 (tutty, 7, 6) with count = 1
	   signal Interrupt occurred in Sig_Handler_2 (fruity, 9, 8) with count = 1
	   waiting for SIGINT or SIGQUIT
	   ^\
	   signal Quit occurred in Sig_Handler_1 (howdy, 3, 1) with count = 2
	   shutting down SIGQUIT in Sig_Handler_1 (howdy, 3, 1)
	   signal Quit occurred in Sig_Handler_1 (doody, 5, 4) with count = 2
	   shutting down SIGQUIT in Sig_Handler_1 (doody, 5, 4)
	   signal Quit occurred in Sig_Handler_2 (tutty, 7, 6) with count = 2
	   shutting down SIGQUIT in Sig_Handler_2 (tutty, 7, 6)
	   signal Quit occurred in Sig_Handler_2 (fruity, 9, 8) with count = 2
	   shutting down SIGQUIT in Sig_Handler_2 (fruity, 9, 8)
	   waiting for SIGINT or SIGQUIT
	   ^C
	   signal Interrupt occurred in external handler!
	   signal Interrupt occurred in Sig_Handler_2 (tutty, 7, 6) with count = 3
	   signal Interrupt occurred in Sig_Handler_2 (fruity, 9, 8) with count = 3
	   waiting for SIGINT or SIGQUIT
	   ^\Quit (core dumped)

           When this test begins all four handlers are registered and
           dispatched when a SIGINT or SIGQUIT occurs.  After the
           first SIGINT, the handle_signal method of the Sig_Handler_1
           objects unregister themselves.  At that point there are 4
           SIGQUIT handlers left, but only 2 of our SIGINT handlers
           left (and the 1 external handler).  After the first
           SIGQUIT, there are no SIGQUIT handlers left since they all
           deregister themselves (which restores the "SIG_DFL"
           disposition).  On the second SIGINT there are only 3
           handlers left (2 of ours and 1 external).  Finally, on the
           second SIGQUIT we exit and dump core since that's what
           happens with the default disposition for SIGQUIT. */


#include "ace/Reactor.h"
#include "ace/WFMO_Reactor.h"
#include "ace/Select_Reactor.h"
#include "ace/Log_Msg.h"
#include "ace/Signal.h"

ACE_RCSID(Misc, test_signals_2, "$Id$")

class Sig_Handler_1 : public ACE_Event_Handler
{
public:
  Sig_Handler_1 (ACE_Reactor &reactor, const char *msg)
    : msg_ (msg),
      count_ (0),
      reactor_ (reactor)
  {
    // Register the signal handlers.
    this->quit_sigkey_ =
      reactor.register_handler (SIGQUIT, this);
    this->int_sigkey_ =
      reactor.register_handler (SIGINT, this);

    if (this->quit_sigkey_ == -1 || this->int_sigkey_ == -1)
      ACE_ERROR ((LM_ERROR,
                  "%p\n",
                  "register_handler"));
  }

  // @@ Note that this code is not portable to all OS platforms since
  // it does print statements within the signal handler.
  virtual int handle_signal (int signum,
                             siginfo_t *,
                             ucontext_t *)
  {
    this->count_++;
    ACE_DEBUG ((LM_DEBUG,
	       "\nsignal %S occurred in Sig_Handler_1 (%s, %d, %d) with count = %d",
	       signum,
                this->msg_,
                this->int_sigkey_,
                this->quit_sigkey_,
                this->count_));

    if (this->count_ != 1 && signum == SIGQUIT)
      {
	if (this->reactor_.remove_handler (SIGQUIT,
                                           0,
                                           0,
					   this->quit_sigkey_) == -1)
	  ACE_ERROR ((LM_ERROR,
                      "\n%p",
                      "remove_handler"));
	else
	  ACE_DEBUG ((LM_DEBUG,
                      "\nshutting down SIGQUIT in Sig_Handler_1 (%s, %d, %d)",
                      this->msg_,
                      this->int_sigkey_,
                      this->quit_sigkey_));
      }
    else if (this->count_ != 2 && signum == SIGINT)
      {
	if (this->reactor_.remove_handler (SIGINT,
                                           0,
                                           0,
					   this->int_sigkey_) == -1)
	  ACE_ERROR ((LM_ERROR,
                      "\n%p",
                      "remove_handler"));
	else
	  ACE_DEBUG ((LM_DEBUG,
                      "\nshutting down SIGINT in Sig_Handler_1 (%s, %d, %d)",
		     this->msg_,
                      this->int_sigkey_,
                      this->quit_sigkey_));
      }
    return 0;
  }

protected:
  const char *msg_;
  int count_;
  int int_sigkey_;
  int quit_sigkey_;
  ACE_Reactor &reactor_;
};

class Sig_Handler_2 : public Sig_Handler_1
{
public:
  Sig_Handler_2 (ACE_Reactor &reactor, const char *msg)
    : Sig_Handler_1 (reactor, msg)
  {
  }

  virtual int handle_signal (int signum,
                             siginfo_t *,
                             ucontext_t *)
  {
    this->count_++;
    ACE_DEBUG ((LM_DEBUG,
	       "\nsignal %S occurred in Sig_Handler_2 (%s, %d, %d) with count = %d",
	       signum,
                this->msg_,
                this->int_sigkey_,
                this->quit_sigkey_,
                this->count_));
    if (this->count_ != 0 && signum == SIGQUIT)
      {
	if (this->reactor_.remove_handler (SIGQUIT, 0, 0,
					   this->quit_sigkey_) == -1)
	  ACE_ERROR ((LM_ERROR,
                      "\n%p",
                      "remove_handler"));
	else
	  ACE_DEBUG ((LM_DEBUG,
                      "\nshutting down SIGQUIT in Sig_Handler_2 (%s, %d, %d)",
                      this->msg_,
                      this->int_sigkey_,
                      this->quit_sigkey_));
      }
    return 0;
  }
};

static void
external_handler (int signum)
{
  ACE_DEBUG ((LM_DEBUG,
              "\nsignal %S occurred in external handler!",
              signum));
}

#if !defined (HPUX)
int
ACE_TMAIN (int argc, ACE_TCHAR *[])
{
  // If argc > 1 then allow multiple handlers per-signal, else just
  // allow 1 handler per-signal.
  ACE_Sig_Handlers multi_handlers;

#if defined (ACE_WIN32)
  ACE_WFMO_Reactor reactor_impl (argc > 1
                                 ? &multi_handlers
                                 : (ACE_Sig_Handler *) 0);
#else
  ACE_Select_Reactor reactor_impl (argc > 1
                                   ? &multi_handlers
                                   : (ACE_Sig_Handler *) 0);
#endif /* ACE_WIN32 */
  ACE_Reactor reactor (&reactor_impl);

  if (argc > 1)
    {
      // Register an "external" signal handler so that the
      // ACE_Sig_Handlers code will have something to incorporate!

      ACE_SignalHandler eh = (ACE_SignalHandler) external_handler;
      ACE_Sig_Action sa (eh);

      sa.register_action (SIGINT);
    }

  // Create a bevy of handlers.
  Sig_Handler_1 h1 (reactor, "howdy");
  Sig_Handler_1 h2 (reactor, "doody");
  Sig_Handler_2 h3 (reactor, "tutty");
  Sig_Handler_2 h4 (reactor, "fruity");

  // Wait for user to type SIGINT and SIGQUIT.

  for (;;)
    {
      ACE_DEBUG ((LM_DEBUG,
                  "\nwaiting for SIGINT or SIGQUIT\n"));

      if (reactor.handle_events () == -1)
	ACE_ERROR ((LM_ERROR,
                    "%p\n",
                    "handle_events"));
    }

  ACE_NOTREACHED (return 0);
}
#else
int
main (int, char *[])
{
  ACE_ERROR_RETURN ((LM_ERROR,
                     "The HP C++ compiler is too lame to support this feature\n"),
                    -1);
}
#endif /* HPUX */