1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is the Netscape Portable Runtime (NSPR).
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
23 * Masayuki Nakano <masayuki@d-toybox.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 /* Windows NT IO module
41 * This module handles IO for LOCAL_SCOPE and GLOBAL_SCOPE threads.
42 * For LOCAL_SCOPE threads, we're using NT fibers. For GLOBAL_SCOPE threads
43 * we're using NT-native threads.
45 * When doing IO, we want to use completion ports for optimal performance
46 * with fibers. But if we use completion ports for all IO, it is difficult
47 * to project a blocking model with GLOBAL_SCOPE threads. To handle this
48 * we create an extra thread for completing IO for GLOBAL_SCOPE threads.
49 * We don't really want to complete IO on a separate thread for LOCAL_SCOPE
50 * threads because it means extra context switches, which are really slow
51 * on NT... Since we're using a single completion port, some IO will
52 * be incorrectly completed on the GLOBAL_SCOPE IO thread; this will mean
53 * extra context switching; but I don't think there is anything I can do
62 static HANDLE _pr_completion_port;
63 static PRThread *_pr_io_completion_thread;
65 #define RECYCLE_SIZE 512
66 static struct _MDLock _pr_recycle_lock;
67 static PRInt32 _pr_recycle_INET_array[RECYCLE_SIZE];
68 static PRInt32 _pr_recycle_INET_tail = 0;
69 static PRInt32 _pr_recycle_INET6_array[RECYCLE_SIZE];
70 static PRInt32 _pr_recycle_INET6_tail = 0;
72 __declspec(thread) PRThread *_pr_io_restarted_io = NULL;
73 DWORD _pr_io_restartedIOIndex; /* The thread local storage slot for each
74 * thread is initialized to NULL. */
76 PRBool _nt_version_gets_lockfile_completion;
78 struct _MDLock _pr_ioq_lock;
79 extern _MDLock _nt_idleLock;
80 extern PRCList _nt_idleList;
81 extern PRUint32 _nt_idleCount;
83 #define CLOSE_TIMEOUT PR_SecondsToInterval(5)
86 * NSPR-to-NT access right mapping table for files.
88 static DWORD fileAccessTable[] = {
95 * NSPR-to-NT access right mapping table for directories.
97 static DWORD dirAccessTable[] = {
99 FILE_GENERIC_WRITE|FILE_DELETE_CHILD,
103 static PRBool IsPrevCharSlash(const char *str, const char *current);
105 #define _NEED_351_FILE_LOCKING_HACK
106 #ifdef _NEED_351_FILE_LOCKING_HACK
107 #define _PR_LOCAL_FILE 1
108 #define _PR_REMOTE_FILE 2
109 PRBool IsFileLocalInit();
110 PRInt32 IsFileLocal(HANDLE hFile);
111 #endif /* _NEED_351_FILE_LOCKING_HACK */
113 static PRInt32 _md_MakeNonblock(HANDLE);
115 static PROsfd _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr *addr, int *addrlen, PRIntervalTime);
116 static PRInt32 _nt_nonblock_connect(PRFileDesc *fd, struct sockaddr *addr, int addrlen, PRIntervalTime);
117 static PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, int flags, PRIntervalTime);
118 static PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime);
119 static PRInt32 _nt_nonblock_writev(PRFileDesc *fd, const PRIOVec *iov, int size, PRIntervalTime);
120 static PRInt32 _nt_nonblock_sendto(PRFileDesc *, const char *, int, const struct sockaddr *, int, PRIntervalTime);
121 static PRInt32 _nt_nonblock_recvfrom(PRFileDesc *, char *, int, struct sockaddr *, int *, PRIntervalTime);
124 * We cannot associate a fd (a socket) with an I/O completion port
125 * if the fd is nonblocking or inheritable.
127 * Nonblocking socket I/O won't work if the socket is associated with
128 * an I/O completion port.
130 * An inheritable fd cannot be associated with an I/O completion port
131 * because the completion notification of async I/O initiated by the
132 * child process is still posted to the I/O completion port in the
135 #define _NT_USE_NB_IO(fd) \
136 ((fd)->secret->nonblocking || (fd)->secret->inheritable == _PR_TRI_TRUE)
141 * UDP is supported on NT by the continuation thread mechanism.
142 * The code is borrowed from ptio.c in pthreads nspr, hence the
143 * PT and pt prefixes. This mechanism is in fact general and
144 * not limited to UDP. For now, only UDP's recvfrom and sendto
145 * go through the continuation thread if they get WSAEWOULDBLOCK
146 * on first try. Recv and send on a connected UDP socket still
147 * goes through asychronous io.
150 #define PT_DEFAULT_SELECT_MSEC 100
152 typedef struct pt_Continuation pt_Continuation;
153 typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revent);
155 typedef enum pr_ContuationStatus
157 pt_continuation_sumbitted,
158 pt_continuation_inprogress,
159 pt_continuation_abort,
161 } pr_ContuationStatus;
163 struct pt_Continuation
165 /* These objects are linked in ascending timeout order */
166 pt_Continuation *next, *prev; /* self linked list of these things */
168 /* The building of the continuation operation */
169 ContinuationFn function; /* what function to continue */
170 union { SOCKET osfd; } arg1; /* #1 - the op's fd */
171 union { void* buffer; } arg2; /* #2 - primary transfer buffer */
172 union { PRIntn amount; } arg3; /* #3 - size of 'buffer' */
173 union { PRIntn flags; } arg4; /* #4 - read/write flags */
174 union { PRNetAddr *addr; } arg5; /* #5 - send/recv address */
176 PRIntervalTime timeout; /* representation of the timeout */
178 PRIntn event; /* flags for select()'s events */
181 ** The representation and notification of the results of the operation.
182 ** These function can either return an int return code or a pointer to
185 union { PRIntn code; void *object; } result;
187 PRIntn syserrno; /* in case it failed, why (errno) */
188 pr_ContuationStatus status; /* the status of the operation */
189 PRCondVar *complete; /* to notify the initiating thread */
192 static struct pt_TimedQueue
194 PRLock *ml; /* a little protection */
195 PRThread *thread; /* internal thread's identification */
196 PRCondVar *new_op; /* new operation supplied */
197 PRCondVar *finish_op; /* an existing operation finished */
198 PRUintn op_count; /* number of operations in the list */
199 pt_Continuation *head, *tail; /* head/tail of list of operations */
201 pt_Continuation *op; /* timed operation furthest in future */
202 PRIntervalTime epoch; /* the epoch of 'timed' */
206 static struct pt_debug_s
208 PRIntn predictionsFoiled;
209 PRIntn pollingListMax;
210 PRIntn continuationsServed;
214 static void ContinuationThread(void *arg);
215 static PRInt32 pt_SendTo(
216 SOCKET osfd, const void *buf,
217 PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
218 PRIntn addrlen, PRIntervalTime timeout);
219 static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
220 PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout);
223 /* The key returned from GetQueuedCompletionStatus() is used to determine what
224 * type of completion we have. We differentiate between IO completions and
227 #define KEY_IO 0xaaaaaaaa
228 #define KEY_CVAR 0xbbbbbbbb
231 _PR_MD_PAUSE_CPU(PRIntervalTime ticks)
234 unsigned long bytes, key;
237 _MDOverlapped *mdOlp;
240 if (_nt_idleCount > 0) {
241 PRThread *deadThread;
243 _MD_LOCK(&_nt_idleLock);
244 while( !PR_CLIST_IS_EMPTY(&_nt_idleList) ) {
245 deadThread = _PR_THREAD_PTR(PR_LIST_HEAD(&_nt_idleList));
246 PR_REMOVE_LINK(&deadThread->links);
248 PR_ASSERT(deadThread->state == _PR_DEAD_STATE);
250 /* XXXMB - cleanup to do here? */
251 if ( !_PR_IS_NATIVE_THREAD(deadThread) ){
252 /* Spinlock while user thread is still running.
253 * There is no way to use a condition variable here. The thread
254 * is dead, and we have to wait until we switch off the dead
255 * thread before we can kill the fiber completely.
257 while ( deadThread->no_sched)
260 DeleteFiber(deadThread->md.fiber_id);
262 memset(deadThread, 0xa, sizeof(PRThread)); /* debugging */
263 if (!deadThread->threadAllocatedOnStack)
264 PR_DELETE(deadThread);
267 _MD_UNLOCK(&_nt_idleLock);
270 if (ticks == PR_INTERVAL_NO_TIMEOUT)
275 * temporary hack to poll the runq every 5 seconds because of bug in
276 * native threads creating user threads and not poking the right cpu.
278 * A local thread that was interrupted is bound to its current
279 * cpu but there is no easy way for the interrupter to poke the
280 * right cpu. This is a hack to poll the runq every 5 seconds.
285 timeout = PR_IntervalToMilliseconds(ticks);
288 * The idea of looping here is to complete as many IOs as possible before
289 * returning. This should minimize trips to the idle thread.
292 rv = GetQueuedCompletionStatus(
298 if (rv == 0 && olp == NULL) {
299 /* Error in GetQueuedCompetionStatus */
300 if (GetLastError() != WAIT_TIMEOUT) {
301 /* ARGH - what can we do here? Log an error? XXXMB */
304 /* If awoken == 0, then we just had a timeout */
312 mdOlp = (_MDOverlapped *)olp;
314 if (mdOlp->ioModel == _MD_MultiWaitIO) {
317 PRThread *thred = NULL;
320 desc = mdOlp->data.mw.desc;
321 PR_ASSERT(desc != NULL);
322 mwstatus = rv ? PR_MW_SUCCESS : PR_MW_FAILURE;
323 if (InterlockedCompareExchange((PVOID *)&desc->outcome,
324 (PVOID)mwstatus, (PVOID)PR_MW_PENDING)
325 == (PVOID)PR_MW_PENDING) {
326 if (mwstatus == PR_MW_SUCCESS) {
327 desc->bytesRecv = bytes;
329 mdOlp->data.mw.error = GetLastError();
332 group = mdOlp->data.mw.group;
333 PR_ASSERT(group != NULL);
335 _PR_MD_LOCK(&group->mdlock);
336 PR_APPEND_LINK(&mdOlp->data.mw.links, &group->io_ready);
337 PR_ASSERT(desc->fd != NULL);
338 NT_HashRemoveInternal(group, desc->fd);
339 if (!PR_CLIST_IS_EMPTY(&group->wait_list)) {
340 thred = _PR_THREAD_CONDQ_PTR(PR_LIST_HEAD(&group->wait_list));
341 PR_REMOVE_LINK(&thred->waitQLinks);
343 _PR_MD_UNLOCK(&group->mdlock);
346 if (!_PR_IS_NATIVE_THREAD(thred)) {
347 int pri = thred->priority;
348 _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU();
349 _PR_THREAD_LOCK(thred);
350 if (thred->flags & _PR_ON_PAUSEQ) {
351 _PR_SLEEPQ_LOCK(thred->cpu);
352 _PR_DEL_SLEEPQ(thred, PR_TRUE);
353 _PR_SLEEPQ_UNLOCK(thred->cpu);
354 _PR_THREAD_UNLOCK(thred);
355 thred->cpu = lockedCPU;
356 thred->state = _PR_RUNNABLE;
357 _PR_RUNQ_LOCK(lockedCPU);
358 _PR_ADD_RUNQ(thred, lockedCPU, pri);
359 _PR_RUNQ_UNLOCK(lockedCPU);
362 * The thread was just interrupted and moved
363 * from the pause queue to the run queue.
365 _PR_THREAD_UNLOCK(thred);
368 _PR_THREAD_LOCK(thred);
369 thred->state = _PR_RUNNABLE;
370 _PR_THREAD_UNLOCK(thred);
371 ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
375 PRThread *completed_io;
377 PR_ASSERT(mdOlp->ioModel == _MD_BlockingIO);
378 completed_io = _PR_THREAD_MD_TO_PTR(mdOlp->data.mdThread);
379 completed_io->md.blocked_io_status = rv;
381 completed_io->md.blocked_io_error = GetLastError();
382 completed_io->md.blocked_io_bytes = bytes;
384 if ( !_PR_IS_NATIVE_THREAD(completed_io) ) {
385 int pri = completed_io->priority;
386 _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU();
388 /* The KEY_CVAR notification only occurs when a native thread
389 * is notifying a user thread. For user-user notifications
390 * the wakeup occurs by having the notifier place the thread
391 * on the runq directly; for native-native notifications the
392 * wakeup occurs by calling ReleaseSemaphore.
394 if ( key == KEY_CVAR ) {
395 PR_ASSERT(completed_io->io_pending == PR_FALSE);
396 PR_ASSERT(completed_io->io_suspended == PR_FALSE);
397 PR_ASSERT(completed_io->md.thr_bound_cpu == NULL);
399 /* Thread has already been deleted from sleepQ */
401 /* Switch CPU and add to runQ */
402 completed_io->cpu = lockedCPU;
403 completed_io->state = _PR_RUNNABLE;
404 _PR_RUNQ_LOCK(lockedCPU);
405 _PR_ADD_RUNQ(completed_io, lockedCPU, pri);
406 _PR_RUNQ_UNLOCK(lockedCPU);
408 PR_ASSERT(key == KEY_IO);
409 PR_ASSERT(completed_io->io_pending == PR_TRUE);
411 _PR_THREAD_LOCK(completed_io);
413 completed_io->io_pending = PR_FALSE;
415 /* If io_suspended is true, then this IO has already resumed.
416 * We don't need to do anything; because the thread is
419 if (completed_io->io_suspended == PR_FALSE) {
420 if (completed_io->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) {
421 _PR_SLEEPQ_LOCK(completed_io->cpu);
422 _PR_DEL_SLEEPQ(completed_io, PR_TRUE);
423 _PR_SLEEPQ_UNLOCK(completed_io->cpu);
425 _PR_THREAD_UNLOCK(completed_io);
428 * If an I/O operation is suspended, the thread
429 * must be running on the same cpu on which the
430 * I/O operation was issued.
432 PR_ASSERT(!completed_io->md.thr_bound_cpu ||
433 (completed_io->cpu == completed_io->md.thr_bound_cpu));
435 if (!completed_io->md.thr_bound_cpu)
436 completed_io->cpu = lockedCPU;
437 completed_io->state = _PR_RUNNABLE;
438 _PR_RUNQ_LOCK(completed_io->cpu);
439 _PR_ADD_RUNQ(completed_io, completed_io->cpu, pri);
440 _PR_RUNQ_UNLOCK(completed_io->cpu);
442 _PR_THREAD_UNLOCK(completed_io);
445 _PR_THREAD_UNLOCK(completed_io);
449 /* For native threads, they are only notified through this loop
450 * when completing IO. So, don't worry about this being a CVAR
451 * notification, because that is not possible.
453 _PR_THREAD_LOCK(completed_io);
454 completed_io->io_pending = PR_FALSE;
455 if (completed_io->io_suspended == PR_FALSE) {
456 completed_io->state = _PR_RUNNABLE;
457 _PR_THREAD_UNLOCK(completed_io);
458 rv = ReleaseSemaphore(completed_io->md.blocked_sema,
462 _PR_THREAD_UNLOCK(completed_io);
468 timeout = 0; /* Don't block on subsequent trips through the loop */
476 _native_thread_md_wait(PRThread *thread, PRIntervalTime ticks)
479 PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
480 INFINITE : PR_IntervalToMilliseconds(ticks);
483 * thread waiting for a cvar or a joining thread
485 rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
491 _PR_THREAD_LOCK(thread);
492 PR_ASSERT (thread->state != _PR_IO_WAIT);
493 if (thread->wait.cvar != NULL) {
494 PR_ASSERT(thread->state == _PR_COND_WAIT);
495 thread->wait.cvar = NULL;
496 thread->state = _PR_RUNNING;
497 _PR_THREAD_UNLOCK(thread);
499 /* The CVAR was notified just as the timeout
500 * occurred. This left the semaphore in the
501 * signaled state. Call WaitForSingleObject()
502 * to clear the semaphore.
504 _PR_THREAD_UNLOCK(thread);
505 rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
506 PR_ASSERT(rv == WAIT_OBJECT_0);
519 _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
523 if (_native_threads_only) {
524 return(_native_thread_md_wait(thread, ticks));
526 if ( thread->flags & _PR_GLOBAL_SCOPE ) {
527 PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
528 INFINITE : PR_IntervalToMilliseconds(ticks);
529 rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
535 _PR_THREAD_LOCK(thread);
536 if (thread->state == _PR_IO_WAIT) {
537 if (thread->io_pending == PR_TRUE) {
538 thread->state = _PR_RUNNING;
539 thread->io_suspended = PR_TRUE;
540 _PR_THREAD_UNLOCK(thread);
542 /* The IO completed just at the same time the timeout
543 * occurred. This left the semaphore in the signaled
544 * state. Call WaitForSingleObject() to clear the
547 _PR_THREAD_UNLOCK(thread);
548 rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
549 PR_ASSERT(rv == WAIT_OBJECT_0);
552 if (thread->wait.cvar != NULL) {
553 PR_ASSERT(thread->state == _PR_COND_WAIT);
554 thread->wait.cvar = NULL;
555 thread->state = _PR_RUNNING;
556 _PR_THREAD_UNLOCK(thread);
558 /* The CVAR was notified just as the timeout
559 * occurred. This left the semaphore in the
560 * signaled state. Call WaitForSingleObject()
561 * to clear the semaphore.
563 _PR_THREAD_UNLOCK(thread);
564 rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
565 PR_ASSERT(rv == WAIT_OBJECT_0);
578 _PR_MD_SWITCH_CONTEXT(thread);
585 _native_thread_io_nowait(
593 _PR_THREAD_LOCK(thread);
594 if (thread->state == _PR_IO_WAIT) {
595 PR_ASSERT(thread->io_suspended == PR_FALSE);
596 PR_ASSERT(thread->io_pending == PR_TRUE);
597 thread->state = _PR_RUNNING;
598 thread->io_pending = PR_FALSE;
599 _PR_THREAD_UNLOCK(thread);
601 /* The IO completed just at the same time the
602 * thread was interrupted. This left the semaphore
603 * in the signaled state. Call WaitForSingleObject()
604 * to clear the semaphore.
606 PR_ASSERT(thread->io_suspended == PR_TRUE);
607 PR_ASSERT(thread->io_pending == PR_TRUE);
608 thread->io_pending = PR_FALSE;
609 _PR_THREAD_UNLOCK(thread);
610 rc = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
611 PR_ASSERT(rc == WAIT_OBJECT_0);
614 thread->md.blocked_io_status = rv;
615 thread->md.blocked_io_bytes = bytes;
616 rc = ResetEvent(thread->md.thr_event);
622 _native_thread_io_wait(PRThread *thread, PRIntervalTime ticks)
625 #define _NATIVE_IO_WAIT_HANDLES 2
626 #define _NATIVE_WAKEUP_EVENT_INDEX 0
627 #define _NATIVE_IO_EVENT_INDEX 1
629 HANDLE wait_handles[_NATIVE_IO_WAIT_HANDLES];
631 PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
632 INFINITE : PR_IntervalToMilliseconds(ticks);
634 PR_ASSERT(thread->flags & _PR_GLOBAL_SCOPE);
636 wait_handles[0] = thread->md.blocked_sema;
637 wait_handles[1] = thread->md.thr_event;
638 rv = WaitForMultipleObjects(_NATIVE_IO_WAIT_HANDLES, wait_handles,
642 case WAIT_OBJECT_0 + _NATIVE_IO_EVENT_INDEX:
646 _PR_THREAD_LOCK(thread);
647 if (thread->state == _PR_IO_WAIT) {
649 PR_ASSERT(thread->io_suspended == PR_FALSE);
650 PR_ASSERT(thread->io_pending == PR_TRUE);
651 thread->state = _PR_RUNNING;
652 thread->io_pending = PR_FALSE;
653 _PR_THREAD_UNLOCK(thread);
655 /* The IO completed just at the same time the
656 * thread was interrupted. This led to us being
657 * notified twice. Call WaitForSingleObject()
658 * to clear the semaphore.
660 PR_ASSERT(thread->io_suspended == PR_TRUE);
661 PR_ASSERT(thread->io_pending == PR_TRUE);
662 thread->io_pending = PR_FALSE;
663 _PR_THREAD_UNLOCK(thread);
664 rv = WaitForSingleObject(thread->md.blocked_sema,
666 PR_ASSERT(rv == WAIT_OBJECT_0);
669 rv = GetOverlappedResult((HANDLE) thread->io_fd,
670 &thread->md.overlapped.overlapped, &bytes, FALSE);
672 thread->md.blocked_io_status = rv;
674 thread->md.blocked_io_bytes = bytes;
676 thread->md.blocked_io_error = GetLastError();
677 PR_ASSERT(ERROR_IO_PENDING != thread->md.blocked_io_error);
679 rv = ResetEvent(thread->md.thr_event);
682 case WAIT_OBJECT_0 + _NATIVE_WAKEUP_EVENT_INDEX:
687 _PR_THREAD_LOCK(thread);
688 PR_ASSERT(thread->io_suspended == PR_TRUE);
689 _PR_THREAD_UNLOCK(thread);
693 _PR_THREAD_LOCK(thread);
694 if (thread->state == _PR_IO_WAIT) {
695 thread->state = _PR_RUNNING;
696 thread->io_suspended = PR_TRUE;
697 _PR_THREAD_UNLOCK(thread);
700 * The thread was interrupted just as the timeout
701 * occurred. This left the semaphore in the signaled
702 * state. Call WaitForSingleObject() to clear the
705 PR_ASSERT(thread->io_suspended == PR_TRUE);
706 _PR_THREAD_UNLOCK(thread);
707 rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
708 PR_ASSERT(rv == WAIT_OBJECT_0);
721 _NT_IO_WAIT(PRThread *thread, PRIntervalTime timeout)
723 PRBool fWait = PR_TRUE;
725 if (_native_threads_only) {
726 return(_native_thread_io_wait(thread, timeout));
728 if (!_PR_IS_NATIVE_THREAD(thread)) {
730 _PR_THREAD_LOCK(thread);
732 /* The IO may have already completed; if so, don't add to sleepQ,
733 * since we are already on the runQ!
735 if (thread->io_pending == PR_TRUE) {
736 _PR_SLEEPQ_LOCK(thread->cpu);
737 _PR_ADD_SLEEPQ(thread, timeout);
738 _PR_SLEEPQ_UNLOCK(thread->cpu);
741 _PR_THREAD_UNLOCK(thread);
744 return _PR_MD_WAIT(thread, timeout);
750 * Unblock threads waiting for I/O
751 * used when interrupting threads
753 * NOTE: The thread lock should held when this function is called.
754 * On return, the thread lock is released.
756 void _PR_Unblock_IO_Wait(PRThread *thr)
759 _PRCPU *cpu = thr->cpu;
761 PR_ASSERT(thr->state == _PR_IO_WAIT);
763 * A thread for which an I/O timed out or was interrupted cannot be
764 * in an IO_WAIT state except as a result of calling PR_Close or
765 * PR_NT_CancelIo for the FD. For these two cases, _PR_IO_WAIT state
766 * is not interruptible
768 if (thr->md.interrupt_disabled == PR_TRUE) {
769 _PR_THREAD_UNLOCK(thr);
772 thr->io_suspended = PR_TRUE;
773 thr->state = _PR_RUNNABLE;
775 if (!_PR_IS_NATIVE_THREAD(thr)) {
776 PRThread *me = _PR_MD_CURRENT_THREAD();
777 PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
778 _PR_SLEEPQ_LOCK(cpu);
779 _PR_DEL_SLEEPQ(thr, PR_TRUE);
780 _PR_SLEEPQ_UNLOCK(cpu);
782 * this thread will continue to run on the same cpu until the
783 * I/O is aborted by closing the FD or calling CancelIO
785 thr->md.thr_bound_cpu = cpu;
787 PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
788 _PR_AddThreadToRunQ(me, thr);
790 _PR_THREAD_UNLOCK(thr);
791 rv = _PR_MD_WAKEUP_WAITER(thr);
792 PR_ASSERT(PR_SUCCESS == rv);
795 /* Resume an outstanding IO; requires that after the switch, we disable */
797 _NT_ResumeIO(PRThread *thread, PRIntervalTime ticks)
799 PRBool fWait = PR_TRUE;
801 if (!_PR_IS_NATIVE_THREAD(thread)) {
802 if (_pr_use_static_tls) {
803 _pr_io_restarted_io = thread;
805 TlsSetValue(_pr_io_restartedIOIndex, thread);
808 _PR_THREAD_LOCK(thread);
809 if (!thread->io_pending)
811 thread->io_suspended = PR_FALSE;
813 _PR_THREAD_UNLOCK(thread);
815 /* We don't put ourselves back on the sleepQ yet; until we
816 * set the suspended bit to false, we can't do that. Just save
817 * the sleep time here, and then continue. The restarted_io handler
818 * will add us to the sleepQ if needed.
820 thread->sleep = ticks;
823 if (!_PR_IS_NATIVE_THREAD(thread))
824 return _PR_MD_WAIT(thread, ticks);
826 return _NT_IO_WAIT(thread, ticks);
832 _PR_MD_WAKEUP_WAITER(PRThread *thread)
834 if (thread == NULL) {
835 /* If thread is NULL, we aren't waking a thread, we're just poking
838 if ( PostQueuedCompletionStatus(_pr_completion_port, 0,
839 KEY_CVAR, NULL) == FALSE)
844 if ( _PR_IS_NATIVE_THREAD(thread) ) {
845 if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE)
850 PRThread *me = _PR_MD_CURRENT_THREAD();
852 /* When a Native thread has to awaken a user thread, it has to poke
853 * the completion port because all user threads might be idle, and
854 * thus the CPUs are just waiting for a completion.
856 * XXXMB - can we know when we are truely idle (and not checking
859 if ((_PR_IS_NATIVE_THREAD(me) || (thread->cpu != me->cpu)) &&
860 (!thread->md.thr_bound_cpu)) {
861 /* The thread should not be in any queue */
862 PR_ASSERT(thread->queueCount == 0);
863 if ( PostQueuedCompletionStatus(_pr_completion_port, 0,
864 KEY_CVAR, &(thread->md.overlapped.overlapped)) == FALSE)
874 WORD WSAVersion = 0x0101;
877 OSVERSIONINFO OSversion;
879 err = WSAStartup( WSAVersion, &WSAData );
882 _pr_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
887 _MD_NEW_LOCK(&_pr_recycle_lock);
888 _MD_NEW_LOCK(&_pr_ioq_lock);
890 OSversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
891 if (GetVersionEx(&OSversion)) {
892 _nt_version_gets_lockfile_completion = PR_FALSE;
893 if (OSversion.dwMajorVersion >= 4) {
894 _nt_version_gets_lockfile_completion = PR_TRUE;
899 #ifdef _NEED_351_FILE_LOCKING_HACK
901 #endif /* _NEED_351_FILE_LOCKING_HACK */
904 * UDP support: start up the continuation thread
908 pt_tq.head = pt_tq.tail = NULL;
909 pt_tq.ml = PR_NewLock();
910 PR_ASSERT(NULL != pt_tq.ml);
911 pt_tq.new_op = PR_NewCondVar(pt_tq.ml);
912 PR_ASSERT(NULL != pt_tq.new_op);
914 memset(&pt_debug, 0, sizeof(struct pt_debug_s));
917 pt_tq.thread = PR_CreateThread(
918 PR_SYSTEM_THREAD, ContinuationThread, NULL,
919 PR_PRIORITY_URGENT, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
921 PR_ASSERT(NULL != pt_tq.thread);
924 /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
933 systime.wYear = 1970;
935 /* wDayOfWeek is ignored */
940 systime.wMilliseconds = 0;
942 rv = SystemTimeToFileTime(&systime, &filetime.ft);
944 PR_ASSERT(filetime.prt == _pr_filetime_offset);
951 /* --- SOCKET IO --------------------------------------------------------- */
953 /* _md_get_recycled_socket()
954 * Get a socket from the recycle bin; if no sockets are in the bin,
955 * create one. The socket will be passed to AcceptEx() as the
959 _md_get_recycled_socket(int af)
963 _MD_LOCK(&_pr_recycle_lock);
964 if (af == AF_INET && _pr_recycle_INET_tail) {
965 _pr_recycle_INET_tail--;
966 rv = _pr_recycle_INET_array[_pr_recycle_INET_tail];
967 _MD_UNLOCK(&_pr_recycle_lock);
970 if (af == AF_INET6 && _pr_recycle_INET6_tail) {
971 _pr_recycle_INET6_tail--;
972 rv = _pr_recycle_INET6_array[_pr_recycle_INET6_tail];
973 _MD_UNLOCK(&_pr_recycle_lock);
976 _MD_UNLOCK(&_pr_recycle_lock);
978 rv = _PR_MD_SOCKET(af, SOCK_STREAM, 0);
979 if (rv != INVALID_SOCKET && _md_Associate((HANDLE)rv) == 0) {
981 return INVALID_SOCKET;
986 /* _md_put_recycled_socket()
987 * Add a socket to the recycle bin.
990 _md_put_recycled_socket(SOCKET newsock, int af)
992 PR_ASSERT(_pr_recycle_INET_tail >= 0);
993 PR_ASSERT(_pr_recycle_INET6_tail >= 0);
995 _MD_LOCK(&_pr_recycle_lock);
996 if (af == AF_INET && _pr_recycle_INET_tail < RECYCLE_SIZE) {
997 _pr_recycle_INET_array[_pr_recycle_INET_tail] = newsock;
998 _pr_recycle_INET_tail++;
999 _MD_UNLOCK(&_pr_recycle_lock);
1000 } else if (af == AF_INET6 && _pr_recycle_INET6_tail < RECYCLE_SIZE) {
1001 _pr_recycle_INET6_array[_pr_recycle_INET6_tail] = newsock;
1002 _pr_recycle_INET6_tail++;
1003 _MD_UNLOCK(&_pr_recycle_lock);
1005 _MD_UNLOCK(&_pr_recycle_lock);
1006 closesocket(newsock);
1013 * Associates a file with the completion port.
1014 * Returns 0 on failure, 1 on success.
1017 _md_Associate(HANDLE file)
1021 if (!_native_threads_only) {
1022 port = CreateIoCompletionPort((HANDLE)file,
1023 _pr_completion_port,
1027 /* XXX should map error codes on failures */
1028 return (port == _pr_completion_port);
1035 * _md_MakeNonblock()
1036 * Make a socket nonblocking.
1037 * Returns 0 on failure, 1 on success.
1040 _md_MakeNonblock(HANDLE file)
1045 rv = ioctlsocket((SOCKET)file, FIONBIO, &one);
1046 /* XXX should map error codes on failures */
1050 static int missing_completions = 0;
1051 static int max_wait_loops = 0;
1054 _NT_IO_ABORT(PROsfd sock)
1056 PRThread *me = _PR_MD_CURRENT_THREAD();
1061 /* This is a clumsy way to abort the IO, but it is all we can do.
1062 * It looks a bit racy, but we handle all the cases.
1063 * case 1: IO completes before calling closesocket
1064 * case 1a: fWait is set to PR_FALSE
1065 * This should e the most likely case. We'll properly
1066 * not wait call _NT_IO_WAIT, since the closesocket()
1067 * won't be forcing a completion.
1068 * case 1b: fWait is set to PR_TRUE
1069 * This hopefully won't happen much. When it does, this
1070 * thread will timeout in _NT_IO_WAIT for CLOSE_INTERVAL
1071 * before cleaning up.
1072 * case 2: IO does not complete before calling closesocket
1073 * case 2a: IO never completes
1074 * This is the likely case. We'll close it and wait
1075 * for the completion forced by the close. Return should
1077 * case 2b: IO completes just after calling closesocket
1078 * Since the closesocket is issued, we'll either get a
1079 * completion back for the real IO or for the close. We
1080 * don't really care. It may not even be possible to get
1081 * a real completion here. In any event, we'll awaken
1082 * from NT_IO_WAIT immediately.
1085 _PR_THREAD_LOCK(me);
1086 fWait = me->io_pending;
1089 * If there's still I/O pending, it should have already timed
1090 * out once before this function is called.
1092 PR_ASSERT(me->io_suspended == PR_TRUE);
1094 /* Set up to wait for I/O completion again */
1095 me->state = _PR_IO_WAIT;
1096 me->io_suspended = PR_FALSE;
1097 me->md.interrupt_disabled = PR_TRUE;
1099 _PR_THREAD_UNLOCK(me);
1101 /* Close the socket if there is one */
1102 if (sock != INVALID_SOCKET) {
1103 rv = closesocket((SOCKET)sock);
1106 /* If there was I/O pending before the close, wait for it to complete */
1109 /* Wait and wait for the I/O to complete */
1110 for (loop_count = 0; fWait; ++loop_count) {
1112 _NT_IO_WAIT(me, CLOSE_TIMEOUT);
1114 _PR_THREAD_LOCK(me);
1115 fWait = me->io_pending;
1117 PR_ASSERT(me->io_suspended == PR_TRUE);
1118 me->state = _PR_IO_WAIT;
1119 me->io_suspended = PR_FALSE;
1121 _PR_THREAD_UNLOCK(me);
1123 if (loop_count > max_wait_loops) {
1124 max_wait_loops = loop_count;
1128 if (loop_count > 1) {
1129 ++missing_completions;
1132 me->md.interrupt_disabled = PR_FALSE;
1133 me->io_pending = PR_FALSE;
1134 me->state = _PR_RUNNING;
1137 PR_ASSERT(me->io_pending == PR_FALSE);
1138 me->md.thr_bound_cpu = NULL;
1139 me->io_suspended = PR_FALSE;
1146 _PR_MD_SOCKET(int af, int type, int flags)
1150 sock = socket(af, type, flags);
1152 if (sock == INVALID_SOCKET) {
1153 _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
1156 return (PROsfd)sock;
1159 struct connect_data_s {
1163 struct sockaddr *addr;
1165 PRIntervalTime timeout;
1169 _PR_MD_connect_thread(void *cdata)
1171 struct connect_data_s *cd = (struct connect_data_s *)cdata;
1173 cd->status = connect(cd->osfd, cd->addr, cd->addrlen);
1175 if (cd->status == SOCKET_ERROR)
1176 cd->error = WSAGetLastError();
1183 _PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
1184 PRIntervalTime timeout)
1186 PROsfd osfd = fd->secret->md.osfd;
1191 if (fd->secret->nonblocking) {
1192 if (!fd->secret->md.io_model_committed) {
1193 rv = _md_MakeNonblock((HANDLE)osfd);
1195 fd->secret->md.io_model_committed = PR_TRUE;
1198 if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1) {
1199 err = WSAGetLastError();
1200 _PR_MD_MAP_CONNECT_ERROR(err);
1206 * Temporarily make the socket non-blocking so that we can
1207 * initiate a non-blocking connect and wait for its completion
1208 * (with a timeout) in select.
1210 PR_ASSERT(!fd->secret->md.io_model_committed);
1212 rv = ioctlsocket((SOCKET)osfd, FIONBIO, &nbio);
1215 rc = _nt_nonblock_connect(fd, (struct sockaddr *) addr, addrlen, timeout);
1217 /* Set the socket back to blocking. */
1219 rv = ioctlsocket((SOCKET)osfd, FIONBIO, &nbio);
1226 _PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
1233 rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
1235 if (rv == SOCKET_ERROR) {
1236 _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
1241 /* Disable nagle- so far unknown if this is good or not...
1243 rv = setsockopt(fd->secret->md.osfd,
1254 void _PR_MD_UPDATE_ACCEPT_CONTEXT(PROsfd accept_sock, PROsfd listen_sock)
1256 /* Sockets accept()'d with AcceptEx need to call this setsockopt before
1257 * calling anything other than ReadFile(), WriteFile(), send(), recv(),
1258 * Transmitfile(), and closesocket(). In order to call any other
1259 * winsock functions, we have to make this setsockopt call.
1261 * XXXMB - For the server, we *NEVER* need this in
1262 * the "normal" code path. But now we have to call it. This is a waste
1263 * of a system call. We'd like to only call it before calling the
1264 * obscure socket calls, but since we don't know at that point what the
1265 * original socket was (or even if it is still alive) we can't do it
1268 setsockopt((SOCKET)accept_sock,
1270 SO_UPDATE_ACCEPT_CONTEXT,
1271 (char *)&listen_sock,
1272 sizeof(listen_sock));
1276 #define INET_ADDR_PADDED (sizeof(PRNetAddr) + 16)
1278 _PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
1279 PRIntervalTime timeout, PRBool fast,
1280 _PR_AcceptTimeoutCallback callback, void *callbackArg)
1282 PROsfd osfd = fd->secret->md.osfd;
1283 PRThread *me = _PR_MD_CURRENT_THREAD();
1291 if (_NT_USE_NB_IO(fd)) {
1292 if (!fd->secret->md.io_model_committed) {
1293 rv = _md_MakeNonblock((HANDLE)osfd);
1295 fd->secret->md.io_model_committed = PR_TRUE;
1298 * The accepted socket inherits the nonblocking and
1299 * inheritable (HANDLE_FLAG_INHERIT) attributes of
1300 * the listening socket.
1302 accept_sock = _nt_nonblock_accept(fd, (struct sockaddr *)raddr, rlen, timeout);
1303 if (!fd->secret->nonblocking) {
1306 rv = ioctlsocket(accept_sock, FIONBIO, &zero);
1312 if (me->io_suspended) {
1313 PR_SetError(PR_INVALID_STATE_ERROR, 0);
1317 if (!fd->secret->md.io_model_committed) {
1318 rv = _md_Associate((HANDLE)osfd);
1320 fd->secret->md.io_model_committed = PR_TRUE;
1323 if (!me->md.acceptex_buf) {
1324 me->md.acceptex_buf = PR_MALLOC(2*INET_ADDR_PADDED);
1325 if (!me->md.acceptex_buf) {
1326 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
1331 accept_sock = _md_get_recycled_socket(fd->secret->af);
1332 if (accept_sock == INVALID_SOCKET)
1335 memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
1336 if (_native_threads_only)
1337 me->md.overlapped.overlapped.hEvent = me->md.thr_event;
1339 _PR_THREAD_LOCK(me);
1340 if (_PR_PENDING_INTERRUPT(me)) {
1341 me->flags &= ~_PR_INTERRUPT;
1342 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1343 _PR_THREAD_UNLOCK(me);
1344 closesocket(accept_sock);
1347 me->io_pending = PR_TRUE;
1348 me->state = _PR_IO_WAIT;
1349 _PR_THREAD_UNLOCK(me);
1352 rv = AcceptEx((SOCKET)osfd,
1354 me->md.acceptex_buf,
1359 &(me->md.overlapped.overlapped));
1361 if ( (rv == 0) && ((err = WSAGetLastError()) != ERROR_IO_PENDING)) {
1362 /* Argh! The IO failed */
1363 closesocket(accept_sock);
1364 _PR_THREAD_LOCK(me);
1365 me->io_pending = PR_FALSE;
1366 me->state = _PR_RUNNING;
1367 if (_PR_PENDING_INTERRUPT(me)) {
1368 me->flags &= ~_PR_INTERRUPT;
1369 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1370 _PR_THREAD_UNLOCK(me);
1373 _PR_THREAD_UNLOCK(me);
1375 _PR_MD_MAP_ACCEPTEX_ERROR(err);
1379 if (_native_threads_only && rv) {
1380 _native_thread_io_nowait(me, rv, bytes);
1381 } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
1383 closesocket(accept_sock);
1387 PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
1389 if (me->io_suspended) {
1390 closesocket(accept_sock);
1391 if (_PR_PENDING_INTERRUPT(me)) {
1392 me->flags &= ~_PR_INTERRUPT;
1393 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1395 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
1400 if (me->md.blocked_io_status == 0) {
1401 closesocket(accept_sock);
1402 _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
1407 _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)accept_sock, (SOCKET)osfd);
1410 GetAcceptExSockaddrs(
1411 me->md.acceptex_buf,
1415 (LPSOCKADDR *)&(Laddr),
1417 (LPSOCKADDR *)&(Raddr),
1418 (unsigned int *)rlen);
1421 memcpy((char *)raddr, (char *)&Raddr->inet, *rlen);
1423 PR_ASSERT(me->io_pending == PR_FALSE);
1429 _PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PROsfd *newSock, PRNetAddr **raddr,
1430 void *buf, PRInt32 amount, PRIntervalTime timeout,
1431 PRBool fast, _PR_AcceptTimeoutCallback callback,
1434 PROsfd sock = sd->secret->md.osfd;
1435 PRThread *me = _PR_MD_CURRENT_THREAD();
1438 PRUint32 llen, rlen, err;
1441 PRBool madeCallback = PR_FALSE;
1443 if (me->io_suspended) {
1444 PR_SetError(PR_INVALID_STATE_ERROR, 0);
1448 if (!sd->secret->md.io_model_committed) {
1449 rv = _md_Associate((HANDLE)sock);
1451 sd->secret->md.io_model_committed = PR_TRUE;
1454 *newSock = _md_get_recycled_socket(sd->secret->af);
1455 if (*newSock == INVALID_SOCKET)
1458 memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
1459 if (_native_threads_only)
1460 me->md.overlapped.overlapped.hEvent = me->md.thr_event;
1462 _PR_THREAD_LOCK(me);
1463 if (_PR_PENDING_INTERRUPT(me)) {
1464 me->flags &= ~_PR_INTERRUPT;
1465 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1466 _PR_THREAD_UNLOCK(me);
1467 closesocket(*newSock);
1470 me->io_pending = PR_TRUE;
1471 me->state = _PR_IO_WAIT;
1472 _PR_THREAD_UNLOCK(me);
1475 rv = AcceptEx((SOCKET)sock,
1482 &(me->md.overlapped.overlapped));
1484 if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
1485 closesocket(*newSock);
1486 _PR_THREAD_LOCK(me);
1487 me->io_pending = PR_FALSE;
1488 me->state = _PR_RUNNING;
1489 if (_PR_PENDING_INTERRUPT(me)) {
1490 me->flags &= ~_PR_INTERRUPT;
1491 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1492 _PR_THREAD_UNLOCK(me);
1495 _PR_THREAD_UNLOCK(me);
1497 _PR_MD_MAP_ACCEPTEX_ERROR(err);
1501 if (_native_threads_only && rv) {
1502 _native_thread_io_nowait(me, rv, bytes);
1503 } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
1505 closesocket(*newSock);
1510 if (me->io_suspended) {
1513 INT bytes = sizeof(seconds);
1515 PR_ASSERT(timeout != PR_INTERVAL_NO_TIMEOUT);
1517 err = getsockopt(*newSock,
1522 if ( err == NO_ERROR ) {
1523 PRIntervalTime elapsed = PR_SecondsToInterval(seconds);
1525 if (seconds == 0xffffffff)
1526 isConnected = PR_FALSE;
1528 isConnected = PR_TRUE;
1531 if (madeCallback == PR_FALSE && callback)
1532 callback(callbackArg);
1533 madeCallback = PR_TRUE;
1534 me->state = _PR_IO_WAIT;
1535 if (_NT_ResumeIO(me, timeout) == PR_FAILURE) {
1536 closesocket(*newSock);
1542 if (elapsed < timeout) {
1543 /* Socket is connected but time not elapsed, RESUME IO */
1545 me->state = _PR_IO_WAIT;
1546 if (_NT_ResumeIO(me, timeout) == PR_FAILURE) {
1547 closesocket(*newSock);
1553 /* What to do here? Assume socket not open?*/
1555 isConnected = PR_FALSE;
1558 rv = _NT_IO_ABORT(*newSock);
1560 PR_ASSERT(me->io_pending == PR_FALSE);
1561 PR_ASSERT(me->io_suspended == PR_FALSE);
1562 PR_ASSERT(me->md.thr_bound_cpu == NULL);
1563 /* If the IO is still suspended, it means we didn't get any
1564 * completion from NT_IO_WAIT. This is not disasterous, I hope,
1565 * but it may mean we still have an IO outstanding... Try to
1566 * recover by just allowing ourselves to continue.
1568 me->io_suspended = PR_FALSE;
1569 if (_PR_PENDING_INTERRUPT(me)) {
1570 me->flags &= ~_PR_INTERRUPT;
1571 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1573 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
1575 me->state = _PR_RUNNING;
1576 closesocket(*newSock);
1580 PR_ASSERT(me->io_pending == PR_FALSE);
1581 PR_ASSERT(me->io_suspended == PR_FALSE);
1582 PR_ASSERT(me->md.thr_bound_cpu == NULL);
1584 if (me->md.blocked_io_status == 0) {
1585 _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
1586 closesocket(*newSock);
1591 _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)*newSock, (SOCKET)sock);
1594 GetAcceptExSockaddrs(
1599 (LPSOCKADDR *)&(Laddr),
1601 (LPSOCKADDR *)(raddr),
1602 (unsigned int *)&rlen);
1604 return me->md.blocked_io_bytes;
1608 _PR_MD_SENDFILE(PRFileDesc *sock, PRSendFileData *sfd,
1609 PRInt32 flags, PRIntervalTime timeout)
1611 PRThread *me = _PR_MD_CURRENT_THREAD();
1615 if (me->io_suspended) {
1616 PR_SetError(PR_INVALID_STATE_ERROR, 0);
1620 if (!sock->secret->md.io_model_committed) {
1621 rv = _md_Associate((HANDLE)sock->secret->md.osfd);
1623 sock->secret->md.io_model_committed = PR_TRUE;
1625 if (!me->md.xmit_bufs) {
1626 me->md.xmit_bufs = PR_NEW(TRANSMIT_FILE_BUFFERS);
1627 if (!me->md.xmit_bufs) {
1628 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
1632 me->md.xmit_bufs->Head = (void *)sfd->header;
1633 me->md.xmit_bufs->HeadLength = sfd->hlen;
1634 me->md.xmit_bufs->Tail = (void *)sfd->trailer;
1635 me->md.xmit_bufs->TailLength = sfd->tlen;
1637 memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
1638 me->md.overlapped.overlapped.Offset = sfd->file_offset;
1639 if (_native_threads_only)
1640 me->md.overlapped.overlapped.hEvent = me->md.thr_event;
1643 if (flags & PR_TRANSMITFILE_CLOSE_SOCKET)
1644 tflags = TF_DISCONNECT | TF_REUSE_SOCKET;
1646 _PR_THREAD_LOCK(me);
1647 if (_PR_PENDING_INTERRUPT(me)) {
1648 me->flags &= ~_PR_INTERRUPT;
1649 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1650 _PR_THREAD_UNLOCK(me);
1653 me->io_pending = PR_TRUE;
1654 me->state = _PR_IO_WAIT;
1655 _PR_THREAD_UNLOCK(me);
1656 me->io_fd = sock->secret->md.osfd;
1658 rv = TransmitFile((SOCKET)sock->secret->md.osfd,
1659 (HANDLE)sfd->fd->secret->md.osfd,
1660 (DWORD)sfd->file_nbytes,
1662 (LPOVERLAPPED)&(me->md.overlapped.overlapped),
1663 (TRANSMIT_FILE_BUFFERS *)me->md.xmit_bufs,
1665 if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
1666 _PR_THREAD_LOCK(me);
1667 me->io_pending = PR_FALSE;
1668 me->state = _PR_RUNNING;
1669 if (_PR_PENDING_INTERRUPT(me)) {
1670 me->flags &= ~_PR_INTERRUPT;
1671 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1672 _PR_THREAD_UNLOCK(me);
1675 _PR_THREAD_UNLOCK(me);
1677 _PR_MD_MAP_TRANSMITFILE_ERROR(err);
1681 if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
1686 PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
1688 if (me->io_suspended) {
1689 if (_PR_PENDING_INTERRUPT(me)) {
1690 me->flags &= ~_PR_INTERRUPT;
1691 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1693 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
1698 if (me->md.blocked_io_status == 0) {
1699 _PR_MD_MAP_TRANSMITFILE_ERROR(me->md.blocked_io_error);
1703 if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
1704 _md_put_recycled_socket(sock->secret->md.osfd, sock->secret->af);
1707 PR_ASSERT(me->io_pending == PR_FALSE);
1709 return me->md.blocked_io_bytes;
1713 _PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
1714 PRIntervalTime timeout)
1716 PROsfd osfd = fd->secret->md.osfd;
1717 PRThread *me = _PR_MD_CURRENT_THREAD();
1721 if (_NT_USE_NB_IO(fd)) {
1722 if (!fd->secret->md.io_model_committed) {
1723 rv = _md_MakeNonblock((HANDLE)osfd);
1725 fd->secret->md.io_model_committed = PR_TRUE;
1727 return _nt_nonblock_recv(fd, buf, amount, flags, timeout);
1730 if (me->io_suspended) {
1731 PR_SetError(PR_INVALID_STATE_ERROR, 0);
1735 if (!fd->secret->md.io_model_committed) {
1736 rv = _md_Associate((HANDLE)osfd);
1738 fd->secret->md.io_model_committed = PR_TRUE;
1741 memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
1742 if (_native_threads_only)
1743 me->md.overlapped.overlapped.hEvent = me->md.thr_event;
1745 _PR_THREAD_LOCK(me);
1746 if (_PR_PENDING_INTERRUPT(me)) {
1747 me->flags &= ~_PR_INTERRUPT;
1748 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1749 _PR_THREAD_UNLOCK(me);
1752 me->io_pending = PR_TRUE;
1753 me->state = _PR_IO_WAIT;
1754 _PR_THREAD_UNLOCK(me);
1757 rv = ReadFile((HANDLE)osfd,
1761 &(me->md.overlapped.overlapped));
1762 if ( (rv == 0) && (GetLastError() != ERROR_IO_PENDING) ) {
1763 _PR_THREAD_LOCK(me);
1764 me->io_pending = PR_FALSE;
1765 me->state = _PR_RUNNING;
1766 if (_PR_PENDING_INTERRUPT(me)) {
1767 me->flags &= ~_PR_INTERRUPT;
1768 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1769 _PR_THREAD_UNLOCK(me);
1772 _PR_THREAD_UNLOCK(me);
1774 if ((err = GetLastError()) == ERROR_HANDLE_EOF)
1776 _PR_MD_MAP_READ_ERROR(err);
1780 if (_native_threads_only && rv) {
1781 _native_thread_io_nowait(me, rv, bytes);
1782 } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
1787 PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
1789 if (me->io_suspended) {
1790 if (_PR_PENDING_INTERRUPT(me)) {
1791 me->flags &= ~_PR_INTERRUPT;
1792 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1794 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
1799 if (me->md.blocked_io_status == 0) {
1800 if (me->md.blocked_io_error == ERROR_HANDLE_EOF)
1802 _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
1806 PR_ASSERT(me->io_pending == PR_FALSE);
1808 return me->md.blocked_io_bytes;
1812 _PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
1813 PRIntervalTime timeout)
1815 PROsfd osfd = fd->secret->md.osfd;
1816 PRThread *me = _PR_MD_CURRENT_THREAD();
1820 if (_NT_USE_NB_IO(fd)) {
1821 if (!fd->secret->md.io_model_committed) {
1822 rv = _md_MakeNonblock((HANDLE)osfd);
1824 fd->secret->md.io_model_committed = PR_TRUE;
1826 return _nt_nonblock_send(fd, (char *)buf, amount, timeout);
1829 if (me->io_suspended) {
1830 PR_SetError(PR_INVALID_STATE_ERROR, 0);
1834 if (!fd->secret->md.io_model_committed) {
1835 rv = _md_Associate((HANDLE)osfd);
1837 fd->secret->md.io_model_committed = PR_TRUE;
1840 memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
1841 if (_native_threads_only)
1842 me->md.overlapped.overlapped.hEvent = me->md.thr_event;
1844 _PR_THREAD_LOCK(me);
1845 if (_PR_PENDING_INTERRUPT(me)) {
1846 me->flags &= ~_PR_INTERRUPT;
1847 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1848 _PR_THREAD_UNLOCK(me);
1851 me->io_pending = PR_TRUE;
1852 me->state = _PR_IO_WAIT;
1853 _PR_THREAD_UNLOCK(me);
1856 rv = WriteFile((HANDLE)osfd,
1860 &(me->md.overlapped.overlapped));
1861 if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
1862 _PR_THREAD_LOCK(me);
1863 me->io_pending = PR_FALSE;
1864 me->state = _PR_RUNNING;
1865 if (_PR_PENDING_INTERRUPT(me)) {
1866 me->flags &= ~_PR_INTERRUPT;
1867 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1868 _PR_THREAD_UNLOCK(me);
1871 _PR_THREAD_UNLOCK(me);
1873 _PR_MD_MAP_WRITE_ERROR(err);
1877 if (_native_threads_only && rv) {
1878 _native_thread_io_nowait(me, rv, bytes);
1879 } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
1884 PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
1886 if (me->io_suspended) {
1887 if (_PR_PENDING_INTERRUPT(me)) {
1888 me->flags &= ~_PR_INTERRUPT;
1889 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1891 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
1896 if (me->md.blocked_io_status == 0) {
1897 _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
1901 PR_ASSERT(me->io_pending == PR_FALSE);
1903 return me->md.blocked_io_bytes;
1907 _PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
1908 const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
1910 PROsfd osfd = fd->secret->md.osfd;
1913 if (!fd->secret->md.io_model_committed) {
1914 rv = _md_MakeNonblock((HANDLE)osfd);
1916 fd->secret->md.io_model_committed = PR_TRUE;
1918 if (_NT_USE_NB_IO(fd))
1919 return _nt_nonblock_sendto(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
1921 return pt_SendTo(osfd, buf, amount, flags, addr, addrlen, timeout);
1925 _PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
1926 PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
1928 PROsfd osfd = fd->secret->md.osfd;
1931 if (!fd->secret->md.io_model_committed) {
1932 rv = _md_MakeNonblock((HANDLE)osfd);
1934 fd->secret->md.io_model_committed = PR_TRUE;
1936 if (_NT_USE_NB_IO(fd))
1937 return _nt_nonblock_recvfrom(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
1939 return pt_RecvFrom(osfd, buf, amount, flags, addr, addrlen, timeout);
1942 /* XXXMB - for now this is a sockets call only */
1944 _PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
1946 PROsfd osfd = fd->secret->md.osfd;
1951 if (_NT_USE_NB_IO(fd)) {
1952 if (!fd->secret->md.io_model_committed) {
1953 rv = _md_MakeNonblock((HANDLE)osfd);
1955 fd->secret->md.io_model_committed = PR_TRUE;
1957 return _nt_nonblock_writev(fd, iov, iov_size, timeout);
1960 for (index=0; index<iov_size; index++) {
1961 rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0,
1965 if ( rv != iov[index].iov_len ) {
1976 _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
1980 rv = listen(fd->secret->md.osfd, backlog);
1982 _PR_MD_MAP_LISTEN_ERROR(WSAGetLastError());
1987 _PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
1991 rv = shutdown(fd->secret->md.osfd, how);
1993 _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
1998 _PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
2002 rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
2006 _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
2012 _PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
2017 * NT has a bug that, when invoked on a socket accepted by
2018 * AcceptEx(), getpeername() returns an all-zero peer address.
2019 * To work around this bug, we store the peer's address (returned
2020 * by AcceptEx()) with the socket fd and use the cached peer
2021 * address if the socket is an accepted socket.
2024 if (fd->secret->md.accepted_socket) {
2026 INT bytes = sizeof(seconds);
2029 * Determine if the socket is connected.
2032 rv = getsockopt(fd->secret->md.osfd,
2037 if (rv == NO_ERROR) {
2038 if (seconds == 0xffffffff) {
2039 PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
2042 *len = PR_NETADDR_SIZE(&fd->secret->md.peer_addr);
2043 memcpy(addr, &fd->secret->md.peer_addr, *len);
2046 _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
2050 rv = getpeername((SOCKET)fd->secret->md.osfd,
2051 (struct sockaddr *) addr, len);
2055 _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
2062 _PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
2066 rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
2070 _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
2076 _PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
2080 rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
2084 _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
2089 /* --- FILE IO ----------------------------------------------------------- */
2092 _PR_MD_OPEN(const char *name, PRIntn osflags, PRIntn mode)
2099 if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
2101 if (osflags & PR_RDONLY || osflags & PR_RDWR) access |= GENERIC_READ;
2102 if (osflags & PR_WRONLY || osflags & PR_RDWR) access |= GENERIC_WRITE;
2104 if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL )
2106 else if (osflags & PR_CREATE_FILE)
2107 flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
2108 else if (osflags & PR_TRUNCATE) flags = TRUNCATE_EXISTING;
2109 else flags = OPEN_EXISTING;
2112 flag6 |= FILE_FLAG_OVERLAPPED;
2114 file = CreateFile(name,
2116 FILE_SHARE_READ|FILE_SHARE_WRITE,
2121 if (file == INVALID_HANDLE_VALUE) {
2122 _PR_MD_MAP_OPEN_ERROR(GetLastError());
2126 if (osflags & PR_APPEND) {
2127 if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) {
2128 _PR_MD_MAP_LSEEK_ERROR(GetLastError());
2134 return (PROsfd)file;
2138 _PR_MD_OPEN_FILE(const char *name, PRIntn osflags, PRIntn mode)
2144 SECURITY_ATTRIBUTES sa;
2145 LPSECURITY_ATTRIBUTES lpSA = NULL;
2146 PSECURITY_DESCRIPTOR pSD = NULL;
2149 if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
2151 if (osflags & PR_RDONLY || osflags & PR_RDWR) access |= GENERIC_READ;
2152 if (osflags & PR_WRONLY || osflags & PR_RDWR) access |= GENERIC_WRITE;
2154 if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL )
2156 else if (osflags & PR_CREATE_FILE)
2157 flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
2158 else if (osflags & PR_TRUNCATE) flags = TRUNCATE_EXISTING;
2159 else flags = OPEN_EXISTING;
2162 flag6 |= FILE_FLAG_OVERLAPPED;
2164 if (osflags & PR_CREATE_FILE) {
2165 if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable,
2166 &pSD, &pACL) == PR_SUCCESS) {
2167 sa.nLength = sizeof(sa);
2168 sa.lpSecurityDescriptor = pSD;
2169 sa.bInheritHandle = FALSE;
2173 file = CreateFile(name,
2175 FILE_SHARE_READ|FILE_SHARE_WRITE,
2181 _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
2183 if (file == INVALID_HANDLE_VALUE) {
2184 _PR_MD_MAP_OPEN_ERROR(GetLastError());
2188 if (osflags & PR_APPEND) {
2189 if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) {
2190 _PR_MD_MAP_LSEEK_ERROR(GetLastError());
2196 return (PROsfd)file;
2200 _PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
2202 PROsfd f = fd->secret->md.osfd;
2208 if (!fd->secret->md.sync_file_io) {
2209 PRThread *me = _PR_MD_CURRENT_THREAD();
2211 if (me->io_suspended) {
2212 PR_SetError(PR_INVALID_STATE_ERROR, 0);
2216 memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
2218 me->md.overlapped.overlapped.Offset = SetFilePointer((HANDLE)f, 0, &me->md.overlapped.overlapped.OffsetHigh, FILE_CURRENT);
2219 PR_ASSERT((me->md.overlapped.overlapped.Offset != 0xffffffff) || (GetLastError() == NO_ERROR));
2221 if (fd->secret->inheritable == _PR_TRI_TRUE) {
2222 rv = ReadFile((HANDLE)f,
2226 &me->md.overlapped.overlapped);
2228 loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
2229 PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
2232 err = GetLastError();
2233 if (err == ERROR_IO_PENDING) {
2234 rv = GetOverlappedResult((HANDLE)f,
2235 &me->md.overlapped.overlapped, &bytes, TRUE);
2237 loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
2238 PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
2241 err = GetLastError();
2243 if (err == ERROR_HANDLE_EOF) {
2246 _PR_MD_MAP_READ_ERROR(err);
2250 if (!fd->secret->md.io_model_committed) {
2251 rv = _md_Associate((HANDLE)f);
2253 fd->secret->md.io_model_committed = PR_TRUE;
2256 if (_native_threads_only)
2257 me->md.overlapped.overlapped.hEvent = me->md.thr_event;
2259 _PR_THREAD_LOCK(me);
2260 if (_PR_PENDING_INTERRUPT(me)) {
2261 me->flags &= ~_PR_INTERRUPT;
2262 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
2263 _PR_THREAD_UNLOCK(me);
2266 me->io_pending = PR_TRUE;
2267 me->state = _PR_IO_WAIT;
2268 _PR_THREAD_UNLOCK(me);
2271 rv = ReadFile((HANDLE)f,
2275 &me->md.overlapped.overlapped);
2276 if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
2277 _PR_THREAD_LOCK(me);
2278 me->io_pending = PR_FALSE;
2279 me->state = _PR_RUNNING;
2280 if (_PR_PENDING_INTERRUPT(me)) {
2281 me->flags &= ~_PR_INTERRUPT;
2282 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
2283 _PR_THREAD_UNLOCK(me);
2286 _PR_THREAD_UNLOCK(me);
2288 if (err == ERROR_HANDLE_EOF) {
2291 _PR_MD_MAP_READ_ERROR(err);
2295 if (_native_threads_only && rv) {
2296 _native_thread_io_nowait(me, rv, bytes);
2297 } else if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
2302 PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
2304 if (me->io_suspended) {
2305 if (_PR_PENDING_INTERRUPT(me)) {
2306 me->flags &= ~_PR_INTERRUPT;
2307 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
2309 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
2314 if (me->md.blocked_io_status == 0) {
2315 if (me->md.blocked_io_error == ERROR_HANDLE_EOF) {
2318 _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
2322 SetFilePointer((HANDLE)f, me->md.blocked_io_bytes, 0, FILE_CURRENT);
2324 PR_ASSERT(me->io_pending == PR_FALSE);
2326 return me->md.blocked_io_bytes;
2330 rv = ReadFile((HANDLE)f,
2336 err = GetLastError();
2337 /* ERROR_HANDLE_EOF can only be returned by async io */
2338 PR_ASSERT(err != ERROR_HANDLE_EOF);
2339 if (err == ERROR_BROKEN_PIPE) {
2340 /* The write end of the pipe has been closed. */
2343 _PR_MD_MAP_READ_ERROR(err);
2351 _PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len)
2353 PROsfd f = fd->secret->md.osfd;
2358 LARGE_INTEGER offset; /* use for the calculation of the new offset */
2360 if (!fd->secret->md.sync_file_io) {
2361 PRThread *me = _PR_MD_CURRENT_THREAD();
2363 if (me->io_suspended) {
2364 PR_SetError(PR_INVALID_STATE_ERROR, 0);
2368 memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
2370 me->md.overlapped.overlapped.Offset = SetFilePointer((HANDLE)f, 0, &me->md.overlapped.overlapped.OffsetHigh, FILE_CURRENT);
2371 PR_ASSERT((me->md.overlapped.overlapped.Offset != 0xffffffff) || (GetLastError() == NO_ERROR));
2373 if (fd->secret->inheritable == _PR_TRI_TRUE) {
2374 rv = WriteFile((HANDLE)f,
2378 &me->md.overlapped.overlapped);
2380 loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
2381 PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
2384 err = GetLastError();
2385 if (err == ERROR_IO_PENDING) {
2386 rv = GetOverlappedResult((HANDLE)f,
2387 &me->md.overlapped.overlapped, &bytes, TRUE);
2389 loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
2390 PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
2393 err = GetLastError();
2395 _PR_MD_MAP_READ_ERROR(err);
2398 if (!fd->secret->md.io_model_committed) {
2399 rv = _md_Associate((HANDLE)f);
2401 fd->secret->md.io_model_committed = PR_TRUE;
2403 if (_native_threads_only)
2404 me->md.overlapped.overlapped.hEvent = me->md.thr_event;
2406 _PR_THREAD_LOCK(me);
2407 if (_PR_PENDING_INTERRUPT(me)) {
2408 me->flags &= ~_PR_INTERRUPT;
2409 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
2410 _PR_THREAD_UNLOCK(me);
2413 me->io_pending = PR_TRUE;
2414 me->state = _PR_IO_WAIT;
2415 _PR_THREAD_UNLOCK(me);
2418 rv = WriteFile((HANDLE)f,
2422 &(me->md.overlapped.overlapped));
2423 if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
2424 _PR_THREAD_LOCK(me);
2425 me->io_pending = PR_FALSE;
2426 me->state = _PR_RUNNING;
2427 if (_PR_PENDING_INTERRUPT(me)) {
2428 me->flags &= ~_PR_INTERRUPT;
2429 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
2430 _PR_THREAD_UNLOCK(me);
2433 _PR_THREAD_UNLOCK(me);
2435 _PR_MD_MAP_WRITE_ERROR(err);
2439 if (_native_threads_only && rv) {
2440 _native_thread_io_nowait(me, rv, bytes);
2441 } else if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
2446 PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
2448 if (me->io_suspended) {
2449 if (_PR_PENDING_INTERRUPT(me)) {
2450 me->flags &= ~_PR_INTERRUPT;
2451 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
2453 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
2458 if (me->md.blocked_io_status == 0) {
2459 _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
2464 * Moving the file pointer by a relative offset (FILE_CURRENT)
2465 * does not work with a file on a network drive exported by a
2466 * Win2K system. We still don't know why. A workaround is to
2467 * move the file pointer by an absolute offset (FILE_BEGIN).
2468 * (Bugzilla bug 70765)
2470 offset.LowPart = me->md.overlapped.overlapped.Offset;
2471 offset.HighPart = me->md.overlapped.overlapped.OffsetHigh;
2472 offset.QuadPart += me->md.blocked_io_bytes;
2474 SetFilePointer((HANDLE)f, offset.LowPart, &offset.HighPart, FILE_BEGIN);
2476 PR_ASSERT(me->io_pending == PR_FALSE);
2478 return me->md.blocked_io_bytes;
2481 rv = WriteFile((HANDLE)f,
2487 _PR_MD_MAP_WRITE_ERROR(GetLastError());
2495 _PR_MD_SOCKETAVAILABLE(PRFileDesc *fd)
2499 if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
2500 PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
2507 _PR_MD_PIPEAVAILABLE(PRFileDesc *fd)
2510 PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
2512 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
2517 _PR_MD_LSEEK(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence)
2524 moveMethod = FILE_BEGIN;
2527 moveMethod = FILE_CURRENT;
2530 moveMethod = FILE_END;
2533 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
2537 rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod);
2540 * If the lpDistanceToMoveHigh argument (third argument) is
2541 * NULL, SetFilePointer returns 0xffffffff on failure.
2544 _PR_MD_MAP_LSEEK_ERROR(GetLastError());
2550 _PR_MD_LSEEK64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence)
2558 moveMethod = FILE_BEGIN;
2561 moveMethod = FILE_CURRENT;
2564 moveMethod = FILE_END;
2567 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
2571 li.QuadPart = offset;
2572 li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd,
2573 li.LowPart, &li.HighPart, moveMethod);
2575 if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) {
2576 _PR_MD_MAP_LSEEK_ERROR(err);
2583 * This is documented to succeed on read-only files, but Win32's
2584 * FlushFileBuffers functions fails with "access denied" in such a
2585 * case. So we only signal an error if the error is *not* "access
2589 _PR_MD_FSYNC(PRFileDesc *fd)
2592 * From the documentation:
2594 * On Windows NT, the function FlushFileBuffers fails if hFile
2595 * is a handle to console output. That is because console
2596 * output is not buffered. The function returns FALSE, and
2597 * GetLastError returns ERROR_INVALID_HANDLE.
2599 * On the other hand, on Win95, it returns without error. I cannot
2600 * assume that 0, 1, and 2 are console, because if someone closes
2601 * System.out and then opens a file, they might get file descriptor
2602 * 1. An error on *that* version of 1 should be reported, whereas
2603 * an error on System.out (which was the original 1) should be
2604 * ignored. So I use isatty() to ensure that such an error was
2605 * because of this, and if it was, I ignore the error.
2608 BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd);
2611 DWORD err = GetLastError();
2613 if (err != ERROR_ACCESS_DENIED) { /* from winerror.h */
2614 _PR_MD_MAP_FSYNC_ERROR(err);
2622 _PR_MD_CLOSE(PROsfd osfd, PRBool socket)
2625 PRThread *me = _PR_MD_CURRENT_THREAD();
2628 rv = closesocket((SOCKET)osfd);
2630 _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
2632 rv = CloseHandle((HANDLE)osfd)?0:-1;
2634 _PR_MD_MAP_CLOSE_ERROR(GetLastError());
2637 if (rv == 0 && me->io_suspended) {
2638 if (me->io_fd == osfd) {
2641 _PR_THREAD_LOCK(me);
2642 me->state = _PR_IO_WAIT;
2643 /* The IO could have completed on another thread just after
2644 * calling closesocket while the io_suspended flag was true.
2645 * So we now grab the lock to do a safe check on io_pending to
2646 * see if we need to wait or not.
2648 fWait = me->io_pending;
2649 me->io_suspended = PR_FALSE;
2650 me->md.interrupt_disabled = PR_TRUE;
2651 _PR_THREAD_UNLOCK(me);
2654 _NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
2655 PR_ASSERT(me->io_suspended == PR_FALSE);
2656 PR_ASSERT(me->io_pending == PR_FALSE);
2658 * I/O operation is no longer pending; the thread can now
2661 _PR_THREAD_LOCK(me);
2662 me->md.interrupt_disabled = PR_FALSE;
2663 me->md.thr_bound_cpu = NULL;
2664 me->io_suspended = PR_FALSE;
2665 me->io_pending = PR_FALSE;
2666 me->state = _PR_RUNNING;
2667 _PR_THREAD_UNLOCK(me);
2674 _PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable)
2678 if (fd->secret->md.io_model_committed) {
2679 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
2682 rv = SetHandleInformation(
2683 (HANDLE)fd->secret->md.osfd,
2684 HANDLE_FLAG_INHERIT,
2685 inheritable ? HANDLE_FLAG_INHERIT : 0);
2687 _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
2694 _PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported)
2697 fd->secret->inheritable = _PR_TRI_UNKNOWN;
2699 fd->secret->inheritable = _PR_TRI_FALSE;
2704 _PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd)
2708 PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable);
2709 if (fd->secret->md.io_model_committed) {
2712 if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) {
2713 if (flags & HANDLE_FLAG_INHERIT) {
2714 fd->secret->inheritable = _PR_TRI_TRUE;
2716 fd->secret->inheritable = _PR_TRI_FALSE;
2722 /* --- DIR IO ------------------------------------------------------------ */
2723 #define GetFileFromDIR(d) (d)->d_entry.cFileName
2724 #define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
2726 void FlipSlashes(char *cp, int len)
2728 while (--len >= 0) {
2730 cp[0] = PR_DIRECTORY_SEPARATOR;
2734 } /* end FlipSlashes() */
2738 ** Local implementations of standard Unix RTL functions which are not provided
2744 _PR_MD_CLOSE_DIR(_MDDir *d)
2747 if (FindClose( d->d_hdl )) {
2748 d->magic = (PRUint32)-1;
2751 _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
2755 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
2761 _PR_MD_OPEN_DIR(_MDDir *d, const char *name)
2763 char filename[ MAX_PATH ];
2767 /* Need 5 bytes for \*.* and the trailing null byte. */
2768 if (len + 5 > MAX_PATH) {
2769 PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
2772 strcpy(filename, name);
2775 * If 'name' ends in a slash or backslash, do not append
2776 * another backslash.
2778 if (IsPrevCharSlash(filename, filename + len)) {
2781 strcpy(&filename[len], "\\*.*");
2782 FlipSlashes( filename, strlen(filename) );
2784 d->d_hdl = FindFirstFile( filename, &(d->d_entry) );
2785 if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
2786 _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
2789 d->firstEntry = PR_TRUE;
2790 d->magic = _MD_MAGIC_DIR;
2795 _PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
2803 if (d->firstEntry) {
2804 d->firstEntry = PR_FALSE;
2807 rv = FindNextFile(d->d_hdl, &(d->d_entry));
2812 fileName = GetFileFromDIR(d);
2813 if ( (flags & PR_SKIP_DOT) &&
2814 (fileName[0] == '.') && (fileName[1] == '\0'))
2816 if ( (flags & PR_SKIP_DOT_DOT) &&
2817 (fileName[0] == '.') && (fileName[1] == '.') &&
2818 (fileName[2] == '\0'))
2820 if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d))
2824 err = GetLastError();
2825 PR_ASSERT(NO_ERROR != err);
2826 _PR_MD_MAP_READDIR_ERROR(err);
2829 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
2834 _PR_MD_DELETE(const char *name)
2836 if (DeleteFile(name)) {
2839 _PR_MD_MAP_DELETE_ERROR(GetLastError());
2845 _PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
2847 PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
2848 CopyMemory(prtm, filetime, sizeof(PRTime));
2850 *prtm = (*prtm - _pr_filetime_offset) / 10LL;
2852 *prtm = (*prtm - _pr_filetime_offset) / 10i64;
2856 /* Doublecheck our calculation. */
2860 PRTime cmp; /* for comparison */
2863 rv = FileTimeToSystemTime(filetime, &systime);
2867 * PR_ImplodeTime ignores wday and yday.
2869 etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
2870 etm.tm_sec = systime.wSecond;
2871 etm.tm_min = systime.wMinute;
2872 etm.tm_hour = systime.wHour;
2873 etm.tm_mday = systime.wDay;
2874 etm.tm_month = systime.wMonth - 1;
2875 etm.tm_year = systime.wYear;
2877 * It is not well-documented what time zone the FILETIME's
2878 * are in. WIN32_FIND_DATA is documented to be in UTC (GMT).
2879 * But BY_HANDLE_FILE_INFORMATION is unclear about this.
2880 * By our best judgement, we assume that FILETIME is in UTC.
2882 etm.tm_params.tp_gmt_offset = 0;
2883 etm.tm_params.tp_dst_offset = 0;
2884 cmp = PR_ImplodeTime(&etm);
2887 * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
2888 * microseconds to milliseconds before doing the comparison.
2890 PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
2896 _PR_MD_STAT(const char *fn, struct stat *info)
2900 rv = _stat(fn, (struct _stat *)info);
2903 * Check for MSVC runtime library _stat() bug.
2904 * (It's really a bug in FindFirstFile().)
2905 * If a pathname ends in a backslash or slash,
2906 * e.g., c:\temp\ or c:/temp/, _stat() will fail.
2907 * Note: a pathname ending in a slash (e.g., c:/temp/)
2908 * can be handled by _stat() on NT but not on Win95.
2910 * We remove the backslash or slash at the end and
2914 int len = strlen(fn);
2915 if (len > 0 && len <= _MAX_PATH
2916 && IsPrevCharSlash(fn, fn + len)) {
2917 char newfn[_MAX_PATH + 1];
2920 newfn[len - 1] = '\0';
2921 rv = _stat(newfn, (struct _stat *)info);
2926 _PR_MD_MAP_STAT_ERROR(errno);
2931 #define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
2934 IsPrevCharSlash(const char *str, const char *current)
2940 prev = _mbsdec(str, current);
2941 return (prev == current - 1) && _PR_IS_SLASH(*prev);
2945 * IsRootDirectory --
2947 * Return PR_TRUE if the pathname 'fn' is a valid root directory,
2948 * else return PR_FALSE. The char buffer pointed to by 'fn' must
2949 * be writable. During the execution of this function, the contents
2950 * of the buffer pointed to by 'fn' may be modified, but on return
2951 * the original contents will be restored. 'buflen' is the size of
2952 * the buffer pointed to by 'fn'.
2954 * Root directories come in three formats:
2955 * 1. / or \, meaning the root directory of the current drive.
2956 * 2. C:/ or C:\, where C is a drive letter.
2957 * 3. \\<server name>\<share point name>\ or
2958 * \\<server name>\<share point name>, meaning the root directory
2959 * of a UNC (Universal Naming Convention) name.
2963 IsRootDirectory(char *fn, size_t buflen)
2966 PRBool slashAdded = PR_FALSE;
2967 PRBool rv = PR_FALSE;
2969 if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
2973 if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2])
2975 rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
2979 /* The UNC root directory */
2981 if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
2982 /* The 'server' part should have at least one character. */
2984 if (*p == '\0' || _PR_IS_SLASH(*p)) {
2988 /* look for the next slash */
2991 } while (*p != '\0' && !_PR_IS_SLASH(*p));
2996 /* The 'share' part should have at least one character. */
2998 if (*p == '\0' || _PR_IS_SLASH(*p)) {
3002 /* look for the final slash */
3005 } while (*p != '\0' && !_PR_IS_SLASH(*p));
3006 if (_PR_IS_SLASH(*p) && p[1] != '\0') {
3011 * GetDriveType() doesn't work correctly if the
3012 * path is of the form \\server\share, so we add
3013 * a final slash temporarily.
3015 if ((p + 1) < (fn + buflen)) {
3018 slashAdded = PR_TRUE;
3020 return PR_FALSE; /* name too long */
3023 rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
3024 /* restore the 'fn' buffer */
3033 _PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
3035 WIN32_FILE_ATTRIBUTE_DATA findFileData;
3037 if (NULL == fn || '\0' == *fn) {
3038 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
3042 if (!GetFileAttributesEx(fn, GetFileExInfoStandard, &findFileData)) {
3043 _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
3047 if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3048 info->type = PR_FILE_DIRECTORY;
3050 info->type = PR_FILE_FILE;
3053 info->size = findFileData.nFileSizeHigh;
3054 info->size = (info->size << 32) + findFileData.nFileSizeLow;
3056 _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
3058 if (0 == findFileData.ftCreationTime.dwLowDateTime &&
3059 0 == findFileData.ftCreationTime.dwHighDateTime) {
3060 info->creationTime = info->modifyTime;
3062 _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
3063 &info->creationTime);
3070 _PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
3072 PRFileInfo64 info64;
3073 PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
3076 info->type = info64.type;
3077 info->size = (PRUint32) info64.size;
3078 info->modifyTime = info64.modifyTime;
3079 info->creationTime = info64.creationTime;
3085 _PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
3089 BY_HANDLE_FILE_INFORMATION hinfo;
3091 rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
3093 _PR_MD_MAP_FSTAT_ERROR(GetLastError());
3097 if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3098 info->type = PR_FILE_DIRECTORY;
3100 info->type = PR_FILE_FILE;
3102 info->size = hinfo.nFileSizeHigh;
3103 info->size = (info->size << 32) + hinfo.nFileSizeLow;
3105 _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
3106 _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
3112 _PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
3116 BY_HANDLE_FILE_INFORMATION hinfo;
3118 rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
3120 _PR_MD_MAP_FSTAT_ERROR(GetLastError());
3124 if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3125 info->type = PR_FILE_DIRECTORY;
3127 info->type = PR_FILE_FILE;
3129 info->size = hinfo.nFileSizeLow;
3131 _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
3132 _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
3138 _PR_MD_RENAME(const char *from, const char *to)
3140 /* Does this work with dot-relative pathnames? */
3141 if (MoveFile(from, to)) {
3144 _PR_MD_MAP_RENAME_ERROR(GetLastError());
3150 _PR_MD_ACCESS(const char *name, PRAccessHow how)
3155 case PR_ACCESS_WRITE_OK:
3156 rv = _access(name, 02);
3158 case PR_ACCESS_READ_OK:
3159 rv = _access(name, 04);
3161 case PR_ACCESS_EXISTS:
3162 rv = _access(name, 00);
3165 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
3169 _PR_MD_MAP_ACCESS_ERROR(errno);
3175 _PR_MD_MKDIR(const char *name, PRIntn mode)
3177 /* XXXMB - how to translate the "mode"??? */
3178 if (CreateDirectory(name, NULL)) {
3181 _PR_MD_MAP_MKDIR_ERROR(GetLastError());
3187 _PR_MD_MAKE_DIR(const char *name, PRIntn mode)
3190 SECURITY_ATTRIBUTES sa;
3191 LPSECURITY_ATTRIBUTES lpSA = NULL;
3192 PSECURITY_DESCRIPTOR pSD = NULL;
3195 if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable,
3196 &pSD, &pACL) == PR_SUCCESS) {
3197 sa.nLength = sizeof(sa);
3198 sa.lpSecurityDescriptor = pSD;
3199 sa.bInheritHandle = FALSE;
3202 rv = CreateDirectory(name, lpSA);
3204 _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
3209 _PR_MD_MAP_MKDIR_ERROR(GetLastError());
3215 _PR_MD_RMDIR(const char *name)
3217 if (RemoveDirectory(name)) {
3220 _PR_MD_MAP_RMDIR_ERROR(GetLastError());
3226 _PR_MD_LOCKFILE(PROsfd f)
3229 PRThread *me = _PR_MD_CURRENT_THREAD();
3231 if (me->io_suspended) {
3232 PR_SetError(PR_INVALID_STATE_ERROR, 0);
3236 memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
3238 _PR_THREAD_LOCK(me);
3239 if (_PR_PENDING_INTERRUPT(me)) {
3240 me->flags &= ~_PR_INTERRUPT;
3241 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3242 _PR_THREAD_UNLOCK(me);
3245 me->io_pending = PR_TRUE;
3246 me->state = _PR_IO_WAIT;
3247 _PR_THREAD_UNLOCK(me);
3249 rv = LockFileEx((HANDLE)f,
3250 LOCKFILE_EXCLUSIVE_LOCK,
3254 &me->md.overlapped.overlapped);
3256 if (_native_threads_only) {
3257 _PR_THREAD_LOCK(me);
3258 me->io_pending = PR_FALSE;
3259 me->state = _PR_RUNNING;
3260 if (_PR_PENDING_INTERRUPT(me)) {
3261 me->flags &= ~_PR_INTERRUPT;
3262 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3263 _PR_THREAD_UNLOCK(me);
3266 _PR_THREAD_UNLOCK(me);
3269 err = GetLastError();
3270 PR_ASSERT(err != ERROR_IO_PENDING);
3271 _PR_MD_MAP_LOCKF_ERROR(err);
3277 /* HACK AROUND NT BUG
3278 * NT 3.51 has a bug. In NT 3.51, if LockFileEx returns true, you
3279 * don't get any completion on the completion port. This is a bug.
3281 * They fixed it on NT4.0 so that you do get a completion.
3283 * If we pretend we won't get a completion, NSPR gets confused later
3284 * when the unexpected completion arrives. If we assume we do get
3285 * a completion, we hang on 3.51. Worse, Microsoft informs me that the
3286 * behavior varies on 3.51 depending on if you are using a network
3287 * file system or a local disk!
3289 * Solution: For now, _nt_version_gets_lockfile_completion is set
3290 * depending on whether or not this system is EITHER
3292 * - running NT 3.51 with a service pack greater than 5.
3294 * In the meantime, this code may not work on network file systems.
3298 if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
3299 _PR_THREAD_LOCK(me);
3300 me->io_pending = PR_FALSE;
3301 me->state = _PR_RUNNING;
3302 if (_PR_PENDING_INTERRUPT(me)) {
3303 me->flags &= ~_PR_INTERRUPT;
3304 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3305 _PR_THREAD_UNLOCK(me);
3308 _PR_THREAD_UNLOCK(me);
3310 _PR_MD_MAP_LOCKF_ERROR(err);
3313 #ifdef _NEED_351_FILE_LOCKING_HACK
3315 /* If this is NT 3.51 and the file is local, then we won't get a
3316 * completion back from LockFile when it succeeded.
3318 if (_nt_version_gets_lockfile_completion == PR_FALSE) {
3319 if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
3320 me->io_pending = PR_FALSE;
3321 me->state = _PR_RUNNING;
3326 #endif /* _NEED_351_FILE_LOCKING_HACK */
3328 if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
3329 _PR_THREAD_LOCK(me);
3330 me->io_pending = PR_FALSE;
3331 me->state = _PR_RUNNING;
3332 _PR_THREAD_UNLOCK(me);
3336 if (me->md.blocked_io_status == 0) {
3337 _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
3345 _PR_MD_TLOCKFILE(PROsfd f)
3348 PRThread *me = _PR_MD_CURRENT_THREAD();
3350 if (me->io_suspended) {
3351 PR_SetError(PR_INVALID_STATE_ERROR, 0);
3355 memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
3357 _PR_THREAD_LOCK(me);
3358 if (_PR_PENDING_INTERRUPT(me)) {
3359 me->flags &= ~_PR_INTERRUPT;
3360 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3361 _PR_THREAD_UNLOCK(me);
3364 me->io_pending = PR_TRUE;
3365 me->state = _PR_IO_WAIT;
3366 _PR_THREAD_UNLOCK(me);
3368 rv = LockFileEx((HANDLE)f,
3369 LOCKFILE_FAIL_IMMEDIATELY|LOCKFILE_EXCLUSIVE_LOCK,
3373 &me->md.overlapped.overlapped);
3374 if (_native_threads_only) {
3375 _PR_THREAD_LOCK(me);
3376 me->io_pending = PR_FALSE;
3377 me->state = _PR_RUNNING;
3378 if (_PR_PENDING_INTERRUPT(me)) {
3379 me->flags &= ~_PR_INTERRUPT;
3380 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3381 _PR_THREAD_UNLOCK(me);
3384 _PR_THREAD_UNLOCK(me);
3387 err = GetLastError();
3388 PR_ASSERT(err != ERROR_IO_PENDING);
3389 _PR_MD_MAP_LOCKF_ERROR(err);
3394 if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
3395 _PR_THREAD_LOCK(me);
3396 me->io_pending = PR_FALSE;
3397 me->state = _PR_RUNNING;
3398 if (_PR_PENDING_INTERRUPT(me)) {
3399 me->flags &= ~_PR_INTERRUPT;
3400 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3401 _PR_THREAD_UNLOCK(me);
3404 _PR_THREAD_UNLOCK(me);
3406 _PR_MD_MAP_LOCKF_ERROR(err);
3409 #ifdef _NEED_351_FILE_LOCKING_HACK
3411 /* If this is NT 3.51 and the file is local, then we won't get a
3412 * completion back from LockFile when it succeeded.
3414 if (_nt_version_gets_lockfile_completion == PR_FALSE) {
3415 if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
3416 _PR_THREAD_LOCK(me);
3417 me->io_pending = PR_FALSE;
3418 me->state = _PR_RUNNING;
3419 if (_PR_PENDING_INTERRUPT(me)) {
3420 me->flags &= ~_PR_INTERRUPT;
3421 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3422 _PR_THREAD_UNLOCK(me);
3425 _PR_THREAD_UNLOCK(me);
3431 #endif /* _NEED_351_FILE_LOCKING_HACK */
3433 if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
3434 _PR_THREAD_LOCK(me);
3435 me->io_pending = PR_FALSE;
3436 me->state = _PR_RUNNING;
3437 if (_PR_PENDING_INTERRUPT(me)) {
3438 me->flags &= ~_PR_INTERRUPT;
3439 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3440 _PR_THREAD_UNLOCK(me);
3443 _PR_THREAD_UNLOCK(me);
3448 if (me->md.blocked_io_status == 0) {
3449 _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
3458 _PR_MD_UNLOCKFILE(PROsfd f)
3461 PRThread *me = _PR_MD_CURRENT_THREAD();
3463 if (me->io_suspended) {
3464 PR_SetError(PR_INVALID_STATE_ERROR, 0);
3468 memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
3470 rv = UnlockFileEx((HANDLE)f,
3474 &me->md.overlapped.overlapped);
3479 int err = GetLastError();
3480 _PR_MD_MAP_LOCKF_ERROR(err);
3486 _PR_MD_MAKE_NONBLOCK(PRFileDesc *f)
3489 * On NT, we either call _md_Associate() or _md_MakeNonblock(),
3490 * depending on whether the socket is blocking or not.
3492 * Once we associate a socket with the io completion port,
3493 * there is no way to disassociate it from the io completion
3494 * port. So we have to call _md_Associate/_md_MakeNonblock
3499 #ifdef _NEED_351_FILE_LOCKING_HACK
3504 ** The following code is a hack to work around a microsoft bug with lockfile.
3505 ** The problem is that on NT 3.51, if LockFileEx() succeeds, you never
3506 ** get a completion back for files that are on local disks. So, we need to
3507 ** know if a file is local or remote so we can tell if we should expect
3510 ** The only way to check if a file is local or remote based on the handle is
3511 ** to get the serial number for the volume it is mounted on and then to
3512 ** compare that with mounted drives. This code caches the volume numbers of
3513 ** fixed disks and does a relatively quick check.
3515 ** Locking: Since the only thing we ever do when multithreaded is a 32bit
3516 ** assignment, we probably don't need locking. It is included just
3519 ** Limitations: Does not work on floppies because they are too slow
3520 ** Unknown if it will work on wierdo 3rd party file systems
3525 /* There can only be 26 drive letters on NT */
3526 #define _PR_MAX_DRIVES 26
3528 _MDLock cachedVolumeLock;
3529 DWORD dwCachedVolumeSerialNumbers[_PR_MAX_DRIVES] = {0};
3530 DWORD dwLastCachedDrive = 0;
3531 DWORD dwRemoveableDrivesToCheck = 0; /* bitmask for removeable drives */
3533 PRBool IsFileLocalInit()
3535 TCHAR lpBuffer[_PR_MAX_DRIVES*5];
3536 DWORD nBufferLength = _PR_MAX_DRIVES*5;
3537 DWORD nBufferNeeded = GetLogicalDriveStrings(0, NULL);
3540 DWORD dwVolumeSerialNumber;
3541 DWORD dwDriveIndex = 0;
3542 DWORD oldmode = (DWORD) -1;
3544 _MD_NEW_LOCK(&cachedVolumeLock);
3546 nBufferNeeded = GetLogicalDriveStrings(nBufferLength, lpBuffer);
3547 if (nBufferNeeded == 0 || nBufferNeeded > nBufferLength)
3550 // Calling GetVolumeInformation on a removeable drive where the
3551 // disk is currently removed will cause a dialog box to the
3552 // console. This is not good.
3553 // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
3556 dwCachedVolumeSerialNumbers[dwDriveIndex] = 0;
3557 oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
3559 // now loop through the logical drives
3560 while(lpBuffer[dwIndex] != TEXT('\0'))
3562 // skip the floppy drives. This is *SLOW*
3563 if ((lpBuffer[dwIndex] == TEXT('A')) || (lpBuffer[dwIndex] == TEXT('B')))
3564 /* Skip over floppies */;
3567 dwDriveIndex = (lpBuffer[dwIndex] - TEXT('A'));
3569 dwDriveType = GetDriveType(&lpBuffer[dwIndex]);
3573 // Ignore these drive types
3577 default: // If the drive type is unknown, ignore it.
3580 // Removable media drives can have different serial numbers
3581 // at different times, so cache the current serial number
3582 // but keep track of them so they can be rechecked if necessary.
3583 case DRIVE_REMOVABLE:
3585 // CDROM is a removable media
3588 // no idea if ramdisks can change serial numbers or not
3589 // but it doesn't hurt to treat them as removable.
3594 // Here is where we keep track of removable drives.
3595 dwRemoveableDrivesToCheck |= 1 << dwDriveIndex;
3597 // removable drives fall through to fixed drives and get cached.
3601 // cache volume serial numbers.
3602 if (GetVolumeInformation(
3605 &dwVolumeSerialNumber,
3606 NULL, NULL, NULL, 0)
3609 if (dwLastCachedDrive < dwDriveIndex)
3610 dwLastCachedDrive = dwDriveIndex;
3611 dwCachedVolumeSerialNumbers[dwDriveIndex] = dwVolumeSerialNumber;
3618 dwIndex += lstrlen(&lpBuffer[dwIndex]) +1;
3621 if (oldmode != (DWORD) -1) {
3622 SetErrorMode(oldmode);
3623 oldmode = (DWORD) -1;
3629 PRInt32 IsFileLocal(HANDLE hFile)
3631 DWORD dwIndex = 0, dwMask;
3632 BY_HANDLE_FILE_INFORMATION Info;
3633 TCHAR szDrive[4] = TEXT("C:\\");
3634 DWORD dwVolumeSerialNumber;
3635 DWORD oldmode = (DWORD) -1;
3636 int rv = _PR_REMOTE_FILE;
3638 if (!GetFileInformationByHandle(hFile, &Info))
3641 // look to see if the volume serial number has been cached.
3642 _MD_LOCK(&cachedVolumeLock);
3643 while(dwIndex <= dwLastCachedDrive)
3644 if (dwCachedVolumeSerialNumbers[dwIndex++] == Info.dwVolumeSerialNumber)
3646 _MD_UNLOCK(&cachedVolumeLock);
3647 return _PR_LOCAL_FILE;
3649 _MD_UNLOCK(&cachedVolumeLock);
3651 // volume serial number not found in the cache. Check removable files.
3652 // removable drives are noted as a bitmask. If the bit associated with
3653 // a specific drive is set, then we should query its volume serial number
3654 // as its possible it has changed.
3655 dwMask = dwRemoveableDrivesToCheck;
3660 while(!(dwMask & 1))
3663 dwMask = dwMask >> 1;
3666 szDrive[0] = TEXT('A')+ (TCHAR) dwIndex;
3668 // Calling GetVolumeInformation on a removeable drive where the
3669 // disk is currently removed will cause a dialog box to the
3670 // console. This is not good.
3671 // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
3674 oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
3676 if (GetVolumeInformation(
3679 &dwVolumeSerialNumber,
3680 NULL, NULL, NULL, 0)
3683 if (dwVolumeSerialNumber == Info.dwVolumeSerialNumber)
3685 _MD_LOCK(&cachedVolumeLock);
3686 if (dwLastCachedDrive < dwIndex)
3687 dwLastCachedDrive = dwIndex;
3688 dwCachedVolumeSerialNumbers[dwIndex] = dwVolumeSerialNumber;
3689 _MD_UNLOCK(&cachedVolumeLock);
3690 rv = _PR_LOCAL_FILE;
3693 if (oldmode != (DWORD) -1) {
3694 SetErrorMode(oldmode);
3695 oldmode = (DWORD) -1;
3698 if (rv == _PR_LOCAL_FILE)
3699 return _PR_LOCAL_FILE;
3702 dwMask = dwMask >> 1;
3705 return _PR_REMOTE_FILE;
3707 #endif /* _NEED_351_FILE_LOCKING_HACK */
3709 PR_IMPLEMENT(PRStatus) PR_NT_CancelIo(PRFileDesc *fd)
3711 PRThread *me = _PR_MD_CURRENT_THREAD();
3715 bottom = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
3716 if (!me->io_suspended || (NULL == bottom) ||
3717 (me->io_fd != bottom->secret->md.osfd)) {
3718 PR_SetError(PR_INVALID_STATE_ERROR, 0);
3722 * The CancelIO operation has to be issued by the same NT thread that
3723 * issued the I/O operation
3725 PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || (me->cpu == me->md.thr_bound_cpu));
3726 if (me->io_pending) {
3727 if (!CancelIo((HANDLE)bottom->secret->md.osfd)) {
3728 PR_SetError(PR_INVALID_STATE_ERROR, GetLastError());
3732 _PR_THREAD_LOCK(me);
3733 fWait = me->io_pending;
3734 me->io_suspended = PR_FALSE;
3735 me->state = _PR_IO_WAIT;
3736 me->md.interrupt_disabled = PR_TRUE;
3737 _PR_THREAD_UNLOCK(me);
3739 _NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
3740 PR_ASSERT(me->io_suspended == PR_FALSE);
3741 PR_ASSERT(me->io_pending == PR_FALSE);
3743 _PR_THREAD_LOCK(me);
3744 me->md.interrupt_disabled = PR_FALSE;
3745 me->md.thr_bound_cpu = NULL;
3746 me->io_suspended = PR_FALSE;
3747 me->io_pending = PR_FALSE;
3748 me->state = _PR_RUNNING;
3749 _PR_THREAD_UNLOCK(me);
3753 static PROsfd _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout)
3755 PROsfd osfd = fd->secret->md.osfd;
3759 struct timeval tv, *tvp;
3762 FD_SET((SOCKET)osfd, &rd);
3763 if (timeout == PR_INTERVAL_NO_TIMEOUT) {
3764 while ((sock = accept(osfd, addr, addrlen)) == -1) {
3765 if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
3766 && (!fd->secret->nonblocking)) {
3767 if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL,
3769 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
3773 _PR_MD_MAP_ACCEPT_ERROR(err);
3777 } else if (timeout == PR_INTERVAL_NO_WAIT) {
3778 if ((sock = accept(osfd, addr, addrlen)) == -1) {
3779 if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
3780 && (!fd->secret->nonblocking)) {
3781 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
3783 _PR_MD_MAP_ACCEPT_ERROR(err);
3788 if ((sock = accept(osfd, addr, addrlen)) == -1) {
3789 if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
3790 && (!fd->secret->nonblocking)) {
3791 tv.tv_sec = PR_IntervalToSeconds(timeout);
3792 tv.tv_usec = PR_IntervalToMicroseconds(
3793 timeout - PR_SecondsToInterval(tv.tv_sec));
3796 rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL, tvp);
3799 } else if (rv == 0) {
3800 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
3802 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
3805 _PR_MD_MAP_ACCEPT_ERROR(err);
3809 return (PROsfd)sock;
3812 static PRInt32 _nt_nonblock_connect(PRFileDesc *fd, struct sockaddr *addr, int addrlen, PRIntervalTime timeout)
3814 PROsfd osfd = fd->secret->md.osfd;
3818 struct timeval tv, *tvp;
3821 if ((rv = connect(osfd, addr, addrlen)) == -1) {
3822 if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
3823 if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
3826 tv.tv_sec = PR_IntervalToSeconds(timeout);
3827 tv.tv_usec = PR_IntervalToMicroseconds(
3828 timeout - PR_SecondsToInterval(tv.tv_sec));
3833 FD_SET((SOCKET)osfd, &wr);
3834 FD_SET((SOCKET)osfd, &ex);
3835 if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wr, &ex,
3837 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
3841 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
3844 /* Call Sleep(0) to work around a Winsock timeing bug. */
3846 if (FD_ISSET((SOCKET)osfd, &ex)) {
3848 if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
3849 (char *) &err, &len) == SOCKET_ERROR) {
3850 _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
3853 _PR_MD_MAP_CONNECT_ERROR(err);
3856 PR_ASSERT(FD_ISSET((SOCKET)osfd, &wr));
3859 _PR_MD_MAP_CONNECT_ERROR(err);
3865 static PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, int flags, PRIntervalTime timeout)
3867 PROsfd osfd = fd->secret->md.osfd;
3869 struct timeval tv, *tvp;
3876 PR_ASSERT(PR_MSG_PEEK == flags);
3879 while ((rv = recv(osfd,buf,len,osflags)) == -1) {
3880 if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
3881 && (!fd->secret->nonblocking)) {
3883 FD_SET((SOCKET)osfd, &rd);
3884 if (timeout == PR_INTERVAL_NO_TIMEOUT) {
3887 tv.tv_sec = PR_IntervalToSeconds(timeout);
3888 tv.tv_usec = PR_IntervalToMicroseconds(
3889 timeout - PR_SecondsToInterval(tv.tv_sec));
3892 if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL,
3894 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
3896 } else if (rv == 0) {
3897 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
3902 _PR_MD_MAP_RECV_ERROR(err);
3909 static PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime timeout)
3911 PROsfd osfd = fd->secret->md.osfd;
3913 struct timeval tv, *tvp;
3915 PRInt32 bytesSent = 0;
3917 while(bytesSent < len) {
3918 while ((rv = send(osfd,buf,len,0)) == -1) {
3919 if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
3920 && (!fd->secret->nonblocking)) {
3921 if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
3924 tv.tv_sec = PR_IntervalToSeconds(timeout);
3925 tv.tv_usec = PR_IntervalToMicroseconds(
3926 timeout - PR_SecondsToInterval(tv.tv_sec));
3930 FD_SET((SOCKET)osfd, &wd);
3931 if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL,
3933 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
3937 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
3941 _PR_MD_MAP_SEND_ERROR(err);
3946 if (fd->secret->nonblocking) {
3949 if (bytesSent < len) {
3950 if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
3953 tv.tv_sec = PR_IntervalToSeconds(timeout);
3954 tv.tv_usec = PR_IntervalToMicroseconds(
3955 timeout - PR_SecondsToInterval(tv.tv_sec));
3959 FD_SET((SOCKET)osfd, &wd);
3960 if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL,
3962 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
3966 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
3974 static PRInt32 _nt_nonblock_writev(PRFileDesc *fd, const PRIOVec *iov, int size, PRIntervalTime timeout)
3980 for (index=0; index<size; index++) {
3981 rv = _nt_nonblock_send(fd, iov[index].iov_base, iov[index].iov_len, timeout);
3984 if ( rv != iov[index].iov_len ) {
3986 if (fd->secret->nonblocking
3987 && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
3994 /* Only a nonblocking socket can have partial sends */
3995 PR_ASSERT(fd->secret->nonblocking);
4003 static PRInt32 _nt_nonblock_sendto(
4004 PRFileDesc *fd, const char *buf, int len,
4005 const struct sockaddr *addr, int addrlen, PRIntervalTime timeout)
4007 PROsfd osfd = fd->secret->md.osfd;
4009 struct timeval tv, *tvp;
4011 PRInt32 bytesSent = 0;
4013 while(bytesSent < len) {
4014 while ((rv = sendto(osfd,buf,len,0, addr, addrlen)) == -1) {
4015 if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
4016 && (!fd->secret->nonblocking)) {
4017 if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
4020 tv.tv_sec = PR_IntervalToSeconds(timeout);
4021 tv.tv_usec = PR_IntervalToMicroseconds(
4022 timeout - PR_SecondsToInterval(tv.tv_sec));
4026 FD_SET((SOCKET)osfd, &wd);
4027 if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL,
4029 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
4033 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
4037 _PR_MD_MAP_SENDTO_ERROR(err);
4042 if (fd->secret->nonblocking) {
4045 if (bytesSent < len) {
4046 if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
4049 tv.tv_sec = PR_IntervalToSeconds(timeout);
4050 tv.tv_usec = PR_IntervalToMicroseconds(
4051 timeout - PR_SecondsToInterval(tv.tv_sec));
4055 FD_SET((SOCKET)osfd, &wd);
4056 if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL,
4058 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
4062 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
4070 static PRInt32 _nt_nonblock_recvfrom(PRFileDesc *fd, char *buf, int len, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout)
4072 PROsfd osfd = fd->secret->md.osfd;
4074 struct timeval tv, *tvp;
4077 while ((rv = recvfrom(osfd,buf,len,0,addr, addrlen)) == -1) {
4078 if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
4079 && (!fd->secret->nonblocking)) {
4080 if (timeout == PR_INTERVAL_NO_TIMEOUT) {
4083 tv.tv_sec = PR_IntervalToSeconds(timeout);
4084 tv.tv_usec = PR_IntervalToMicroseconds(
4085 timeout - PR_SecondsToInterval(tv.tv_sec));
4089 FD_SET((SOCKET)osfd, &rd);
4090 if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL,
4092 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
4094 } else if (rv == 0) {
4095 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
4100 _PR_MD_MAP_RECVFROM_ERROR(err);
4108 * UDP support: the continuation thread functions and recvfrom and sendto.
4111 static void pt_InsertTimedInternal(pt_Continuation *op)
4114 pt_Continuation *t_op = NULL;
4115 PRIntervalTime now = PR_IntervalNow(), op_tmo, qd_tmo;
4118 * If this element operation isn't timed, it gets queued at the
4119 * end of the list (just after pt_tq.tail) and we're
4122 if (PR_INTERVAL_NO_TIMEOUT == op->timeout)
4124 t_op = pt_tq.tail; /* put it at the end */
4129 * The rest of this routine actaully deals with timed ops.
4132 if (NULL != pt_tq.op)
4135 * To find where in the list to put the new operation, form
4136 * the absolute time the operations in question will expire.
4138 * The new operation ('op') will expire at now() + op->timeout.
4140 * The operation that will time out furthest in the future will
4141 * do so at pt_tq.epoch + pt_tq.op->timeout.
4143 * Subsequently earlier timeouts are computed based on the latter
4144 * knowledge by subracting the timeout deltas that are stored in
4145 * the operation list. There are operation[n]->timeout ticks
4146 * between the expiration of operation[n-1] and operation[n].e e
4148 * Therefore, the operation[n-1] will expire operation[n]->timeout
4149 * ticks prior to operation[n].
4151 * This should be easy!
4153 t_op = pt_tq.op; /* running pointer to queued op */
4154 op_tmo = now + op->timeout; /* that's in absolute ticks */
4155 qd_tmo = pt_tq.epoch + t_op->timeout; /* likewise */
4160 * If 'op' expires later than t_op, then insert 'op' just
4161 * ahead of t_op. Otherwise, compute when operation[n-1]
4162 * expires and try again.
4164 * The actual different between the expiriation of 'op'
4165 * and the current operation what becomes the new operaton's
4166 * timeout interval. That interval is also subtracted from
4167 * the interval of the operation immediately following where
4168 * we stick 'op' (unless the next one isn't timed). The new
4169 * timeout assigned to 'op' takes into account the values of
4170 * now() and when the previous intervals were compured.
4172 delta = op_tmo - qd_tmo;
4175 op->timeout += (now - pt_tq.epoch);
4179 qd_tmo -= t_op->timeout; /* previous operaton expiration */
4180 t_op = t_op->prev; /* point to previous operation */
4181 if (NULL != t_op) qd_tmo += t_op->timeout;
4182 } while (NULL != t_op);
4185 * If we got here we backed off the head of the list. That means that
4186 * this timed entry has to go at the head of the list. This is just
4187 * about like having an empty timer list.
4189 delta = op->timeout; /* $$$ is this right? */
4195 * Insert 'op' into the queue just after t_op or if t_op is null,
4196 * at the head of the list.
4198 * If t_op is NULL, the list is currently empty and this is pretty
4204 op->next = pt_tq.head;
4206 if (NULL == pt_tq.tail) pt_tq.tail = op;
4207 else op->next->prev = op;
4212 op->next = t_op->next;
4213 if (NULL != op->prev)
4214 op->prev->next = op;
4215 if (NULL != op->next)
4216 op->next->prev = op;
4217 if (t_op == pt_tq.tail)
4222 * Are we adjusting our epoch, etc? Are we replacing
4223 * what was previously the element due to expire furthest
4224 * out in the future? Is this even a timed operation?
4226 if (PR_INTERVAL_NO_TIMEOUT != op->timeout)
4228 if ((NULL == pt_tq.op) /* we're the one and only */
4229 || (t_op == pt_tq.op)) /* we're replacing */
4236 pt_tq.op_count += 1;
4238 } /* pt_InsertTimedInternal */
4241 * function: pt_FinishTimed
4243 * Takes the finished operation out of the timed queue. It
4244 * notifies the initiating thread that the opertions is
4245 * complete and returns to the caller the value of the next
4246 * operation in the list (or NULL).
4248 static pt_Continuation *pt_FinishTimedInternal(pt_Continuation *op)
4250 pt_Continuation *next;
4252 /* remove this one from the list */
4253 if (NULL == op->prev) pt_tq.head = op->next;
4254 else op->prev->next = op->next;
4255 if (NULL == op->next) pt_tq.tail = op->prev;
4256 else op->next->prev = op->prev;
4258 /* did we happen to hit the timed op? */
4259 if (op == pt_tq.op) pt_tq.op = op->prev;
4262 op->next = op->prev = NULL;
4263 op->status = pt_continuation_done;
4265 pt_tq.op_count -= 1;
4267 pt_debug.continuationsServed += 1;
4269 PR_NotifyCondVar(op->complete);
4272 } /* pt_FinishTimedInternal */
4274 static void ContinuationThread(void *arg)
4276 /* initialization */
4277 fd_set readSet, writeSet, exceptSet;
4279 SOCKET *pollingList = 0; /* list built for polling */
4280 PRIntn pollingListUsed; /* # entries used in the list */
4281 PRIntn pollingListNeeded; /* # entries needed this time */
4282 PRIntn pollingSlotsAllocated = 0; /* # entries available in list */
4283 PRIntervalTime mx_select_ticks = PR_MillisecondsToInterval(PT_DEFAULT_SELECT_MSEC);
4285 /* do some real work */
4291 pt_Continuation *op;
4292 PRIntervalTime now = PR_IntervalNow();
4293 PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
4296 while (NULL == pt_tq.head)
4298 status = PR_WaitCondVar(pt_tq.new_op, PR_INTERVAL_NO_TIMEOUT);
4299 if ((PR_FAILURE == status)
4300 && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
4302 pollingListNeeded = pt_tq.op_count;
4303 PR_Unlock(pt_tq.ml);
4305 /* Okay. We're history */
4306 if ((PR_FAILURE == status)
4307 && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
4310 * We are not holding the pt_tq.ml lock now, so more items may
4311 * get added to pt_tq during this window of time. We hope
4312 * that 10 more spaces in the polling list should be enough.
4317 FD_ZERO(&exceptSet);
4318 pollingListNeeded += 10;
4319 if (pollingListNeeded > pollingSlotsAllocated)
4321 if (NULL != pollingList) PR_DELETE(pollingList);
4322 pollingList = PR_MALLOC(pollingListNeeded * sizeof(PRPollDesc));
4323 PR_ASSERT(NULL != pollingList);
4324 pollingSlotsAllocated = pollingListNeeded;
4328 if (pollingListNeeded > pt_debug.pollingListMax)
4329 pt_debug.pollingListMax = pollingListUsed;
4333 * Build up a polling list.
4334 * This list is sorted on time. Operations that have been
4335 * interrupted are completed and not included in the list.
4336 * There is an assertion that the operation is in progress.
4338 pollingListUsed = 0;
4341 for (op = pt_tq.head; NULL != op;)
4343 if (pt_continuation_abort == op->status)
4345 op->result.code = -1;
4346 op->syserrno = WSAEINTR;
4347 op = pt_FinishTimedInternal(op);
4351 PR_ASSERT(pt_continuation_done != op->status);
4352 op->status = pt_continuation_inprogress;
4353 if (op->event & PR_POLL_READ) {
4354 FD_SET(op->arg1.osfd, &readSet);
4356 if (op->event & PR_POLL_WRITE) {
4357 FD_SET(op->arg1.osfd, &writeSet);
4359 if (op->event & PR_POLL_EXCEPT) {
4360 FD_SET(op->arg1.osfd, &exceptSet);
4362 pollingList[pollingListUsed] = op->arg1.osfd;
4363 pollingListUsed += 1;
4364 if (pollingListUsed == pollingSlotsAllocated) break;
4369 PR_Unlock(pt_tq.ml);
4372 * If 'op' isn't NULL at this point, then we didn't get to
4373 * the end of the list. That means that more items got added
4374 * to the list than we anticipated. So, forget this iteration,
4375 * go around the horn again.
4376 * One would hope this doesn't happen all that often.
4381 pt_debug.predictionsFoiled += 1; /* keep track */
4383 continue; /* make it rethink things */
4386 /* there's a chance that all ops got blown away */
4387 if (NULL == pt_tq.head) continue;
4388 /* if not, we know this is the shortest timeout */
4389 timeout = pt_tq.head->timeout;
4392 * We don't want to wait forever on this poll. So keep
4393 * the interval down. The operations, if they are timed,
4394 * still have to timeout, while those that are not timed
4395 * should persist forever. But they may be aborted. That's
4396 * what this anxiety is all about.
4398 if (timeout > mx_select_ticks) timeout = mx_select_ticks;
4400 if (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout)
4401 pt_tq.head->timeout -= timeout;
4402 tv.tv_sec = PR_IntervalToSeconds(timeout);
4403 tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;
4405 rv = select(0, &readSet, &writeSet, &exceptSet, &tv);
4407 if (0 == rv) /* poll timed out - what about leading op? */
4409 if (0 == pt_tq.head->timeout)
4412 * The leading element of the timed queue has timed
4413 * out. Get rid of it. In any case go around the
4414 * loop again, computing the polling list, checking
4415 * for interrupted operations.
4420 pt_tq.head->result.code = -1;
4421 pt_tq.head->syserrno = WSAETIMEDOUT;
4422 op = pt_FinishTimedInternal(pt_tq.head);
4423 } while ((NULL != op) && (0 == op->timeout));
4424 PR_Unlock(pt_tq.ml);
4429 if (-1 == rv && (WSAGetLastError() == WSAEINTR
4430 || WSAGetLastError() == WSAEINPROGRESS))
4432 continue; /* go around the loop again */
4436 * select() says that something in our list is ready for some more
4437 * action or is an invalid fd. Find it, load up the operation and
4441 PR_ASSERT(rv > 0 || WSAGetLastError() == WSAENOTSOCK);
4445 * $$$ There's a problem here. I'm running the operations list
4446 * and I'm not holding any locks. I don't want to hold the lock
4447 * and do the operation, so this is really messed up..
4449 * This may work out okay. The rule is that only this thread,
4450 * the continuation thread, can remove elements from the list.
4451 * Therefore, the list is at worst, longer than when we built
4455 for (pollIndex = 0; pollIndex < pollingListUsed; ++pollIndex)
4457 PRInt16 revents = 0;
4459 PR_ASSERT(NULL != op);
4462 * This one wants attention. Redo the operation.
4463 * We know that there can only be more elements
4464 * in the op list than we knew about when we created
4465 * the poll list. Therefore, we might have to skip
4466 * a few ops to find the right one to operation on.
4468 while (pollingList[pollIndex] != op->arg1.osfd )
4471 PR_ASSERT(NULL != op);
4474 if (FD_ISSET(op->arg1.osfd, &readSet)) {
4475 revents |= PR_POLL_READ;
4477 if (FD_ISSET(op->arg1.osfd, &writeSet)) {
4478 revents |= PR_POLL_WRITE;
4480 if (FD_ISSET(op->arg1.osfd, &exceptSet)) {
4481 revents |= PR_POLL_EXCEPT;
4485 * Sip over all those not in progress. They'll be
4486 * pruned next time we build a polling list. Call
4487 * the continuation function. If it reports completion,
4488 * finish off the operation.
4490 if (revents && (pt_continuation_inprogress == op->status)
4491 && (op->function(op, revents)))
4494 op = pt_FinishTimedInternal(op);
4495 PR_Unlock(pt_tq.ml);
4499 if (NULL != pollingList) PR_DELETE(pollingList);
4500 } /* ContinuationThread */
4502 static int pt_Continue(pt_Continuation *op)
4505 /* Finish filling in the blank slots */
4506 op->status = pt_continuation_sumbitted;
4507 op->complete = PR_NewCondVar(pt_tq.ml);
4509 PR_Lock(pt_tq.ml); /* we provide the locking */
4511 pt_InsertTimedInternal(op); /* insert in the structure */
4513 PR_NotifyCondVar(pt_tq.new_op); /* notify the continuation thread */
4515 while (pt_continuation_done != op->status) /* wait for completion */
4517 rv = PR_WaitCondVar(op->complete, PR_INTERVAL_NO_TIMEOUT);
4519 * If we get interrupted, we set state the continuation thread will
4520 * see and allow it to finish the I/O operation w/ error. That way
4521 * the rule that only the continuation thread is removing elements
4522 * from the list is still valid.
4524 * Don't call interrupt on the continuation thread. That'll just
4525 * piss him off. He's cycling around at least every mx_select_ticks
4526 * anyhow and should notice the request in there.
4528 if ((PR_FAILURE == rv)
4529 && (PR_PENDING_INTERRUPT_ERROR == PR_GetError()))
4530 op->status = pt_continuation_abort; /* our status */
4533 PR_Unlock(pt_tq.ml); /* we provide the locking */
4535 PR_DestroyCondVar(op->complete);
4537 return op->result.code; /* and the primary answer */
4540 static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents)
4542 PRIntn bytes = sendto(
4543 op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
4544 (struct sockaddr*)op->arg5.addr, sizeof(*(op->arg5.addr)));
4545 op->syserrno = WSAGetLastError();
4546 if (bytes > 0) /* this is progress */
4548 char *bp = op->arg2.buffer;
4549 bp += bytes; /* adjust the buffer pointer */
4550 op->arg2.buffer = bp;
4551 op->result.code += bytes; /* accumulate the number sent */
4552 op->arg3.amount -= bytes; /* and reduce the required count */
4553 return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
4555 else return ((-1 == bytes) && (WSAEWOULDBLOCK == op->syserrno)) ?
4557 } /* pt_sendto_cont */
4559 static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents)
4561 PRIntn addr_len = sizeof(*(op->arg5.addr));
4562 op->result.code = recvfrom(
4563 op->arg1.osfd, op->arg2.buffer, op->arg3.amount,
4564 op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len);
4565 op->syserrno = WSAGetLastError();
4566 return ((-1 == op->result.code) && (WSAEWOULDBLOCK == op->syserrno)) ?
4568 } /* pt_recvfrom_cont */
4570 static PRInt32 pt_SendTo(
4571 SOCKET osfd, const void *buf,
4572 PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
4573 PRIntn addrlen, PRIntervalTime timeout)
4575 PRInt32 bytes = -1, err;
4576 PRBool fNeedContinue = PR_FALSE;
4579 osfd, buf, amount, flags,
4580 (struct sockaddr*)addr, PR_NETADDR_SIZE(addr));
4582 if ((err = WSAGetLastError()) == WSAEWOULDBLOCK)
4583 fNeedContinue = PR_TRUE;
4585 _PR_MD_MAP_SENDTO_ERROR(err);
4587 if (fNeedContinue == PR_TRUE)
4590 op.arg1.osfd = osfd;
4591 op.arg2.buffer = (void*)buf;
4592 op.arg3.amount = amount;
4593 op.arg4.flags = flags;
4594 op.arg5.addr = (PRNetAddr*)addr;
4595 op.timeout = timeout;
4596 op.result.code = 0; /* initialize the number sent */
4597 op.function = pt_sendto_cont;
4598 op.event = PR_POLL_WRITE | PR_POLL_EXCEPT;
4599 bytes = pt_Continue(&op);
4601 WSASetLastError(op.syserrno);
4602 _PR_MD_MAP_SENDTO_ERROR(op.syserrno);
4608 static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
4609 PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout)
4611 PRInt32 bytes = -1, err;
4612 PRBool fNeedContinue = PR_FALSE;
4615 osfd, buf, amount, flags,
4616 (struct sockaddr*)addr, addr_len);
4618 if ((err = WSAGetLastError()) == WSAEWOULDBLOCK)
4619 fNeedContinue = PR_TRUE;
4621 _PR_MD_MAP_RECVFROM_ERROR(err);
4624 if (fNeedContinue == PR_TRUE)
4627 op.arg1.osfd = osfd;
4628 op.arg2.buffer = buf;
4629 op.arg3.amount = amount;
4630 op.arg4.flags = flags;
4631 op.arg5.addr = addr;
4632 op.timeout = timeout;
4633 op.function = pt_recvfrom_cont;
4634 op.event = PR_POLL_READ | PR_POLL_EXCEPT;
4635 bytes = pt_Continue(&op);
4637 WSASetLastError(op.syserrno);
4638 _PR_MD_MAP_RECVFROM_ERROR(op.syserrno);