Annotation of libwww/Library/src/HTNet.c, revision 2.23
2.23 ! frystyk 1: /* HTNet.c
! 2: ** ASYNCRONOUS SOCKET MANAGEMENT
2.1 frystyk 3: **
2.10 frystyk 4: ** (c) COPYRIGHT MIT 1995.
2.4 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
6: **
7: ** This is the implementation of the internal library multithreading
2.1 frystyk 8: ** functions. This includes an interrupt handler and a event loop.
9: **
10: ** History:
2.14 frystyk 11: ** 12 June 94 Written by Henrik Frystyk, frystyk@w3.org
2.17 frystyk 12: ** 31 May 95 Charlie Brooks cbrooks@osf.org
13: **
2.1 frystyk 14: */
15:
2.9 frystyk 16: /* Implemention dependent include files */
17: #include "tcp.h"
18:
2.1 frystyk 19: /* Library include files */
20: #include "HTUtils.h"
2.16 frystyk 21: #include "HTProt.h"
2.1 frystyk 22: #include "HTError.h"
2.23 ! frystyk 23: #include "HTReqMan.h"
2.17 frystyk 24: #include "HTEvntrg.h"
2.23 ! frystyk 25: #include "HTStream.h"
! 26: #include "HTNet.h" /* Implemented here */
2.1 frystyk 27:
2.9 frystyk 28: #ifdef WIN32
29: #include <io.h>
30: #endif
31:
2.23 ! frystyk 32: #ifndef HT_MAX_SOCKETS
! 33: #define HT_MAX_SOCKETS 6
! 34: #endif
! 35:
! 36: typedef struct _CBFInfo {
! 37: HTNetCallBack * cbf;
! 38: int status; /* Status associated with this callback */
! 39: } CBFInfo;
! 40:
! 41: struct _HTStream {
! 42: CONST HTStreamClass * isa;
! 43: /* ... */
! 44: };
! 45:
! 46: PRIVATE int HTMaxActive = HT_MAX_SOCKETS; /* Max active requests */
! 47: PRIVATE HTList *HTNetActive = NULL; /* List of active requests */
! 48: PRIVATE HTList *HTNetPending = NULL; /* List of pending requests */
! 49: PRIVATE HTList *HTNetCBF = NULL; /* List of call back functions */
2.1 frystyk 50:
51: /* ------------------------------------------------------------------------- */
52:
2.23 ! frystyk 53: /*
! 54: ** Set the max number of simultanous sockets. Default is HT_MAX_SOCKETS
2.1 frystyk 55: */
2.23 ! frystyk 56: PUBLIC BOOL HTNet_setMaxSocket (int newmax)
! 57: {
! 58: if (newmax > 0) {
! 59: HTMaxActive = newmax;
! 60: return YES;
! 61: }
! 62: return NO;
! 63: }
! 64:
! 65: PUBLIC int HTNet_maxSocket (void)
2.1 frystyk 66: {
2.23 ! frystyk 67: return HTMaxActive;
! 68: }
! 69:
! 70: /* ------------------------------------------------------------------------- */
! 71: /* Termination Call Back Functions */
! 72: /* ------------------------------------------------------------------------- */
! 73:
! 74: /* HTNet_Register
! 75: ** --------------
! 76: ** Register a call back function that is to be called on every
! 77: ** termination of a request. Several call back functions can be registered
! 78: ** in which case all of them are called in the order of which they
! 79: ** were registered.
! 80: **
! 81: ** The status signifies which call back function to call depending of the
! 82: ** result of the request. This can be
! 83: **
! 84: ** HT_ERROR An error occured
! 85: ** HT_LOADED The document was loaded
! 86: ** HT_NO_DATA OK, but no data
! 87: ** HT_RETRY Retry request after at a later time
! 88: ** HT_ALL All of above
! 89: */
! 90: PUBLIC BOOL HTNet_register (HTNetCallBack *cbf, int status)
! 91: {
! 92: if (THD_TRACE)
! 93: fprintf(TDEST, "Net register HTNetCallBack %p\n", (void *) cbf);
! 94: if (cbf) {
! 95: CBFInfo *cbfinfo = (CBFInfo *) calloc(1, sizeof(CBFInfo));
! 96: if (!cbfinfo) outofmem(__FILE__, "HTNet_register");
! 97: cbfinfo->cbf = cbf;
! 98: cbfinfo->status = status;
! 99: if (!HTNetCBF) HTNetCBF = HTList_new();
! 100: return HTList_addObject(HTNetCBF, (void *) cbfinfo);
! 101: }
! 102: return NO;
! 103: }
! 104:
! 105: /* HTNet_unregister
! 106: ** --------------
! 107: ** Unregister a call back function that is to be called on every
! 108: ** termination of a request.
! 109: */
! 110: PUBLIC BOOL HTNet_unregister (HTNetCallBack *cbf)
! 111: {
! 112: if (THD_TRACE)
! 113: fprintf(TDEST, "Net unreg.. HTNetCallBack %p\n", (void *) cbf);
! 114: if (HTNetCBF && cbf) {
! 115: HTList *cur = HTNetCBF;
! 116: CBFInfo *pres;
! 117: while ((pres = (CBFInfo *) HTList_nextObject(cur))) {
! 118: if (pres->cbf == cbf) {
! 119: HTList_removeObject(HTNetCBF, (void *) pres);
! 120: free(pres);
! 121: return YES;
! 122: }
! 123: }
! 124: }
! 125: return NO;
! 126: }
2.1 frystyk 127:
2.23 ! frystyk 128: /* HTNet_unregisterAll
! 129: ** -------------------
! 130: ** Unregisters all call back functions
! 131: */
! 132: PUBLIC BOOL HTNet_unregisterAll (void)
! 133: {
! 134: if (THD_TRACE)
! 135: fprintf(TDEST, "Net unreg.. All callback functions\n");
! 136: if (HTNetCBF) {
! 137: HTList *cur = HTNetCBF;
! 138: CBFInfo *pres;
! 139: while ((pres = (CBFInfo *) HTList_nextObject(cur))) {
! 140: HTList_removeObject(HTNetCBF, (void *) pres);
! 141: free(pres);
! 142: }
! 143: HTList_delete(HTNetCBF);
! 144: HTNetCBF = NULL;
! 145: return YES;
! 146: }
! 147: return NO;
! 148: }
! 149:
! 150: /* HTNet_callback
! 151: ** --------------
! 152: ** Call all the call back functions registered in the list IF not the
! 153: ** status is HT_IGNORE.
! 154: ** The callback functions are called in the order of which they
! 155: ** were registered. At the moment an application callback function is
! 156: ** called, it can free the request object - it is no longer used by the
! 157: ** Library.
! 158: ** Returns YES if OK, else NO.
! 159: */
! 160: PUBLIC BOOL HTNet_callback (HTRequest * request, int status)
! 161: {
! 162: if (HTNetCBF && request && status != HT_IGNORE) {
! 163: int cnt = HTList_count(HTNetCBF);
! 164: while (--cnt >= 0) {
! 165: CBFInfo *pres = HTList_objectAt(HTNetCBF, cnt);
! 166: if (pres && (pres->status == status || pres->status == HT_ALL)) {
! 167: if (THD_TRACE)
! 168: fprintf(TDEST, "Net callback %p (request=%p, status=%d)\n",
! 169: (void *) pres->cbf, request, status);
! 170: (*(pres->cbf))(request, status);
! 171: }
! 172: }
! 173: return YES;
2.1 frystyk 174: }
2.23 ! frystyk 175: return NO;
2.1 frystyk 176: }
177:
2.23 ! frystyk 178: /* ------------------------------------------------------------------------- */
! 179: /* Request Queue */
! 180: /* ------------------------------------------------------------------------- */
2.1 frystyk 181:
2.23 ! frystyk 182: /* HTNet_activeQueue
! 183: ** -----------------
! 184: ** Returns the list of active requests that are currently having an open
! 185: ** connection.
! 186: ** Returns list of HTNet objects or NULL if error
2.1 frystyk 187: */
2.23 ! frystyk 188: PUBLIC HTList *HTNet_activeQueue (void)
! 189: {
! 190: return HTNetActive;
! 191: }
2.17 frystyk 192:
2.23 ! frystyk 193: /* HTNet_pendingQueue
! 194: ** ------------------
! 195: ** Returns the list of pending requests that are waiting to become active
! 196: ** Returns list of HTNet objects or NULL if error
! 197: */
! 198: PUBLIC HTList *HTNet_pendingQueue (void)
2.1 frystyk 199: {
2.23 ! frystyk 200: return HTNetPending;
2.1 frystyk 201: }
202:
2.23 ! frystyk 203: /* ------------------------------------------------------------------------- */
! 204: /* Creation and deletion methods */
! 205: /* ------------------------------------------------------------------------- */
! 206:
! 207: /* HTNet_new
2.15 frystyk 208: **
2.23 ! frystyk 209: ** Create a new HTNet object as a new request to be handled. If we have
! 210: ** more than HTMaxActive connections already then put this into the
! 211: ** pending queue, else start the request by calling the call back
! 212: ** function registered with this access method.
! 213: ** Returns YES if OK, else NO
! 214: */
! 215: PUBLIC BOOL HTNet_new (HTRequest * request, HTPriority priority)
! 216: {
! 217: HTNet *me;
! 218: HTProtocol *prot;
! 219: if (!request) return HT_ERROR;
! 220: if (!HTNetActive) HTNetActive = HTList_new();
! 221: prot = HTAnchor_protocol(request->anchor);
! 222:
! 223: /* Create new net object and bind it to the request object */
! 224: if ((me = (HTNet *) calloc(1, sizeof(HTNet))) == NULL)
! 225: outofmem(__FILE__, "HTNet_new");
! 226: me->request = request;
! 227: request->net = me;
! 228: me->preemtive = (HTProtocol_preemtive(prot) || request->preemtive);
! 229: me->priority = priority;
! 230: me->sockfd = INVSOC;
! 231: if (!(me->cbf = HTProtocol_callback(prot))) {
! 232: if (THD_TRACE)
! 233: fprintf(TDEST, "HTNet_new... NO CALL BACK FUNCTION!\n");
! 234: free(me);
! 235: return NO;
! 236: }
! 237:
! 238: /*
! 239: ** Check if we can start the request, else put it into pending queue
! 240: ** If so then call the call back function associated with the anchor.
! 241: ** We use the INVSOC as we don't have a valid socket yet!
! 242: */
! 243: if (HTList_count(HTNetActive) < HTMaxActive) {
! 244: HTList_addObject(HTNetActive, (void *) me);
! 245: me->request->retrys++;
! 246: if (THD_TRACE)
! 247: fprintf(TDEST, "HTNet_new... starting request %p (retry=%d)\n",
! 248: me->request, me->request->retrys);
! 249: (*(me->cbf))(INVSOC, me->request, FD_NONE);
! 250: } else {
! 251: if (!HTNetPending) HTNetPending = HTList_new();
! 252: if (THD_TRACE)
! 253: fprintf(TDEST, "HTNet_new... request %p registered as pending\n",
! 254: me->request);
! 255: HTList_addObject(HTNetPending, (void *) me);
! 256: }
! 257: return YES;
! 258: }
! 259:
! 260: /* delete_object
! 261: ** -------------
! 262: ** Deletes an HTNet object
2.15 frystyk 263: */
2.23 ! frystyk 264: PRIVATE BOOL delete_object (HTNet *net, int status)
2.15 frystyk 265: {
2.23 ! frystyk 266: if (THD_TRACE)
! 267: fprintf(TDEST, "HTNet_delete Remove net object %p\n", net);
! 268: if (net) {
! 269: int status = 0;
! 270:
! 271: /* Free stream with data FROM network to application */
! 272: if (net->target) {
! 273: if (status == HT_INTERRUPTED)
! 274: (*net->target->isa->abort)(net->target, NULL);
! 275: else
! 276: (*net->target->isa->_free)(net->target);
! 277: }
! 278:
! 279: /* Close socket */
! 280: if (net->sockfd != INVSOC) {
! 281: if ((status = NETCLOSE(net->sockfd)) < 0)
! 282: HTErrorSysAdd(net->request, ERR_FATAL,socerrno,NO,"NETCLOSE");
! 283: if (THD_TRACE)
! 284: fprintf(TDEST, "HTNet_delete closing socket %d\n",net->sockfd);
! 285: HTEvent_UnRegister(net->sockfd, (SockOps) FD_ALL);
! 286: }
! 287: if (net->isoc)
! 288: HTInputSocket_free(net->isoc);
! 289: if (net->request)
! 290: net->request->net = NULL; /* Break link to request */
! 291: free(net);
! 292: return status ? NO : YES;
! 293: }
! 294: return NO;
! 295: }
! 296:
! 297: /* HTNet_delete
! 298: ** ------------
! 299: ** Deletes the HTNet object from the list of active requests and calls
! 300: ** any registered call back functions IF not the status is HT_IGNORE.
! 301: ** This is used if we have internal requests that the app doesn't know
! 302: ** about. We also see if we have pending requests that can be started
! 303: ** up now when we have a socket free.
! 304: ** The callback functions are called in the reverse order of which they
! 305: ** were registered (last one first)
! 306: */
! 307: PUBLIC BOOL HTNet_delete (HTNet * net, int status)
! 308: {
! 309: if (THD_TRACE)
! 310: fprintf(TDEST,"HTNetDelete. Net Object and call callback functions\n");
! 311: if (HTNetActive && net) {
! 312:
! 313: /* Remove object and call callback functions */
! 314: HTRequest *request = net->request;
! 315: HTList_removeObject(HTNetActive, (void *) net);
! 316: delete_object(net, status);
! 317: HTNet_callback(request, status);
! 318:
! 319: /* See if we can start a pending request */
! 320: if (HTList_count(HTNetActive) < HTMaxActive &&
! 321: HTList_count(HTNetPending)) {
! 322: HTNet *next = (HTNet *) HTList_removeFirstObject(HTNetPending);
! 323: if (next) {
! 324: HTList_addObject(HTNetActive, (void *) next);
! 325: next->request->retrys++;
! 326: if (THD_TRACE)
! 327: fprintf(TDEST,"HTNet_delete start request %p from queue\n",
! 328: next->request);
! 329: (*(next->cbf))(INVSOC, next->request, FD_NONE);
! 330: }
! 331: }
! 332: return YES;
! 333: }
! 334: return NO;
! 335: }
! 336:
! 337: /* HTNet_deleteAll
! 338: ** ---------------
! 339: ** Deletes all HTNet object that might either be active or pending
! 340: ** We DO NOT call the call back functions and we don't care about open
! 341: ** socket descriptors. A crude way of saying goodbye!
! 342: */
! 343: PUBLIC BOOL HTNet_deleteAll (void)
! 344: {
! 345: if (THD_TRACE)
! 346: fprintf(TDEST, "HTNetDelete. Removing ALL Net - NO call backs\n");
! 347: if (HTNetPending) {
! 348: HTList *cur = HTNetPending;
! 349: HTNet *pres;
! 350: while ((pres = (HTNet *) HTList_nextObject(cur)))
! 351: delete_object(pres, HT_INTERRUPTED);
! 352: HTList_delete(HTNetPending);
! 353: HTNetPending = NULL;
! 354: }
! 355: if (HTNetActive) {
! 356: HTList *cur = HTNetActive;
! 357: HTNet *pres;
! 358: while ((pres = (HTNet *) HTList_nextObject(cur)))
! 359: delete_object(pres, HT_INTERRUPTED);
! 360: HTList_delete(HTNetActive);
! 361: HTNetActive = NULL;
! 362: }
! 363: return NO;
2.15 frystyk 364: }
365:
2.23 ! frystyk 366: /* ------------------------------------------------------------------------- */
! 367: /* Killing requests */
! 368: /* ------------------------------------------------------------------------- */
! 369:
! 370: /* HTNet_kill
! 371: ** ----------
! 372: ** Kill the request by calling the call back function with a request for
! 373: ** closing the connection. Does not remove the object. This is done by
! 374: ** HTNet_delete() function which is called by the load routine.
! 375: ** Returns OK if success, NO on error
! 376: */
! 377: PUBLIC BOOL HTNet_kill (HTNet * me)
! 378: {
! 379: if (HTNetActive && me) {
! 380: HTNet *pres;
! 381: while ((pres = (HTNet *) HTList_lastObject(HTNetActive)) != NULL) {
! 382: if (pres == me) {
! 383: (*(pres->cbf))(INVSOC, pres->request, FD_CLOSE);
! 384: return YES;
! 385: }
! 386: }
! 387: }
! 388: if (THD_TRACE)
! 389: fprintf(TDEST, "HTNet_kill.. Request %p is not registered\n", me);
! 390: return NO;
! 391: }
! 392:
! 393: /* HTNet_killAll
! 394: ** -------------
! 395: ** Kills all registered (active+pending) requests by calling the call
! 396: ** back function with a request for closing the connection. We do not
! 397: ** remove the HTNet object as it is done by HTNet_delete().
! 398: ** Returns OK if success, NO on error
! 399: */
! 400: PUBLIC BOOL HTNet_killAll (void)
! 401: {
! 402: HTNet *pres;
! 403: if (THD_TRACE)
! 404: fprintf(TDEST, "HTNet_kill.. ALL registered requests!!!\n");
! 405:
! 406: /* We start off in pending queue so we avoid racing */
! 407: if (HTNetPending) {
! 408: while ((pres = (HTNet *) HTList_lastObject(HTNetPending)) != NULL) {
! 409: (*(pres->cbf))(INVSOC, pres->request, FD_CLOSE);
! 410: HTList_removeObject(HTNetPending, pres);
! 411: }
! 412: }
! 413: if (HTNetActive) {
! 414: while ((pres = (HTNet *) HTList_lastObject(HTNetActive)) != NULL)
! 415: (*(pres->cbf))(INVSOC, pres->request, FD_CLOSE);
! 416: }
! 417: return YES;
! 418: }
2.17 frystyk 419:
2.23 ! frystyk 420: #if 0
2.20 frystyk 421: /*
422: ** LibraryCallback - "glue" between 3.0 thread code and new callback functions
423: ** map return codes into a simple yes/no model.
424: */
2.21 frystyk 425: PRIVATE int LibraryCallback ARGS3(SOCKET, s, HTRequest *, rq, SockOps, f)
2.20 frystyk 426: {
427: int status = 0 ;
428: HTEventState state ;
429: HTProtocol * proto = (HTProtocol *)
430: HTAnchor_protocol( rq -> anchor) ;
431:
432: /* begin */
433:
434: if (proto == 0) /* Whoa! No protocol! */
435: return -1;
2.23 ! frystyk 436: status = proto->callback( s, rq, f) ;
2.20 frystyk 437: if (status != HT_WOULD_BLOCK) { /* completed - good or bad... */
438: if (THD_TRACE)
439: fprintf(TDEST, "LibCallBack. Calling Terminate...\n");
440: if (status != HT_OK) {
441: HTLoadTerminate(rq, status);
442: state = HTEventRequestTerminate( rq, status) ;
443: /* if the state isn't EVENT_QUIT */
444: if (! HTEventCheckState( rq, state ))
445: return HT_OK; /* treat as failure */
446: }
447: } /* if status */
448: return HT_WOULD_BLOCK;
449: }
2.17 frystyk 450:
2.23 ! frystyk 451: /* HTNetState
2.1 frystyk 452: **
453: ** This function registers a socket as waiting for the action given
454: ** (read or write etc.).
2.17 frystyk 455: **
456: ** Charlie Brooks - we handle the interrupt thread state internally to this module
457: ** setting the interrupt on a socket disables it from read/write.
2.1 frystyk 458: */
2.23 ! frystyk 459: PUBLIC void HTNetState ARGS2(SOCKFD, sockfd, HTNetAction, action)
2.1 frystyk 460: {
2.23 ! frystyk 461: register HTNet * pres ;
! 462: HTList * cur = HTNetActive ;
2.17 frystyk 463: int found = 0 ;
464: HTRequest * reqst ;
465:
2.9 frystyk 466: #ifdef _WIN32
2.23 ! frystyk 467: if (sockfd <= 2)
! 468: sockfd = _get_osfhandle(sockfd);
2.9 frystyk 469: #endif
470:
2.1 frystyk 471: if (THD_TRACE) {
472: static char *actionmsg[] = {
473: "SET WRITE",
474: "CLEAR WRITE",
475: "SET READ",
476: "CLEAR READ",
2.4 frystyk 477: "SET INTERRUPT",
478: "CLEAR INTERRUPT",
2.17 frystyk 479: "CLOSE",
480: "SET CONNECT",
481: "CLEAR CONNECT"
2.1 frystyk 482: };
2.7 frystyk 483: fprintf(TDEST,
2.23 ! frystyk 484: "Net......... Registering socket number %d for action %s\n",
2.1 frystyk 485: sockfd, *(actionmsg+action));
2.17 frystyk 486: } /* if */
487:
488: FD_SET( sockfd, &HTfd_libs) ;
489: if (libMaxSock < sockfd)
490: libMaxSock = sockfd ;
491:
492:
2.23 ! frystyk 493: while ((pres = (HTNet *)HTList_nextObject(cur) ) != 0) {
2.17 frystyk 494: if (pres->sockfd == sockfd) {
495: found = 1 ;
496: break ;
497: } /* if */
498: } /* while */
499:
500: if (! found) /* how'd you get here? */
501: return ;
502:
503: reqst = pres->request ;
2.1 frystyk 504: switch (action) {
505: case THD_SET_WRITE:
2.17 frystyk 506: case THD_SET_CONNECT:
507: HTEvent_Register( sockfd, reqst, action == THD_SET_WRITE ? (SockOps)FD_WRITE : (SockOps)FD_CONNECT ,
508: LibraryCallback, 0);
2.1 frystyk 509: break;
510:
511: case THD_CLR_WRITE:
2.17 frystyk 512: case THD_CLR_CONNECT:
513: HTEvent_UnRegister( sockfd, action == THD_CLR_WRITE ? (SockOps)FD_WRITE : (SockOps)FD_CONNECT) ;
2.1 frystyk 514: break;
515:
516: case THD_SET_READ:
2.17 frystyk 517: HTEvent_Register( sockfd, reqst, (SockOps)FD_READ, LibraryCallback, 0);
2.1 frystyk 518: break;
519:
520: case THD_CLR_READ:
2.17 frystyk 521: HTEvent_UnRegister( sockfd, FD_WRITE) ;
2.1 frystyk 522: break;
523:
524: case THD_CLOSE:
2.17 frystyk 525: HTEvent_UnRegister( sockfd, FD_ALL) ;
526: FD_CLR( sockfd, &HTfd_libs);
527: FD_CLR( sockfd, &HTfd_intr);
528: libMaxSock = 0 ;
2.23 ! frystyk 529: while ((pres = (HTNet *)HTList_nextObject(cur) ) != 0) {
2.17 frystyk 530: if (pres->sockfd > libMaxSock) {
531: libMaxSock = sockfd ;
532: } /* if */
533: } /* while */
534:
2.1 frystyk 535: break;
536:
2.17 frystyk 537: /*
538: * we handle interrupts locally ... only library sockets can
539: * be interrupted?
540: */
541:
2.2 frystyk 542: case THD_SET_INTR:
2.17 frystyk 543: HTEvent_UnRegister( sockfd, (SockOps)(FD_READ | FD_WRITE) );
544: FD_SET( sockfd, &HTfd_intr) ;
2.1 frystyk 545: break;
546:
2.2 frystyk 547: case THD_CLR_INTR:
2.17 frystyk 548: FD_CLR( sockfd, &HTfd_intr) ;
549: HTEvent_UnRegister(sockfd, FD_ALL) ; /* no sin to unregister and unregistered socket */
2.2 frystyk 550: break;
551:
2.1 frystyk 552: default:
553: if (THD_TRACE)
2.23 ! frystyk 554: fprintf(TDEST, "Net...... Illegal socket action (%d)\n", (int)action);
2.1 frystyk 555: }
2.17 frystyk 556: return ;
2.1 frystyk 557: }
2.23 ! frystyk 558: #endif
2.1 frystyk 559:
560:
Webmaster