~ubuntu-branches/ubuntu/saucy/resiprocate/saucy-proposed

« back to all changes in this revision

Viewing changes to rutil/Condition.cxx

  • Committer: Package Import Robot
  • Author(s): Daniel Pocock
  • Date: 2012-05-17 19:29:59 UTC
  • Revision ID: package-import@ubuntu.com-20120517192959-vv00m77isztdy64q
Tags: upstream-1.8.2
ImportĀ upstreamĀ versionĀ 1.8.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <cassert>
 
2
#include <climits>
 
3
 
 
4
#ifndef WIN32
 
5
#  include <pthread.h>
 
6
#  include <errno.h>
 
7
#  include <sys/time.h>
 
8
#  include <sys/syscall.h>
 
9
#  include <unistd.h>
 
10
#endif
 
11
 
 
12
#include "rutil/compat.hxx"
 
13
#include "rutil/Condition.hxx"
 
14
#include "rutil/Mutex.hxx"
 
15
#include "rutil/Timer.hxx"
 
16
 
 
17
#ifdef _RESIP_MONOTONIC_CLOCK
 
18
#ifdef __APPLE__
 
19
#undef _RESIP_MONOTONIC_CLOCK
 
20
#warning Mac OS X does not support POSIX monotonic timers.
 
21
#endif
 
22
#endif
 
23
 
 
24
using namespace resip;
 
25
 
 
26
Condition::Condition()
 
27
{
 
28
   //std::cerr << this << " Condition::Condition" << std::endl;
 
29
 
 
30
#ifdef WIN32
 
31
#  ifdef RESIP_CONDITION_WIN32_CONFORMANCE_TO_POSIX
 
32
   m_blocked = 0;
 
33
   m_gone = 0;
 
34
   m_waiting = 0;
 
35
   m_gate = reinterpret_cast<void*>(CreateSemaphore(0, 1, 1, 0));
 
36
   m_queue = reinterpret_cast<void*>(CreateSemaphore(0, 0, LONG_MAX, 0));
 
37
   m_mutex = reinterpret_cast<void*>(CreateMutex(0, 0, 0));
 
38
 
 
39
   if (!m_gate || !m_queue || !m_mutex)
 
40
   {
 
41
      int res = 0;
 
42
      if (m_gate)
 
43
      {
 
44
         res = CloseHandle(reinterpret_cast<HANDLE>(m_gate));
 
45
         assert(res);
 
46
      }
 
47
      if (m_queue)
 
48
      {
 
49
         res = CloseHandle(reinterpret_cast<HANDLE>(m_queue));
 
50
         assert(res);
 
51
      }
 
52
      if (m_mutex)
 
53
      {
 
54
         res = CloseHandle(reinterpret_cast<HANDLE>(m_mutex));
 
55
         assert(res);
 
56
      }
 
57
 
 
58
      assert(0);
 
59
   }
 
60
#  else
 
61
   mId =  CreateEvent(
 
62
      NULL, //LPSECURITY_ATTRIBUTES lpEventAttributes,
 
63
      // pointer to security attributes
 
64
      FALSE, // BOOL bManualReset,  // flag for manual-reset event
 
65
      FALSE, //BOOL bInitialState, // flag for initial state
 
66
      NULL //LPCTSTR lpName      // pointer to event-object name
 
67
      );
 
68
   assert(mId);
 
69
#  endif
 
70
#else
 
71
#ifdef _RESIP_MONOTONIC_CLOCK
 
72
   pthread_condattr_t attr;
 
73
   struct timespec dummy;
 
74
   int ret = pthread_condattr_init( &attr );
 
75
   assert( ret == 0 );
 
76
 
 
77
//   if((syscall( __NR_clock_getres, CLOCK_MONOTONIC, &dummy ) == 0) &&
 
78
     if((clock_getres( CLOCK_MONOTONIC, &dummy ) == 0) &&
 
79
       (pthread_condattr_setclock( &attr, CLOCK_MONOTONIC ) == 0))
 
80
   {
 
81
      ret = pthread_cond_init( &mId, &attr );
 
82
      assert( ret == 0 );
 
83
      pthread_condattr_destroy( &attr );
 
84
      return;
 
85
   }
 
86
   pthread_condattr_destroy( &attr );
 
87
#endif
 
88
   int  rc =  pthread_cond_init(&mId,0);
 
89
   (void)rc;
 
90
   assert( rc == 0 );
 
91
#endif
 
92
}
 
93
 
 
94
 
 
95
Condition::~Condition ()
 
96
{
 
97
#ifdef WIN32
 
98
#  ifdef RESIP_CONDITION_WIN32_CONFORMANCE_TO_POSIX
 
99
    int res = 0;
 
100
    res = CloseHandle(reinterpret_cast<HANDLE>(m_gate));
 
101
    assert(res);
 
102
    res = CloseHandle(reinterpret_cast<HANDLE>(m_queue));
 
103
    assert(res);
 
104
    res = CloseHandle(reinterpret_cast<HANDLE>(m_mutex));
 
105
    assert(res);
 
106
#  else
 
107
   BOOL ok = CloseHandle(mId);
 
108
   assert( ok );
 
109
#  endif
 
110
#else
 
111
   if (pthread_cond_destroy(&mId) == EBUSY)
 
112
   {
 
113
      assert(0);
 
114
   }
 
115
#endif
 
116
}
 
117
 
 
118
#if defined(WIN32) && defined(RESIP_CONDITION_WIN32_CONFORMANCE_TO_POSIX)
 
119
void
 
120
Condition::enterWait ()
 
121
{
 
122
   int res = 0;
 
123
   res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_gate), INFINITE);
 
124
   assert(res == WAIT_OBJECT_0);
 
125
   ++m_blocked;
 
126
   res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0);
 
127
   assert(res);
 
128
}
 
129
#endif
 
130
 
 
131
void
 
132
Condition::wait (Mutex& mutex)
 
133
{
 
134
   //std::cerr << "Condition::wait " << mutex << std::endl;
 
135
#ifdef WIN32
 
136
#  ifdef RESIP_CONDITION_WIN32_CONFORMANCE_TO_POSIX
 
137
   enterWait();
 
138
 
 
139
   // Release the mutex
 
140
   mutex.unlock();
 
141
 
 
142
   // do wait
 
143
   {
 
144
      int res = 0;
 
145
      res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue), INFINITE);
 
146
      assert(res == WAIT_OBJECT_0);
 
147
 
 
148
      unsigned was_waiting=0;
 
149
      unsigned was_gone=0;
 
150
 
 
151
      res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_mutex), INFINITE);
 
152
      assert(res == WAIT_OBJECT_0);
 
153
      was_waiting = m_waiting;
 
154
      was_gone = m_gone;
 
155
      if (was_waiting != 0)
 
156
      {
 
157
         if (--m_waiting == 0)
 
158
         {
 
159
            if (m_blocked != 0)
 
160
            {
 
161
               res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0); // open m_gate
 
162
               assert(res);
 
163
               was_waiting = 0;
 
164
            }
 
165
            else if (m_gone != 0)
 
166
                m_gone = 0;
 
167
         }
 
168
      }
 
169
      else if (++m_gone == (ULONG_MAX / 2))
 
170
      {
 
171
         // timeout occured, normalize the m_gone count
 
172
         // this may occur if many calls to wait with a timeout are made and
 
173
         // no call to notify_* is made
 
174
         res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_gate), INFINITE);
 
175
         assert(res == WAIT_OBJECT_0);
 
176
         m_blocked -= m_gone;
 
177
         res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0);
 
178
         assert(res);
 
179
         m_gone = 0;
 
180
      }
 
181
      res = ReleaseMutex(reinterpret_cast<HANDLE>(m_mutex));
 
182
      assert(res);
 
183
 
 
184
      if (was_waiting == 1)
 
185
      {
 
186
         for (/* */ ; was_gone; --was_gone)
 
187
         {
 
188
            // better now than spurious later
 
189
            res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue),
 
190
                  INFINITE);
 
191
            assert(res == WAIT_OBJECT_0);
 
192
         }
 
193
         res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0);
 
194
         assert(res);
 
195
      }
 
196
   }
 
197
 
 
198
   // Reacquire the mutex
 
199
   mutex.lock();
 
200
 
 
201
#   else
 
202
   // FixMe: Race condition between time we get mId and when we
 
203
   // re-acquire the mutex.
 
204
   mutex.unlock();
 
205
   WaitForSingleObject(mId,INFINITE);
 
206
   mutex.lock();
 
207
#   endif
 
208
#else
 
209
   int ret = pthread_cond_wait(&mId, mutex.getId());
 
210
   (void)ret;
 
211
   assert( ret == 0 );
 
212
#endif
 
213
}
 
214
 
 
215
void
 
216
Condition::wait (Mutex* mutex)
 
217
{
 
218
   this->wait(*mutex);
 
219
}
 
220
 
 
221
bool
 
222
Condition::wait(Mutex& mutex, 
 
223
                unsigned int ms)
 
224
{
 
225
   if (ms == 0)
 
226
   {
 
227
      wait(mutex);
 
228
      return true;
 
229
   }
 
230
 
 
231
#ifdef WIN32
 
232
#   ifdef RESIP_CONDITION_WIN32_CONFORMANCE_TO_POSIX
 
233
   enterWait();
 
234
 
 
235
   // Release the mutex
 
236
   mutex.unlock();
 
237
 
 
238
   //  do timed wait
 
239
   bool ret = false;
 
240
   unsigned int res = 0;
 
241
 
 
242
#if 0  /*  unnecessary time stuff - used in BOOST implementation because expiry time is provided to do_timed_wait - we pass in an interval */
 
243
   UInt64  start = Timer::getTimeMs();
 
244
 
 
245
   for (;;)
 
246
   {
 
247
       res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue),
 
248
             ms);
 
249
       assert(res != WAIT_FAILED && res != WAIT_ABANDONED);
 
250
       ret = (res == WAIT_OBJECT_0);
 
251
       if (res == WAIT_TIMEOUT)
 
252
       {
 
253
          UInt64  now = Timer::getTimeMs();
 
254
          unsigned int elapsed = (unsigned int)(now - start);
 
255
          if (ms > elapsed)
 
256
          {
 
257
             ms -= elapsed;
 
258
             continue;
 
259
          }
 
260
       }
 
261
 
 
262
       break;
 
263
   }
 
264
#endif
 
265
 
 
266
   res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue),ms);
 
267
   assert(res != WAIT_FAILED && res != WAIT_ABANDONED);
 
268
   ret = (res == WAIT_OBJECT_0);
 
269
 
 
270
   unsigned was_waiting=0;
 
271
   unsigned was_gone=0;
 
272
 
 
273
   res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_mutex), INFINITE);
 
274
   assert(res == WAIT_OBJECT_0);
 
275
   was_waiting = m_waiting;
 
276
   was_gone = m_gone;
 
277
   if (was_waiting != 0)
 
278
   {
 
279
      if (!ret) // timeout
 
280
      {
 
281
         if (m_blocked != 0)
 
282
            --m_blocked;
 
283
         else
 
284
            ++m_gone; // count spurious wakeups
 
285
      }
 
286
      if (--m_waiting == 0)
 
287
      {
 
288
         if (m_blocked != 0)
 
289
         {
 
290
            res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0); // open m_gate
 
291
            assert(res);
 
292
            was_waiting = 0;
 
293
         }
 
294
         else if (m_gone != 0)
 
295
            m_gone = 0;
 
296
      }
 
297
   }
 
298
   else if (++m_gone == (ULONG_MAX / 2))
 
299
   {
 
300
      // timeout occured, normalize the m_gone count
 
301
      // this may occur if many calls to wait with a timeout are made and
 
302
      // no call to notify_* is made
 
303
      res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_gate), INFINITE);
 
304
      assert(res == WAIT_OBJECT_0);
 
305
      m_blocked -= m_gone;
 
306
      res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0);
 
307
      assert(res);
 
308
      m_gone = 0;
 
309
   }
 
310
   res = ReleaseMutex(reinterpret_cast<HANDLE>(m_mutex));
 
311
   assert(res);
 
312
 
 
313
   if (was_waiting == 1)
 
314
   {
 
315
      for (/* */ ; was_gone; --was_gone)
 
316
      {
 
317
         // better now than spurious later
 
318
         res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue), INFINITE);
 
319
         assert(res ==  WAIT_OBJECT_0);
 
320
      }
 
321
      res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0);
 
322
      assert(res);
 
323
   }
 
324
 
 
325
   // Reacquire the mutex
 
326
   mutex.lock();
 
327
 
 
328
   return ret;
 
329
 
 
330
#   else
 
331
   // FixMe: Race condition between time we get mId and when we
 
332
   // re-acquire the mutex.
 
333
   //
 
334
   // SLG: A Note about the Win32 Implementation of Conditions
 
335
   //
 
336
   // I have investigated a fix for this.  A solution to this problem is
 
337
   // non-trivial.  Please read http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
 
338
   // for a full explanation.  This is an implementation of the SetEvent solution
 
339
   // discussed in that article.  This solution has the following issues:
 
340
   // 1.  Unfairness - ie.  First thread to call wait may not be first thread
 
341
   //     to be released from condition.
 
342
   // 2.  Incorrectness due to a race condition when a broadcast occurs
 
343
   // (see the link for more details on these issues)
 
344
   //
 
345
   // There is a solution that corrects these two problem, but also introduces 2 more.
 
346
   // This solution (also discussed in the link) requires the use of a primitive only
 
347
   // available in WinNT and above.  It also requires that the Mutex passed in be
 
348
   // implemented using windows Mutexes instead of CriticalSections - they are less
 
349
   // efficient.  Thus the problems with this SignalObjectAndWait solution are:
 
350
   // 1.  Not portable to all versions of windows - ie.  will not work with Win98/Me
 
351
   // 2.  Less efficient than tthe SetEvent solution
 
352
   //
 
353
   // I have choosen to stick with the SetEvent Solution for the following reasons:
 
354
   // 1.  Speed is important.
 
355
   // 2.  The Unfairness issue is not really a big problem since the stack currently
 
356
   //     does not call a wait function from two different threads.  (assuming the
 
357
   //     hosting application always calls process() from the same thread).  The only
 
358
   //     time multi-threading comes into the picture is when the transports queue
 
359
   //     messages from the wire onto the stateMacFifo - but they are retrieved off the
 
360
   //     Fifo by a single thread.
 
361
   // 3.  The Incorrectness issue is also not a big problem, since the stack currently
 
362
   //     doesn't use the broadcast member of this class.
 
363
   //
 
364
   // Note:  The implementation of broadcast remains incomplete - since it is currently
 
365
   //        unused and would require an additional CriticalSection Enter and Leave to
 
366
   //        keep track of a counter (see the above link for more info).  This can be
 
367
   //        easily added in the future if required.
 
368
   mutex.unlock();
 
369
   DWORD ret = WaitForSingleObject(mId, ms);
 
370
   mutex.lock();
 
371
   assert(ret != WAIT_FAILED);
 
372
   return (ret == WAIT_OBJECT_0);
 
373
#   endif
 
374
#else   // WIN32
 
375
   UInt64 expires64 = Timer::getTimeMs() + ms;
 
376
   timespec expiresTS;
 
377
   expiresTS.tv_sec = expires64 / 1000;
 
378
   expiresTS.tv_nsec = (expires64 % 1000) * 1000000L;
 
379
 
 
380
   assert( expiresTS.tv_nsec < 1000000000L );
 
381
 
 
382
   //std::cerr << "Condition::wait " << mutex << "ms=" << ms << " expire=" << expiresTS.tv_sec << " " << expiresTS.tv_nsec << std::endl;
 
383
   int ret = pthread_cond_timedwait(&mId, mutex.getId(), &expiresTS);
 
384
 
 
385
   if (ret == EINTR || ret == ETIMEDOUT)
 
386
   {
 
387
      return false;
 
388
   }
 
389
   else
 
390
   {
 
391
      //std::cerr << this << " pthread_cond_timedwait failed " << ret << " mutex=" << mutex << std::endl;
 
392
      (void)ret;
 
393
      assert( ret == 0 );
 
394
      return true;
 
395
   }
 
396
#endif  // not WIN32
 
397
}
 
398
 
 
399
bool
 
400
Condition::wait (Mutex* mutex, unsigned int ms)
 
401
{
 
402
   return this->wait(*mutex, ms);
 
403
}
 
404
 
 
405
void
 
406
Condition::signal ()
 
407
{
 
408
#ifdef WIN32
 
409
#  ifdef RESIP_CONDITION_WIN32_CONFORMANCE_TO_POSIX
 
410
    unsigned signals = 0;
 
411
 
 
412
   int res = 0;
 
413
   res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_mutex), INFINITE);
 
414
   assert(res == WAIT_OBJECT_0);
 
415
 
 
416
   if (m_waiting != 0) // the m_gate is already closed
 
417
   {
 
418
      if (m_blocked == 0)
 
419
      {
 
420
         res = ReleaseMutex(reinterpret_cast<HANDLE>(m_mutex));
 
421
         assert(res);
 
422
         return;
 
423
      }
 
424
 
 
425
      ++m_waiting;
 
426
      --m_blocked;
 
427
      signals = 1;
 
428
   }
 
429
   else
 
430
   {
 
431
      res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_gate), INFINITE);
 
432
      assert(res == WAIT_OBJECT_0);
 
433
      if (m_blocked > m_gone)
 
434
      {
 
435
         if (m_gone != 0)
 
436
         {
 
437
            m_blocked -= m_gone;
 
438
            m_gone = 0;
 
439
         }
 
440
         signals = m_waiting = 1;
 
441
         --m_blocked;
 
442
      }
 
443
      else
 
444
      {
 
445
         res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0);
 
446
         assert(res);
 
447
      }
 
448
   }
 
449
 
 
450
   res = ReleaseMutex(reinterpret_cast<HANDLE>(m_mutex));
 
451
   assert(res);
 
452
 
 
453
   if (signals)
 
454
   {
 
455
      res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_queue), signals, 0);
 
456
      assert(res);
 
457
   }
 
458
#  else
 
459
   BOOL ret = SetEvent(
 
460
      mId // HANDLE hEvent   // handle to event object
 
461
      );
 
462
   assert(ret);
 
463
#  endif
 
464
#else
 
465
   int ret = pthread_cond_signal(&mId);
 
466
   (void)ret;
 
467
   assert( ret == 0 );
 
468
#endif
 
469
}
 
470
 
 
471
 
 
472
void
 
473
Condition::broadcast()
 
474
{
 
475
#ifdef WIN32
 
476
#  ifdef RESIP_CONDITION_WIN32_CONFORMANCE_TO_POSIX
 
477
   unsigned signals = 0;
 
478
 
 
479
   int res = 0;
 
480
   res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_mutex), INFINITE);
 
481
   assert(res == WAIT_OBJECT_0);
 
482
 
 
483
   if (m_waiting != 0) // the m_gate is already closed
 
484
   {
 
485
      if (m_blocked == 0)
 
486
      {
 
487
         res = ReleaseMutex(reinterpret_cast<HANDLE>(m_mutex));
 
488
         assert(res);
 
489
         return;
 
490
      }
 
491
 
 
492
      m_waiting += (signals = m_blocked);
 
493
      m_blocked = 0;
 
494
   }
 
495
   else
 
496
   {
 
497
      res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_gate), INFINITE);
 
498
      assert(res == WAIT_OBJECT_0);
 
499
      if (m_blocked > m_gone)
 
500
      {
 
501
         if (m_gone != 0)
 
502
         {
 
503
            m_blocked -= m_gone;
 
504
            m_gone = 0;
 
505
         }
 
506
         signals = m_waiting = m_blocked;
 
507
         m_blocked = 0;
 
508
      }
 
509
      else
 
510
      {
 
511
         res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0);
 
512
         assert(res);
 
513
      }
 
514
   }
 
515
 
 
516
   res = ReleaseMutex(reinterpret_cast<HANDLE>(m_mutex));
 
517
   assert(res);
 
518
 
 
519
   if (signals)
 
520
   {
 
521
      res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_queue), signals, 0);
 
522
      assert(res);
 
523
   }
 
524
#  else
 
525
   assert(0);
 
526
#  endif
 
527
#else
 
528
   pthread_cond_broadcast(&mId);
 
529
#endif
 
530
}
 
531
 
 
532
/* ====================================================================
 
533
 * The Vovida Software License, Version 1.0
 
534
 *
 
535
 * Copyright (c) 2000-2005 Vovida Networks, Inc.  All rights reserved.
 
536
 * 
 
537
 * Redistribution and use in source and binary forms, with or without
 
538
 * modification, are permitted provided that the following conditions
 
539
 * are met:
 
540
 *
 
541
 * 1. Redistributions of source code must retain the above copyright
 
542
 *    notice, this list of conditions and the following disclaimer.
 
543
 *
 
544
 * 2. Redistributions in binary form must reproduce the above copyright
 
545
 *    notice, this list of conditions and the following disclaimer in
 
546
 *    the documentation and/or other materials provided with the
 
547
 *    distribution.
 
548
 *
 
549
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 
550
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 
551
 *    not be used to endorse or promote products derived from this
 
552
 *    software without prior written permission. For written
 
553
 *    permission, please contact vocal@vovida.org.
 
554
 *
 
555
 * 4. Products derived from this software may not be called "VOCAL", nor
 
556
 *    may "VOCAL" appear in their name, without prior written
 
557
 *    permission of Vovida Networks, Inc.
 
558
 *
 
559
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 
560
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
561
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 
562
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 
563
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 
564
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 
565
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
566
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
567
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 
568
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
569
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 
570
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 
571
 * DAMAGE.
 
572
 *
 
573
 * ====================================================================
 
574
 *
 
575
 * This software consists of voluntary contributions made by Vovida
 
576
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 
577
 * Inc.  For more information on Vovida Networks, Inc., please see
 
578
 * <http://www.vovida.org/>.
 
579
 *
 
580
 */