nspr: import 3.0 RC1 cutoff from CVS
[mozilla-nspr.git] / nsprpub / pr / src / io / prpolevt.c
blobde5f991c56d6b6129c2ffb791320fafe1b528e46
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
13 * License.
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.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
39 *********************************************************************
41 * Pollable events
43 * Pollable events are implemented using layered I/O. The only
44 * I/O methods that are implemented for pollable events are poll
45 * and close. No other methods can be invoked on a pollable
46 * event.
48 * A pipe or socket pair is created and the pollable event layer
49 * is pushed onto the read end. A pointer to the write end is
50 * saved in the PRFilePrivate structure of the pollable event.
52 *********************************************************************
55 #include "prinit.h"
56 #include "prio.h"
57 #include "prmem.h"
58 #include "prerror.h"
59 #include "prlog.h"
61 #ifdef VMS
64 * On OpenVMS we use an event flag instead of a pipe or a socket since
65 * event flags are much more efficient on OpenVMS.
67 #include "pprio.h"
68 #include <lib$routines.h>
69 #include <starlet.h>
70 #include <stsdef.h>
72 PR_IMPLEMENT(PRFileDesc *) PR_NewPollableEvent(void)
74 unsigned int status;
75 int flag = -1;
76 PRFileDesc *event;
79 ** Allocate an event flag and clear it.
81 status = lib$get_ef(&flag);
82 if ((!$VMS_STATUS_SUCCESS(status)) || (flag == -1)) {
83 PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, status);
84 return NULL;
86 sys$clref(flag);
89 ** Give NSPR the event flag's negative value. We do this because our
90 ** select interprets a negative fd as an event flag rather than a
91 ** regular file fd.
93 event = PR_CreateSocketPollFd(-flag);
94 if (NULL == event) {
95 lib$free_ef(&flag);
96 return NULL;
99 return event;
102 PR_IMPLEMENT(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event)
104 int flag = -PR_FileDesc2NativeHandle(event);
105 PR_DestroySocketPollFd(event);
106 lib$free_ef(&flag);
107 return PR_SUCCESS;
110 PR_IMPLEMENT(PRStatus) PR_SetPollableEvent(PRFileDesc *event)
113 ** Just set the event flag.
115 unsigned int status;
116 status = sys$setef(-PR_FileDesc2NativeHandle(event));
117 if (!$VMS_STATUS_SUCCESS(status)) {
118 PR_SetError(PR_INVALID_ARGUMENT_ERROR, status);
119 return PR_FAILURE;
121 return PR_SUCCESS;
124 PR_IMPLEMENT(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event)
127 ** Just clear the event flag.
129 unsigned int status;
130 status = sys$clref(-PR_FileDesc2NativeHandle(event));
131 if (!$VMS_STATUS_SUCCESS(status)) {
132 PR_SetError(PR_INVALID_ARGUMENT_ERROR, status);
133 return PR_FAILURE;
135 return PR_SUCCESS;
138 #elif defined (XP_MAC)
140 #include "primpl.h"
143 * On Mac, local sockets cannot be used, because the networking stack
144 * closes them when the machine goes to sleep. Instead, we'll use a simple
145 * flag.
150 * PRFilePrivate structure for the NSPR pollable events layer
152 typedef struct PRPollableDesc {
153 PRBool gotEvent;
154 PRThread *pollingThread;
155 } PRPollableDesc;
157 static PRStatus PR_CALLBACK _pr_MacPolEvtClose(PRFileDesc *fd);
159 static PRInt16 PR_CALLBACK _pr_MacPolEvtPoll(
160 PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags);
162 static PRIOMethods _pr_mac_polevt_methods = {
163 PR_DESC_LAYERED,
164 _pr_MacPolEvtClose,
165 (PRReadFN)_PR_InvalidInt,
166 (PRWriteFN)_PR_InvalidInt,
167 (PRAvailableFN)_PR_InvalidInt,
168 (PRAvailable64FN)_PR_InvalidInt64,
169 (PRFsyncFN)_PR_InvalidStatus,
170 (PRSeekFN)_PR_InvalidInt,
171 (PRSeek64FN)_PR_InvalidInt64,
172 (PRFileInfoFN)_PR_InvalidStatus,
173 (PRFileInfo64FN)_PR_InvalidStatus,
174 (PRWritevFN)_PR_InvalidInt,
175 (PRConnectFN)_PR_InvalidStatus,
176 (PRAcceptFN)_PR_InvalidDesc,
177 (PRBindFN)_PR_InvalidStatus,
178 (PRListenFN)_PR_InvalidStatus,
179 (PRShutdownFN)_PR_InvalidStatus,
180 (PRRecvFN)_PR_InvalidInt,
181 (PRSendFN)_PR_InvalidInt,
182 (PRRecvfromFN)_PR_InvalidInt,
183 (PRSendtoFN)_PR_InvalidInt,
184 _pr_MacPolEvtPoll,
185 (PRAcceptreadFN)_PR_InvalidInt,
186 (PRTransmitfileFN)_PR_InvalidInt,
187 (PRGetsocknameFN)_PR_InvalidStatus,
188 (PRGetpeernameFN)_PR_InvalidStatus,
189 (PRReservedFN)_PR_InvalidInt,
190 (PRReservedFN)_PR_InvalidInt,
191 (PRGetsocketoptionFN)_PR_InvalidStatus,
192 (PRSetsocketoptionFN)_PR_InvalidStatus,
193 (PRSendfileFN)_PR_InvalidInt,
194 (PRConnectcontinueFN)_PR_InvalidStatus,
195 (PRReservedFN)_PR_InvalidInt,
196 (PRReservedFN)_PR_InvalidInt,
197 (PRReservedFN)_PR_InvalidInt,
198 (PRReservedFN)_PR_InvalidInt
201 static PRDescIdentity _pr_mac_polevt_id;
202 static PRCallOnceType _pr_mac_polevt_once_control;
203 static PRStatus PR_CALLBACK _pr_MacPolEvtInit(void);
205 static PRInt16 PR_CALLBACK _pr_MacPolEvtPoll(
206 PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
208 PRPollableDesc *pollDesc = (PRPollableDesc *)fd->secret;
209 PR_ASSERT(pollDesc);
211 // set the current thread so that we can wake up the poll thread
212 pollDesc->pollingThread = PR_GetCurrentThread();
214 if ((in_flags & PR_POLL_READ) && pollDesc->gotEvent)
215 *out_flags = PR_POLL_READ;
216 else
217 *out_flags = 0;
218 return pollDesc->gotEvent ? 1 : 0;
221 static PRStatus PR_CALLBACK _pr_MacPolEvtInit(void)
223 _pr_mac_polevt_id = PR_GetUniqueIdentity("NSPR pollable events");
224 if (PR_INVALID_IO_LAYER == _pr_mac_polevt_id) {
225 return PR_FAILURE;
227 return PR_SUCCESS;
230 static PRStatus PR_CALLBACK _pr_MacPolEvtClose(PRFileDesc *fd)
232 PRPollableDesc *pollDesc = (PRPollableDesc *)fd->secret;
233 PR_ASSERT(NULL == fd->higher && NULL == fd->lower);
234 PR_ASSERT(pollDesc);
235 PR_DELETE(pollDesc);
236 fd->dtor(fd);
237 return PR_SUCCESS;
240 PR_IMPLEMENT(PRFileDesc *) PR_NewPollableEvent(void)
242 PRFileDesc *event;
243 PRPollableDesc *pollDesc;
245 if (PR_CallOnce(&_pr_mac_polevt_once_control, _pr_MacPolEvtInit) == PR_FAILURE) {
246 return NULL;
249 event = PR_CreateIOLayerStub(_pr_mac_polevt_id, &_pr_mac_polevt_methods);
250 if (NULL == event) {
251 return NULL;
255 ** Allocate an event flag and clear it.
257 pollDesc = PR_NEW(PRPollableDesc);
258 if (!pollDesc) {
259 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
260 goto errorExit;
263 pollDesc->gotEvent = PR_FALSE;
264 pollDesc->pollingThread = NULL;
266 event->secret = (PRFilePrivate*)pollDesc;
267 return event;
269 errorExit:
271 if (event) {
272 PR_DELETE(event->secret);
273 event->dtor(event);
275 return NULL;
278 PR_IMPLEMENT(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event)
280 return PR_Close(event);
283 /* from macsockotpt.c. I wish there was a cleaner way */
284 extern void WakeUpNotifiedThread(PRThread *thread, OTResult result);
286 PR_IMPLEMENT(PRStatus) PR_SetPollableEvent(PRFileDesc *event)
288 PRPollableDesc *pollDesc = (PRPollableDesc *)event->secret;
289 PR_ASSERT(pollDesc);
290 PR_ASSERT(pollDesc->pollingThread->state != _PR_DEAD_STATE);
292 if (pollDesc->pollingThread->state == _PR_DEAD_STATE)
293 return PR_FAILURE;
295 pollDesc->gotEvent = PR_TRUE;
297 if (pollDesc->pollingThread)
298 WakeUpNotifiedThread(pollDesc->pollingThread, noErr);
300 return PR_SUCCESS;
303 PR_IMPLEMENT(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event)
305 PRPollableDesc *pollDesc = (PRPollableDesc *)event->secret;
306 PRStatus status;
307 PR_ASSERT(pollDesc);
310 FIXME: Danger Will Robinson!
312 The current implementation of PR_WaitForPollableEvent is somewhat
313 bogus; it makes the assumption that, in Mozilla, this will only
314 ever be called when PR_Poll has returned, telling us that an
315 event has been set.
318 PR_ASSERT(pollDesc->gotEvent);
320 status = (pollDesc->gotEvent) ? PR_SUCCESS : PR_FAILURE;
321 pollDesc->gotEvent = PR_FALSE;
322 return status;
325 #else /* VMS */
328 * These internal functions are declared in primpl.h,
329 * but we can't include primpl.h because the definition
330 * of struct PRFilePrivate in this file (for the pollable
331 * event layer) will conflict with the definition of
332 * struct PRFilePrivate in primpl.h (for the NSPR layer).
334 extern PRIntn _PR_InvalidInt(void);
335 extern PRInt64 _PR_InvalidInt64(void);
336 extern PRStatus _PR_InvalidStatus(void);
337 extern PRFileDesc *_PR_InvalidDesc(void);
340 * PRFilePrivate structure for the NSPR pollable events layer
342 struct PRFilePrivate {
343 PRFileDesc *writeEnd; /* the write end of the pipe/socketpair */
346 static PRStatus PR_CALLBACK _pr_PolEvtClose(PRFileDesc *fd);
348 static PRInt16 PR_CALLBACK _pr_PolEvtPoll(
349 PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags);
351 static PRIOMethods _pr_polevt_methods = {
352 PR_DESC_LAYERED,
353 _pr_PolEvtClose,
354 (PRReadFN)_PR_InvalidInt,
355 (PRWriteFN)_PR_InvalidInt,
356 (PRAvailableFN)_PR_InvalidInt,
357 (PRAvailable64FN)_PR_InvalidInt64,
358 (PRFsyncFN)_PR_InvalidStatus,
359 (PRSeekFN)_PR_InvalidInt,
360 (PRSeek64FN)_PR_InvalidInt64,
361 (PRFileInfoFN)_PR_InvalidStatus,
362 (PRFileInfo64FN)_PR_InvalidStatus,
363 (PRWritevFN)_PR_InvalidInt,
364 (PRConnectFN)_PR_InvalidStatus,
365 (PRAcceptFN)_PR_InvalidDesc,
366 (PRBindFN)_PR_InvalidStatus,
367 (PRListenFN)_PR_InvalidStatus,
368 (PRShutdownFN)_PR_InvalidStatus,
369 (PRRecvFN)_PR_InvalidInt,
370 (PRSendFN)_PR_InvalidInt,
371 (PRRecvfromFN)_PR_InvalidInt,
372 (PRSendtoFN)_PR_InvalidInt,
373 _pr_PolEvtPoll,
374 (PRAcceptreadFN)_PR_InvalidInt,
375 (PRTransmitfileFN)_PR_InvalidInt,
376 (PRGetsocknameFN)_PR_InvalidStatus,
377 (PRGetpeernameFN)_PR_InvalidStatus,
378 (PRReservedFN)_PR_InvalidInt,
379 (PRReservedFN)_PR_InvalidInt,
380 (PRGetsocketoptionFN)_PR_InvalidStatus,
381 (PRSetsocketoptionFN)_PR_InvalidStatus,
382 (PRSendfileFN)_PR_InvalidInt,
383 (PRConnectcontinueFN)_PR_InvalidStatus,
384 (PRReservedFN)_PR_InvalidInt,
385 (PRReservedFN)_PR_InvalidInt,
386 (PRReservedFN)_PR_InvalidInt,
387 (PRReservedFN)_PR_InvalidInt
390 static PRDescIdentity _pr_polevt_id;
391 static PRCallOnceType _pr_polevt_once_control;
392 static PRStatus PR_CALLBACK _pr_PolEvtInit(void);
394 static PRInt16 PR_CALLBACK _pr_PolEvtPoll(
395 PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
397 return (fd->lower->methods->poll)(fd->lower, in_flags, out_flags);
400 static PRStatus PR_CALLBACK _pr_PolEvtInit(void)
402 _pr_polevt_id = PR_GetUniqueIdentity("NSPR pollable events");
403 if (PR_INVALID_IO_LAYER == _pr_polevt_id) {
404 return PR_FAILURE;
406 return PR_SUCCESS;
409 #if !defined(XP_UNIX)
410 #define USE_TCP_SOCKETPAIR
411 #endif
413 PR_IMPLEMENT(PRFileDesc *) PR_NewPollableEvent(void)
415 PRFileDesc *event;
416 PRFileDesc *fd[2]; /* fd[0] is the read end; fd[1] is the write end */
417 #ifdef USE_TCP_SOCKETPAIR
418 PRSocketOptionData socket_opt;
419 PRStatus rv;
420 #endif
422 fd[0] = fd[1] = NULL;
424 if (PR_CallOnce(&_pr_polevt_once_control, _pr_PolEvtInit) == PR_FAILURE) {
425 return NULL;
428 event = PR_CreateIOLayerStub(_pr_polevt_id, &_pr_polevt_methods);
429 if (NULL == event) {
430 goto errorExit;
432 event->secret = PR_NEW(PRFilePrivate);
433 if (event->secret == NULL) {
434 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
435 goto errorExit;
438 #ifndef USE_TCP_SOCKETPAIR
439 if (PR_CreatePipe(&fd[0], &fd[1]) == PR_FAILURE) {
440 fd[0] = fd[1] = NULL;
441 goto errorExit;
443 #else
444 if (PR_NewTCPSocketPair(fd) == PR_FAILURE) {
445 fd[0] = fd[1] = NULL;
446 goto errorExit;
449 * set the TCP_NODELAY option to reduce notification latency
451 socket_opt.option = PR_SockOpt_NoDelay;
452 socket_opt.value.no_delay = PR_TRUE;
453 rv = PR_SetSocketOption(fd[1], &socket_opt);
454 PR_ASSERT(PR_SUCCESS == rv);
455 #endif
457 event->secret->writeEnd = fd[1];
458 if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, event) == PR_FAILURE) {
459 goto errorExit;
462 return fd[0];
464 errorExit:
465 if (fd[0]) {
466 PR_Close(fd[0]);
467 PR_Close(fd[1]);
469 if (event) {
470 PR_DELETE(event->secret);
471 event->dtor(event);
473 return NULL;
476 static PRStatus PR_CALLBACK _pr_PolEvtClose(PRFileDesc *fd)
478 PRFileDesc *event;
480 event = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
481 PR_ASSERT(NULL == event->higher && NULL == event->lower);
482 PR_Close(fd);
483 PR_Close(event->secret->writeEnd);
484 PR_DELETE(event->secret);
485 event->dtor(event);
486 return PR_SUCCESS;
489 PR_IMPLEMENT(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event)
491 return PR_Close(event);
494 static const char magicChar = '\x38';
496 PR_IMPLEMENT(PRStatus) PR_SetPollableEvent(PRFileDesc *event)
498 if (PR_Write(event->secret->writeEnd, &magicChar, 1) != 1) {
499 return PR_FAILURE;
501 return PR_SUCCESS;
504 PR_IMPLEMENT(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event)
506 char buf[1024];
507 PRInt32 nBytes;
508 #ifdef DEBUG
509 PRIntn i;
510 #endif
512 nBytes = PR_Read(event->lower, buf, sizeof(buf));
513 if (nBytes == -1) {
514 return PR_FAILURE;
517 #ifdef DEBUG
519 * Make sure people do not write to the pollable event fd
520 * directly.
522 for (i = 0; i < nBytes; i++) {
523 PR_ASSERT(buf[i] == magicChar);
525 #endif
527 return PR_SUCCESS;
530 #endif /* VMS */