Annotation of libwww/Library/src/HTEvtLst.c, revision 2.1
2.1 ! frystyk 1: /* HTEvntrg.c
! 2: ** EVENT MANAGER
! 3: **
! 4: ** (c) COPYRIGHT MIT 1995.
! 5: ** Please first read the full copyright statement in the file COPYRIGH.
! 6: ** @(#) $Id: HTEvtLst.c,v 1.1.2.10 1996/11/26 23:22:13 eric Exp $
! 7: **
! 8: ** Updated HTEvent module
! 9: ** This new module combines the functions of the old HTEvent module and
! 10: ** the HTThread module. We retain the old HTThread module, but it
! 11: ** consists of calls to the HTEvent interfaces
! 12: **
! 13: ** Authors:
! 14: ** HFN Henrik Frystyk <frystyk@w3.org>
! 15: ** CLB Charlie Brooks <cbrooks@osf.org>
! 16: ** Bugs
! 17: **
! 18: */
! 19:
! 20: /* WSAAsyncSelect and windows app stuff need the following definitions:
! 21: * WWW_WIN_ASYNC - enable WSAAsyncSelect instead of select
! 22: * _WIN23 - win32 libararies - may be window or console app
! 23: * _WINSOCKAPI_ - using WINSOCK.DLL - not necessarily the async routines.
! 24: * _CONSOLE - the console app for NT
! 25: *
! 26: * first pass: EGP - 10/26/95
! 27: */
! 28:
! 29: /* Implementation dependent include files */
! 30: #include "sysdep.h"
! 31: #include "WWWUtil.h"
! 32: #include "WWWCore.h"
! 33: #include "HTReqMan.h"
! 34: #include "HTTimer.h"
! 35: #include "HTEvtLst.h" /* Implemented here */
! 36:
! 37: /* Type definitions and global variables etc. local to this module */
! 38: #define PRIME_TABLE_SIZE 67
! 39: #define MILLI_PER_SECOND 1000
! 40: #define HASH(s) ((s) % PRIME_TABLE_SIZE)
! 41:
! 42: typedef struct rq_t RQ;
! 43: struct rq_t {
! 44: RQ * next ;
! 45: SOCKET s ; /* our socket */
! 46: BOOL unregister; /* notify app when completely unregistered */
! 47: HTEvent * events[3]; /* event parameters for read, write, oob */
! 48: HTTimer * timeouts[3]; /* timeout for each of the events */
! 49: };
! 50:
! 51: typedef enum _RQ_action {
! 52: RQ_mayCreate,
! 53: RQ_find
! 54: } RQ_action;
! 55:
! 56: PRIVATE RQ * table[PRIME_TABLE_SIZE];
! 57: PRIVATE SOCKET max_sock = 0 ; /* max socket value in use */
! 58:
! 59: PRIVATE fd_set FdArray[HTEvent_TYPES];
! 60: PRIVATE fd_set all_fds ; /* any descriptor at all */
! 61:
! 62: PRIVATE int HTEndLoop = 0; /* If !0 then exit event loop */
! 63:
! 64: /* ------------------------------------------------------------------------- */
! 65:
! 66: PRIVATE RQ * RQ_get(SOCKET s, RQ_action action)
! 67: {
! 68: RQ * rqp;
! 69: RQ * last = NULL;
! 70: long v = HASH(s);
! 71: for (rqp = table[v]; rqp; rqp = rqp->next) {
! 72: if (rqp->s == s) {
! 73: return rqp;
! 74: }
! 75: last = rqp; /* to set next pointer when creating new */
! 76: }
! 77: if (action == RQ_mayCreate) {
! 78: if ((rqp = (RQ *) HT_CALLOC(1, sizeof(RQ))) == NULL)
! 79: HT_OUTOFMEM("HTEventList_register");
! 80: if (last) last->next = rqp;
! 81: else table[v] = rqp;
! 82: rqp->s = s;
! 83: return rqp;
! 84: }
! 85: return NULL;
! 86: }
! 87:
! 88: /*
! 89: ** A simple debug function that dumps all the socket arrays
! 90: ** as trace messages
! 91: */
! 92: PRIVATE void __DumpFDSet( fd_set * fdp, const char * str)
! 93: {
! 94: SOCKET s ;
! 95: #ifdef _WINSOCKAPI_
! 96: unsigned ui ;
! 97: #endif
! 98: if (THD_TRACE) {
! 99: HTTrace("Event....... Dumping %s file descriptor set\n", str);
! 100: #ifdef _WINSOCKAPI_
! 101: for (ui = 0 ; ui < fdp->fd_count; ui++) {
! 102: s = all_fds.fd_array[ui] ;
! 103: #else
! 104: for (s = 0 ; s <= max_sock; s++) {
! 105: if (FD_ISSET(s, fdp))
! 106: #endif
! 107: {
! 108: HTTrace("%4d\n", s);
! 109: }
! 110: } /* for */
! 111: } /* if */
! 112: return ;
! 113: }
! 114:
! 115: /* ------------------------------------------------------------------------- */
! 116:
! 117: PRIVATE int EventListTimerHandler (HTTimer * timer, void * param)
! 118: {
! 119: RQ * rqp = (RQ *)param;
! 120: HTEvent * event;
! 121: if (rqp->timeouts[HTEvent_INDEX(HTEvent_READ)] == timer) {
! 122: event = rqp->events[HTEvent_INDEX(HTEvent_READ)];
! 123: if (THD_TRACE) HTTrace("Event....... READ timed out on %d.\n", rqp->s);
! 124: return (*event->cbf) (rqp->s, event->param, HTEvent_TIMEOUT);
! 125: }
! 126: if (rqp->timeouts[HTEvent_INDEX(HTEvent_WRITE)] == timer) {
! 127: event = rqp->events[HTEvent_INDEX(HTEvent_WRITE)];
! 128: if (THD_TRACE) HTTrace("Event....... WRITE timed out on %d.\n", rqp->s);
! 129: return (*event->cbf) (rqp->s, event->param, HTEvent_TIMEOUT);
! 130: }
! 131: if (rqp->timeouts[HTEvent_INDEX(HTEvent_OOB)] == timer) {
! 132: event = rqp->events[HTEvent_INDEX(HTEvent_OOB)];
! 133: if (THD_TRACE) HTTrace("Event....... OOB timed out on %d.\n", rqp->s);
! 134: return (*event->cbf) (rqp->s, event->param, HTEvent_TIMEOUT);
! 135: }
! 136: HTTrace("Event....... can't find event for timer %p.\n", timer);
! 137: return HT_ERROR;
! 138: }
! 139:
! 140: /* ------------------------------------------------------------------------- */
! 141:
! 142: /*
! 143: ** For a given socket, reqister a request structure, a set of operations,
! 144: ** a HTEventCallback function, and a priority. For this implementation,
! 145: ** we allow only a single HTEventCallback function for all operations.
! 146: ** and the priority field is ignored.
! 147: */
! 148: PUBLIC int HTEventList_register (SOCKET s, HTEventType type, HTEvent * event)
! 149: {
! 150: RQ * rqp;
! 151: if (THD_TRACE)
! 152: HTTrace("Event....... Register socket %d, request %p handler %p type %x at priority %d\n",
! 153: s, (void *) event->request,
! 154: (void *) event->cbf, (unsigned) type,
! 155: (unsigned) event->priority);
! 156: if (s==INVSOC || HTEvent_INDEX(type) > 2)
! 157: return 0;
! 158: #if 0
! 159: /*
! 160: ** Don't write down TIMEOUT events in the RQ list or the fd sets.
! 161: ** They just manifest in the HTTimer
! 162: */
! 163: if (type == HTEvent_TIMEOUT)
! 164: return HT_OK;
! 165: #endif /* 0 */
! 166:
! 167: /*
! 168: ** Insert socket into appropriate file descriptor set. We also make sure
! 169: ** that it is registered in the global set.
! 170: */
! 171: if (THD_TRACE) HTTrace("Event....... Registering socket for %d\n", type);
! 172: rqp = RQ_get(s, RQ_mayCreate);
! 173: rqp->s = s;
! 174: rqp->events[HTEvent_INDEX(type)] = event;
! 175: FD_SET(s, FdArray+HTEvent_INDEX(type));
! 176: FD_SET(s, &all_fds);
! 177:
! 178: /*
! 179: ** If the timeout has been set (relative in millis) then we register
! 180: ** a new timeout for this event
! 181: */
! 182: if (event->millis >= 0) {
! 183: rqp->timeouts[HTEvent_INDEX(type)] = HTTimer_new(NULL, EventListTimerHandler, rqp, event->millis, YES);
! 184: }
! 185:
! 186: if (s > max_sock) max_sock = s ;
! 187: return HT_OK;
! 188: }
! 189:
! 190: /*
! 191: ** Remove the registered information for the specified socket for the actions
! 192: ** specified in ops. if no actions remain after the unregister, the registered
! 193: ** info is deleted, and, if the socket has been registered for notification,
! 194: ** the HTEventCallback will be invoked.
! 195: */
! 196: PUBLIC int HTEventList_unregister(SOCKET s, HTEventType type)
! 197: {
! 198: RQ * rqp;
! 199: RQ * last = NULL;
! 200: long v = HASH(s);
! 201: for (rqp = table[v]; rqp; rqp = rqp->next) {
! 202: if (rqp->s == s) {
! 203:
! 204: /*
! 205: ** Unregister the event from this action
! 206: */
! 207: rqp->events[HTEvent_INDEX(type)] = NULL;
! 208: FD_CLR(s, FdArray+HTEvent_INDEX(type));
! 209:
! 210: /*
! 211: ** Check to see of there was a timeout connected with the event.
! 212: ** If so then delete the timeout as well.
! 213: */
! 214: {
! 215: HTTimer * timer = rqp->timeouts[HTEvent_INDEX(type)];
! 216: if (timer) HTTimer_delete(timer);
! 217: }
! 218:
! 219: /*
! 220: ** Check to see if we can delete the action completely. We do this
! 221: ** if there are no more events registered.
! 222: */
! 223: if (rqp->events[HTEvent_INDEX(HTEvent_READ)] == NULL &&
! 224: rqp->events[HTEvent_INDEX(HTEvent_WRITE)] == NULL &&
! 225: rqp->events[HTEvent_INDEX(HTEvent_OOB)] == NULL) {
! 226: if (THD_TRACE)
! 227: HTTrace("Event....... No more events registered for socket %d\n", s);
! 228: if (last) last->next = rqp->next;
! 229: else table[v] = rqp->next;
! 230: HT_FREE(rqp);
! 231: FD_CLR(s, &all_fds);
! 232: }
! 233: if (THD_TRACE)
! 234: HTTrace("Event....... Socket %d unregisterd for %x\n", s, type);
! 235: return HT_OK;
! 236: }
! 237: last = rqp; /* to set next pointer when creating new */
! 238: }
! 239: if (THD_TRACE) HTTrace("Event....... Couldn't find socket %d.\n", s);
! 240: return HT_ERROR;
! 241: }
! 242:
! 243: /*
! 244: ** Unregister all sockets
! 245: ** N.B. we just remove them for our internal data structures: it is up to the
! 246: ** application to actually close the socket.
! 247: */
! 248: PUBLIC int HTEventList_unregisterAll( void )
! 249: {
! 250: int i;
! 251: register RQ * rqp, * next ;
! 252: if (THD_TRACE) HTTrace("Unregister.. all sockets\n");
! 253: for (i = 0 ; i < PRIME_TABLE_SIZE; i++) {
! 254: for (rqp = table[i]; rqp != 0; rqp = next) {
! 255: next = rqp->next;
! 256: HT_FREE(rqp);
! 257: }
! 258: table[i] = NULL;
! 259: }
! 260: max_sock = 0 ;
! 261: FD_ZERO(FdArray+HTEvent_INDEX(HTEvent_READ));
! 262: FD_ZERO(FdArray+HTEvent_INDEX(HTEvent_WRITE));
! 263: FD_ZERO(FdArray+HTEvent_INDEX(HTEvent_OOB));
! 264: FD_ZERO(&all_fds);
! 265: return 0;
! 266: }
! 267:
! 268: /*
! 269: ** Dispatch the event to the appropriate event handler.
! 270: ** If no event handler is found then just return.
! 271: */
! 272: PUBLIC int HTEventList_dispatch (SOCKET s, HTEventType type)
! 273: {
! 274: RQ * rqp = RQ_get(s, RQ_find);
! 275: if (rqp) {
! 276: HTEvent * event = rqp->events[HTEvent_INDEX(type)];
! 277:
! 278: /*
! 279: ** If we have found an event object for this event then see
! 280: ** is we should call it.
! 281: */
! 282: if (event && event->priority!=HT_PRIORITY_OFF)
! 283: return (*event->cbf) (s, event->param, type);
! 284: if (THD_TRACE) HTTrace("Dispatch.... Handler %p NOT called\n", rqp);
! 285: return HT_OK;
! 286: }
! 287: if (THD_TRACE) HTTrace("Dispatch.... Bad socket %d\n", s);
! 288: return NO;
! 289: }
! 290:
! 291: /*
! 292: ** Stops the (select based) event loop. The function does not guarantee
! 293: ** that all requests have terminated. This is for the app to do
! 294: */
! 295: PUBLIC void HTEventList_stopLoop (void)
! 296: {
! 297: HTEndLoop = 1;
! 298: }
! 299:
! 300: /*
! 301: ** We wait for activity from one of our registered
! 302: ** channels, and dispatch on that.
! 303: **
! 304: ** There are now two versions of the event loop. The first is if you want
! 305: ** to use async I/O on windows, and the other is if you want to use normal
! 306: ** Unix setup with sockets
! 307: */
! 308: PUBLIC int HTEventList_loop (HTRequest * theRequest)
! 309: {
! 310: fd_set treadset, twriteset, texceptset;
! 311: struct timeval waittime, * wt;
! 312: int active_sockets;
! 313: int maxfds;
! 314: int timeout;
! 315: SOCKET s;
! 316: int status = 0;
! 317: HTEndLoop = 0;
! 318:
! 319: /* Don't leave this loop until we leave the application */
! 320: do {
! 321: treadset = FdArray[HTEvent_INDEX(HTEvent_READ)];
! 322: twriteset = FdArray[HTEvent_INDEX(HTEvent_WRITE)];
! 323: texceptset = FdArray[HTEvent_INDEX(HTEvent_OOB)];
! 324: maxfds = max_sock;
! 325:
! 326: if (THD_TRACE) HTTrace("Event Loop.. calling select: maxfds is %d\n", maxfds);
! 327:
! 328: /*
! 329: ** Timeval struct copy needed for linux, as it set the value to the
! 330: ** remaining timeout while exiting the select. (and perhaps for
! 331: ** other OS). Code borrowed from X server.
! 332: */
! 333: wt = NULL;
! 334: if ((timeout = HTTimer_soonest())) {
! 335: waittime.tv_sec = timeout / MILLI_PER_SECOND;
! 336: waittime.tv_usec = (timeout % MILLI_PER_SECOND) *
! 337: (1000000 / MILLI_PER_SECOND);
! 338: wt = &waittime;
! 339: }
! 340:
! 341: #ifdef __hpux
! 342: active_sockets = select(maxfds+1, (int *)&treadset, (int *)&twriteset,
! 343: (int *)&texceptset, wt);
! 344: #else
! 345: active_sockets = select(maxfds+1, &treadset, &twriteset, &texceptset, wt);
! 346: #endif
! 347: /*
! 348: HTTraceData("", 0, "HTEventList_loop: select returned %d", active_sockets);
! 349: HTTraceData(&treadset, (active_sockets+7)/8, " treadset");
! 350: HTTraceData(&twriteset, (active_sockets+7)/8, " twriteset");
! 351: HTTraceData(&texceptset, (active_sockets+7)/8, " texceptset");
! 352: */
! 353: if (THD_TRACE) HTTrace("Event Loop.. select returns %d\n", active_sockets);
! 354:
! 355: if (active_sockets == -1) {
! 356: HTRequest_addSystemError( theRequest, ERR_FATAL, socerrno, NO, "select");
! 357: __DumpFDSet(FdArray+HTEvent_INDEX(HTEvent_READ), "Read");
! 358: __DumpFDSet(FdArray+HTEvent_INDEX(HTEvent_WRITE), "Write") ;
! 359: __DumpFDSet(FdArray+HTEvent_INDEX(HTEvent_OOB), "Exceptions");
! 360: return HT_ERROR;
! 361: }
! 362:
! 363: /*
! 364: ** We had a timeout so now we check and see if we have a timeout
! 365: ** handler to call
! 366: */
! 367: if (active_sockets == 0) {
! 368: HTTimer_dispatchAll();
! 369: continue;
! 370: }
! 371:
! 372: /*
! 373: ** There were active sockets. Determine which fd sets they were in
! 374: */
! 375: for (s = 0 ; s <= maxfds ; s++) {
! 376: if (FD_ISSET(s, &texceptset))
! 377: if ((status = HTEventList_dispatch(s, HTEvent_OOB)) != HT_OK)
! 378: return status;
! 379: if (FD_ISSET(s, &twriteset))
! 380: if ((status = HTEventList_dispatch(s, HTEvent_WRITE)) != HT_OK)
! 381: return status;
! 382: if (FD_ISSET(s, &treadset))
! 383: if ((status = HTEventList_dispatch(s, HTEvent_READ)) != HT_OK)
! 384: return status;
! 385: }
! 386: } while (!HTEndLoop);
! 387:
! 388: return HT_OK;
! 389: }
! 390:
! 391: PUBLIC HTEvent * HTEventList_lookup (SOCKET s, HTEventType type)
! 392: {
! 393: RQ * rqp = NULL;
! 394: if ((rqp = RQ_get(s, RQ_find)) == NULL)
! 395: return NULL;
! 396: return rqp->events[HTEvent_INDEX(type)];
! 397: }
! 398:
! 399: /* REGISTER DEFULT EVENT MANAGER
! 400: ** -----------------------------
! 401: ** Not done automaticly - may be done by application!
! 402: */
! 403: PUBLIC BOOL HTEventInit (void)
! 404: {
! 405: HTEvent_setRegisterCallback(HTEventList_register);
! 406: HTEvent_setUnregisterCallback(HTEventList_unregister);
! 407: return YES;
! 408: }
! 409:
! 410: PUBLIC BOOL HTEventTerminate (void)
! 411: {
! 412: return YES;
! 413: }
Webmaster