~ps10gel/ubuntu/xenial/trafficserver/6.2.0

« back to all changes in this revision

Viewing changes to proxy/PluginVC.cc

  • Committer: Bazaar Package Importer
  • Author(s): Arno Toell
  • Date: 2011-01-13 11:49:18 UTC
  • Revision ID: james.westby@ubuntu.com-20110113114918-vu422h8dknrgkj15
Tags: upstream-2.1.5-unstable
ImportĀ upstreamĀ versionĀ 2.1.5-unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** @file
 
2
 
 
3
  A brief file description
 
4
 
 
5
  @section license License
 
6
 
 
7
  Licensed to the Apache Software Foundation (ASF) under one
 
8
  or more contributor license agreements.  See the NOTICE file
 
9
  distributed with this work for additional information
 
10
  regarding copyright ownership.  The ASF licenses this file
 
11
  to you under the Apache License, Version 2.0 (the
 
12
  "License"); you may not use this file except in compliance
 
13
  with the License.  You may obtain a copy of the License at
 
14
 
 
15
      http://www.apache.org/licenses/LICENSE-2.0
 
16
 
 
17
  Unless required by applicable law or agreed to in writing, software
 
18
  distributed under the License is distributed on an "AS IS" BASIS,
 
19
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
20
  See the License for the specific language governing permissions and
 
21
  limitations under the License.
 
22
 */
 
23
 
 
24
/****************************************************************************
 
25
 
 
26
   PluginVC.cc
 
27
 
 
28
   Description: Allows bi-directional transfer for data from one
 
29
      continuation to another via a mechanism that impersonates a
 
30
      NetVC.  Should implement all external attributes of NetVConnections.
 
31
 
 
32
   Since data is transfered within Traffic Server, this is a two
 
33
   headed beast.  One NetVC on initiating side (active side) and
 
34
   one NetVC on the receiving side (passive side).
 
35
 
 
36
   The two NetVC subclasses, PluginVC, are part PluginVCCore object.  All
 
37
   three objects share the same mutex.  That mutex is required
 
38
   for doing operations that affect the shared buffers,
 
39
   read state from the PluginVC on the other side or deal with deallocation.
 
40
 
 
41
   To simplify the code, all data passing through the system goes initially
 
42
   into a shared buffer.  There are two shared buffers, one for each
 
43
   direction of the connection.  While it's more efficient to transfer
 
44
   the data from one buffer to another directly, this creates a lot
 
45
   of tricky conditions since you must be holding the lock for both
 
46
   sides, in additional this VC's lock.  Additionally, issues like
 
47
   watermarks are very hard to deal with.  Since we try to
 
48
   to move data by IOBufferData references the efficiency penalty shouldn't
 
49
   be too bad and if it is a big pentaly, a brave soul can reimplement
 
50
   to move the data directly without the intermediate buffer.
 
51
 
 
52
   Locking is difficult issue for this multi-headed beast.  In each
 
53
   PluginVC, there a two locks. The one we got from our PluginVCCore and
 
54
   the lock from the state machine using the PluginVC.  The read side
 
55
   lock & the write side lock must be the same.  The regular net processor has
 
56
   this constraint as well.  In order to handle scheduling of retry events cleanly,
 
57
   we have two event poitners, one for each lock.  sm_lock_retry_event can only
 
58
   be changed while holding the using state machine's lock and
 
59
   core_lock_retry_event can only be manipulated while holding the PluginVC's
 
60
   lock.  On entry to PluginVC::main_handler, we obtain all the locks
 
61
   before looking at the events.  If we can't get all the locks
 
62
   we reschedule the event for further retries.  Since all the locks are
 
63
   obtained in the beginning of the handler, we know we are running
 
64
   exclusively in the later parts of the handler and we will
 
65
   be free from do_io or reenable calls on the PluginVC.
 
66
 
 
67
   The assumption is made (conistant with IO Core spec) that any close,
 
68
   shutdown, reenable, or do_io_{read,write) operation is done by the callee
 
69
   while holding the lock for that side of the operation.
 
70
 
 
71
 
 
72
 ****************************************************************************/
 
73
 
 
74
#include "PluginVC.h"
 
75
#include "P_EventSystem.h"
 
76
#include "P_Net.h"
 
77
#include "Regression.h"
 
78
 
 
79
#define PVC_LOCK_RETRY_TIME HRTIME_MSECONDS(10)
 
80
#undef MIN
 
81
#define MIN(x,y) (((x) <= (y)) ? (x) : (y))
 
82
#undef MAX
 
83
#define MAX(x,y) (((x) >= (y)) ? (x) : (y))
 
84
 
 
85
#define PVC_DEFAULT_MAX_BYTES 32768
 
86
#define MIN_BLOCK_TRANSFER_BYTES 128
 
87
 
 
88
#define EVENT_PTR_LOCKED (void*) 0x1
 
89
#define EVENT_PTR_CLOSED (void*) 0x2
 
90
 
 
91
#define PVC_TYPE    ((vc_type == PLUGIN_VC_ACTIVE) ? "Active" : "Passive")
 
92
#define PVC_ID      (core_obj? core_obj->id : (unsigned)-1)
 
93
 
 
94
PluginVC::PluginVC():
 
95
NetVConnection(),
 
96
magic(PLUGIN_VC_MAGIC_ALIVE), vc_type(PLUGIN_VC_UNKNOWN), core_obj(NULL),
 
97
other_side(NULL), read_state(), write_state(),
 
98
need_read_process(false), need_write_process(false),
 
99
closed(false), sm_lock_retry_event(NULL), core_lock_retry_event(NULL),
 
100
deletable(false), reentrancy_count(0), active_timeout(0), inactive_timeout(0), active_event(NULL), inactive_event(NULL)
 
101
{
 
102
  SET_HANDLER(&PluginVC::main_handler);
 
103
}
 
104
 
 
105
PluginVC::~PluginVC()
 
106
{
 
107
  mutex = NULL;
 
108
}
 
109
 
 
110
int
 
111
PluginVC::main_handler(int event, void *data)
 
112
{
 
113
 
 
114
  Debug("pvc_event", "[%u] %s: Received event %d", PVC_ID, PVC_TYPE, event);
 
115
 
 
116
  ink_release_assert(event == EVENT_INTERVAL || event == EVENT_IMMEDIATE);
 
117
  ink_release_assert(magic == PLUGIN_VC_MAGIC_ALIVE);
 
118
  ink_debug_assert(!deletable);
 
119
  ink_debug_assert(data != NULL);
 
120
 
 
121
  Event *call_event = (Event *) data;
 
122
  EThread *my_ethread = mutex->thread_holding;
 
123
  ink_release_assert(my_ethread != NULL);
 
124
 
 
125
  bool read_mutex_held = false;
 
126
  bool write_mutex_held = false;
 
127
  Ptr<ProxyMutex> read_side_mutex = read_state.vio.mutex;
 
128
  Ptr<ProxyMutex> write_side_mutex = write_state.vio.mutex;
 
129
 
 
130
  if (read_side_mutex) {
 
131
    read_mutex_held = MUTEX_TAKE_TRY_LOCK(read_side_mutex, my_ethread);
 
132
 
 
133
    if (!read_mutex_held) {
 
134
      call_event->schedule_in(PVC_LOCK_RETRY_TIME);
 
135
      return 0;
 
136
    }
 
137
 
 
138
    if (read_side_mutex.m_ptr != read_state.vio.mutex.m_ptr) {
 
139
      // It's possible some swapped the mutex on us before
 
140
      //  we were able to grab it
 
141
      Mutex_unlock(read_side_mutex, my_ethread);
 
142
      call_event->schedule_in(PVC_LOCK_RETRY_TIME);
 
143
      return 0;
 
144
    }
 
145
  }
 
146
 
 
147
  if (write_side_mutex) {
 
148
    write_mutex_held = MUTEX_TAKE_TRY_LOCK(write_side_mutex, my_ethread);
 
149
 
 
150
    if (!write_mutex_held) {
 
151
      if (read_mutex_held) {
 
152
        Mutex_unlock(read_side_mutex, my_ethread);
 
153
      }
 
154
      call_event->schedule_in(PVC_LOCK_RETRY_TIME);
 
155
      return 0;
 
156
    }
 
157
 
 
158
    if (write_side_mutex.m_ptr != write_state.vio.mutex.m_ptr) {
 
159
      // It's possible some swapped the mutex on us before
 
160
      //  we were able to grab it
 
161
      Mutex_unlock(write_side_mutex, my_ethread);
 
162
      if (read_mutex_held) {
 
163
        Mutex_unlock(read_side_mutex, my_ethread);
 
164
      }
 
165
      call_event->schedule_in(PVC_LOCK_RETRY_TIME);
 
166
      return 0;
 
167
    }
 
168
  }
 
169
  // We've got all the locks so there should not be any
 
170
  //   other calls active
 
171
  ink_release_assert(reentrancy_count == 0);
 
172
 
 
173
  if (closed) {
 
174
    process_close();
 
175
    return 0;
 
176
  }
 
177
  // We can get closed while we're calling back the
 
178
  //  continuation.  Set the reentrancy count so we know
 
179
  //  we could be calling the continuation and that we
 
180
  //  need to defer close processing
 
181
  reentrancy_count++;
 
182
 
 
183
  if (call_event == active_event) {
 
184
    process_timeout(call_event, VC_EVENT_ACTIVE_TIMEOUT, &active_event);
 
185
  } else if (call_event == inactive_event) {
 
186
    process_timeout(call_event, VC_EVENT_INACTIVITY_TIMEOUT, &inactive_event);
 
187
  } else {
 
188
    if (call_event == sm_lock_retry_event) {
 
189
      sm_lock_retry_event = NULL;
 
190
    } else {
 
191
      ink_release_assert(call_event == core_lock_retry_event);
 
192
      core_lock_retry_event = NULL;
 
193
    }
 
194
 
 
195
    if (need_read_process) {
 
196
      process_read_side(false);
 
197
    }
 
198
 
 
199
    if (need_write_process && !closed) {
 
200
      process_write_side(false);
 
201
    }
 
202
 
 
203
  }
 
204
 
 
205
  reentrancy_count--;
 
206
  if (closed) {
 
207
    process_close();
 
208
  }
 
209
 
 
210
  if (read_mutex_held) {
 
211
    Mutex_unlock(read_side_mutex, my_ethread);
 
212
  }
 
213
 
 
214
  if (write_mutex_held) {
 
215
    Mutex_unlock(write_side_mutex, my_ethread);
 
216
  }
 
217
 
 
218
  return 0;
 
219
}
 
220
 
 
221
VIO *
 
222
PluginVC::do_io_read(Continuation * c, int64_t nbytes, MIOBuffer * buf)
 
223
{
 
224
 
 
225
  ink_assert(!closed);
 
226
  ink_assert(magic == PLUGIN_VC_MAGIC_ALIVE);
 
227
 
 
228
  if (buf) {
 
229
    read_state.vio.buffer.writer_for(buf);
 
230
  } else {
 
231
    read_state.vio.buffer.clear();
 
232
  }
 
233
 
 
234
  // Note: we set vio.op last because process_read_side looks at it to
 
235
  //  tell if the VConnection is active.
 
236
  read_state.vio.mutex = c->mutex;
 
237
  read_state.vio._cont = c;
 
238
  read_state.vio.nbytes = nbytes;
 
239
  read_state.vio.ndone = 0;
 
240
  read_state.vio.vc_server = (VConnection *) this;
 
241
  read_state.vio.op = VIO::READ;
 
242
 
 
243
  Debug("pvc", "[%u] %s: do_io_read for %d bytes", PVC_ID, PVC_TYPE, nbytes);
 
244
 
 
245
  // Since reentrant callbacks are not allowed on from do_io
 
246
  //   functions schedule ourselves get on a different stack
 
247
  need_read_process = true;
 
248
  setup_event_cb(0, &sm_lock_retry_event);
 
249
 
 
250
  return &read_state.vio;
 
251
}
 
252
 
 
253
VIO *
 
254
PluginVC::do_io_write(Continuation * c, int64_t nbytes, IOBufferReader * abuffer, bool owner)
 
255
{
 
256
 
 
257
  ink_assert(!closed);
 
258
  ink_assert(magic == PLUGIN_VC_MAGIC_ALIVE);
 
259
 
 
260
  if (abuffer) {
 
261
    ink_assert(!owner);
 
262
    write_state.vio.buffer.reader_for(abuffer);
 
263
  } else {
 
264
    write_state.vio.buffer.clear();
 
265
  }
 
266
 
 
267
  // Note: we set vio.op last because process_write_side looks at it to
 
268
  //  tell if the VConnection is active.
 
269
  write_state.vio.mutex = c->mutex;
 
270
  write_state.vio._cont = c;
 
271
  write_state.vio.nbytes = nbytes;
 
272
  write_state.vio.ndone = 0;
 
273
  write_state.vio.vc_server = (VConnection *) this;
 
274
  write_state.vio.op = VIO::WRITE;
 
275
 
 
276
  Debug("pvc", "[%u] %s: do_io_write for %d bytes", PVC_ID, PVC_TYPE, nbytes);
 
277
 
 
278
  // Since reentrant callbacks are not allowed on from do_io
 
279
  //   functions schedule ourselves get on a different stack
 
280
  need_write_process = true;
 
281
  setup_event_cb(0, &sm_lock_retry_event);
 
282
 
 
283
  return &write_state.vio;
 
284
}
 
285
 
 
286
void
 
287
PluginVC::reenable(VIO * vio)
 
288
{
 
289
 
 
290
  ink_assert(!closed);
 
291
  ink_assert(magic == PLUGIN_VC_MAGIC_ALIVE);
 
292
  ink_debug_assert(vio->mutex->thread_holding == this_ethread());
 
293
 
 
294
  Debug("pvc", "[%u] %s: reenable %s", PVC_ID, PVC_TYPE, (vio->op == VIO::WRITE) ? "Write" : "Read");
 
295
 
 
296
  if (vio->op == VIO::WRITE) {
 
297
    ink_assert(vio == &write_state.vio);
 
298
    need_write_process = true;
 
299
  } else if (vio->op == VIO::READ) {
 
300
    need_read_process = true;
 
301
  } else {
 
302
    ink_release_assert(0);
 
303
  }
 
304
  setup_event_cb(0, &sm_lock_retry_event);
 
305
}
 
306
 
 
307
void
 
308
PluginVC::reenable_re(VIO * vio)
 
309
{
 
310
 
 
311
  ink_assert(!closed);
 
312
  ink_assert(magic == PLUGIN_VC_MAGIC_ALIVE);
 
313
  ink_debug_assert(vio->mutex->thread_holding == this_ethread());
 
314
 
 
315
  Debug("pvc", "[%u] %s: reenable_re %s", PVC_ID, PVC_TYPE, (vio->op == VIO::WRITE) ? "Write" : "Read");
 
316
 
 
317
  MUTEX_TRY_LOCK(lock, this->mutex, this_ethread());
 
318
  if (!lock) {
 
319
    if (vio->op == VIO::WRITE) {
 
320
      need_write_process = true;
 
321
    } else {
 
322
      need_read_process = true;
 
323
    }
 
324
    setup_event_cb(PVC_LOCK_RETRY_TIME, &sm_lock_retry_event);
 
325
    return;
 
326
  }
 
327
 
 
328
  reentrancy_count++;
 
329
 
 
330
  if (vio->op == VIO::WRITE) {
 
331
    ink_assert(vio == &write_state.vio);
 
332
    process_write_side(false);
 
333
  } else if (vio->op == VIO::READ) {
 
334
    ink_assert(vio == &read_state.vio);
 
335
    process_read_side(false);
 
336
  } else {
 
337
    ink_release_assert(0);
 
338
  }
 
339
 
 
340
  reentrancy_count--;
 
341
 
 
342
  // To process the close, we need the lock
 
343
  //   for the PluginVC.  Schedule an event
 
344
  //   to make sure we get it
 
345
  if (closed) {
 
346
    setup_event_cb(0, &sm_lock_retry_event);
 
347
  }
 
348
}
 
349
 
 
350
void
 
351
PluginVC::do_io_close(int flag)
 
352
{
 
353
  NOWARN_UNUSED(flag);
 
354
  ink_assert(closed == false);
 
355
  ink_assert(magic == PLUGIN_VC_MAGIC_ALIVE);
 
356
 
 
357
  Debug("pvc", "[%u] %s: do_io_close", PVC_ID, PVC_TYPE);
 
358
 
 
359
  if (reentrancy_count > 0) {
 
360
    // Do nothing since dealloacting ourselves
 
361
    //  now will lead to us running on a dead
 
362
    //  PluginVC since we are being called
 
363
    //  reentrantly
 
364
    closed = true;
 
365
    return;
 
366
  }
 
367
 
 
368
  MUTEX_TRY_LOCK(lock, mutex, this_ethread());
 
369
 
 
370
  if (!lock) {
 
371
    setup_event_cb(PVC_LOCK_RETRY_TIME, &sm_lock_retry_event);
 
372
    closed = true;
 
373
    return;
 
374
  } else {
 
375
    closed = true;
 
376
  }
 
377
 
 
378
  process_close();
 
379
}
 
380
 
 
381
void
 
382
PluginVC::do_io_shutdown(ShutdownHowTo_t howto)
 
383
{
 
384
 
 
385
  ink_assert(!closed);
 
386
  ink_assert(magic == PLUGIN_VC_MAGIC_ALIVE);
 
387
 
 
388
  switch (howto) {
 
389
  case IO_SHUTDOWN_READ:
 
390
    read_state.shutdown = true;
 
391
    break;
 
392
  case IO_SHUTDOWN_WRITE:
 
393
    write_state.shutdown = true;
 
394
    break;
 
395
  case IO_SHUTDOWN_READWRITE:
 
396
    read_state.shutdown = true;
 
397
    write_state.shutdown = true;
 
398
    break;
 
399
  }
 
400
}
 
401
 
 
402
// int PluginVC::transfer_bytes(MIOBuffer* transfer_to,
 
403
//                              IOBufferReader* transfer_from, int act_on)
 
404
//
 
405
//   Takes care of transfering bytes from a reader to another buffer
 
406
//      In the case of large transfers, we move blocks.  In the case
 
407
//      of small transfers we copy data so as to not build too many
 
408
//      buffer blocks
 
409
//
 
410
// Args:
 
411
//   transfer_to:  buffer to copy to
 
412
//   transfer_from:  buffer_copy_from
 
413
//   act_on: is the max number of bytes we are to copy.  There must
 
414
//          be at least act_on bytes available from transfer_from
 
415
//
 
416
// Returns number of bytes transfered
 
417
//
 
418
int64_t
 
419
PluginVC::transfer_bytes(MIOBuffer * transfer_to, IOBufferReader * transfer_from, int64_t act_on)
 
420
{
 
421
 
 
422
  int64_t total_added = 0;
 
423
 
 
424
  ink_debug_assert(act_on <= transfer_from->read_avail());
 
425
 
 
426
  while (act_on > 0) {
 
427
    int64_t block_read_avail = transfer_from->block_read_avail();
 
428
    int64_t to_move = MIN(act_on, block_read_avail);
 
429
    int64_t moved = 0;
 
430
 
 
431
    if (to_move <= 0) {
 
432
      break;
 
433
    }
 
434
 
 
435
    if (to_move >= MIN_BLOCK_TRANSFER_BYTES) {
 
436
      moved = transfer_to->write(transfer_from, to_move, 0);
 
437
    } else {
 
438
      // We have a really small amount of data.  To make
 
439
      //  sure we don't get a huge build up of blocks which
 
440
      //  can lead to stack overflows if the buffer is destroyed
 
441
      //  before we read from it, we need copy over to the new
 
442
      //  buffer instead of doing a block transfer
 
443
      moved = transfer_to->write(transfer_from->start(), to_move);
 
444
 
 
445
      if (moved == 0) {
 
446
        // We are out of buffer space
 
447
        break;
 
448
      }
 
449
    }
 
450
 
 
451
    act_on -= moved;
 
452
    transfer_from->consume(moved);
 
453
    total_added += moved;
 
454
  }
 
455
 
 
456
  return total_added;
 
457
}
 
458
 
 
459
// void PluginVC::process_write_side(bool cb_ok)
 
460
//
 
461
//   This function may only be called while holding
 
462
//      this->mutex & while it is ok to callback the
 
463
//      write side continuation
 
464
//
 
465
//   Does write side processing
 
466
//
 
467
void
 
468
PluginVC::process_write_side(bool other_side_call)
 
469
{
 
470
 
 
471
  ink_assert(!deletable);
 
472
  ink_assert(magic == PLUGIN_VC_MAGIC_ALIVE);
 
473
 
 
474
  MIOBuffer *core_buffer = (vc_type == PLUGIN_VC_ACTIVE) ? core_obj->a_to_p_buffer : core_obj->p_to_a_buffer;
 
475
 
 
476
  need_write_process = false;
 
477
 
 
478
  if (write_state.vio.op != VIO::WRITE || closed || write_state.shutdown) {
 
479
    return;
 
480
  }
 
481
  // Acquire the lock of the write side continuation
 
482
  EThread *my_ethread = mutex->thread_holding;
 
483
  ink_assert(my_ethread != NULL);
 
484
  MUTEX_TRY_LOCK(lock, write_state.vio.mutex, my_ethread);
 
485
  if (!lock) {
 
486
    Debug("pvc_event", "[%u] %s: process_write_side lock miss, retrying", PVC_ID, PVC_TYPE);
 
487
 
 
488
    need_write_process = true;
 
489
    setup_event_cb(PVC_LOCK_RETRY_TIME, &core_lock_retry_event);
 
490
    return;
 
491
  }
 
492
 
 
493
  Debug("pvc", "[%u] %s: process_write_side", PVC_ID, PVC_TYPE);
 
494
  need_write_process = false;
 
495
 
 
496
 
 
497
  // Check the state of our write buffer as well as ntodo
 
498
  int64_t ntodo = write_state.vio.ntodo();
 
499
  if (ntodo == 0) {
 
500
    return;
 
501
  }
 
502
 
 
503
  IOBufferReader *reader = write_state.vio.get_reader();
 
504
  int64_t bytes_avail = reader->read_avail();
 
505
  int64_t act_on = MIN(bytes_avail, ntodo);
 
506
 
 
507
  Debug("pvc", "[%u] %s: process_write_side; act_on %d", PVC_ID, PVC_TYPE, act_on);
 
508
 
 
509
  if (other_side->closed || other_side->read_state.shutdown) {
 
510
    write_state.vio._cont->handleEvent(VC_EVENT_ERROR, &write_state.vio);
 
511
    return;
 
512
  }
 
513
 
 
514
  if (act_on <= 0) {
 
515
    if (ntodo > 0) {
 
516
      // Notify the continuation that we are "disabling"
 
517
      //  ourselves due to to nothing to write
 
518
      write_state.vio._cont->handleEvent(VC_EVENT_WRITE_READY, &write_state.vio);
 
519
    }
 
520
    return;
 
521
  }
 
522
  // Bytes available, try to transfer to the PluginVCCore
 
523
  //   intermediate buffer
 
524
  //
 
525
  int64_t buf_space = PVC_DEFAULT_MAX_BYTES - core_buffer->max_read_avail();
 
526
  if (buf_space <= 0) {
 
527
    Debug("pvc", "[%u] %s: process_write_side no buffer space", PVC_ID, PVC_TYPE);
 
528
    return;
 
529
  }
 
530
  act_on = MIN(act_on, buf_space);
 
531
 
 
532
  int64_t added = transfer_bytes(core_buffer, reader, act_on);
 
533
  if (added < 0) {
 
534
    // Couldn't actually get the buffer space.  This only
 
535
    //   happens on small transfers with the above
 
536
    //   PVC_DEFAULT_MAX_BYTES factor doesn't apply
 
537
    Debug("pvc", "[%u] %s: process_write_side out of buffer space", PVC_ID, PVC_TYPE);
 
538
    return;
 
539
  }
 
540
 
 
541
  write_state.vio.ndone += added;
 
542
 
 
543
  Debug("pvc", "[%u] %s: process_write_side; added %d", PVC_ID, PVC_TYPE, added);
 
544
 
 
545
  if (write_state.vio.ntodo() == 0) {
 
546
    write_state.vio._cont->handleEvent(VC_EVENT_WRITE_COMPLETE, &write_state.vio);
 
547
  } else {
 
548
    write_state.vio._cont->handleEvent(VC_EVENT_WRITE_READY, &write_state.vio);
 
549
  }
 
550
 
 
551
  update_inactive_time();
 
552
 
 
553
  // Wake up the read side on the other side to process these bytes
 
554
  if (!other_side->closed) {
 
555
    if (!other_side_call) {
 
556
      other_side->process_read_side(true);
 
557
    } else {
 
558
      other_side->read_state.vio.reenable();
 
559
    }
 
560
  }
 
561
}
 
562
 
 
563
 
 
564
// void PluginVC::process_read_side()
 
565
//
 
566
//   This function may only be called while holding
 
567
//      this->mutex & while it is ok to callback the
 
568
//      read side continuation
 
569
//
 
570
//   Does read side processing
 
571
//
 
572
void
 
573
PluginVC::process_read_side(bool other_side_call)
 
574
{
 
575
 
 
576
  ink_assert(!deletable);
 
577
  ink_assert(magic == PLUGIN_VC_MAGIC_ALIVE);
 
578
 
 
579
  // TODO: Never used??
 
580
  //MIOBuffer *core_buffer;
 
581
 
 
582
  IOBufferReader *core_reader;
 
583
 
 
584
  if (vc_type == PLUGIN_VC_ACTIVE) {
 
585
    //core_buffer = core_obj->p_to_a_buffer;
 
586
    core_reader = core_obj->p_to_a_reader;
 
587
  } else {
 
588
    ink_assert(vc_type == PLUGIN_VC_PASSIVE);
 
589
    //core_buffer = core_obj->a_to_p_buffer;
 
590
    core_reader = core_obj->a_to_p_reader;
 
591
  }
 
592
 
 
593
  need_read_process = false;
 
594
 
 
595
  if (read_state.vio.op != VIO::READ || closed || read_state.shutdown) {
 
596
    return;
 
597
  }
 
598
  // Acquire the lock of the read side continuation
 
599
  EThread *my_ethread = mutex->thread_holding;
 
600
  ink_assert(my_ethread != NULL);
 
601
  MUTEX_TRY_LOCK(lock, read_state.vio.mutex, my_ethread);
 
602
  if (!lock) {
 
603
    Debug("pvc_event", "[%u] %s: process_read_side lock miss, retrying", PVC_ID, PVC_TYPE);
 
604
 
 
605
    need_read_process = true;
 
606
    setup_event_cb(PVC_LOCK_RETRY_TIME, &core_lock_retry_event);
 
607
    return;
 
608
  }
 
609
 
 
610
  Debug("pvc", "[%u] %s: process_read_side", PVC_ID, PVC_TYPE);
 
611
  need_read_process = false;
 
612
 
 
613
  // Check the state of our read buffer as well as ntodo
 
614
  int64_t ntodo = read_state.vio.ntodo();
 
615
  if (ntodo == 0) {
 
616
    return;
 
617
  }
 
618
 
 
619
  int64_t bytes_avail = core_reader->read_avail();
 
620
  int64_t act_on = MIN(bytes_avail, ntodo);
 
621
 
 
622
  Debug("pvc", "[%u] %s: process_read_side; act_on %d", PVC_ID, PVC_TYPE, act_on);
 
623
 
 
624
  if (act_on <= 0) {
 
625
    if (other_side->closed || other_side->write_state.shutdown) {
 
626
      read_state.vio._cont->handleEvent(VC_EVENT_EOS, &read_state.vio);
 
627
    }
 
628
    return;
 
629
  }
 
630
  // Bytes available, try to transfer from the PluginVCCore
 
631
  //   intermediate buffer
 
632
  //
 
633
  MIOBuffer *output_buffer = read_state.vio.get_writer();
 
634
 
 
635
  int64_t water_mark = output_buffer->water_mark;
 
636
  water_mark = MAX(water_mark, PVC_DEFAULT_MAX_BYTES);
 
637
  int64_t buf_space = water_mark - output_buffer->max_read_avail();
 
638
  if (buf_space <= 0) {
 
639
    Debug("pvc", "[%u] %s: process_read_side no buffer space", PVC_ID, PVC_TYPE);
 
640
    return;
 
641
  }
 
642
  act_on = MIN(act_on, buf_space);
 
643
 
 
644
  int64_t added = transfer_bytes(output_buffer, core_reader, act_on);
 
645
  if (added <= 0) {
 
646
    // Couldn't actually get the buffer space.  This only
 
647
    //   happens on small transfers with the above
 
648
    //   PVC_DEFAULT_MAX_BYTES factor doesn't apply
 
649
    Debug("pvc", "[%u] %s: process_read_side out of buffer space", PVC_ID, PVC_TYPE);
 
650
    return;
 
651
  }
 
652
 
 
653
  read_state.vio.ndone += added;
 
654
 
 
655
  Debug("pvc", "[%u] %s: process_read_side; added %d", PVC_ID, PVC_TYPE, added);
 
656
 
 
657
  if (read_state.vio.ntodo() == 0) {
 
658
    read_state.vio._cont->handleEvent(VC_EVENT_READ_COMPLETE, &read_state.vio);
 
659
  } else {
 
660
    read_state.vio._cont->handleEvent(VC_EVENT_READ_READY, &read_state.vio);
 
661
  }
 
662
 
 
663
  update_inactive_time();
 
664
 
 
665
  // Wake up the other side so it knows there is space available in
 
666
  //  intermediate buffer
 
667
  if (!other_side->closed) {
 
668
    if (!other_side_call) {
 
669
      other_side->process_write_side(true);
 
670
    } else {
 
671
      other_side->write_state.vio.reenable();
 
672
    }
 
673
  }
 
674
}
 
675
 
 
676
// void PluginVC::process_read_close()
 
677
//
 
678
//   This function may only be called while holding
 
679
//      this->mutex
 
680
//
 
681
//   Tries to close the and dealloc the the vc
 
682
//
 
683
void
 
684
PluginVC::process_close()
 
685
{
 
686
 
 
687
  ink_assert(magic == PLUGIN_VC_MAGIC_ALIVE);
 
688
 
 
689
  Debug("pvc", "[%u] %s: process_close", PVC_ID, PVC_TYPE);
 
690
 
 
691
  if (!deletable) {
 
692
    deletable = true;
 
693
  }
 
694
 
 
695
  if (sm_lock_retry_event) {
 
696
    sm_lock_retry_event->cancel();
 
697
    sm_lock_retry_event = NULL;
 
698
  }
 
699
 
 
700
  if (core_lock_retry_event) {
 
701
    core_lock_retry_event->cancel();
 
702
    core_lock_retry_event = NULL;
 
703
  }
 
704
 
 
705
  if (active_event) {
 
706
    active_event->cancel();
 
707
    active_event = NULL;
 
708
  }
 
709
 
 
710
  if (inactive_event) {
 
711
    inactive_event->cancel();
 
712
    inactive_event = NULL;
 
713
  }
 
714
  // If the other side of the PluginVC is not closed
 
715
  //  we need to force it process both living sides
 
716
  //  of the connection in order that it recognizes
 
717
  //  the close
 
718
  if (!other_side->closed && core_obj->connected) {
 
719
    other_side->need_write_process = true;
 
720
    other_side->need_read_process = true;
 
721
    other_side->setup_event_cb(0, &other_side->core_lock_retry_event);
 
722
  }
 
723
 
 
724
  core_obj->attempt_delete();
 
725
}
 
726
 
 
727
// void PluginVC::process_timeout(Event* e, int event_to_send, Event** our_eptr)
 
728
//
 
729
//   Handles sending timeout event to the VConnection.  e is the event we got
 
730
//     which indicats the timeout.  event_to_send is the event to the
 
731
//     vc user.  Our_eptr is a pointer our event either inactive_event,
 
732
//     or active_event.  If we successfully send the timeout to vc user,
 
733
//     we clear the pointer, otherwise we reschedule it.
 
734
//
 
735
//   Because the possibility of reentrant close from vc user, we don't want to
 
736
//      touch any state after making the call back
 
737
//
 
738
void
 
739
PluginVC::process_timeout(Event * e, int event_to_send, Event ** our_eptr)
 
740
{
 
741
 
 
742
  ink_assert(e = *our_eptr);
 
743
 
 
744
  if (read_state.vio.op == VIO::READ && !read_state.shutdown && read_state.vio.ntodo() > 0) {
 
745
    MUTEX_TRY_LOCK(lock, read_state.vio.mutex, e->ethread);
 
746
    if (!lock) {
 
747
      e->schedule_in(PVC_LOCK_RETRY_TIME);
 
748
      return;
 
749
    }
 
750
    *our_eptr = NULL;
 
751
    read_state.vio._cont->handleEvent(event_to_send, &read_state.vio);
 
752
  } else if (write_state.vio.op == VIO::WRITE && !write_state.shutdown && write_state.vio.ntodo() > 0) {
 
753
    MUTEX_TRY_LOCK(lock, write_state.vio.mutex, e->ethread);
 
754
    if (!lock) {
 
755
      e->schedule_in(PVC_LOCK_RETRY_TIME);
 
756
      return;
 
757
    }
 
758
    *our_eptr = NULL;
 
759
    write_state.vio._cont->handleEvent(event_to_send, &write_state.vio);
 
760
  } else {
 
761
    *our_eptr = NULL;
 
762
  }
 
763
}
 
764
 
 
765
void
 
766
PluginVC::update_inactive_time()
 
767
{
 
768
  if (inactive_event) {
 
769
    inactive_event->cancel();
 
770
    inactive_event = eventProcessor.schedule_in(this, inactive_timeout);
 
771
  }
 
772
}
 
773
 
 
774
// void PluginVC::setup_event_cb(ink_hrtime in)
 
775
//
 
776
//    Setup up the event processor to call us back.
 
777
//      We've got two different event pointers to handle
 
778
//      locking issues
 
779
//
 
780
void
 
781
PluginVC::setup_event_cb(ink_hrtime in, Event ** e_ptr)
 
782
{
 
783
 
 
784
  ink_assert(magic == PLUGIN_VC_MAGIC_ALIVE);
 
785
 
 
786
  if (*e_ptr == NULL) {
 
787
 
 
788
    // We locked the pointer so we can now allocate an event
 
789
    //   to call us back
 
790
    if (in == 0) {
 
791
      *e_ptr = eventProcessor.schedule_imm(this);
 
792
    } else {
 
793
      *e_ptr = eventProcessor.schedule_in(this, in);
 
794
    }
 
795
  }
 
796
}
 
797
 
 
798
void
 
799
PluginVC::set_active_timeout(ink_hrtime timeout_in)
 
800
{
 
801
  active_timeout = timeout_in;
 
802
 
 
803
  // FIX - Do we need to handle the case where the timeout is set
 
804
  //   but no io has been done?
 
805
  if (active_event) {
 
806
    ink_assert(!active_event->cancelled);
 
807
    active_event->cancel();
 
808
    active_event = NULL;
 
809
  }
 
810
 
 
811
  if (active_timeout > 0) {
 
812
    active_event = eventProcessor.schedule_in(this, active_timeout);
 
813
  }
 
814
}
 
815
 
 
816
void
 
817
PluginVC::set_inactivity_timeout(ink_hrtime timeout_in)
 
818
{
 
819
  inactive_timeout = timeout_in;
 
820
 
 
821
  // FIX - Do we need to handle the case where the timeout is set
 
822
  //   but no io has been done?
 
823
  if (inactive_event) {
 
824
    ink_assert(!inactive_event->cancelled);
 
825
    inactive_event->cancel();
 
826
    inactive_event = NULL;
 
827
  }
 
828
 
 
829
  if (inactive_timeout > 0) {
 
830
    inactive_event = eventProcessor.schedule_in(this, inactive_timeout);
 
831
  }
 
832
}
 
833
 
 
834
void
 
835
PluginVC::cancel_active_timeout()
 
836
{
 
837
  set_active_timeout(0);
 
838
}
 
839
 
 
840
void
 
841
PluginVC::cancel_inactivity_timeout()
 
842
{
 
843
  set_inactivity_timeout(0);
 
844
}
 
845
 
 
846
ink_hrtime
 
847
PluginVC::get_active_timeout()
 
848
{
 
849
  return active_timeout;
 
850
}
 
851
 
 
852
ink_hrtime
 
853
PluginVC::get_inactivity_timeout()
 
854
{
 
855
  return inactive_timeout;
 
856
}
 
857
 
 
858
SOCKET
 
859
PluginVC::get_socket()
 
860
{
 
861
  return 0;
 
862
}
 
863
 
 
864
void
 
865
PluginVC::set_local_addr()
 
866
{
 
867
  if (vc_type == PLUGIN_VC_ACTIVE) {
 
868
    local_addr = core_obj->active_addr_struct;
 
869
  } else {
 
870
    local_addr = core_obj->passive_addr_struct;
 
871
  }
 
872
}
 
873
 
 
874
void
 
875
PluginVC::set_remote_addr()
 
876
{
 
877
  if (vc_type == PLUGIN_VC_ACTIVE) {
 
878
    remote_addr = core_obj->passive_addr_struct;
 
879
  } else {
 
880
    remote_addr = core_obj->active_addr_struct;
 
881
  }
 
882
}
 
883
 
 
884
 
 
885
bool
 
886
PluginVC::get_data(int id, void *data)
 
887
{
 
888
  if (data == NULL) {
 
889
    return false;
 
890
  }
 
891
  switch (id) {
 
892
  case PLUGIN_VC_DATA_LOCAL:
 
893
    if (vc_type == PLUGIN_VC_ACTIVE) {
 
894
      *(void **) data = core_obj->active_data;
 
895
    } else {
 
896
      *(void **) data = core_obj->passive_data;
 
897
    }
 
898
    return true;
 
899
  case PLUGIN_VC_DATA_REMOTE:
 
900
    if (vc_type == PLUGIN_VC_ACTIVE) {
 
901
      *(void **) data = core_obj->passive_data;
 
902
    } else {
 
903
      *(void **) data = core_obj->active_data;
 
904
    }
 
905
    return true;
 
906
  default:
 
907
    *(void **) data = NULL;
 
908
    return false;
 
909
  }
 
910
}
 
911
 
 
912
bool
 
913
PluginVC::set_data(int id, void *data)
 
914
{
 
915
  switch (id) {
 
916
  case PLUGIN_VC_DATA_LOCAL:
 
917
    if (vc_type == PLUGIN_VC_ACTIVE) {
 
918
      core_obj->active_data = data;
 
919
    } else {
 
920
      core_obj->passive_data = data;
 
921
    }
 
922
    return true;
 
923
  case PLUGIN_VC_DATA_REMOTE:
 
924
    if (vc_type == PLUGIN_VC_ACTIVE) {
 
925
      core_obj->passive_data = data;
 
926
    } else {
 
927
      core_obj->active_data = data;
 
928
    }
 
929
    return true;
 
930
  default:
 
931
    return false;
 
932
  }
 
933
}
 
934
 
 
935
// PluginVCCore
 
936
 
 
937
vint32
 
938
  PluginVCCore::nextid = 0;
 
939
 
 
940
PluginVCCore::~PluginVCCore()
 
941
{
 
942
}
 
943
 
 
944
PluginVCCore *
 
945
PluginVCCore::alloc()
 
946
{
 
947
  PluginVCCore *pvc = NEW(new PluginVCCore);
 
948
  pvc->init();
 
949
  return pvc;
 
950
}
 
951
 
 
952
void
 
953
PluginVCCore::init()
 
954
{
 
955
  mutex = new_ProxyMutex();
 
956
 
 
957
  active_vc.vc_type = PLUGIN_VC_ACTIVE;
 
958
  active_vc.other_side = &passive_vc;
 
959
  active_vc.core_obj = this;
 
960
  active_vc.mutex = mutex;
 
961
  active_vc.thread = this_ethread();
 
962
 
 
963
  passive_vc.vc_type = PLUGIN_VC_PASSIVE;
 
964
  passive_vc.other_side = &active_vc;
 
965
  passive_vc.core_obj = this;
 
966
  passive_vc.mutex = mutex;
 
967
  passive_vc.thread = active_vc.thread;
 
968
 
 
969
  p_to_a_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_32K);
 
970
  p_to_a_reader = p_to_a_buffer->alloc_reader();
 
971
 
 
972
  a_to_p_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_32K);
 
973
  a_to_p_reader = a_to_p_buffer->alloc_reader();
 
974
 
 
975
  Debug("pvc", "[%u] Created PluginVCCore at 0x%X, active 0x%X, passive 0x%X", id, this, &active_vc, &passive_vc);
 
976
}
 
977
 
 
978
void
 
979
PluginVCCore::destroy()
 
980
{
 
981
 
 
982
  Debug("pvc", "[%u] Destroying PluginVCCore at 0x%X", id, this);
 
983
 
 
984
  ink_assert(active_vc.closed == true || !connected);
 
985
  active_vc.mutex = NULL;
 
986
  active_vc.read_state.vio.buffer.clear();
 
987
  active_vc.write_state.vio.buffer.clear();
 
988
  active_vc.magic = PLUGIN_VC_MAGIC_DEAD;
 
989
 
 
990
  ink_assert(passive_vc.closed == true || !connected);
 
991
  passive_vc.mutex = NULL;
 
992
  passive_vc.read_state.vio.buffer.clear();
 
993
  passive_vc.write_state.vio.buffer.clear();
 
994
  passive_vc.magic = PLUGIN_VC_MAGIC_DEAD;
 
995
 
 
996
  if (p_to_a_buffer) {
 
997
    free_MIOBuffer(p_to_a_buffer);
 
998
    p_to_a_buffer = NULL;
 
999
  }
 
1000
 
 
1001
  if (a_to_p_buffer) {
 
1002
    free_MIOBuffer(a_to_p_buffer);
 
1003
    a_to_p_buffer = NULL;
 
1004
  }
 
1005
 
 
1006
  this->mutex = NULL;
 
1007
  delete this;
 
1008
}
 
1009
 
 
1010
void
 
1011
PluginVCCore::set_accept_cont(Continuation * c)
 
1012
{
 
1013
  connect_to = c;
 
1014
 
 
1015
  // FIX ME - must return action
 
1016
}
 
1017
 
 
1018
PluginVC *
 
1019
PluginVCCore::connect()
 
1020
{
 
1021
 
 
1022
  // Make sure there is another end to connect to
 
1023
  if (connect_to == NULL) {
 
1024
    return NULL;
 
1025
  }
 
1026
 
 
1027
  connected = true;
 
1028
  state_send_accept(EVENT_IMMEDIATE, NULL);
 
1029
 
 
1030
  return &active_vc;
 
1031
}
 
1032
 
 
1033
Action *
 
1034
PluginVCCore::connect_re(Continuation * c)
 
1035
{
 
1036
 
 
1037
  // Make sure there is another end to connect to
 
1038
  if (connect_to == NULL) {
 
1039
    return NULL;
 
1040
  }
 
1041
 
 
1042
  EThread *my_thread = this_ethread();
 
1043
  MUTEX_TAKE_LOCK(this->mutex, my_thread);
 
1044
 
 
1045
  connected = true;
 
1046
  state_send_accept(EVENT_IMMEDIATE, NULL);
 
1047
 
 
1048
  // We have to take out our mutex because rest of the
 
1049
  //   system expects the VC mutex to held when calling back.
 
1050
  // We can use take lock here instead of try lock because the
 
1051
  //   lock should never already be held.
 
1052
 
 
1053
  c->handleEvent(NET_EVENT_OPEN, &active_vc);
 
1054
  MUTEX_UNTAKE_LOCK(this->mutex, my_thread);
 
1055
 
 
1056
  return ACTION_RESULT_DONE;
 
1057
}
 
1058
 
 
1059
int
 
1060
PluginVCCore::state_send_accept_failed(int event, void *data)
 
1061
{
 
1062
  NOWARN_UNUSED(event);
 
1063
  NOWARN_UNUSED(data);
 
1064
  MUTEX_TRY_LOCK(lock, connect_to->mutex, this_ethread());
 
1065
 
 
1066
  if (lock) {
 
1067
    connect_to->handleEvent(NET_EVENT_ACCEPT_FAILED, NULL);
 
1068
    destroy();
 
1069
  } else {
 
1070
    SET_HANDLER(&PluginVCCore::state_send_accept_failed);
 
1071
    eventProcessor.schedule_in(this, PVC_LOCK_RETRY_TIME);
 
1072
  }
 
1073
 
 
1074
  return 0;
 
1075
 
 
1076
}
 
1077
 
 
1078
int
 
1079
PluginVCCore::state_send_accept(int event, void *data)
 
1080
{
 
1081
  NOWARN_UNUSED(event);
 
1082
  NOWARN_UNUSED(data);
 
1083
  MUTEX_TRY_LOCK(lock, connect_to->mutex, this_ethread());
 
1084
 
 
1085
  if (lock) {
 
1086
    connect_to->handleEvent(NET_EVENT_ACCEPT, &passive_vc);
 
1087
  } else {
 
1088
    SET_HANDLER(&PluginVCCore::state_send_accept);
 
1089
    eventProcessor.schedule_in(this, PVC_LOCK_RETRY_TIME);
 
1090
  }
 
1091
 
 
1092
  return 0;
 
1093
}
 
1094
 
 
1095
 
 
1096
// void PluginVCCore::attempt_delete()
 
1097
//
 
1098
//  Mutex must be held when calling this function
 
1099
//
 
1100
void
 
1101
PluginVCCore::attempt_delete()
 
1102
{
 
1103
 
 
1104
  if (active_vc.deletable) {
 
1105
    if (passive_vc.deletable) {
 
1106
      destroy();
 
1107
    } else if (!connected) {
 
1108
      state_send_accept_failed(EVENT_IMMEDIATE, NULL);
 
1109
    }
 
1110
  }
 
1111
}
 
1112
 
 
1113
// void PluginVCCore::kill_no_connect()
 
1114
//
 
1115
//   Called to kill the PluginVCCore when the
 
1116
//     connect call hasn't been made yet
 
1117
//
 
1118
void
 
1119
PluginVCCore::kill_no_connect()
 
1120
{
 
1121
  ink_assert(!connected);
 
1122
  ink_assert(!active_vc.closed);
 
1123
  active_vc.do_io_close();
 
1124
}
 
1125
 
 
1126
void
 
1127
PluginVCCore::set_passive_addr(uint32_t ip, int port)
 
1128
{
 
1129
  ((struct sockaddr_in *)&(passive_addr_struct))->sin_addr.s_addr = htonl(ip);
 
1130
  ((struct sockaddr_in *)&(passive_addr_struct))->sin_port = htons(port);
 
1131
}
 
1132
 
 
1133
void
 
1134
PluginVCCore::set_active_addr(uint32_t ip, int port)
 
1135
{
 
1136
  ((struct sockaddr_in *)&(active_addr_struct))->sin_addr.s_addr = htonl(ip);
 
1137
  ((struct sockaddr_in *)&(active_addr_struct))->sin_port = htons(port);
 
1138
}
 
1139
 
 
1140
void
 
1141
PluginVCCore::set_passive_data(void *data)
 
1142
{
 
1143
  passive_data = data;
 
1144
}
 
1145
 
 
1146
void
 
1147
PluginVCCore::set_active_data(void *data)
 
1148
{
 
1149
  active_data = data;
 
1150
}
 
1151
 
 
1152
/*************************************************************
 
1153
 *
 
1154
 *   REGRESSION TEST STUFF
 
1155
 *
 
1156
 **************************************************************/
 
1157
 
 
1158
#if TS_HAS_TESTS
 
1159
class PVCTestDriver:public NetTestDriver
 
1160
{
 
1161
public:
 
1162
  PVCTestDriver();
 
1163
  ~PVCTestDriver();
 
1164
 
 
1165
  void start_tests(RegressionTest * r_arg, int *pstatus_arg);
 
1166
  void run_next_test();
 
1167
  int main_handler(int event, void *data);
 
1168
 
 
1169
private:
 
1170
  int i;
 
1171
  int completions_received;
 
1172
};
 
1173
 
 
1174
PVCTestDriver::PVCTestDriver():
 
1175
NetTestDriver(), i(0), completions_received(0)
 
1176
{
 
1177
}
 
1178
 
 
1179
PVCTestDriver::~PVCTestDriver()
 
1180
{
 
1181
  mutex = NULL;
 
1182
}
 
1183
 
 
1184
void
 
1185
PVCTestDriver::start_tests(RegressionTest * r_arg, int *pstatus_arg)
 
1186
{
 
1187
  mutex = new_ProxyMutex();
 
1188
  MUTEX_TRY_LOCK(lock, mutex, this_ethread());
 
1189
 
 
1190
  r = r_arg;
 
1191
  pstatus = pstatus_arg;
 
1192
 
 
1193
  run_next_test();
 
1194
 
 
1195
  SET_HANDLER(&PVCTestDriver::main_handler);
 
1196
}
 
1197
 
 
1198
void
 
1199
PVCTestDriver::run_next_test()
 
1200
{
 
1201
 
 
1202
  int a_index = i * 2;
 
1203
  int p_index = a_index + 1;
 
1204
 
 
1205
  if (p_index >= num_netvc_tests) {
 
1206
    // We are done - // FIX - PASS or FAIL?
 
1207
    if (errors == 0) {
 
1208
      *pstatus = REGRESSION_TEST_PASSED;
 
1209
    } else {
 
1210
      *pstatus = REGRESSION_TEST_FAILED;
 
1211
    }
 
1212
    delete this;
 
1213
    return;
 
1214
  }
 
1215
  completions_received = 0;
 
1216
  i++;
 
1217
 
 
1218
  Debug("pvc_test", "Starting test %s", netvc_tests_def[a_index].test_name);
 
1219
 
 
1220
  NetVCTest *p = NEW(new NetVCTest);
 
1221
  NetVCTest *a = NEW(new NetVCTest);
 
1222
  PluginVCCore *core = PluginVCCore::alloc();
 
1223
  core->set_accept_cont(p);
 
1224
 
 
1225
  p->init_test(NET_VC_TEST_PASSIVE, this, NULL, r, &netvc_tests_def[p_index], "PluginVC", "pvc_test_detail");
 
1226
  PluginVC *a_vc = core->connect();
 
1227
 
 
1228
  a->init_test(NET_VC_TEST_ACTIVE, this, a_vc, r, &netvc_tests_def[a_index], "PluginVC", "pvc_test_detail");
 
1229
}
 
1230
 
 
1231
int
 
1232
PVCTestDriver::main_handler(int event, void *data)
 
1233
{
 
1234
  NOWARN_UNUSED(event);
 
1235
  NOWARN_UNUSED(data);
 
1236
  completions_received++;
 
1237
 
 
1238
  if (completions_received == 2) {
 
1239
    run_next_test();
 
1240
  }
 
1241
 
 
1242
  return 0;
 
1243
}
 
1244
 
 
1245
EXCLUSIVE_REGRESSION_TEST(PVC) (RegressionTest * t, int atype, int *pstatus)
 
1246
{
 
1247
  NOWARN_UNUSED(atype);
 
1248
  PVCTestDriver *driver = NEW(new PVCTestDriver);
 
1249
  driver->start_tests(t, pstatus);
 
1250
}
 
1251
#endif