Back to index

lightning-sunbird  0.9+nobinonly
prpolevt.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039  *********************************************************************
00040  *
00041  * Pollable events
00042  *
00043  * Pollable events are implemented using layered I/O.  The only
00044  * I/O methods that are implemented for pollable events are poll
00045  * and close.  No other methods can be invoked on a pollable
00046  * event.
00047  *
00048  * A pipe or socket pair is created and the pollable event layer
00049  * is pushed onto the read end.  A pointer to the write end is
00050  * saved in the PRFilePrivate structure of the pollable event.
00051  *
00052  *********************************************************************
00053  */
00054 
00055 #include "prinit.h"
00056 #include "prio.h"
00057 #include "prmem.h"
00058 #include "prerror.h"
00059 #include "prlog.h"
00060 
00061 #ifdef VMS
00062 
00063 /*
00064  * On OpenVMS we use an event flag instead of a pipe or a socket since
00065  * event flags are much more efficient on OpenVMS.
00066  */
00067 #include "pprio.h"
00068 #include <lib$routines.h>
00069 #include <starlet.h>
00070 #include <stsdef.h>
00071 
00072 PR_IMPLEMENT(PRFileDesc *) PR_NewPollableEvent(void)
00073 {
00074     unsigned int status;
00075     int flag = -1;
00076     PRFileDesc *event;
00077 
00078     /*
00079     ** Allocate an event flag and clear it.
00080     */
00081     status = lib$get_ef(&flag);
00082     if ((!$VMS_STATUS_SUCCESS(status)) || (flag == -1)) {
00083         PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, status);
00084         return NULL;
00085     }
00086     sys$clref(flag);
00087 
00088     /*
00089     ** Give NSPR the event flag's negative value. We do this because our
00090     ** select interprets a negative fd as an event flag rather than a
00091     ** regular file fd.
00092     */
00093     event = PR_CreateSocketPollFd(-flag);
00094     if (NULL == event) {
00095         lib$free_ef(&flag);
00096         return NULL;
00097     }
00098 
00099     return event;
00100 }
00101 
00102 PR_IMPLEMENT(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event)
00103 {
00104     int flag = -PR_FileDesc2NativeHandle(event);
00105     PR_DestroySocketPollFd(event);
00106     lib$free_ef(&flag);
00107     return PR_SUCCESS;
00108 }
00109 
00110 PR_IMPLEMENT(PRStatus) PR_SetPollableEvent(PRFileDesc *event)
00111 {
00112     /*
00113     ** Just set the event flag.
00114     */
00115     unsigned int status;
00116     status = sys$setef(-PR_FileDesc2NativeHandle(event));
00117     if (!$VMS_STATUS_SUCCESS(status)) {
00118         PR_SetError(PR_INVALID_ARGUMENT_ERROR, status);
00119         return PR_FAILURE;
00120     }
00121     return PR_SUCCESS;
00122 }
00123 
00124 PR_IMPLEMENT(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event)
00125 {
00126     /*
00127     ** Just clear the event flag.
00128     */
00129     unsigned int status;
00130     status = sys$clref(-PR_FileDesc2NativeHandle(event));
00131     if (!$VMS_STATUS_SUCCESS(status)) {
00132         PR_SetError(PR_INVALID_ARGUMENT_ERROR, status);
00133         return PR_FAILURE;
00134     }
00135     return PR_SUCCESS;
00136 }
00137 
00138 #elif defined (XP_MAC)
00139 
00140 #include "primpl.h"
00141 
00142 /*
00143  * On Mac, local sockets cannot be used, because the networking stack
00144  * closes them when the machine goes to sleep. Instead, we'll use a simple
00145  * flag.
00146  */
00147 
00148 
00149 /*
00150  * PRFilePrivate structure for the NSPR pollable events layer
00151  */
00152 typedef struct PRPollableDesc {
00153     PRBool      gotEvent;
00154     PRThread    *pollingThread;
00155 } PRPollableDesc;
00156 
00157 static PRStatus PR_CALLBACK _pr_MacPolEvtClose(PRFileDesc *fd);
00158 
00159 static PRInt16 PR_CALLBACK _pr_MacPolEvtPoll(
00160     PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags);
00161 
00162 static PRIOMethods _pr_mac_polevt_methods = {
00163     PR_DESC_LAYERED,
00164     _pr_MacPolEvtClose,
00165     (PRReadFN)_PR_InvalidInt,
00166     (PRWriteFN)_PR_InvalidInt,
00167     (PRAvailableFN)_PR_InvalidInt,
00168     (PRAvailable64FN)_PR_InvalidInt64,
00169     (PRFsyncFN)_PR_InvalidStatus,
00170     (PRSeekFN)_PR_InvalidInt,
00171     (PRSeek64FN)_PR_InvalidInt64,
00172     (PRFileInfoFN)_PR_InvalidStatus,
00173     (PRFileInfo64FN)_PR_InvalidStatus,
00174     (PRWritevFN)_PR_InvalidInt,        
00175     (PRConnectFN)_PR_InvalidStatus,        
00176     (PRAcceptFN)_PR_InvalidDesc,        
00177     (PRBindFN)_PR_InvalidStatus,        
00178     (PRListenFN)_PR_InvalidStatus,        
00179     (PRShutdownFN)_PR_InvalidStatus,    
00180     (PRRecvFN)_PR_InvalidInt,        
00181     (PRSendFN)_PR_InvalidInt,        
00182     (PRRecvfromFN)_PR_InvalidInt,    
00183     (PRSendtoFN)_PR_InvalidInt,        
00184     _pr_MacPolEvtPoll,
00185     (PRAcceptreadFN)_PR_InvalidInt,   
00186     (PRTransmitfileFN)_PR_InvalidInt, 
00187     (PRGetsocknameFN)_PR_InvalidStatus,    
00188     (PRGetpeernameFN)_PR_InvalidStatus,    
00189     (PRReservedFN)_PR_InvalidInt,    
00190     (PRReservedFN)_PR_InvalidInt,    
00191     (PRGetsocketoptionFN)_PR_InvalidStatus,
00192     (PRSetsocketoptionFN)_PR_InvalidStatus,
00193     (PRSendfileFN)_PR_InvalidInt, 
00194     (PRConnectcontinueFN)_PR_InvalidStatus, 
00195     (PRReservedFN)_PR_InvalidInt, 
00196     (PRReservedFN)_PR_InvalidInt, 
00197     (PRReservedFN)_PR_InvalidInt, 
00198     (PRReservedFN)_PR_InvalidInt
00199 };
00200 
00201 static PRDescIdentity _pr_mac_polevt_id;
00202 static PRCallOnceType _pr_mac_polevt_once_control;
00203 static PRStatus PR_CALLBACK _pr_MacPolEvtInit(void);
00204 
00205 static PRInt16 PR_CALLBACK _pr_MacPolEvtPoll(
00206     PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
00207 {
00208     PRPollableDesc   *pollDesc = (PRPollableDesc *)fd->secret;
00209     PR_ASSERT(pollDesc);
00210 
00211     // set the current thread so that we can wake up the poll thread
00212     pollDesc->pollingThread = PR_GetCurrentThread();
00213 
00214     if ((in_flags & PR_POLL_READ) && pollDesc->gotEvent)
00215         *out_flags = PR_POLL_READ;
00216     else
00217         *out_flags = 0;
00218     return pollDesc->gotEvent ? 1 : 0;
00219 }
00220 
00221 static PRStatus PR_CALLBACK _pr_MacPolEvtInit(void)
00222 {
00223     _pr_mac_polevt_id = PR_GetUniqueIdentity("NSPR pollable events");
00224     if (PR_INVALID_IO_LAYER == _pr_mac_polevt_id) {
00225         return PR_FAILURE;
00226     }
00227     return PR_SUCCESS;
00228 }
00229 
00230 static PRStatus PR_CALLBACK _pr_MacPolEvtClose(PRFileDesc *fd)
00231 {
00232     PRPollableDesc   *pollDesc = (PRPollableDesc *)fd->secret;
00233     PR_ASSERT(NULL == fd->higher && NULL == fd->lower);
00234     PR_ASSERT(pollDesc);
00235     PR_DELETE(pollDesc);
00236     fd->dtor(fd);
00237     return PR_SUCCESS;
00238 }
00239 
00240 PR_IMPLEMENT(PRFileDesc *) PR_NewPollableEvent(void)
00241 {
00242     PRFileDesc      *event;
00243     PRPollableDesc   *pollDesc;
00244     
00245     if (PR_CallOnce(&_pr_mac_polevt_once_control, _pr_MacPolEvtInit) == PR_FAILURE) {
00246         return NULL;
00247     }
00248 
00249     event = PR_CreateIOLayerStub(_pr_mac_polevt_id, &_pr_mac_polevt_methods);
00250     if (NULL == event) {
00251         return NULL;
00252     } 
00253 
00254     /*
00255     ** Allocate an event flag and clear it.
00256     */
00257     pollDesc = PR_NEW(PRPollableDesc);
00258     if (!pollDesc) {
00259         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00260         goto errorExit;
00261     }
00262 
00263     pollDesc->gotEvent = PR_FALSE;
00264     pollDesc->pollingThread = NULL;
00265     
00266     event->secret = (PRFilePrivate*)pollDesc;
00267     return event;
00268 
00269 errorExit:
00270 
00271     if (event) {
00272         PR_DELETE(event->secret);
00273         event->dtor(event);
00274     }
00275     return NULL;
00276 }
00277 
00278 PR_IMPLEMENT(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event)
00279 {
00280     return PR_Close(event);
00281 }
00282 
00283 /* from macsockotpt.c. I wish there was a cleaner way */
00284 extern void WakeUpNotifiedThread(PRThread *thread, OTResult result);
00285 
00286 PR_IMPLEMENT(PRStatus) PR_SetPollableEvent(PRFileDesc *event)
00287 {
00288     PRPollableDesc   *pollDesc = (PRPollableDesc *)event->secret;
00289     PR_ASSERT(pollDesc);
00290     PR_ASSERT(pollDesc->pollingThread->state != _PR_DEAD_STATE);
00291     
00292     if (pollDesc->pollingThread->state == _PR_DEAD_STATE)
00293       return PR_FAILURE;
00294 
00295     pollDesc->gotEvent = PR_TRUE;
00296     
00297     if (pollDesc->pollingThread)
00298         WakeUpNotifiedThread(pollDesc->pollingThread, noErr);
00299         
00300     return PR_SUCCESS;
00301 }
00302 
00303 PR_IMPLEMENT(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event)
00304 {
00305     PRPollableDesc   *pollDesc = (PRPollableDesc *)event->secret;
00306     PRStatus status;    
00307     PR_ASSERT(pollDesc);
00308     
00309     /*
00310       FIXME: Danger Will Robinson!
00311       
00312       The current implementation of PR_WaitForPollableEvent is somewhat
00313       bogus; it makes the assumption that, in Mozilla, this will only
00314       ever be called when PR_Poll has returned, telling us that an
00315       event has been set.
00316     */
00317     
00318     PR_ASSERT(pollDesc->gotEvent);
00319     
00320     status = (pollDesc->gotEvent) ? PR_SUCCESS : PR_FAILURE;
00321     pollDesc->gotEvent = PR_FALSE;
00322     return status;    
00323 }
00324 
00325 #else /* VMS */
00326 
00327 /*
00328  * These internal functions are declared in primpl.h,
00329  * but we can't include primpl.h because the definition
00330  * of struct PRFilePrivate in this file (for the pollable
00331  * event layer) will conflict with the definition of
00332  * struct PRFilePrivate in primpl.h (for the NSPR layer).
00333  */
00334 extern PRIntn _PR_InvalidInt(void);
00335 extern PRInt64 _PR_InvalidInt64(void);
00336 extern PRStatus _PR_InvalidStatus(void);
00337 extern PRFileDesc *_PR_InvalidDesc(void);
00338 
00339 /*
00340  * PRFilePrivate structure for the NSPR pollable events layer
00341  */
00342 struct PRFilePrivate {
00343     PRFileDesc *writeEnd;  /* the write end of the pipe/socketpair */
00344 };
00345 
00346 static PRStatus PR_CALLBACK _pr_PolEvtClose(PRFileDesc *fd);
00347 
00348 static PRInt16 PR_CALLBACK _pr_PolEvtPoll(
00349     PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags);
00350 
00351 static PRIOMethods _pr_polevt_methods = {
00352     PR_DESC_LAYERED,
00353     _pr_PolEvtClose,
00354     (PRReadFN)_PR_InvalidInt,
00355     (PRWriteFN)_PR_InvalidInt,
00356     (PRAvailableFN)_PR_InvalidInt,
00357     (PRAvailable64FN)_PR_InvalidInt64,
00358     (PRFsyncFN)_PR_InvalidStatus,
00359     (PRSeekFN)_PR_InvalidInt,
00360     (PRSeek64FN)_PR_InvalidInt64,
00361     (PRFileInfoFN)_PR_InvalidStatus,
00362     (PRFileInfo64FN)_PR_InvalidStatus,
00363     (PRWritevFN)_PR_InvalidInt,        
00364     (PRConnectFN)_PR_InvalidStatus,        
00365     (PRAcceptFN)_PR_InvalidDesc,        
00366     (PRBindFN)_PR_InvalidStatus,        
00367     (PRListenFN)_PR_InvalidStatus,        
00368     (PRShutdownFN)_PR_InvalidStatus,    
00369     (PRRecvFN)_PR_InvalidInt,        
00370     (PRSendFN)_PR_InvalidInt,        
00371     (PRRecvfromFN)_PR_InvalidInt,    
00372     (PRSendtoFN)_PR_InvalidInt,        
00373     _pr_PolEvtPoll,
00374     (PRAcceptreadFN)_PR_InvalidInt,   
00375     (PRTransmitfileFN)_PR_InvalidInt, 
00376     (PRGetsocknameFN)_PR_InvalidStatus,    
00377     (PRGetpeernameFN)_PR_InvalidStatus,    
00378     (PRReservedFN)_PR_InvalidInt,    
00379     (PRReservedFN)_PR_InvalidInt,    
00380     (PRGetsocketoptionFN)_PR_InvalidStatus,
00381     (PRSetsocketoptionFN)_PR_InvalidStatus,
00382     (PRSendfileFN)_PR_InvalidInt, 
00383     (PRConnectcontinueFN)_PR_InvalidStatus, 
00384     (PRReservedFN)_PR_InvalidInt, 
00385     (PRReservedFN)_PR_InvalidInt, 
00386     (PRReservedFN)_PR_InvalidInt, 
00387     (PRReservedFN)_PR_InvalidInt
00388 };
00389 
00390 static PRDescIdentity _pr_polevt_id;
00391 static PRCallOnceType _pr_polevt_once_control;
00392 static PRStatus PR_CALLBACK _pr_PolEvtInit(void);
00393 
00394 static PRInt16 PR_CALLBACK _pr_PolEvtPoll(
00395     PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
00396 {
00397     return (fd->lower->methods->poll)(fd->lower, in_flags, out_flags);
00398 }
00399 
00400 static PRStatus PR_CALLBACK _pr_PolEvtInit(void)
00401 {
00402     _pr_polevt_id = PR_GetUniqueIdentity("NSPR pollable events");
00403     if (PR_INVALID_IO_LAYER == _pr_polevt_id) {
00404         return PR_FAILURE;
00405     }
00406     return PR_SUCCESS;
00407 }
00408 
00409 #if !defined(XP_UNIX)
00410 #define USE_TCP_SOCKETPAIR
00411 #endif
00412 
00413 PR_IMPLEMENT(PRFileDesc *) PR_NewPollableEvent(void)
00414 {
00415     PRFileDesc *event;
00416     PRFileDesc *fd[2]; /* fd[0] is the read end; fd[1] is the write end */
00417 #ifdef USE_TCP_SOCKETPAIR
00418     PRSocketOptionData socket_opt;
00419     PRStatus rv;
00420 #endif
00421 
00422     fd[0] = fd[1] = NULL;
00423 
00424     if (PR_CallOnce(&_pr_polevt_once_control, _pr_PolEvtInit) == PR_FAILURE) {
00425         return NULL;
00426     }
00427 
00428     event = PR_CreateIOLayerStub(_pr_polevt_id, &_pr_polevt_methods);
00429     if (NULL == event) {
00430         goto errorExit;
00431     } 
00432     event->secret = PR_NEW(PRFilePrivate);
00433     if (event->secret == NULL) {
00434         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00435         goto errorExit;
00436     }
00437 
00438 #ifndef USE_TCP_SOCKETPAIR
00439     if (PR_CreatePipe(&fd[0], &fd[1]) == PR_FAILURE) {
00440         fd[0] = fd[1] = NULL;
00441         goto errorExit;
00442     }
00443 #else
00444     if (PR_NewTCPSocketPair(fd) == PR_FAILURE) {
00445         fd[0] = fd[1] = NULL;
00446         goto errorExit;
00447     }
00448        /*
00449         * set the TCP_NODELAY option to reduce notification latency
00450         */
00451     socket_opt.option = PR_SockOpt_NoDelay;
00452     socket_opt.value.no_delay = PR_TRUE;
00453     rv = PR_SetSocketOption(fd[1], &socket_opt);
00454     PR_ASSERT(PR_SUCCESS == rv);
00455 #endif
00456 
00457     event->secret->writeEnd = fd[1];
00458     if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, event) == PR_FAILURE) {
00459         goto errorExit;
00460     }
00461 
00462     return fd[0];
00463 
00464 errorExit:
00465     if (fd[0]) {
00466         PR_Close(fd[0]);
00467         PR_Close(fd[1]);
00468     }
00469     if (event) {
00470         PR_DELETE(event->secret);
00471         event->dtor(event);
00472     }
00473     return NULL;
00474 }
00475 
00476 static PRStatus PR_CALLBACK _pr_PolEvtClose(PRFileDesc *fd)
00477 {
00478     PRFileDesc *event;
00479 
00480     event = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
00481     PR_ASSERT(NULL == event->higher && NULL == event->lower);
00482     PR_Close(fd);
00483     PR_Close(event->secret->writeEnd);
00484     PR_DELETE(event->secret);
00485     event->dtor(event);
00486     return PR_SUCCESS;
00487 }
00488 
00489 PR_IMPLEMENT(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event)
00490 {
00491     return PR_Close(event);
00492 }
00493 
00494 static const char magicChar = '\x38';
00495 
00496 PR_IMPLEMENT(PRStatus) PR_SetPollableEvent(PRFileDesc *event)
00497 {
00498     if (PR_Write(event->secret->writeEnd, &magicChar, 1) != 1) {
00499         return PR_FAILURE;
00500     }
00501     return PR_SUCCESS;
00502 }
00503 
00504 PR_IMPLEMENT(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event)
00505 {
00506     char buf[1024];
00507     PRInt32 nBytes;
00508 #ifdef DEBUG
00509     PRIntn i;
00510 #endif
00511 
00512     nBytes = PR_Read(event->lower, buf, sizeof(buf));
00513     if (nBytes == -1) {
00514         return PR_FAILURE;
00515     }
00516 
00517 #ifdef DEBUG
00518     /*
00519      * Make sure people do not write to the pollable event fd
00520      * directly.
00521      */
00522     for (i = 0; i < nBytes; i++) {
00523         PR_ASSERT(buf[i] == magicChar);
00524     }
00525 #endif
00526 
00527     return PR_SUCCESS;
00528 }
00529 
00530 #endif /* VMS */