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