PUBLIC int HTTimer_next (ms_t * pSoonest) { HTList * cur = Timers; HTList * last = Timers; HTTimer * pres; ms_t now = HTGetTimeInMillis(); int ret = HT_OK; /* ** Dispatch all timers that have expired */ while (Timers && (pres = (HTTimer *) HTList_nextObject(cur))) { if (pres->expires <= now) { if ((ret = Timer_dispatch(cur, last)) != HT_OK) break; cur = last = Timers; } else { last = cur; } } if (pSoonest) { /* ** First element in Timers is the next to expire. */ HTList * cur = Timers; /* for now */ pres = (HTTimer *) HTList_nextObject(cur); *pSoonest = pres ? pres->expires - now : 0; } return ret; }
/* Only responsible for WM_TIMER and WSA_AsyncSelect */ PRIVATE LRESULT CALLBACK AsyncWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WORD event; SOCKET sock; HTEventType type; ms_t now = HTGetTimeInMillis(); /* timeout stuff */ if (uMsg == WM_TIMER) { HTTimer_dispatch((HTTimer *)wParam); return (0); } if (uMsg != HTwinMsg) /* not our async message */ return (DefWindowProc(hwnd, uMsg, wParam, lParam)); event = LOWORD(lParam); sock = (SOCKET)wParam; switch (event) { case FD_READ: type = HTEvent_READ; break; case FD_WRITE: type = HTEvent_WRITE; break; case FD_ACCEPT: type = HTEvent_ACCEPT; break; case FD_CONNECT: type = HTEvent_CONNECT; break; case FD_OOB: type = HTEvent_OOB; break; /* JK: was returning HTEvent_CLOSE before, and this was a source of errors, as libwww detects the socket shutdown with a call to recv */ case FD_CLOSE: type = HTEvent_READ; break; default: HTDEBUGBREAK("Unknown event %d\n" _ event); } if (HTEventList_dispatch((int)sock, type, now) != HT_OK) HTEndLoop = -1; return (0); }
PRIVATE ms_t HTMemLog_addTime(void) { char buff[20]; ms_t ms = HTGetTimeInMillis(); int len = sprintf(buff, "%lu", ms); HTMemLog_add(buff, len); return ms; }
PRIVATE int HTBufferWriter_write (HTOutputStream * me, const char * buf, int len) { int status; while (1) { int available = me->data + me->allocated - me->read; /* If we have enough buffer space */ if (len <= available) { int size = 0; memcpy(me->read, buf, len); me->read += len; /* If we have accumulated enough data then flush */ if ((size = me->read - me->data) > me->growby) { me->lastFlushTime = HTGetTimeInMillis(); status = PUTBLOCK(me->data, size); if (status == HT_OK) { me->read = me->data; } else { return (status == HT_WOULD_BLOCK) ? HT_OK : HT_ERROR; } } return HT_OK; } else { /* Fill the existing buffer (if not already) and flush */ if (available) { memcpy(me->read, buf, available); buf += available; len -= available; me->read += available; } me->lastFlushTime = HTGetTimeInMillis(); status = PUTBLOCK(me->data, me->allocated); if (status == HT_OK) { me->read = me->data; } else if (status == HT_WOULD_BLOCK) { HTBufferWriter_addBuffer(me, len); memcpy(me->read, buf, len); me->read += len; return HT_OK; } } } }
/* ** This function is only called from either FlushEvent or HTBufferWriter_lazyFlush ** which means that only the host object or timeout can cause a flush */ PRIVATE int HTBufferWriter_flush (HTOutputStream * me) { int status = HT_OK; if (me && me->read > me->data) { me->lastFlushTime = HTGetTimeInMillis(); if ((status = PUTBLOCK(me->data, me->read - me->data))==HT_WOULD_BLOCK) return HT_WOULD_BLOCK; me->read = me->data; } return status; }
/* ** Check if the timer object has already expired */ PUBLIC BOOL HTTimer_hasTimerExpired (HTTimer * timer) { return (timer && timer->expires <= HTGetTimeInMillis()); }
PUBLIC HTTimer * HTTimer_new (HTTimer * timer, HTTimerCallback * cbf, void * param, ms_t millis, BOOL relative, BOOL repetitive) { HTList * last; HTList * cur; ms_t now = HTGetTimeInMillis(); ms_t expires; HTTimer * pres; CHECKME(timer); expires = millis; if (relative) expires += now; else millis = expires-now; if (Timers == NULL) Timers = HTList_new(); if (timer) { /* if a timer is specified, it should already exist */ if ((cur = HTList_elementOf(Timers, (void *)timer, &last)) == NULL) { HTDEBUGBREAK("Timer %p not found\n" _ timer); CLEARME(timer); return NULL; } HTList_quickRemoveElement(cur, last); HTTRACE(THD_TRACE, "Timer....... Found timer %p with callback %p, context %p, and %s timeout %d\n" _ timer _ cbf _ param _ relative ? "relative" : "absolute" _ millis); /* could optimize by sorting from last when ((HTList *)(last->object))->expires < expires (most common case) */ } else { /* create a new timer */ if ((timer = (HTTimer *) HT_CALLOC(1, sizeof(HTTimer))) == NULL) HT_OUTOFMEM("HTTimer_new"); last = Timers; HTTRACE(THD_TRACE, "Timer....... Created %s timer %p with callback %p, context %p, and %s timeout %d\n" _ repetitive ? "repetitive" : "one shot" _ timer _ cbf _ param _ relative ? "relative" : "absolute" _ millis); } /* ** Sort new element into list */ for (cur = last; (pres = (HTTimer *) HTList_nextObject(cur)) != NULL && pres->expires < expires; last = cur); /* ** If the expiration is 0 then we still register it but dispatch it immediately. */ if (!millis) HTTRACE(THD_TRACE, "Timer....... Timeout is 0 - expires NOW\n"); timer->expires = expires; timer->cbf = cbf; timer->param = param; timer->millis = millis; timer->relative = relative; timer->repetitive = repetitive; SETME(timer); /* ** add to list if timer is new */ cur = HTList_addList(last, (void *)timer); /* ** Call any platform specific timer handler */ if (SetPlatformTimer) SetPlatformTimer(timer); /* Check if the timer object has already expired. If so then dispatch */ if (timer->expires <= now) Timer_dispatch(cur, last); CLEARME(timer); return timer; }
/* ** There are now two versions of the event loop. The first is if you want ** to use async I/O on windows, and the other is if you want to use normal ** Unix setup with sockets */ PUBLIC int HTEventList_loop (HTRequest * theRequest) { #ifdef WWW_WIN_ASYNC MSG msg; int status; while (!HTEndLoop && GetMessage(&msg,0,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } status = HTEndLoop; /* Reset HTEndLoop in case we want to start again */ HTEndLoop = 0; return (status == 1 ? HT_OK : HT_ERROR); #else /* WWW_WIN_ASYNC */ fd_set treadset, twriteset, texceptset; struct timeval waittime, * wt; int active_sockets; int maxfds; ms_t timeout; ms_t now; SOCKET s; int status = HT_OK; /* Check that we don't have multiple loops started at once */ if (HTInLoop) { HTTRACE(THD_TRACE, "Event Loop.. Already one loop running - exiting\n"); return HT_ERROR; } HTInLoop = YES; /* Set up list of events - is kept around until EventOrder_deleteAll */ if (!EventOrderList) EventOrderList = HTList_new(); else EventOrder_clearAll(); /* Don't leave this loop until we leave the application */ while (!HTEndLoop) { /* ** Timeval struct copy needed for linux, as it set the value to the ** remaining timeout while exiting the select. (and perhaps for ** other OS). Code borrowed from X server. */ wt = NULL; if ((status = HTTimer_next(&timeout))) break; if (timeout != 0) { waittime.tv_sec = timeout / MILLI_PER_SECOND; waittime.tv_usec = (timeout % MILLI_PER_SECOND) * (1000000 / MILLI_PER_SECOND); wt = &waittime; } /* ** Check whether we still have to continue the event loop. It could ** be that one of the timer handlers ended the loop. */ if (HTEndLoop) break; /* ** Now we copy the current active file descriptors to pass them to select. */ treadset = FdArray[HTEvent_INDEX(HTEvent_READ)]; twriteset = FdArray[HTEvent_INDEX(HTEvent_WRITE)]; texceptset = FdArray[HTEvent_INDEX(HTEvent_OOB)]; /* And also get the max socket value */ maxfds = MaxSock; HTTRACE(THD_TRACE, "Event Loop.. calling select: maxfds is %d\n" _ maxfds); #ifdef HTDEBUG fd_dump(maxfds, &treadset, &twriteset, &texceptset, wt); #endif #ifdef __hpux active_sockets = select(maxfds+1, (int *)&treadset, (int *)&twriteset, (int *)&texceptset, wt); #elif defined(_WINSOCKAPI_) /* * [email protected] * * On some WINSOCK versions select() with 3 empty sets and NULL timeout * returns 0 and in some it returns -1. * If 0 is returned in such situation, we will go into an infinite loop * (cause the sets will stay empty forever ...), * so make sure to set the active_sockets = -1 which will take us out * of the loop. */ if ((treadset.fd_count || twriteset.fd_count || texceptset.fd_count) && wt) active_sockets = select(maxfds+1, &treadset, &twriteset, &texceptset, wt); else active_sockets = -1; #else active_sockets = select(maxfds+1, &treadset, &twriteset, &texceptset, wt); #endif now = HTGetTimeInMillis(); HTTRACE(THD_TRACE, "Event Loop.. select returns %d\n" _ active_sockets); #ifdef HTDEBUG fd_dump(maxfds, &treadset, &twriteset, &texceptset, wt); #endif if (active_sockets == -1) { #ifdef EINTR if (socerrno == EINTR) { /* ** EINTR The select() function was interrupted before any ** of the selected events occurred and before the ** timeout interval expired. ** ** If SA_RESTART has been set for the interrupting ** signal, it is implementation-dependent whether ** select() restarts or returns with EINTR. */ HTTRACE(THD_TRACE, "Event Loop.. select was interruted - try again\n"); continue; } #endif /* EINTR */ #ifdef EBADF if (socerrno == EBADF) { /* ** EBADF One or more of the file descriptor sets specified ** a file descriptor that is not a valid open file ** descriptor. */ HTTRACE(THD_TRACE, "Event Loop.. One or more sockets were not through their connect phase - try again\n"); continue; } #endif HTTRACE(THD_TRACE, "Event Loop.. select returned error %d\n" _ socerrno); #ifdef HTDEBUG EventList_dump(); #endif /* HTDEBUG */ status = HT_ERROR; break; } /* ** We had a timeout so now we check and see if we have a timeout ** handler to call. Let HTTimer_next get it. */ if (active_sockets == 0) continue; /* There were active sockets. Determine which fd sets they were in */ for (s = 0 ; s <= maxfds ; s++) { if (FD_ISSET(s, &texceptset)) if ((status = EventOrder_add(s, HTEvent_OOB, now)) != HT_OK) goto stop_loop; if (FD_ISSET(s, &twriteset)) if ((status = EventOrder_add(s, HTEvent_WRITE, now)) != HT_OK) goto stop_loop; if (FD_ISSET(s, &treadset)) if ((status = EventOrder_add(s, HTEvent_READ, now)) != HT_OK) goto stop_loop; } if ((status = EventOrder_executeAndDelete()) != HT_OK) break; }; /* Reset HTEndLoop in case we want to start again */ stop_loop: HTEndLoop = 0; HTInLoop = NO; return status; #endif /* !WWW_WIN_ASYNC */ }