Coverage Report

Created: 2026-08-18 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nspr/pr/src/io/prmwait.c
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "primpl.h"
6
#include "pprmwait.h"
7
8
0
#define _MW_REHASH_MAX 11
9
10
static PRLock* mw_lock = NULL;
11
static _PRGlobalState* mw_state = NULL;
12
13
static PRIntervalTime max_polling_interval;
14
15
#ifdef WINNT
16
17
typedef struct TimerEvent {
18
  PRIntervalTime absolute;
19
  void (*func)(void*);
20
  void* arg;
21
  LONG ref_count;
22
  PRCList links;
23
} TimerEvent;
24
25
#  define TIMER_EVENT_PTR(_qp) \
26
    ((TimerEvent*)((char*)(_qp) - offsetof(TimerEvent, links)))
27
28
struct {
29
  PRLock* ml;
30
  PRCondVar* new_timer;
31
  PRCondVar* cancel_timer;
32
  PRThread* manager_thread;
33
  PRCList timer_queue;
34
} tm_vars;
35
36
static PRStatus TimerInit(void);
37
static void TimerManager(void* arg);
38
static TimerEvent* CreateTimer(PRIntervalTime timeout, void (*func)(void*),
39
                               void* arg);
40
static PRBool CancelTimer(TimerEvent* timer);
41
42
static void TimerManager(void* arg) {
43
  PRIntervalTime now;
44
  PRIntervalTime timeout;
45
  PRCList* head;
46
  TimerEvent* timer;
47
48
  PR_Lock(tm_vars.ml);
49
  while (1) {
50
    if (PR_CLIST_IS_EMPTY(&tm_vars.timer_queue)) {
51
      PR_WaitCondVar(tm_vars.new_timer, PR_INTERVAL_NO_TIMEOUT);
52
    } else {
53
      now = PR_IntervalNow();
54
      head = PR_LIST_HEAD(&tm_vars.timer_queue);
55
      timer = TIMER_EVENT_PTR(head);
56
      if ((PRInt32)(now - timer->absolute) >= 0) {
57
        PR_REMOVE_LINK(head);
58
        /*
59
         * make its prev and next point to itself so that
60
         * it's obvious that it's not on the timer_queue.
61
         */
62
        PR_INIT_CLIST(head);
63
        PR_ASSERT(2 == timer->ref_count);
64
        PR_Unlock(tm_vars.ml);
65
        timer->func(timer->arg);
66
        PR_Lock(tm_vars.ml);
67
        timer->ref_count -= 1;
68
        if (0 == timer->ref_count) {
69
          PR_NotifyAllCondVar(tm_vars.cancel_timer);
70
        }
71
      } else {
72
        timeout = (PRIntervalTime)(timer->absolute - now);
73
        PR_WaitCondVar(tm_vars.new_timer, timeout);
74
      }
75
    }
76
  }
77
  PR_Unlock(tm_vars.ml);
78
}
79
80
static TimerEvent* CreateTimer(PRIntervalTime timeout, void (*func)(void*),
81
                               void* arg) {
82
  TimerEvent* timer;
83
  PRCList *links, *tail;
84
  TimerEvent* elem;
85
86
  timer = PR_NEW(TimerEvent);
87
  if (NULL == timer) {
88
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
89
    return timer;
90
  }
91
  timer->absolute = PR_IntervalNow() + timeout;
92
  timer->func = func;
93
  timer->arg = arg;
94
  timer->ref_count = 2;
95
  PR_Lock(tm_vars.ml);
96
  tail = links = PR_LIST_TAIL(&tm_vars.timer_queue);
97
  while (links->prev != tail) {
98
    elem = TIMER_EVENT_PTR(links);
99
    if ((PRInt32)(timer->absolute - elem->absolute) >= 0) {
100
      break;
101
    }
102
    links = links->prev;
103
  }
104
  PR_INSERT_AFTER(&timer->links, links);
105
  PR_NotifyCondVar(tm_vars.new_timer);
106
  PR_Unlock(tm_vars.ml);
107
  return timer;
108
}
109
110
static PRBool CancelTimer(TimerEvent* timer) {
111
  PRBool canceled = PR_FALSE;
112
113
  PR_Lock(tm_vars.ml);
114
  timer->ref_count -= 1;
115
  if (timer->links.prev == &timer->links) {
116
    while (timer->ref_count == 1) {
117
      PR_WaitCondVar(tm_vars.cancel_timer, PR_INTERVAL_NO_TIMEOUT);
118
    }
119
  } else {
120
    PR_REMOVE_LINK(&timer->links);
121
    canceled = PR_TRUE;
122
  }
123
  PR_Unlock(tm_vars.ml);
124
  PR_DELETE(timer);
125
  return canceled;
126
}
127
128
static PRStatus TimerInit(void) {
129
  tm_vars.ml = PR_NewLock();
130
  if (NULL == tm_vars.ml) {
131
    goto failed;
132
  }
133
  tm_vars.new_timer = PR_NewCondVar(tm_vars.ml);
134
  if (NULL == tm_vars.new_timer) {
135
    goto failed;
136
  }
137
  tm_vars.cancel_timer = PR_NewCondVar(tm_vars.ml);
138
  if (NULL == tm_vars.cancel_timer) {
139
    goto failed;
140
  }
141
  PR_INIT_CLIST(&tm_vars.timer_queue);
142
  tm_vars.manager_thread =
143
      PR_CreateThread(PR_SYSTEM_THREAD, TimerManager, NULL, PR_PRIORITY_NORMAL,
144
                      PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0);
145
  if (NULL == tm_vars.manager_thread) {
146
    goto failed;
147
  }
148
  return PR_SUCCESS;
149
150
failed:
151
  if (NULL != tm_vars.cancel_timer) {
152
    PR_DestroyCondVar(tm_vars.cancel_timer);
153
  }
154
  if (NULL != tm_vars.new_timer) {
155
    PR_DestroyCondVar(tm_vars.new_timer);
156
  }
157
  if (NULL != tm_vars.ml) {
158
    PR_DestroyLock(tm_vars.ml);
159
  }
160
  return PR_FAILURE;
161
}
162
163
#endif /* WINNT */
164
165
/******************************************************************/
166
/******************************************************************/
167
/************************ The private portion *********************/
168
/******************************************************************/
169
/******************************************************************/
170
19
void _PR_InitMW(void) {
171
#ifdef WINNT
172
  /*
173
   * We use NT 4's InterlockedCompareExchange() to operate
174
   * on PRMWStatus variables.
175
   */
176
  PR_ASSERT(sizeof(LONG) == sizeof(PRMWStatus));
177
  TimerInit();
178
#endif
179
19
  mw_lock = PR_NewLock();
180
19
  PR_ASSERT(NULL != mw_lock);
181
19
  mw_state = PR_NEWZAP(_PRGlobalState);
182
19
  PR_ASSERT(NULL != mw_state);
183
19
  PR_INIT_CLIST(&mw_state->group_list);
184
19
  max_polling_interval = PR_MillisecondsToInterval(MAX_POLLING_INTERVAL);
185
19
} /* _PR_InitMW */
186
187
0
void _PR_CleanupMW(void) {
188
0
  PR_DestroyLock(mw_lock);
189
0
  mw_lock = NULL;
190
0
  if (mw_state->group) {
191
0
    PR_DestroyWaitGroup(mw_state->group);
192
    /* mw_state->group is set to NULL as a side effect. */
193
0
  }
194
0
  PR_DELETE(mw_state);
195
0
} /* _PR_CleanupMW */
196
197
0
static PRWaitGroup* MW_Init2(void) {
198
0
  PRWaitGroup* group = mw_state->group; /* it's the null group */
199
0
  if (NULL == group)                    /* there is this special case */
200
0
  {
201
0
    group = PR_CreateWaitGroup(_PR_DEFAULT_HASH_LENGTH);
202
0
    if (NULL == group) {
203
0
      goto failed_alloc;
204
0
    }
205
0
    PR_Lock(mw_lock);
206
0
    if (NULL == mw_state->group) {
207
0
      mw_state->group = group;
208
0
      group = NULL;
209
0
    }
210
0
    PR_Unlock(mw_lock);
211
0
    if (group != NULL) {
212
0
      (void)PR_DestroyWaitGroup(group);
213
0
    }
214
0
    group = mw_state->group; /* somebody beat us to it */
215
0
  }
216
0
failed_alloc:
217
0
  return group; /* whatever */
218
0
} /* MW_Init2 */
219
220
0
static _PR_HashStory MW_AddHashInternal(PRRecvWait* desc, _PRWaiterHash* hash) {
221
  /*
222
  ** The entries are put in the table using the fd (PRFileDesc*) of
223
  ** the receive descriptor as the key. This allows us to locate
224
  ** the appropriate entry aqain when the poll operation finishes.
225
  **
226
  ** The pointer to the file descriptor object is first divided by
227
  ** the natural alignment of a pointer in the belief that object
228
  ** will have at least that many zeros in the low order bits.
229
  ** This may not be a good assuption.
230
  **
231
  ** We try to put the entry in by rehashing _MW_REHASH_MAX times. After
232
  ** that we declare defeat and force the table to be reconstructed.
233
  ** Since some fds might be added more than once, won't that cause
234
  ** collisions even in an empty table?
235
  */
236
0
  PRIntn rehash = _MW_REHASH_MAX;
237
0
  PRRecvWait** waiter;
238
0
  PRUintn hidx = _MW_HASH(desc->fd, hash->length);
239
0
  PRUintn hoffset = 0;
240
241
0
  while (rehash-- > 0) {
242
0
    waiter = &hash->recv_wait;
243
0
    if (NULL == waiter[hidx]) {
244
0
      waiter[hidx] = desc;
245
0
      hash->count += 1;
246
#if 0
247
            printf("Adding 0x%x->0x%x ", desc, desc->fd);
248
            printf(
249
                "table[%u:%u:*%u]: 0x%x->0x%x\n",
250
                hidx, hash->count, hash->length, waiter[hidx], waiter[hidx]->fd);
251
#endif
252
0
      return _prmw_success;
253
0
    }
254
0
    if (desc == waiter[hidx]) {
255
0
      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); /* desc already in table */
256
0
      return _prmw_error;
257
0
    }
258
#if 0
259
        printf("Failing 0x%x->0x%x ", desc, desc->fd);
260
        printf(
261
            "table[*%u:%u:%u]: 0x%x->0x%x\n",
262
            hidx, hash->count, hash->length, waiter[hidx], waiter[hidx]->fd);
263
#endif
264
0
    if (0 == hoffset) {
265
0
      hoffset = _MW_HASH2(desc->fd, hash->length);
266
0
      PR_ASSERT(0 != hoffset);
267
0
    }
268
0
    hidx = (hidx + hoffset) % (hash->length);
269
0
  }
270
0
  return _prmw_rehash;
271
0
} /* MW_AddHashInternal */
272
273
0
static _PR_HashStory MW_ExpandHashInternal(PRWaitGroup* group) {
274
0
  PRRecvWait** desc;
275
0
  PRUint32 pidx, length;
276
0
  _PRWaiterHash *newHash, *oldHash = group->waiter;
277
0
  PRBool retry;
278
0
  _PR_HashStory hrv;
279
280
0
  static const PRInt32 prime_number[] = {_PR_DEFAULT_HASH_LENGTH,
281
0
                                         179,
282
0
                                         521,
283
0
                                         907,
284
0
                                         1427,
285
0
                                         2711,
286
0
                                         3917,
287
0
                                         5021,
288
0
                                         8219,
289
0
                                         11549,
290
0
                                         18911,
291
0
                                         26711,
292
0
                                         33749,
293
0
                                         44771};
294
0
  PRUintn primes = (sizeof(prime_number) / sizeof(PRInt32));
295
296
  /* look up the next size we'd like to use for the hash table */
297
0
  for (pidx = 0; pidx < primes; ++pidx) {
298
0
    if (prime_number[pidx] == oldHash->length) {
299
0
      break;
300
0
    }
301
0
  }
302
  /* table size must be one of the prime numbers */
303
0
  PR_ASSERT(pidx < primes);
304
305
  /* if pidx == primes - 1, we can't expand the table any more */
306
0
  while (pidx < primes - 1) {
307
    /* next size */
308
0
    ++pidx;
309
0
    length = prime_number[pidx];
310
311
    /* allocate the new hash table and fill it in with the old */
312
0
    newHash = (_PRWaiterHash*)PR_CALLOC(sizeof(_PRWaiterHash) +
313
0
                                        (length * sizeof(PRRecvWait*)));
314
0
    if (NULL == newHash) {
315
0
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
316
0
      return _prmw_error;
317
0
    }
318
319
0
    newHash->length = length;
320
0
    retry = PR_FALSE;
321
0
    for (desc = &oldHash->recv_wait; newHash->count < oldHash->count; ++desc) {
322
0
      PR_ASSERT(desc < &oldHash->recv_wait + oldHash->length);
323
0
      if (NULL != *desc) {
324
0
        hrv = MW_AddHashInternal(*desc, newHash);
325
0
        PR_ASSERT(_prmw_error != hrv);
326
0
        if (_prmw_success != hrv) {
327
0
          PR_DELETE(newHash);
328
0
          retry = PR_TRUE;
329
0
          break;
330
0
        }
331
0
      }
332
0
    }
333
0
    if (retry) {
334
0
      continue;
335
0
    }
336
337
0
    PR_DELETE(group->waiter);
338
0
    group->waiter = newHash;
339
0
    group->p_timestamp += 1;
340
0
    return _prmw_success;
341
0
  }
342
343
0
  PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
344
0
  return _prmw_error; /* we're hosed */
345
0
} /* MW_ExpandHashInternal */
346
347
#ifndef WINNT
348
static void _MW_DoneInternal(PRWaitGroup* group, PRRecvWait** waiter,
349
0
                             PRMWStatus outcome) {
350
  /*
351
  ** Add this receive wait object to the list of finished I/O
352
  ** operations for this particular group. If there are other
353
  ** threads waiting on the group, notify one. If not, arrange
354
  ** for this thread to return.
355
  */
356
357
#  if 0
358
    printf("Removing 0x%x->0x%x\n", *waiter, (*waiter)->fd);
359
#  endif
360
0
  (*waiter)->outcome = outcome;
361
0
  PR_APPEND_LINK(&((*waiter)->internal), &group->io_ready);
362
0
  PR_NotifyCondVar(group->io_complete);
363
0
  PR_ASSERT(0 != group->waiter->count);
364
0
  group->waiter->count -= 1;
365
0
  *waiter = NULL;
366
0
} /* _MW_DoneInternal */
367
#endif /* WINNT */
368
369
0
static PRRecvWait** _MW_LookupInternal(PRWaitGroup* group, PRFileDesc* fd) {
370
  /*
371
  ** Find the receive wait object corresponding to the file descriptor.
372
  ** Only search the wait group specified.
373
  */
374
0
  PRRecvWait** desc;
375
0
  PRIntn rehash = _MW_REHASH_MAX;
376
0
  _PRWaiterHash* hash = group->waiter;
377
0
  PRUintn hidx = _MW_HASH(fd, hash->length);
378
0
  PRUintn hoffset = 0;
379
380
0
  while (rehash-- > 0) {
381
0
    desc = (&hash->recv_wait) + hidx;
382
0
    if ((*desc != NULL) && ((*desc)->fd == fd)) {
383
0
      return desc;
384
0
    }
385
0
    if (0 == hoffset) {
386
0
      hoffset = _MW_HASH2(fd, hash->length);
387
0
      PR_ASSERT(0 != hoffset);
388
0
    }
389
0
    hidx = (hidx + hoffset) % (hash->length);
390
0
  }
391
0
  return NULL;
392
0
} /* _MW_LookupInternal */
393
394
#ifndef WINNT
395
0
static PRStatus _MW_PollInternal(PRWaitGroup* group) {
396
0
  PRRecvWait** waiter;
397
0
  PRStatus rv = PR_FAILURE;
398
0
  PRInt32 count, count_ready;
399
0
  PRIntervalTime polling_interval;
400
401
0
  group->poller = PR_GetCurrentThread();
402
403
0
  while (PR_TRUE) {
404
0
    PRIntervalTime now, since_last_poll;
405
0
    PRPollDesc* poll_list;
406
407
0
    while (0 == group->waiter->count) {
408
0
      PRStatus st;
409
0
      st = PR_WaitCondVar(group->new_business, PR_INTERVAL_NO_TIMEOUT);
410
0
      if (_prmw_running != group->state) {
411
0
        PR_SetError(PR_INVALID_STATE_ERROR, 0);
412
0
        goto aborted;
413
0
      }
414
0
      if (_MW_ABORTED(st)) {
415
0
        goto aborted;
416
0
      }
417
0
    }
418
419
    /*
420
    ** There's something to do. See if our existing polling list
421
    ** is large enough for what we have to do?
422
    */
423
424
0
    while (group->polling_count < group->waiter->count) {
425
0
      PRUint32 old_count = group->waiter->count;
426
0
      PRUint32 new_count = PR_ROUNDUP(old_count, _PR_POLL_COUNT_FUDGE);
427
0
      PRSize new_size = sizeof(PRPollDesc) * new_count;
428
0
      PRPollDesc* old_polling_list = group->polling_list;
429
430
0
      PR_Unlock(group->ml);
431
0
      poll_list = (PRPollDesc*)PR_CALLOC(new_size);
432
0
      if (NULL == poll_list) {
433
0
        PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
434
0
        PR_Lock(group->ml);
435
0
        goto failed_alloc;
436
0
      }
437
0
      if (NULL != old_polling_list) {
438
0
        PR_DELETE(old_polling_list);
439
0
      }
440
0
      PR_Lock(group->ml);
441
0
      if (_prmw_running != group->state) {
442
0
        PR_DELETE(poll_list);
443
0
        PR_SetError(PR_INVALID_STATE_ERROR, 0);
444
0
        goto aborted;
445
0
      }
446
0
      group->polling_list = poll_list;
447
0
      group->polling_count = new_count;
448
0
    }
449
450
0
    now = PR_IntervalNow();
451
0
    polling_interval = max_polling_interval;
452
0
    since_last_poll = now - group->last_poll;
453
454
0
    waiter = &group->waiter->recv_wait;
455
0
    poll_list = group->polling_list;
456
0
    for (count = 0; count < group->waiter->count; ++waiter) {
457
0
      PR_ASSERT(waiter < &group->waiter->recv_wait + group->waiter->length);
458
0
      if (NULL != *waiter) /* a live one! */
459
0
      {
460
0
        if ((PR_INTERVAL_NO_TIMEOUT != (*waiter)->timeout) &&
461
0
            (since_last_poll >= (*waiter)->timeout)) {
462
0
          _MW_DoneInternal(group, waiter, PR_MW_TIMEOUT);
463
0
        } else {
464
0
          if (PR_INTERVAL_NO_TIMEOUT != (*waiter)->timeout) {
465
0
            (*waiter)->timeout -= since_last_poll;
466
0
            if ((*waiter)->timeout < polling_interval) {
467
0
              polling_interval = (*waiter)->timeout;
468
0
            }
469
0
          }
470
0
          PR_ASSERT(poll_list < group->polling_list + group->polling_count);
471
0
          poll_list->fd = (*waiter)->fd;
472
0
          poll_list->in_flags = PR_POLL_READ;
473
0
          poll_list->out_flags = 0;
474
#  if 0
475
                    printf(
476
                        "Polling 0x%x[%d]: [fd: 0x%x, tmo: %u]\n",
477
                        poll_list, count, poll_list->fd, (*waiter)->timeout);
478
#  endif
479
0
          poll_list += 1;
480
0
          count += 1;
481
0
        }
482
0
      }
483
0
    }
484
485
0
    PR_ASSERT(count == group->waiter->count);
486
487
    /*
488
    ** If there are no more threads waiting for completion,
489
    ** we need to return.
490
    */
491
0
    if ((!PR_CLIST_IS_EMPTY(&group->io_ready)) &&
492
0
        (1 == group->waiting_threads)) {
493
0
      break;
494
0
    }
495
496
0
    if (0 == count) {
497
0
      continue; /* wait for new business */
498
0
    }
499
500
0
    group->last_poll = now;
501
502
0
    PR_Unlock(group->ml);
503
504
0
    count_ready = PR_Poll(group->polling_list, count, polling_interval);
505
506
0
    PR_Lock(group->ml);
507
508
0
    if (_prmw_running != group->state) {
509
0
      PR_SetError(PR_INVALID_STATE_ERROR, 0);
510
0
      goto aborted;
511
0
    }
512
0
    if (-1 == count_ready) {
513
0
      goto failed_poll; /* that's a shame */
514
0
    } else if (0 < count_ready) {
515
0
      for (poll_list = group->polling_list; count > 0; poll_list++, count--) {
516
0
        PR_ASSERT(poll_list < group->polling_list + group->polling_count);
517
0
        if (poll_list->out_flags != 0) {
518
0
          waiter = _MW_LookupInternal(group, poll_list->fd);
519
          /*
520
          ** If 'waiter' is NULL, that means the wait receive
521
          ** descriptor has been canceled.
522
          */
523
0
          if (NULL != waiter) {
524
0
            _MW_DoneInternal(group, waiter, PR_MW_SUCCESS);
525
0
          }
526
0
        }
527
0
      }
528
0
    }
529
    /*
530
    ** If there are no more threads waiting for completion,
531
    ** we need to return.
532
    ** This thread was "borrowed" to do the polling, but it really
533
    ** belongs to the client.
534
    */
535
0
    if ((!PR_CLIST_IS_EMPTY(&group->io_ready)) &&
536
0
        (1 == group->waiting_threads)) {
537
0
      break;
538
0
    }
539
0
  }
540
541
0
  rv = PR_SUCCESS;
542
543
0
aborted:
544
0
failed_poll:
545
0
failed_alloc:
546
0
  group->poller = NULL; /* we were that, not we ain't */
547
0
  if ((_prmw_running == group->state) && (group->waiting_threads > 1)) {
548
    /* Wake up one thread to become the new poller. */
549
0
    PR_NotifyCondVar(group->io_complete);
550
0
  }
551
0
  return rv; /* we return with the lock held */
552
0
} /* _MW_PollInternal */
553
#endif /* !WINNT */
554
555
0
static PRMWGroupState MW_TestForShutdownInternal(PRWaitGroup* group) {
556
0
  PRMWGroupState rv = group->state;
557
  /*
558
  ** Looking at the group's fields is safe because
559
  ** once the group's state is no longer running, it
560
  ** cannot revert and there is a safe check on entry
561
  ** to make sure no more threads are made to wait.
562
  */
563
0
  if ((_prmw_stopping == rv) && (0 == group->waiting_threads)) {
564
0
    rv = group->state = _prmw_stopped;
565
0
    PR_NotifyCondVar(group->mw_manage);
566
0
  }
567
0
  return rv;
568
0
} /* MW_TestForShutdownInternal */
569
570
#ifndef WINNT
571
0
static void _MW_InitialRecv(PRCList* io_ready) {
572
0
  PRRecvWait* desc = (PRRecvWait*)io_ready;
573
0
  if ((NULL == desc->buffer.start) || (0 == desc->buffer.length)) {
574
0
    desc->bytesRecv = 0;
575
0
  } else {
576
0
    desc->bytesRecv = (desc->fd->methods->recv)(
577
0
        desc->fd, desc->buffer.start, desc->buffer.length, 0, desc->timeout);
578
0
    if (desc->bytesRecv < 0) { /* SetError should already be there */
579
0
      desc->outcome = PR_MW_FAILURE;
580
0
    }
581
0
  }
582
0
} /* _MW_InitialRecv */
583
#endif
584
585
#ifdef WINNT
586
static void NT_TimeProc(void* arg) {
587
  _MDOverlapped* overlapped = (_MDOverlapped*)arg;
588
  PRRecvWait* desc = overlapped->data.mw.desc;
589
  PRFileDesc* bottom;
590
591
  if (InterlockedCompareExchange((LONG*)&desc->outcome, (LONG)PR_MW_TIMEOUT,
592
                                 (LONG)PR_MW_PENDING) != (LONG)PR_MW_PENDING) {
593
    /* This wait recv descriptor has already completed. */
594
    return;
595
  }
596
597
  /* close the osfd to abort the outstanding async io request */
598
  /* $$$$
599
  ** Little late to be checking if NSPR's on the bottom of stack,
600
  ** but if we don't check, we can't assert that the private data
601
  ** is what we think it is.
602
  ** $$$$
603
  */
604
  bottom = PR_GetIdentitiesLayer(desc->fd, PR_NSPR_IO_LAYER);
605
  PR_ASSERT(NULL != bottom);
606
  if (NULL != bottom) /* now what!?!?! */
607
  {
608
    bottom->secret->state = _PR_FILEDESC_CLOSED;
609
    if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR) {
610
      fprintf(stderr, "closesocket failed: %d\n", WSAGetLastError());
611
      PR_NOT_REACHED("What shall I do?");
612
    }
613
  }
614
  return;
615
} /* NT_TimeProc */
616
617
static PRStatus NT_HashRemove(PRWaitGroup* group, PRFileDesc* fd) {
618
  PRRecvWait** waiter;
619
620
  _PR_MD_LOCK(&group->mdlock);
621
  waiter = _MW_LookupInternal(group, fd);
622
  if (NULL != waiter) {
623
    group->waiter->count -= 1;
624
    *waiter = NULL;
625
  }
626
  _PR_MD_UNLOCK(&group->mdlock);
627
  return (NULL != waiter) ? PR_SUCCESS : PR_FAILURE;
628
}
629
630
PRStatus NT_HashRemoveInternal(PRWaitGroup* group, PRFileDesc* fd) {
631
  PRRecvWait** waiter;
632
633
  waiter = _MW_LookupInternal(group, fd);
634
  if (NULL != waiter) {
635
    group->waiter->count -= 1;
636
    *waiter = NULL;
637
  }
638
  return (NULL != waiter) ? PR_SUCCESS : PR_FAILURE;
639
}
640
#endif /* WINNT */
641
642
/******************************************************************/
643
/******************************************************************/
644
/********************** The public API portion ********************/
645
/******************************************************************/
646
/******************************************************************/
647
PR_IMPLEMENT(PRStatus)
648
0
PR_AddWaitFileDesc(PRWaitGroup* group, PRRecvWait* desc) {
649
0
  _PR_HashStory hrv;
650
0
  PRStatus rv = PR_FAILURE;
651
#ifdef WINNT
652
  _MDOverlapped* overlapped;
653
  HANDLE hFile;
654
  BOOL bResult;
655
  DWORD dwError;
656
  PRFileDesc* bottom;
657
#endif
658
659
0
  if (!_pr_initialized) {
660
0
    _PR_ImplicitInitialization();
661
0
  }
662
0
  if ((NULL == group) && (NULL == (group = MW_Init2()))) {
663
0
    return rv;
664
0
  }
665
666
0
  PR_ASSERT(NULL != desc->fd);
667
668
0
  desc->outcome = PR_MW_PENDING; /* nice, well known value */
669
0
  desc->bytesRecv = 0;           /* likewise, though this value is ambiguious */
670
671
0
  PR_Lock(group->ml);
672
673
0
  if (_prmw_running != group->state) {
674
    /* Not allowed to add after cancelling the group */
675
0
    desc->outcome = PR_MW_INTERRUPT;
676
0
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
677
0
    PR_Unlock(group->ml);
678
0
    return rv;
679
0
  }
680
681
#ifdef WINNT
682
  _PR_MD_LOCK(&group->mdlock);
683
#endif
684
685
  /*
686
  ** If the waiter count is zero at this point, there's no telling
687
  ** how long we've been idle. Therefore, initialize the beginning
688
  ** of the timing interval. As long as the list doesn't go empty,
689
  ** it will maintain itself.
690
  */
691
0
  if (0 == group->waiter->count) {
692
0
    group->last_poll = PR_IntervalNow();
693
0
  }
694
695
0
  do {
696
0
    hrv = MW_AddHashInternal(desc, group->waiter);
697
0
    if (_prmw_rehash != hrv) {
698
0
      break;
699
0
    }
700
0
    hrv = MW_ExpandHashInternal(group); /* gruesome */
701
0
    if (_prmw_success != hrv) {
702
0
      break;
703
0
    }
704
0
  } while (PR_TRUE);
705
706
#ifdef WINNT
707
  _PR_MD_UNLOCK(&group->mdlock);
708
#endif
709
710
0
  PR_NotifyCondVar(group->new_business); /* tell the world */
711
0
  rv = (_prmw_success == hrv) ? PR_SUCCESS : PR_FAILURE;
712
0
  PR_Unlock(group->ml);
713
714
#ifdef WINNT
715
  overlapped = PR_NEWZAP(_MDOverlapped);
716
  if (NULL == overlapped) {
717
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
718
    NT_HashRemove(group, desc->fd);
719
    return rv;
720
  }
721
  overlapped->ioModel = _MD_MultiWaitIO;
722
  overlapped->data.mw.desc = desc;
723
  overlapped->data.mw.group = group;
724
  if (desc->timeout != PR_INTERVAL_NO_TIMEOUT) {
725
    overlapped->data.mw.timer =
726
        CreateTimer(desc->timeout, NT_TimeProc, overlapped);
727
    if (0 == overlapped->data.mw.timer) {
728
      NT_HashRemove(group, desc->fd);
729
      PR_DELETE(overlapped);
730
      /*
731
       * XXX It appears that a maximum of 16 timer events can
732
       * be outstanding. GetLastError() returns 0 when I try it.
733
       */
734
      PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, GetLastError());
735
      return PR_FAILURE;
736
    }
737
  }
738
739
  /* Reach to the bottom layer to get the OS fd */
740
  bottom = PR_GetIdentitiesLayer(desc->fd, PR_NSPR_IO_LAYER);
741
  PR_ASSERT(NULL != bottom);
742
  if (NULL == bottom) {
743
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
744
    return PR_FAILURE;
745
  }
746
  hFile = (HANDLE)bottom->secret->md.osfd;
747
  if (!bottom->secret->md.io_model_committed) {
748
    PRInt32 st;
749
    st = _md_Associate(hFile);
750
    PR_ASSERT(0 != st);
751
    bottom->secret->md.io_model_committed = PR_TRUE;
752
  }
753
  bResult = ReadFile(hFile, desc->buffer.start, (DWORD)desc->buffer.length,
754
                     NULL, &overlapped->overlapped);
755
  if (FALSE == bResult && (dwError = GetLastError()) != ERROR_IO_PENDING) {
756
    if (desc->timeout != PR_INTERVAL_NO_TIMEOUT) {
757
      if (InterlockedCompareExchange((LONG*)&desc->outcome, (LONG)PR_MW_FAILURE,
758
                                     (LONG)PR_MW_PENDING) ==
759
          (LONG)PR_MW_PENDING) {
760
        CancelTimer(overlapped->data.mw.timer);
761
      }
762
      NT_HashRemove(group, desc->fd);
763
      PR_DELETE(overlapped);
764
    }
765
    _PR_MD_MAP_READ_ERROR(dwError);
766
    rv = PR_FAILURE;
767
  }
768
#endif
769
770
0
  return rv;
771
0
} /* PR_AddWaitFileDesc */
772
773
0
PR_IMPLEMENT(PRRecvWait*) PR_WaitRecvReady(PRWaitGroup* group) {
774
0
  PRCList* io_ready = NULL;
775
#ifdef WINNT
776
  PRThread* me = _PR_MD_CURRENT_THREAD();
777
  _MDOverlapped* overlapped;
778
#endif
779
780
0
  if (!_pr_initialized) {
781
0
    _PR_ImplicitInitialization();
782
0
  }
783
0
  if ((NULL == group) && (NULL == (group = MW_Init2()))) {
784
0
    goto failed_init;
785
0
  }
786
787
0
  PR_Lock(group->ml);
788
789
0
  if (_prmw_running != group->state) {
790
0
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
791
0
    goto invalid_state;
792
0
  }
793
794
0
  group->waiting_threads += 1; /* the polling thread is counted */
795
796
#ifdef WINNT
797
  _PR_MD_LOCK(&group->mdlock);
798
  while (PR_CLIST_IS_EMPTY(&group->io_ready)) {
799
    _PR_THREAD_LOCK(me);
800
    me->state = _PR_IO_WAIT;
801
    PR_APPEND_LINK(&me->waitQLinks, &group->wait_list);
802
    if (!_PR_IS_NATIVE_THREAD(me)) {
803
      _PR_SLEEPQ_LOCK(me->cpu);
804
      _PR_ADD_SLEEPQ(me, PR_INTERVAL_NO_TIMEOUT);
805
      _PR_SLEEPQ_UNLOCK(me->cpu);
806
    }
807
    _PR_THREAD_UNLOCK(me);
808
    _PR_MD_UNLOCK(&group->mdlock);
809
    PR_Unlock(group->ml);
810
    _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
811
    me->state = _PR_RUNNING;
812
    PR_Lock(group->ml);
813
    _PR_MD_LOCK(&group->mdlock);
814
    if (_PR_PENDING_INTERRUPT(me)) {
815
      PR_REMOVE_LINK(&me->waitQLinks);
816
      _PR_MD_UNLOCK(&group->mdlock);
817
      me->flags &= ~_PR_INTERRUPT;
818
      me->io_suspended = PR_FALSE;
819
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
820
      goto aborted;
821
    }
822
  }
823
  io_ready = PR_LIST_HEAD(&group->io_ready);
824
  PR_ASSERT(io_ready != NULL);
825
  PR_REMOVE_LINK(io_ready);
826
  _PR_MD_UNLOCK(&group->mdlock);
827
  overlapped =
828
      (_MDOverlapped*)((char*)io_ready - offsetof(_MDOverlapped, data));
829
  io_ready = &overlapped->data.mw.desc->internal;
830
#else
831
0
  do {
832
    /*
833
    ** If the I/O ready list isn't empty, have this thread
834
    ** return with the first receive wait object that's available.
835
    */
836
0
    if (PR_CLIST_IS_EMPTY(&group->io_ready)) {
837
      /*
838
      ** Is there a polling thread yet? If not, grab this thread
839
      ** and use it.
840
      */
841
0
      if (NULL == group->poller) {
842
        /*
843
        ** This thread will stay do polling until it becomes the only one
844
        ** left to service a completion. Then it will return and there will
845
        ** be none left to actually poll or to run completions.
846
        **
847
        ** The polling function should only return w/ failure or
848
        ** with some I/O ready.
849
        */
850
0
        if (PR_FAILURE == _MW_PollInternal(group)) {
851
0
          goto failed_poll;
852
0
        }
853
0
      } else {
854
        /*
855
        ** There are four reasons a thread can be awakened from
856
        ** a wait on the io_complete condition variable.
857
        ** 1. Some I/O has completed, i.e., the io_ready list
858
        **    is nonempty.
859
        ** 2. The wait group is canceled.
860
        ** 3. The thread is interrupted.
861
        ** 4. The current polling thread has to leave and needs
862
        **    a replacement.
863
        ** The logic to find a new polling thread is made more
864
        ** complicated by all the other possible events.
865
        ** I tried my best to write the logic clearly, but
866
        ** it is still full of if's with continue and goto.
867
        */
868
0
        PRStatus st;
869
0
        do {
870
0
          st = PR_WaitCondVar(group->io_complete, PR_INTERVAL_NO_TIMEOUT);
871
0
          if (_prmw_running != group->state) {
872
0
            PR_SetError(PR_INVALID_STATE_ERROR, 0);
873
0
            goto aborted;
874
0
          }
875
0
          if (_MW_ABORTED(st) || (NULL == group->poller)) {
876
0
            break;
877
0
          }
878
0
        } while (PR_CLIST_IS_EMPTY(&group->io_ready));
879
880
        /*
881
        ** The thread is interrupted and has to leave.  It might
882
        ** have also been awakened to process ready i/o or be the
883
        ** new poller.  To be safe, if either condition is true,
884
        ** we awaken another thread to take its place.
885
        */
886
0
        if (_MW_ABORTED(st)) {
887
0
          if ((NULL == group->poller || !PR_CLIST_IS_EMPTY(&group->io_ready)) &&
888
0
              group->waiting_threads > 1) {
889
0
            PR_NotifyCondVar(group->io_complete);
890
0
          }
891
0
          goto aborted;
892
0
        }
893
894
        /*
895
        ** A new poller is needed, but can I be the new poller?
896
        ** If there is no i/o ready, sure.  But if there is any
897
        ** i/o ready, it has a higher priority.  I want to
898
        ** process the ready i/o first and wake up another
899
        ** thread to be the new poller.
900
        */
901
0
        if (NULL == group->poller) {
902
0
          if (PR_CLIST_IS_EMPTY(&group->io_ready)) {
903
0
            continue;
904
0
          }
905
0
          if (group->waiting_threads > 1) {
906
0
            PR_NotifyCondVar(group->io_complete);
907
0
          }
908
0
        }
909
0
      }
910
0
      PR_ASSERT(!PR_CLIST_IS_EMPTY(&group->io_ready));
911
0
    }
912
0
    io_ready = PR_LIST_HEAD(&group->io_ready);
913
0
    PR_NotifyCondVar(group->io_taken);
914
0
    PR_ASSERT(io_ready != NULL);
915
0
    PR_REMOVE_LINK(io_ready);
916
0
  } while (NULL == io_ready);
917
918
0
failed_poll:
919
920
0
#endif
921
922
0
aborted:
923
924
0
  group->waiting_threads -= 1;
925
0
invalid_state:
926
0
  (void)MW_TestForShutdownInternal(group);
927
0
  PR_Unlock(group->ml);
928
929
0
failed_init:
930
0
  if (NULL != io_ready) {
931
    /* If the operation failed, record the reason why */
932
0
    switch (((PRRecvWait*)io_ready)->outcome) {
933
0
      case PR_MW_PENDING:
934
0
        PR_ASSERT(0);
935
0
        break;
936
0
      case PR_MW_SUCCESS:
937
0
#ifndef WINNT
938
0
        _MW_InitialRecv(io_ready);
939
0
#endif
940
0
        break;
941
#ifdef WINNT
942
      case PR_MW_FAILURE:
943
        _PR_MD_MAP_READ_ERROR(overlapped->data.mw.error);
944
        break;
945
#endif
946
0
      case PR_MW_TIMEOUT:
947
0
        PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
948
0
        break;
949
0
      case PR_MW_INTERRUPT:
950
0
        PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
951
0
        break;
952
0
      default:
953
0
        break;
954
0
    }
955
#ifdef WINNT
956
    if (NULL != overlapped->data.mw.timer) {
957
      PR_ASSERT(PR_INTERVAL_NO_TIMEOUT != overlapped->data.mw.desc->timeout);
958
      CancelTimer(overlapped->data.mw.timer);
959
    } else {
960
      PR_ASSERT(PR_INTERVAL_NO_TIMEOUT == overlapped->data.mw.desc->timeout);
961
    }
962
    PR_DELETE(overlapped);
963
#endif
964
0
  }
965
0
  return (PRRecvWait*)io_ready;
966
0
} /* PR_WaitRecvReady */
967
968
PR_IMPLEMENT(PRStatus)
969
0
PR_CancelWaitFileDesc(PRWaitGroup* group, PRRecvWait* desc) {
970
0
#if !defined(WINNT)
971
0
  PRRecvWait** recv_wait;
972
0
#endif
973
0
  PRStatus rv = PR_SUCCESS;
974
0
  if (NULL == group) {
975
0
    group = mw_state->group;
976
0
  }
977
0
  PR_ASSERT(NULL != group);
978
0
  if (NULL == group) {
979
0
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
980
0
    return PR_FAILURE;
981
0
  }
982
983
0
  PR_Lock(group->ml);
984
985
0
  if (_prmw_running != group->state) {
986
0
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
987
0
    rv = PR_FAILURE;
988
0
    goto unlock;
989
0
  }
990
991
#ifdef WINNT
992
  if (InterlockedCompareExchange((LONG*)&desc->outcome, (LONG)PR_MW_INTERRUPT,
993
                                 (LONG)PR_MW_PENDING) == (LONG)PR_MW_PENDING) {
994
    PRFileDesc* bottom = PR_GetIdentitiesLayer(desc->fd, PR_NSPR_IO_LAYER);
995
    PR_ASSERT(NULL != bottom);
996
    if (NULL == bottom) {
997
      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
998
      goto unlock;
999
    }
1000
    bottom->secret->state = _PR_FILEDESC_CLOSED;
1001
#  if 0
1002
        fprintf(stderr, "cancel wait recv: closing socket\n");
1003
#  endif
1004
    if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR) {
1005
      fprintf(stderr, "closesocket failed: %d\n", WSAGetLastError());
1006
      exit(1);
1007
    }
1008
  }
1009
#else
1010
0
  if (NULL != (recv_wait = _MW_LookupInternal(group, desc->fd))) {
1011
    /* it was in the wait table */
1012
0
    _MW_DoneInternal(group, recv_wait, PR_MW_INTERRUPT);
1013
0
    goto unlock;
1014
0
  }
1015
0
  if (!PR_CLIST_IS_EMPTY(&group->io_ready)) {
1016
    /* is it already complete? */
1017
0
    PRCList* head = PR_LIST_HEAD(&group->io_ready);
1018
0
    do {
1019
0
      PRRecvWait* done = (PRRecvWait*)head;
1020
0
      if (done == desc) {
1021
0
        goto unlock;
1022
0
      }
1023
0
      head = PR_NEXT_LINK(head);
1024
0
    } while (head != &group->io_ready);
1025
0
  }
1026
0
  PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
1027
0
  rv = PR_FAILURE;
1028
1029
0
#endif
1030
0
unlock:
1031
0
  PR_Unlock(group->ml);
1032
0
  return rv;
1033
0
} /* PR_CancelWaitFileDesc */
1034
1035
0
PR_IMPLEMENT(PRRecvWait*) PR_CancelWaitGroup(PRWaitGroup* group) {
1036
0
  PRRecvWait** desc;
1037
0
  PRRecvWait* recv_wait = NULL;
1038
#ifdef WINNT
1039
  _MDOverlapped* overlapped;
1040
  PRRecvWait** end;
1041
  PRThread* me = _PR_MD_CURRENT_THREAD();
1042
#endif
1043
1044
0
  if (NULL == group) {
1045
0
    group = mw_state->group;
1046
0
  }
1047
0
  PR_ASSERT(NULL != group);
1048
0
  if (NULL == group) {
1049
0
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
1050
0
    return NULL;
1051
0
  }
1052
1053
0
  PR_Lock(group->ml);
1054
0
  if (_prmw_stopped != group->state) {
1055
0
    if (_prmw_running == group->state) {
1056
0
      group->state = _prmw_stopping; /* so nothing new comes in */
1057
0
    }
1058
0
    if (0 == group->waiting_threads) { /* is there anybody else? */
1059
0
      group->state = _prmw_stopped;    /* we can stop right now */
1060
0
    } else {
1061
0
      PR_NotifyAllCondVar(group->new_business);
1062
0
      PR_NotifyAllCondVar(group->io_complete);
1063
0
    }
1064
0
    while (_prmw_stopped != group->state) {
1065
0
      (void)PR_WaitCondVar(group->mw_manage, PR_INTERVAL_NO_TIMEOUT);
1066
0
    }
1067
0
  }
1068
1069
#ifdef WINNT
1070
  _PR_MD_LOCK(&group->mdlock);
1071
#endif
1072
  /* make all the existing descriptors look done/interrupted */
1073
#ifdef WINNT
1074
  end = &group->waiter->recv_wait + group->waiter->length;
1075
  for (desc = &group->waiter->recv_wait; desc < end; ++desc) {
1076
    if (NULL != *desc) {
1077
      if (InterlockedCompareExchange(
1078
              (LONG*)&(*desc)->outcome, (LONG)PR_MW_INTERRUPT,
1079
              (LONG)PR_MW_PENDING) == (LONG)PR_MW_PENDING) {
1080
        PRFileDesc* bottom =
1081
            PR_GetIdentitiesLayer((*desc)->fd, PR_NSPR_IO_LAYER);
1082
        PR_ASSERT(NULL != bottom);
1083
        if (NULL == bottom) {
1084
          PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
1085
          goto invalid_arg;
1086
        }
1087
        bottom->secret->state = _PR_FILEDESC_CLOSED;
1088
#  if 0
1089
                fprintf(stderr, "cancel wait group: closing socket\n");
1090
#  endif
1091
        if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR) {
1092
          fprintf(stderr, "closesocket failed: %d\n", WSAGetLastError());
1093
          exit(1);
1094
        }
1095
      }
1096
    }
1097
  }
1098
  while (group->waiter->count > 0) {
1099
    _PR_THREAD_LOCK(me);
1100
    me->state = _PR_IO_WAIT;
1101
    PR_APPEND_LINK(&me->waitQLinks, &group->wait_list);
1102
    if (!_PR_IS_NATIVE_THREAD(me)) {
1103
      _PR_SLEEPQ_LOCK(me->cpu);
1104
      _PR_ADD_SLEEPQ(me, PR_INTERVAL_NO_TIMEOUT);
1105
      _PR_SLEEPQ_UNLOCK(me->cpu);
1106
    }
1107
    _PR_THREAD_UNLOCK(me);
1108
    _PR_MD_UNLOCK(&group->mdlock);
1109
    PR_Unlock(group->ml);
1110
    _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
1111
    me->state = _PR_RUNNING;
1112
    PR_Lock(group->ml);
1113
    _PR_MD_LOCK(&group->mdlock);
1114
  }
1115
#else
1116
0
  for (desc = &group->waiter->recv_wait; group->waiter->count > 0; ++desc) {
1117
0
    PR_ASSERT(desc < &group->waiter->recv_wait + group->waiter->length);
1118
0
    if (NULL != *desc) {
1119
0
      _MW_DoneInternal(group, desc, PR_MW_INTERRUPT);
1120
0
    }
1121
0
  }
1122
0
#endif
1123
1124
  /* take first element of finished list and return it or NULL */
1125
0
  if (PR_CLIST_IS_EMPTY(&group->io_ready)) {
1126
0
    PR_SetError(PR_GROUP_EMPTY_ERROR, 0);
1127
0
  } else {
1128
0
    PRCList* head = PR_LIST_HEAD(&group->io_ready);
1129
0
    PR_REMOVE_AND_INIT_LINK(head);
1130
#ifdef WINNT
1131
    overlapped = (_MDOverlapped*)((char*)head - offsetof(_MDOverlapped, data));
1132
    head = &overlapped->data.mw.desc->internal;
1133
    if (NULL != overlapped->data.mw.timer) {
1134
      PR_ASSERT(PR_INTERVAL_NO_TIMEOUT != overlapped->data.mw.desc->timeout);
1135
      CancelTimer(overlapped->data.mw.timer);
1136
    } else {
1137
      PR_ASSERT(PR_INTERVAL_NO_TIMEOUT == overlapped->data.mw.desc->timeout);
1138
    }
1139
    PR_DELETE(overlapped);
1140
#endif
1141
0
    recv_wait = (PRRecvWait*)head;
1142
0
  }
1143
#ifdef WINNT
1144
invalid_arg:
1145
  _PR_MD_UNLOCK(&group->mdlock);
1146
#endif
1147
0
  PR_Unlock(group->ml);
1148
1149
0
  return recv_wait;
1150
0
} /* PR_CancelWaitGroup */
1151
1152
0
PR_IMPLEMENT(PRWaitGroup*) PR_CreateWaitGroup(PRInt32 size /* ignored */) {
1153
0
  PRWaitGroup* wg;
1154
1155
0
  if (NULL == (wg = PR_NEWZAP(PRWaitGroup))) {
1156
0
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
1157
0
    goto failed;
1158
0
  }
1159
  /* the wait group itself */
1160
0
  wg->ml = PR_NewLock();
1161
0
  if (NULL == wg->ml) {
1162
0
    goto failed_lock;
1163
0
  }
1164
0
  wg->io_taken = PR_NewCondVar(wg->ml);
1165
0
  if (NULL == wg->io_taken) {
1166
0
    goto failed_cvar0;
1167
0
  }
1168
0
  wg->io_complete = PR_NewCondVar(wg->ml);
1169
0
  if (NULL == wg->io_complete) {
1170
0
    goto failed_cvar1;
1171
0
  }
1172
0
  wg->new_business = PR_NewCondVar(wg->ml);
1173
0
  if (NULL == wg->new_business) {
1174
0
    goto failed_cvar2;
1175
0
  }
1176
0
  wg->mw_manage = PR_NewCondVar(wg->ml);
1177
0
  if (NULL == wg->mw_manage) {
1178
0
    goto failed_cvar3;
1179
0
  }
1180
1181
0
  PR_INIT_CLIST(&wg->group_link);
1182
0
  PR_INIT_CLIST(&wg->io_ready);
1183
1184
  /* the waiters sequence */
1185
0
  wg->waiter = (_PRWaiterHash*)PR_CALLOC(
1186
0
      sizeof(_PRWaiterHash) + (_PR_DEFAULT_HASH_LENGTH * sizeof(PRRecvWait*)));
1187
0
  if (NULL == wg->waiter) {
1188
0
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
1189
0
    goto failed_waiter;
1190
0
  }
1191
0
  wg->waiter->count = 0;
1192
0
  wg->waiter->length = _PR_DEFAULT_HASH_LENGTH;
1193
1194
#ifdef WINNT
1195
  _PR_MD_NEW_LOCK(&wg->mdlock);
1196
  PR_INIT_CLIST(&wg->wait_list);
1197
#endif /* WINNT */
1198
1199
0
  PR_Lock(mw_lock);
1200
0
  PR_APPEND_LINK(&wg->group_link, &mw_state->group_list);
1201
0
  PR_Unlock(mw_lock);
1202
0
  return wg;
1203
1204
0
failed_waiter:
1205
0
  PR_DestroyCondVar(wg->mw_manage);
1206
0
failed_cvar3:
1207
0
  PR_DestroyCondVar(wg->new_business);
1208
0
failed_cvar2:
1209
0
  PR_DestroyCondVar(wg->io_complete);
1210
0
failed_cvar1:
1211
0
  PR_DestroyCondVar(wg->io_taken);
1212
0
failed_cvar0:
1213
0
  PR_DestroyLock(wg->ml);
1214
0
failed_lock:
1215
0
  PR_DELETE(wg);
1216
0
  wg = NULL;
1217
1218
0
failed:
1219
0
  return wg;
1220
0
} /* MW_CreateWaitGroup */
1221
1222
0
PR_IMPLEMENT(PRStatus) PR_DestroyWaitGroup(PRWaitGroup* group) {
1223
0
  PRStatus rv = PR_SUCCESS;
1224
0
  if (NULL == group) {
1225
0
    group = mw_state->group;
1226
0
  }
1227
0
  PR_ASSERT(NULL != group);
1228
0
  if (NULL != group) {
1229
0
    PR_Lock(group->ml);
1230
0
    if ((group->waiting_threads == 0) && (group->waiter->count == 0) &&
1231
0
        PR_CLIST_IS_EMPTY(&group->io_ready)) {
1232
0
      group->state = _prmw_stopped;
1233
0
    } else {
1234
0
      PR_SetError(PR_INVALID_STATE_ERROR, 0);
1235
0
      rv = PR_FAILURE;
1236
0
    }
1237
0
    PR_Unlock(group->ml);
1238
0
    if (PR_FAILURE == rv) {
1239
0
      return rv;
1240
0
    }
1241
1242
0
    PR_Lock(mw_lock);
1243
0
    PR_REMOVE_LINK(&group->group_link);
1244
0
    PR_Unlock(mw_lock);
1245
1246
#ifdef WINNT
1247
    /*
1248
     * XXX make sure wait_list is empty and waiter is empty.
1249
     * These must be checked while holding mdlock.
1250
     */
1251
    _PR_MD_FREE_LOCK(&group->mdlock);
1252
#endif
1253
1254
0
    PR_DELETE(group->waiter);
1255
0
    PR_DELETE(group->polling_list);
1256
0
    PR_DestroyCondVar(group->mw_manage);
1257
0
    PR_DestroyCondVar(group->new_business);
1258
0
    PR_DestroyCondVar(group->io_complete);
1259
0
    PR_DestroyCondVar(group->io_taken);
1260
0
    PR_DestroyLock(group->ml);
1261
0
    if (group == mw_state->group) {
1262
0
      mw_state->group = NULL;
1263
0
    }
1264
0
    PR_DELETE(group);
1265
0
  } else {
1266
    /* The default wait group is not created yet. */
1267
0
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
1268
0
    rv = PR_FAILURE;
1269
0
  }
1270
0
  return rv;
1271
0
} /* PR_DestroyWaitGroup */
1272
1273
/**********************************************************************
1274
***********************************************************************
1275
******************** Wait group enumerations **************************
1276
***********************************************************************
1277
**********************************************************************/
1278
1279
0
PR_IMPLEMENT(PRMWaitEnumerator*) PR_CreateMWaitEnumerator(PRWaitGroup* group) {
1280
0
  PRMWaitEnumerator* enumerator = PR_NEWZAP(PRMWaitEnumerator);
1281
0
  if (NULL == enumerator) {
1282
0
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
1283
0
  } else {
1284
0
    enumerator->group = group;
1285
0
    enumerator->seal = _PR_ENUM_SEALED;
1286
0
  }
1287
0
  return enumerator;
1288
0
} /* PR_CreateMWaitEnumerator */
1289
1290
PR_IMPLEMENT(PRStatus)
1291
0
PR_DestroyMWaitEnumerator(PRMWaitEnumerator* enumerator) {
1292
0
  PR_ASSERT(NULL != enumerator);
1293
0
  PR_ASSERT(_PR_ENUM_SEALED == enumerator->seal);
1294
0
  if ((NULL == enumerator) || (_PR_ENUM_SEALED != enumerator->seal)) {
1295
0
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
1296
0
    return PR_FAILURE;
1297
0
  }
1298
0
  enumerator->seal = _PR_ENUM_UNSEALED;
1299
0
  PR_Free(enumerator);
1300
0
  return PR_SUCCESS;
1301
0
} /* PR_DestroyMWaitEnumerator */
1302
1303
PR_IMPLEMENT(PRRecvWait*)
1304
PR_EnumerateWaitGroup(PRMWaitEnumerator* enumerator,
1305
0
                      const PRRecvWait* previous) {
1306
0
  PRRecvWait* result = NULL;
1307
1308
  /* entry point sanity checking */
1309
0
  PR_ASSERT(NULL != enumerator);
1310
0
  PR_ASSERT(_PR_ENUM_SEALED == enumerator->seal);
1311
0
  if ((NULL == enumerator) || (_PR_ENUM_SEALED != enumerator->seal)) {
1312
0
    goto bad_argument;
1313
0
  }
1314
1315
  /* beginning of enumeration */
1316
0
  if (NULL == previous) {
1317
0
    if (NULL == enumerator->group) {
1318
0
      enumerator->group = mw_state->group;
1319
0
      if (NULL == enumerator->group) {
1320
0
        PR_SetError(PR_GROUP_EMPTY_ERROR, 0);
1321
0
        return NULL;
1322
0
      }
1323
0
    }
1324
0
    enumerator->waiter = &enumerator->group->waiter->recv_wait;
1325
0
    enumerator->p_timestamp = enumerator->group->p_timestamp;
1326
0
    enumerator->thread = PR_GetCurrentThread();
1327
0
    enumerator->index = 0;
1328
0
  }
1329
  /* continuing an enumeration */
1330
0
  else {
1331
0
    PRThread* me = PR_GetCurrentThread();
1332
0
    PR_ASSERT(me == enumerator->thread);
1333
0
    if (me != enumerator->thread) {
1334
0
      goto bad_argument;
1335
0
    }
1336
1337
    /* need to restart the enumeration */
1338
0
    if (enumerator->p_timestamp != enumerator->group->p_timestamp) {
1339
0
      return PR_EnumerateWaitGroup(enumerator, NULL);
1340
0
    }
1341
0
  }
1342
1343
  /* actually progress the enumeration */
1344
#if defined(WINNT)
1345
  _PR_MD_LOCK(&enumerator->group->mdlock);
1346
#else
1347
0
  PR_Lock(enumerator->group->ml);
1348
0
#endif
1349
0
  while (enumerator->index++ < enumerator->group->waiter->length) {
1350
0
    if (NULL != (result = *(enumerator->waiter)++)) {
1351
0
      break;
1352
0
    }
1353
0
  }
1354
#if defined(WINNT)
1355
  _PR_MD_UNLOCK(&enumerator->group->mdlock);
1356
#else
1357
0
  PR_Unlock(enumerator->group->ml);
1358
0
#endif
1359
1360
0
  return result; /* what we live for */
1361
1362
0
bad_argument:
1363
0
  PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
1364
  return NULL; /* probably ambiguous */
1365
0
} /* PR_EnumerateWaitGroup */
1366
1367
/* prmwait.c */