summaryrefslogtreecommitdiff
path: root/TAO/tao/Connection_Handler.cpp
blob: 6fd4351e45fc011bf543a8d5422547324da80b72 (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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
//$Id$

#include "Connection_Handler.h"
#include "ORB_Core.h"
#include "Server_Strategy_Factory.h"
#include "debug.h"
#include "Object.h"
#include "Resume_Handle.h"
#include "Transport.h"
#include "Wait_Strategy.h"

#include "ace/SOCK.h"
#include "ace/Reactor.h"

#if !defined (__ACE_INLINE__)
#include "tao/Connection_Handler.inl"
#endif /* __ACE_INLINE__ */

ACE_RCSID (tao,
           Connection_Handler,
           "$Id$")

TAO_Connection_Handler::TAO_Connection_Handler (TAO_ORB_Core *orb_core)
  : orb_core_ (orb_core)
  , transport_ (0)
  , tss_resources_ (orb_core->get_tss_resources ())
  , reference_count_ (1)
{
  // @@todo: We need to have a distinct option/ method in the resource
  // factory for this and TAO_Transport.

  this->refcount_lock_ =
    this->orb_core_->resource_factory ()->create_cached_connection_lock ();

  this->lock_ =
    this->orb_core_->resource_factory ()->create_cached_connection_lock ();

  // Put ourselves in the connection wait state as soon as we get
  // created
  this->state_changed (TAO_LF_Event::LFS_CONNECTION_WAIT);
}


TAO_Connection_Handler::~TAO_Connection_Handler (void)
{
  ACE_ASSERT(this->transport_ == 0);
  ACE_ASSERT(this->reference_count_ == 0);

  // @@ TODO Use auto_ptr<>
  delete this->lock_;
  delete this->refcount_lock_;
}


int
TAO_Connection_Handler::set_socket_option (ACE_SOCK &sock,
                                           int snd_size,
                                           int rcv_size)
{
#if !defined (ACE_LACKS_SOCKET_BUFSIZ)

  if (sock.set_option (SOL_SOCKET,
                       SO_SNDBUF,
                       (void *) &snd_size,
                       sizeof (snd_size)) == -1
      && errno != ENOTSUP)
    return -1;
  else if (sock.set_option (SOL_SOCKET,
                            SO_RCVBUF,
                            (void *) &rcv_size,
                            sizeof (int)) == -1
           && errno != ENOTSUP)
    return -1;
#else
   ACE_UNUSED_ARG (snd_size);
   ACE_UNUSED_ARG (rcv_size);
#endif /* !ACE_LACKS_SOCKET_BUFSIZ */

  (void) sock.enable (ACE_CLOEXEC);
  // Set the close-on-exec flag for that file descriptor. If the
  // operation fails we are out of luck (some platforms do not support
  // it and return -1).

  return 0;
}

int
TAO_Connection_Handler::svc_i (void)
{
  int result = 0;

  // Inheriting the ORB_Core tss stuff from the parent thread.
  this->orb_core_->inherit_from_parent_thread (this->tss_resources_);

  if (TAO_debug_level > 0)
    ACE_DEBUG ((LM_DEBUG,
                ACE_TEXT ("TAO (%P|%t) - TAO_Connection_Handler::svc_i begin\n")));

  // Here we simply synthesize the "typical" event loop one might find
  // in a reactive handler, except that this can simply block waiting
  // for input.

  ACE_Time_Value *max_wait_time = 0;
  ACE_Time_Value timeout;
  ACE_Time_Value current_timeout;

  if (this->orb_core_->thread_per_connection_timeout (timeout))
    {
      current_timeout = timeout;
      max_wait_time = &current_timeout;
    }

  TAO_Resume_Handle rh (this->orb_core_,
                        ACE_INVALID_HANDLE);

  while (!this->orb_core_->has_shutdown ()
         && result >= 0)
    {
      result =
        this->transport ()->handle_input_i (rh,
                                            max_wait_time);

      if (result == -1 && errno == ETIME)
        {
          // Ignore timeouts, they are only used to wake up and
          // shutdown.
          result = 0;

          // Reset errno to make sure we don't trip over an old value
          // of errno in case it is not reset when the recv() call
          // fails if the socket has been closed.
          errno = 0;
        }

      current_timeout = timeout;

      if (TAO_debug_level > 0)
        ACE_DEBUG ((LM_DEBUG,
                    "TAO (%P|%t) - Connection_Handler::svc_i - "
                    "loop <%d>\n", current_timeout.msec ()));
    }

  if (TAO_debug_level > 0)
    ACE_DEBUG  ((LM_DEBUG,
                 "TAO (%P|%t) - Connection_Handler::svc_i end\n"));

  return result;
}

void
TAO_Connection_Handler::transport (TAO_Transport* transport)
{
  // The transport can be reset, but not changed!
  ACE_ASSERT(this->transport_ == 0 || transport == 0);

  TAO_Transport * tmp = 0;
  {
    // Make the change atomic
    ACE_GUARD (ACE_Lock, ace_mon, *this->lock_);
    tmp = this->transport_;
    this->transport_ = TAO_Transport::_duplicate (transport);
  }

  if (tmp != 0) {
    tmp->connection_handler_closing ();
    TAO_Transport::release (tmp);
  }
}

long
TAO_Connection_Handler::incr_refcount (void)
{
  ACE_GUARD_RETURN (ACE_Lock, ace_mon, *this->refcount_lock_, -1);

  ACE_ASSERT(this->reference_count_ > 0);
  return ++this->reference_count_;
}

long
TAO_Connection_Handler::decr_refcount (void)
{
  {
    ACE_GUARD_RETURN (ACE_Lock, ace_mon, *this->refcount_lock_, -1);

    if(--this->reference_count_ > 0)
      return this->reference_count_;
  }

  ACE_ASSERT(this->reference_count_ == 0);


  int r = this->release_os_resources ();

  if (r == -1 && TAO_debug_level)
    {
      ACE_ERROR ((LM_ERROR,
                  "TAO (%P|%t) - Connection_Handler::"
                  "decr_refcount, release_os_resources() failed %p\n"));
    }

  delete this;

  return 0;
}

void
TAO_Connection_Handler::connection_close_wait (void)
{
  ACE_GUARD (ACE_Lock, ace_mon, *this->lock_);
  this->state_changed (TAO_LF_Event::LFS_CONNECTION_CLOSE_WAIT);
}



int
TAO_Connection_Handler::handle_close_eh (
    ACE_HANDLE handle, unsigned long reactor_mask, ACE_Event_Handler * eh)
{
  ACE_HANDLE my_handle = eh->get_handle ();

  if (TAO_debug_level)
    {
      ACE_DEBUG  ((LM_DEBUG,
                   "TAO (%P|%t) - Connection_Handler[%d]::handle_close_eh, "
                   "(%d, %d)\n",
                   my_handle, handle, reactor_mask));
    }

  if (this->close_connection () == 0)
    {
      if (TAO_debug_level)
        ACE_DEBUG  ((LM_DEBUG,
                     "TAO (%P|%t) - Connection_Handler[%d]::"
                     "handle_close, connection closing or already closed\n",
                     my_handle));
      return 0;
    }

  if (TAO_debug_level)
    ACE_DEBUG  ((LM_DEBUG,
                 "TAO (%P|%t) - Connection_Handler[%d]::"
                 "handle_close, connection fully closed\n",
                 my_handle));

  return 0;
}

int
TAO_Connection_Handler::handle_output_eh (
    ACE_HANDLE, ACE_Event_Handler * eh)
{
  // We are going to use the transport object to write data. Just make
  // sure that we have transport objects to proceed. This  is
  // perfectly valid assert
  ACE_ASSERT (this->transport () != 0);

  // Let the transport that it is going to be used
  (void) this->transport ()->update_transport ();

  // Instantiate the resume handle here.. This will automatically
  // resume the handle once data is written..
  TAO_Resume_Handle resume_handle (this->orb_core (),
                                   eh->get_handle ());

  int return_value = 0;
  this->pre_io_hook (return_value);
  if (return_value != 0)
    {
      resume_handle.set_flag (TAO_Resume_Handle::TAO_HANDLE_LEAVE_SUSPENDED);
      return return_value;
    }

  return_value = this->transport ()->handle_output ();

  this->pos_io_hook(return_value);
  if (return_value != 0)
    {
      resume_handle.set_flag (TAO_Resume_Handle::TAO_HANDLE_LEAVE_SUSPENDED);
    }

  return return_value;
}

int
TAO_Connection_Handler::handle_input_eh (
    ACE_HANDLE h, ACE_Event_Handler * eh)
{
  // We are going to use the transport object to read data. Just make
  // sure that we have transport objects to proceed. This  is
  // perfectly valid assert
  ACE_ASSERT (this->transport () != 0);

  // Let the transport know that it is used
  (void) this->transport ()->update_transport ();

  // Grab the transport id now and use the cached value for printing
  // since the  transport could dissappear by the time the thread
  // returns.
  size_t t_id =
    this->transport ()->id ();


  // Increase the reference count on the upcall that have passed us.
  //
  // REFCNT: Matches decr_refcount() in this function...
  long refcount = this->incr_refcount ();
  ACE_ASSERT (refcount > 0);

  if (TAO_debug_level > 6)
    {
      ACE_HANDLE handle = eh->get_handle();
      ACE_DEBUG ((LM_DEBUG,
                  "TAO (%P|%t) - Connection_Handler[%d]::handle_input, "
                  "handle = %d/%d, refcount = %d\n",
                  t_id, handle, h, refcount));
    }

  TAO_Resume_Handle resume_handle (this->orb_core (),
                                   eh->get_handle ());

  int return_value = 0;

  this->pre_io_hook (return_value);
  if (return_value != 0)
    {
      // REFCNT: Matches incr_refcount() at the beginning...
      refcount = this->decr_refcount ();
      ACE_ASSERT (refcount >= 0);
      return return_value;
    }


  return_value = this->transport ()->handle_input_i (resume_handle);

  this->pos_io_hook(return_value);

  // REFCNT: Matches incr_refcount() at the beginning...
  refcount = this->decr_refcount ();
  ACE_ASSERT (refcount >= 0);

  if (TAO_debug_level > 6)
    {
      ACE_HANDLE handle = eh->get_handle ();
      ACE_DEBUG ((LM_DEBUG,
                  "TAO (%P|%t) - Connection_Handler[%d]::handle_input, "
                  "handle = %d/%d, refcount = %d, retval = %d\n",
                  t_id, handle, h, refcount, return_value));
    }

  if (return_value == -1 || refcount == 0)
    {
      // This is really a odd case. We could have a race condition if
      // we dont do this. Looks like this what happens
      // - imagine we have more than 1 server threads
      // - The server has got more than one connection from the
      //   clients
      // - The clients make requests and they start dissappearing.
      // - The connections start getting closed
      // - at that point one of the server threads is woken up to
      //   and handle_input () is called.
      // - the handle_input sees no data and so is about return a -1.
      // - if the handle is resumed, it looks like the oen more thread
      //   gets access to the handle and the handle_input is called by
      //   another thread.
      // - at that point of time if the thread returning -1 to the
      //   reactor starts closing down the handler, bad things start
      //   happening.
      // Looks subtle though. After adding this I dont see anything
      // bad happenin and so let us stick with it...
      resume_handle.set_flag (TAO_Resume_Handle::TAO_HANDLE_LEAVE_SUSPENDED);
    }

  return return_value;
}

int
TAO_Connection_Handler::close_connection_eh (ACE_Event_Handler * eh)
{
  // Perform a double checked locking on the underlying ACE_HANDLE
  ACE_HANDLE handle = eh->get_handle ();

  // If the handle is ACE_INVALID_HANDLE then there is no work to be
  // done in this function, and we return immediately.  Returning 0
  // indicates the caller (handle_close() most likely), that there is
  // no work to be done.
  if (handle == ACE_INVALID_HANDLE)
    {
      return 0;
    }

  size_t id = 0;
  {
    ACE_GUARD_RETURN (ACE_Lock, ace_mon, *this->lock_, 0);

    handle = eh->get_handle ();

    // Double-checked locking.
    if(handle == ACE_INVALID_HANDLE)
      {
        return 0;
      }

    // Before closing the socket we need to remove ourselves from the
    // Reactor.  Sounds silly, as supposedly handle_close() was called
    // *BY* the Reactor, but the Reactor calls handle_close() with
    // only the masks implied by the handle_XXX() call that returned
    // -1, and it does *NOT* remove the Event Handler from all masks.
    // Furthermore, this method is also called outside the Reactor
    // event loop, for example, when an I/O error is detected during a
    // write().

    // The following assertion is true because:
    //
    //
    // 1) When a connection handler is initialized Transport is not zero
    //    and the handle is *NOT* ACE_INVALID_HANDLE.
    // 2) The code here is run only once, if we get to this point the
    //    handle was not ACE_INVALID_HANDLE
    // 3) this->transport() is only reset after we run this code
    //    successfully
    //
    // Or: for this code to run the handle must have changed state from
    // something valid to ACE_INVALID_HANDLE, and the transport() field
    // will not be changed before that state transition.
    //
    ACE_ASSERT (this->transport () != 0);

    // Save the ID for debugging messages
    id = this->transport()->id ();
    if (TAO_debug_level)
      {
        ACE_DEBUG  ((LM_DEBUG,
                     "TAO (%P|%t) - Connection_Handler[%d]::"
                     "close_connection, purging entry from cache\n",
                     handle));
      }
    this->transport ()->purge_entry ();

    // @@ This seems silly, but if we have no reason to be in the
    // reactor, then we dont remove ourselves.
    if (this->transport ()->wait_strategy ()->is_registered ())
      {
        ACE_Reactor * eh_reactor = eh->reactor ();

        // These checks are valid as long as the ORB_Core is not
        // shutdown. It is good to have these checks and they are valid
        // for most of the cases. Please see below for exceptions
        if (this->orb_core_->has_shutdown () == 0)
          {
            // The exception when these are not valid is for RTCORBA. With
            // RTCORBA on, you can threads in different lanes creating
            // handlers and registering them with reactor in those
            // respective lanes. These threads could then even go away
            // leaving the task of reclaiming these resources to the main
            // thread. For the main thread that takes the responsibility
            // of finalizing () the lanes and the pools, the calls and
            // comparison make no sense.
            ACE_Reactor * reactor =
              this->transport()->orb_core()->reactor ();
            ACE_ASSERT (eh_reactor == 0 || eh_reactor == reactor);

            ACE_Reactor * orb_core_reactor = this->orb_core_->reactor ();
            ACE_ASSERT (reactor == orb_core_reactor);

            if (eh_reactor == 0)
              eh_reactor = reactor;

            ACE_UNUSED_ARG (orb_core_reactor);
          }

        // The Reactor must not be null, otherwise something else is
        // horribly broken.
        ACE_ASSERT (eh_reactor != 0);

        if (TAO_debug_level)
          {
            ACE_DEBUG  ((LM_DEBUG,
                         "TAO (%P|%t) - Connection_Handler[%d]::"
                         "close_connection, removing from the reactor\n",
                         handle));
          }

        int r =
          eh_reactor->remove_handler (handle,
                                      (ACE_Event_Handler::ALL_EVENTS_MASK
                                       | ACE_Event_Handler::DONT_CALL));
        if(r == -1 && TAO_debug_level)
          {
            ACE_ERROR ((LM_ERROR,
                        "TAO (%P|%t) - Connection_Handler[%d]::"
                        "close_connection, problem in remove_handler (%d)\n",
                        handle, r));
          }

        // Also cancel any timers, we may create those for time-limited
        // buffering
        if (TAO_debug_level)
          {
            ACE_DEBUG  ((LM_DEBUG,
                         "TAO (%P|%t) - Connection_Handler[%d]::"
                         "close_connection, cancel all timers and refcount [%d]\n",
                         handle, reference_count_));
          }
        r = eh_reactor->cancel_timer (eh);
        if (r == -1 && TAO_debug_level)
          {
            ACE_ERROR ((LM_ERROR,
                        "TAO (%P|%t) - Connection_Handler[%d]::"
                        "close_connection, error cancelling timers\n",
                        handle));
          }

        // @@ This seems silly, the reactor is a much better authority to
        //    find out if a handle is registered...
        this->transport ()->wait_strategy ()->is_registered (0);
      }

    this->state_changed (TAO_LF_Event::LFS_CONNECTION_CLOSED);
  }

  ACE_ASSERT (this->transport () != 0);

  // Signal the transport that we will no longer have
  // a reference to it.  This will eventually call
  // TAO_Transport::release ().
  this->transport (0);

  // The Reactor (or the Connector) holds an implicit reference.
  // REFCNT: Matches start count
  // REFCNT: only this or handle_input_eh() are called
  long refcount = this->decr_refcount ();

  if (TAO_debug_level)
    {
      ACE_DEBUG  ((LM_DEBUG,
                   "TAO (%P|%t) - Connection_Handler[%d]::"
                   "close_connection, refcount = %d\n",
                   id, refcount));
    }

  return 1;
}

int
TAO_Connection_Handler::release_os_resources (void)
{
  return 0;
}

void
TAO_Connection_Handler::pre_io_hook (int & )
{
}

void
TAO_Connection_Handler::pos_io_hook (int & )
{
}