Annotation of libwww/Library/src/HTEvtLst.c, revision 2.2

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.
2.2     ! eric        6: **     @(#) $Id: HTEvtLst.c,v 2.1 1996/12/01 00:26:35 frystyk Exp $
2.1       frystyk     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) 
2.2     ! eric       41: #define HT_EVENT_ORDER                           /* use event ordering code */
        !            42: #define EVENTS_TO_EXECUTE      5  /* how many to execute in one select loop */
2.1       frystyk    43: 
                     44: typedef struct rq_t RQ;
                     45: struct rq_t {
                     46:     SOCKET     s ;             /* our socket */
                     47:     BOOL       unregister;     /* notify app when completely unregistered */
                     48:     HTEvent *  events[3];      /* event parameters for read, write, oob */
                     49:     HTTimer *   timeouts[3];   /* timeout for each of the events */
                     50: };
                     51: 
                     52: typedef enum _RQ_action {
                     53:     RQ_mayCreate,
                     54:     RQ_find
                     55: } RQ_action;
                     56: 
2.2     ! eric       57: HTList * HashTable[PRIME_TABLE_SIZE]; 
2.1       frystyk    58: PRIVATE SOCKET max_sock = 0 ;                    /* max socket value in use */
                     59: 
                     60: PRIVATE fd_set FdArray[HTEvent_TYPES];
                     61: PRIVATE fd_set all_fds ;                           /* any descriptor at all */
                     62: 
                     63: PRIVATE int HTEndLoop = 0;                    /* If !0 then exit event loop */
                     64: 
                     65: /* ------------------------------------------------------------------------- */
                     66: 
2.2     ! eric       67: PRIVATE RQ * RQ_get (SOCKET s, RQ_action action)
2.1       frystyk    68: {
                     69:     long v = HASH(s);
2.2     ! eric       70:     HTList* cur;
        !            71:     RQ * pres;
        !            72: 
        !            73:     if (HashTable[v] == NULL)
        !            74:        HashTable[v] = HTList_new();
        !            75:     cur = HashTable[v];
        !            76:     while ((pres = (RQ *) HTList_nextObject(cur)))
        !            77:        if (pres->s == s)
        !            78:            return pres;
        !            79: 
2.1       frystyk    80:     if (action == RQ_mayCreate) {
2.2     ! eric       81:         if ((pres = (RQ *) HT_CALLOC(1, sizeof(RQ))) == NULL)
2.1       frystyk    82:            HT_OUTOFMEM("HTEventList_register");
2.2     ! eric       83:        pres->s = s;
        !            84:        HTList_addObject(HashTable[v], (void *)pres);
        !            85:        return pres;
2.1       frystyk    86:     }
                     87:     return NULL;
                     88: }
                     89: 
                     90: /*
                     91: **  A simple debug function that dumps all the socket arrays
                     92: **  as trace messages
                     93: */
2.2     ! eric       94: PRIVATE void __DumpFDSet (fd_set * fdp, const char * str) 
2.1       frystyk    95: {
                     96:     SOCKET s ;
                     97: #ifdef _WINSOCKAPI_
                     98:     unsigned ui ;
                     99: #endif
                    100:     if (THD_TRACE) {
                    101:        HTTrace("Event....... Dumping %s file descriptor set\n", str);
                    102: #ifdef _WINSOCKAPI_ 
                    103:         for (ui = 0 ; ui < fdp->fd_count; ui++) { 
                    104:             s = all_fds.fd_array[ui] ;
                    105: #else 
                    106:         for (s = 0 ; s <= max_sock; s++) { 
                    107:             if (FD_ISSET(s, fdp))
                    108: #endif
                    109:            {
                    110:                HTTrace("%4d\n", s);
                    111:            }
                    112:         }      /* for */
                    113:     }           /* if */
                    114:     return ;
                    115: }
                    116: 
                    117: /* ------------------------------------------------------------------------- */
2.2     ! eric      118: /*             E V E N T   O R D E R I N G   S T U F F                      */
        !           119: #ifdef HT_EVENT_ORDER
        !           120: typedef struct {
        !           121:     HTEvent *  event;
        !           122:     SOCKET     s;
        !           123:     HTEventType        type;
        !           124:     HTPriority skipped;
        !           125: } EventOrder;
2.1       frystyk   126: 
2.2     ! eric      127: HTList * EventOrderList = NULL;
        !           128: #if 0
        !           129: /*
        !           130: **     return -1 if a should be after b
        !           131:  */
        !           132: int EventOrderComparer (const void * a, const void * b)
        !           133: {
        !           134:     EventOrder * placeMe = (EventOrder *)a;
        !           135:     EventOrder * maybeHere = (EventOrder *)b;
        !           136:     if (placeMe->event->priority+placeMe->skipped >= maybeHere->event->priority+maybeHere->skipped)
        !           137:        return 1;
        !           138:     return -1;
        !           139: }
        !           140: #endif
        !           141: 
        !           142: int EventOrder_add (SOCKET s, HTEventType type)
        !           143: {
        !           144:     EventOrder * pres;
        !           145:     HTList * cur = EventOrderList;
        !           146:     HTList * insertAfter = cur;
        !           147:     HTEvent * event = HTEventList_lookup(s, type);
        !           148: 
        !           149:     if (event == NULL) {
        !           150:        HTTrace("EventOrder.. no event found for socket %d, type %x.\n", s, type);
        !           151:        return HT_ERROR;
        !           152:     }
        !           153: 
        !           154:     /*
        !           155:     ** Look to see if it's already here from before
        !           156:     */
        !           157:     while ((pres = (EventOrder *) HTList_nextObject(cur))) {
        !           158:        if (pres->s == s && pres->event == event && pres->type == type) {
        !           159:            pres->skipped++;
        !           160:            return HT_OK;
        !           161:        }
        !           162:        if (pres->event->priority+pres->skipped > event->priority)
        !           163:            insertAfter = cur;
        !           164:     }
        !           165: 
        !           166:     /*
        !           167:     ** No, so create a new element
        !           168:     */
        !           169:     if ((pres = (EventOrder *) HT_CALLOC(1, sizeof(EventOrder))) == NULL)
        !           170:        HT_OUTOFMEM("EventOrder_add");
        !           171:     pres->event = event;
        !           172:     pres->s = s;
        !           173:     pres->type = type;
        !           174:     HTList_addObject(insertAfter, (void *)pres);
        !           175:     return HT_OK;
        !           176: }
        !           177: 
        !           178: PUBLIC int EventOrder_executeAndDelete (void) 
        !           179: {
        !           180:     HTList * cur;
        !           181:     EventOrder * pres;
        !           182:     int i = 0;
        !           183: 
        !           184:     if (cur == NULL)
        !           185:        return NO;
        !           186: #if 0
        !           187:     HTList_insertionSort(EventOrderList, EventOrderComparer);
        !           188: #endif
        !           189:     cur = EventOrderList;
        !           190:     if (THD_TRACE) HTTrace("EventOrder.. execute ordered events\n");
        !           191:     while ((pres = (EventOrder *) HTList_removeLastObject(cur)) && i < EVENTS_TO_EXECUTE) {
        !           192:        int ret = (*pres->event->cbf)(pres->s, pres->event->param, pres->type);
        !           193:        HT_FREE(pres);
        !           194:        if (ret != HT_OK)
        !           195:            return ret;
        !           196:        i++;
        !           197:     }
        !           198:     return HT_OK;
        !           199: }
        !           200: 
        !           201: PUBLIC BOOL EventOrder_deleteAll (void) 
        !           202: {
        !           203:     HTList * cur = EventOrderList;
        !           204:     EventOrder * pres;
        !           205: 
        !           206:     if (cur == NULL)
        !           207:        return NO;
        !           208:     if (THD_TRACE) HTTrace("EventOrder.. all ordered events\n");
        !           209:     while ((pres = (EventOrder *) HTList_nextObject(cur)))
        !           210:        HT_FREE(pres);
        !           211:     HTList_delete(EventOrderList);
        !           212:     EventOrderList = NULL;
        !           213:     return YES;
        !           214: }
        !           215: #endif /* HT_EVENT_ORDER */
        !           216: 
        !           217: /* ------------------------------------------------------------------------- */
        !           218: /*             T I M E O U T   H A N D L E R                                */
2.1       frystyk   219: PRIVATE int EventListTimerHandler (HTTimer * timer, void * param)
                    220: {
                    221:     RQ * rqp = (RQ *)param;
                    222:     HTEvent * event;
                    223:     if (rqp->timeouts[HTEvent_INDEX(HTEvent_READ)] == timer) {
                    224:        event = rqp->events[HTEvent_INDEX(HTEvent_READ)];
                    225:        if (THD_TRACE) HTTrace("Event....... READ timed out on %d.\n", rqp->s);
                    226:        return (*event->cbf) (rqp->s, event->param, HTEvent_TIMEOUT);
                    227:     }
                    228:     if (rqp->timeouts[HTEvent_INDEX(HTEvent_WRITE)] == timer) {
                    229:        event = rqp->events[HTEvent_INDEX(HTEvent_WRITE)];
                    230:        if (THD_TRACE) HTTrace("Event....... WRITE timed out on %d.\n", rqp->s);
                    231:        return (*event->cbf) (rqp->s, event->param, HTEvent_TIMEOUT);
                    232:     }
                    233:     if (rqp->timeouts[HTEvent_INDEX(HTEvent_OOB)] == timer) {
                    234:        event = rqp->events[HTEvent_INDEX(HTEvent_OOB)];
                    235:        if (THD_TRACE) HTTrace("Event....... OOB timed out on %d.\n", rqp->s);
                    236:        return (*event->cbf) (rqp->s, event->param, HTEvent_TIMEOUT);
                    237:     }
                    238:     HTTrace("Event....... can't find event for timer %p.\n", timer);
                    239:     return HT_ERROR;
                    240: }
                    241: 
                    242: /* ------------------------------------------------------------------------- */
                    243: 
                    244: /*
                    245: **  For a given socket, reqister a request structure, a set of operations, 
                    246: **  a HTEventCallback function, and a priority. For this implementation, 
                    247: **  we allow only a single HTEventCallback function for all operations.
                    248: **  and the priority field is ignored.
                    249: */
                    250: PUBLIC int HTEventList_register (SOCKET s, HTEventType type, HTEvent * event)
                    251: {
                    252:     RQ * rqp;
                    253:     if (THD_TRACE) 
                    254:        HTTrace("Event....... Register socket %d, request %p handler %p type %x at priority %d\n",
                    255:                s, (void *) event->request,
                    256:                (void *) event->cbf, (unsigned) type,
                    257:                (unsigned) event->priority);
                    258:     if (s==INVSOC || HTEvent_INDEX(type) > 2)
                    259:        return 0;
                    260: #if 0
                    261:     /*
                    262:     ** Don't write down TIMEOUT events in the RQ list or the fd sets.
                    263:     ** They just manifest in the HTTimer
                    264:     */
                    265:     if (type == HTEvent_TIMEOUT)
                    266:        return HT_OK;
                    267: #endif /* 0 */
                    268: 
                    269:     /*
                    270:     ** Insert socket into appropriate file descriptor set. We also make sure
                    271:     ** that it is registered in the global set.
                    272:     */
                    273:     if (THD_TRACE) HTTrace("Event....... Registering socket for %d\n", type);
                    274:     rqp = RQ_get(s, RQ_mayCreate);
                    275:     rqp->s = s;
                    276:     rqp->events[HTEvent_INDEX(type)] = event;
                    277:     FD_SET(s, FdArray+HTEvent_INDEX(type));
                    278:     FD_SET(s, &all_fds);
                    279: 
                    280:     /*
                    281:     ** If the timeout has been set (relative in millis) then we register 
                    282:     ** a new timeout for this event
                    283:     */
                    284:     if (event->millis >= 0) {
                    285:        rqp->timeouts[HTEvent_INDEX(type)] = HTTimer_new(NULL, EventListTimerHandler, rqp, event->millis, YES);
                    286:     }
                    287: 
                    288:     if (s > max_sock) max_sock = s ;
                    289:     return HT_OK;
                    290: }
                    291: 
                    292: /*
                    293: ** Remove the registered information for the specified socket for the actions 
                    294: ** specified in ops. if no actions remain after the unregister, the registered
                    295: ** info is deleted, and, if the socket has been registered for notification, 
                    296: ** the HTEventCallback will be invoked.
                    297: */
                    298: PUBLIC int HTEventList_unregister(SOCKET s, HTEventType type) 
                    299: {
                    300:     long v = HASH(s);
2.2     ! eric      301:     HTList * cur = HashTable[v];
        !           302:     HTList * last = cur;
        !           303:     RQ * pres;
        !           304: 
        !           305:     while ((pres = (RQ *) HTList_nextObject(cur))) {
        !           306:         if (pres->s == s) {
2.1       frystyk   307: 
                    308:            /*
                    309:            **  Unregister the event from this action
                    310:            */
2.2     ! eric      311:            pres->events[HTEvent_INDEX(type)] = NULL;
2.1       frystyk   312:            FD_CLR(s, FdArray+HTEvent_INDEX(type));
                    313: 
                    314:            /*
                    315:            **  Check to see of there was a timeout connected with the event.
                    316:            **  If so then delete the timeout as well.
                    317:            */
                    318:            {
2.2     ! eric      319:                HTTimer * timer = pres->timeouts[HTEvent_INDEX(type)];
2.1       frystyk   320:                if (timer) HTTimer_delete(timer);
                    321:            }
                    322:            
                    323:            /*
                    324:            **  Check to see if we can delete the action completely. We do this
                    325:            **  if there are no more events registered.
                    326:            */
2.2     ! eric      327:            if (pres->events[HTEvent_INDEX(HTEvent_READ)] == NULL && 
        !           328:                pres->events[HTEvent_INDEX(HTEvent_WRITE)] == NULL && 
        !           329:                pres->events[HTEvent_INDEX(HTEvent_OOB)] == NULL) {
2.1       frystyk   330:                if (THD_TRACE)
                    331:                    HTTrace("Event....... No more events registered for socket %d\n", s);
2.2     ! eric      332:                HT_FREE(pres);
2.1       frystyk   333:                FD_CLR(s, &all_fds);
2.2     ! eric      334:                HTList_quickRemoveObject(cur, last);
        !           335:            } else
        !           336:                last = cur;  /* to set next pointer when creating new */
2.1       frystyk   337:            if (THD_TRACE)
                    338:                HTTrace("Event....... Socket %d unregisterd for %x\n", s, type);
                    339:            return HT_OK;
                    340:        }
                    341:     }
                    342:     if (THD_TRACE) HTTrace("Event....... Couldn't find socket %d.\n", s);
                    343:     return HT_ERROR;
                    344: }
                    345: 
                    346: /*
                    347: ** Unregister all sockets 
                    348: ** N.B. we just remove them for our internal data structures: it is up to the 
                    349: ** application to actually close the socket. 
                    350: */
2.2     ! eric      351: PUBLIC int HTEventList_unregisterAll (void) 
2.1       frystyk   352: {
                    353:     int i;
                    354:     if (THD_TRACE) HTTrace("Unregister.. all sockets\n");
                    355:     for (i = 0 ; i < PRIME_TABLE_SIZE; i++) {
2.2     ! eric      356:        HTList * cur = HashTable[i];
        !           357:        RQ * pres;
        !           358:        while ((pres = (RQ *) HTList_nextObject(cur)))
        !           359:            HT_FREE(pres);
        !           360:        HTList_delete(HashTable[i]);
        !           361:        HashTable[i] = NULL;
2.1       frystyk   362:     }
                    363:     max_sock = 0 ;
                    364:     FD_ZERO(FdArray+HTEvent_INDEX(HTEvent_READ));
                    365:     FD_ZERO(FdArray+HTEvent_INDEX(HTEvent_WRITE));
                    366:     FD_ZERO(FdArray+HTEvent_INDEX(HTEvent_OOB));
                    367:     FD_ZERO(&all_fds);
2.2     ! eric      368: #ifdef HT_EVENT_ORDER
        !           369:     EventOrder_deleteAll();
        !           370: #endif /* HT_EVENT_ORDER */
2.1       frystyk   371:     return 0;
                    372: }
                    373: 
                    374: /*
                    375: **  Dispatch the event to the appropriate event handler.
                    376: **  If no event handler is found then just return.
                    377: */
                    378: PUBLIC int HTEventList_dispatch (SOCKET s, HTEventType type)
                    379: {
                    380:     RQ * rqp = RQ_get(s, RQ_find);
                    381:     if (rqp) {
                    382:        HTEvent * event = rqp->events[HTEvent_INDEX(type)];
                    383: 
                    384:        /*
                    385:        **  If we have found an event object for this event then see
                    386:        **  is we should call it.
                    387:        */
                    388:        if (event && event->priority!=HT_PRIORITY_OFF)
                    389:            return (*event->cbf) (s, event->param, type);
                    390:        if (THD_TRACE) HTTrace("Dispatch.... Handler %p NOT called\n", rqp);
                    391:        return HT_OK;
                    392:     }
                    393:     if (THD_TRACE) HTTrace("Dispatch.... Bad socket %d\n", s);
                    394:     return NO;
                    395: }
                    396: 
                    397: /*
                    398: **  Stops the (select based) event loop. The function does not guarantee
                    399: **  that all requests have terminated. This is for the app to do
                    400: */
                    401: PUBLIC void HTEventList_stopLoop (void)
                    402: {
                    403:     HTEndLoop = 1;
                    404: }
                    405: 
                    406: /*
                    407: **  We wait for activity from one of our registered 
                    408: **  channels, and dispatch on that.
                    409: **
                    410: **  There are now two versions of the event loop. The first is if you want
                    411: **  to use async I/O on windows, and the other is if you want to use normal
                    412: **  Unix setup with sockets
                    413: */
                    414: PUBLIC int HTEventList_loop (HTRequest * theRequest) 
                    415: {
                    416:     fd_set treadset, twriteset, texceptset;
                    417:     struct timeval waittime, * wt;
                    418:     int active_sockets;
                    419:     int maxfds;
                    420:     int timeout;
                    421:     SOCKET s;
                    422:     int status = 0;
                    423:     HTEndLoop = 0;
                    424: 
2.2     ! eric      425:     EventOrderList = HTList_new();     /* is kept around until EventOrder_deleteAll */
        !           426: 
2.1       frystyk   427:     /* Don't leave this loop until we leave the application */
                    428:     do {
                    429:         treadset = FdArray[HTEvent_INDEX(HTEvent_READ)];
                    430:         twriteset = FdArray[HTEvent_INDEX(HTEvent_WRITE)];
                    431:         texceptset = FdArray[HTEvent_INDEX(HTEvent_OOB)];
                    432:         maxfds = max_sock; 
                    433: 
                    434:        if (THD_TRACE) HTTrace("Event Loop.. calling select: maxfds is %d\n", maxfds);
                    435: 
                    436:         /*
                    437:        **  Timeval struct copy needed for linux, as it set the value to the
                    438:        **  remaining timeout while exiting the select. (and perhaps for
                    439:        **  other OS). Code borrowed from X server.
                    440:        */
                    441:        wt = NULL;
                    442:        if ((timeout = HTTimer_soonest())) {
                    443:            waittime.tv_sec = timeout / MILLI_PER_SECOND;
                    444:            waittime.tv_usec = (timeout % MILLI_PER_SECOND) *
                    445:                (1000000 / MILLI_PER_SECOND);
                    446:            wt = &waittime;
                    447:        }
                    448: 
2.2     ! eric      449:        HTTraceData((char*)&treadset, maxfds/8 + 1, "HTEventList_loop pre treadset: (maxfd:%d)", maxfds);
        !           450:        HTTraceData((char*)&twriteset, maxfds/8 + 1, "HTEventList_loop pre twriteset:");
        !           451:        HTTraceData((char*)&texceptset, maxfds/8 + 1, "HTEventList_loop pre texceptset:");
        !           452: 
2.1       frystyk   453: #ifdef __hpux 
                    454:         active_sockets = select(maxfds+1, (int *)&treadset, (int *)&twriteset,
                    455:                                (int *)&texceptset, wt);
                    456: #else
                    457:         active_sockets = select(maxfds+1, &treadset, &twriteset, &texceptset, wt);
                    458: #endif
2.2     ! eric      459: 
        !           460:        HTTraceData((char*)&treadset, maxfds/8 + 1, "HTEventList_loop post treadset: (active_sockets:%d)", active_sockets);
        !           461:        HTTraceData((char*)&twriteset, maxfds/8 + 1, "HTEventList_loop post twriteset: (errno:%d)", errno);
        !           462:        HTTraceData((char*)&texceptset, maxfds/8 + 1, "HTEventList_loop post texceptset:");
        !           463: 
2.1       frystyk   464:        if (THD_TRACE) HTTrace("Event Loop.. select returns %d\n", active_sockets);
                    465: 
                    466:         if (active_sockets == -1) {
                    467:            HTRequest_addSystemError( theRequest, ERR_FATAL, socerrno, NO, "select");
                    468:            __DumpFDSet(FdArray+HTEvent_INDEX(HTEvent_READ), "Read");
                    469:            __DumpFDSet(FdArray+HTEvent_INDEX(HTEvent_WRITE), "Write") ;
                    470:            __DumpFDSet(FdArray+HTEvent_INDEX(HTEvent_OOB), "Exceptions");
                    471:            return HT_ERROR;
                    472:         }
                    473: 
                    474:        /*
                    475:        **  We had a timeout so now we check and see if we have a timeout
                    476:        **  handler to call
                    477:        */ 
                    478:        if (active_sockets == 0) {
                    479:            HTTimer_dispatchAll();
                    480:            continue;
                    481:        }
                    482: 
                    483:        /*
                    484:        **  There were active sockets. Determine which fd sets they were in
                    485:        */
2.2     ! eric      486: #ifdef HT_EVENT_ORDER
        !           487: #define DISPATCH(socket, type) EventOrder_add(socket, type)
        !           488: #else /* HT_EVENT_ORDER */
        !           489: #define DISPATCH(socket, type) HTEventList_dispatch(socket, type)
        !           490: #endif /* !HT_EVENT_ORDER */
2.1       frystyk   491:        for (s = 0 ; s <= maxfds ; s++) { 
                    492:            if (FD_ISSET(s, &texceptset))
2.2     ! eric      493:                if ((status = DISPATCH(s, HTEvent_OOB)) != HT_OK)
2.1       frystyk   494:                    return status;
                    495:            if (FD_ISSET(s, &twriteset))
2.2     ! eric      496:                if ((status = DISPATCH(s, HTEvent_WRITE)) != HT_OK)
2.1       frystyk   497:                    return status;
                    498:            if (FD_ISSET(s, &treadset))
2.2     ! eric      499:                if ((status = DISPATCH(s, HTEvent_READ)) != HT_OK)
2.1       frystyk   500:                    return status;
                    501:        }
2.2     ! eric      502: #ifdef HT_EVENT_ORDER
        !           503:        if ((status = EventOrder_executeAndDelete()) != HT_OK)
        !           504:            return status;
        !           505: #endif /* HT_EVENT_ORDER */
2.1       frystyk   506:     } while (!HTEndLoop);
                    507: 
                    508:     return HT_OK;
                    509: }
                    510: 
                    511: PUBLIC HTEvent * HTEventList_lookup (SOCKET s, HTEventType type)
                    512: {
                    513:     RQ * rqp = NULL;
                    514:     if ((rqp = RQ_get(s, RQ_find)) == NULL)
                    515:        return NULL;
                    516:     return rqp->events[HTEvent_INDEX(type)];
                    517: }
                    518: 
                    519: /*     REGISTER DEFULT EVENT MANAGER
                    520: **     -----------------------------
                    521: **     Not done automaticly - may be done by application!
                    522: */
                    523: PUBLIC BOOL HTEventInit (void)
                    524: {
                    525:     HTEvent_setRegisterCallback(HTEventList_register);
                    526:     HTEvent_setUnregisterCallback(HTEventList_unregister);
                    527:     return YES;
                    528: }
                    529: 
                    530: PUBLIC BOOL HTEventTerminate (void)
                    531: {
                    532:     return YES;
                    533: }

Webmaster