/* * Write the first <size> bytes of <data> to <fd>, for which the disOnData function must have been * called previously. You may write to <fd> via any other means, but if you use this function the * data will be written out, possibly piece by piece but always without blocking, when the given * file descriptor becomes writable. */ void disWrite(Dispatcher *dis, int fd, const char *data, size_t size) { DIS_File *file = paGet(&dis->files, fd); dbgAssert(stderr, file != NULL, "unknown file descriptor: %d\n", fd); bufAdd(&file->outgoing, data, size); }
/* * Handle readable file descriptor <fd>. */ void disHandleReadable(Dispatcher *dis, int fd) { DIS_File *file = paGet(&dis->files, fd); dbgAssert(stderr, file != NULL, "unknown file descriptor: %d\n", fd); file->cb(dis, fd, (void *) file->udata); }
/* * Drop the subscription on file descriptor <fd>. */ void disDropData(Dispatcher *dis, int fd) { DIS_File *file = paGet(&dis->files, fd); P dbgPrint(stderr, "Dropping fd %d\n", fd); dbgAssert(stderr, file != NULL, "unknown file descriptor: %d\n", fd); paDrop(&dis->files, fd); }
/* * Handle writable file descriptor <fd>. */ void disHandleWritable(Dispatcher *dis, int fd) { int r; DIS_File *file = paGet(&dis->files, fd); dbgAssert(stderr, file != NULL, "unknown file descriptor: %d\n", fd); r = write(fd, bufGet(&file->outgoing), bufLen(&file->outgoing)); if (r > 0) { bufTrim(&file->outgoing, r, 0); } }
static void ns_handle_data(Dispatcher *dis, int fd, void *udata) { char data[9000]; NS *ns = (NS *) dis; NS_Connection *conn = paGet(&ns->connections, fd); int n; P dbgPrint(stderr, "Reading from fd %d...\n", fd); n = read(fd, data, sizeof(data)); P dbgPrint(stderr, "read() returned %d.\n", n); if (n > 0) { P dbgPrint(stderr, "Adding to incoming buffer.\n"); bufAdd(&conn->incoming, data, n); if (ns->on_socket_cb != NULL) { P dbgPrint(stderr, "Calling on_socket_cb.\n"); ns->on_socket_cb(ns, fd, bufGet(&conn->incoming), bufLen(&conn->incoming), ns->on_socket_udata); } } else if (n == 0) { P dbgPrint(stderr, "End of file, disconnecting.\n"); nsDisconnect(ns, fd); if (ns->on_disconnect_cb != NULL) { P dbgPrint(stderr, "Calling on_disconnect_cb.\n"); ns->on_disconnect_cb(ns, fd, ns->on_disconnect_udata); } } else { P dbgPrint(stderr, "Error, disconnecting.\n"); nsDisconnect(ns, fd); if (ns->on_error_cb != NULL) { P dbgPrint(stderr, "Calling on_error_cb.\n"); ns->on_error_cb(ns, fd, errno, ns->on_error_udata); } } }
/* * Disconnect from a file descriptor that was returned earlier using nsConnect(). */ void nsDisconnect(NS *ns, int fd) { NS_Connection *conn = paGet(&ns->connections, fd); P dbgPrint(stderr, "Closing fd %d\n", fd); close(fd); disDropData(&ns->dis, fd); paDrop(&ns->connections, fd); bufReset(&conn->incoming); free(conn); }
/* * Arrange for <cb> to be called when there is data available on file descriptor <fd>. <cb> will be * called with the given <dis>, <fd> and <udata>, which is a pointer to "user data" that will be * returned <cb> as it was given here, and that will not be accessed by dis in any way. */ void disOnData(Dispatcher *dis, int fd, void (*cb)(Dispatcher *dis, int fd, void *udata), const void *udata) { DIS_File *file; dbgAssert(stderr, fd >= 0, "bad file descriptor: %d\n", fd); P dbgPrint(stderr, "Adding file on fd %d\n", fd); if ((file = paGet(&dis->files, fd)) == NULL) { file = calloc(1, sizeof(DIS_File)); paSet(&dis->files, fd, file); } file->cb = cb; file->udata = udata; }
/* * Close dispatcher <dis>. This removes all file descriptors and timers, which will * cause disRun() to return. */ void disClose(Dispatcher *dis) { int fd; DIS_Timer *timer; for (fd = 0; fd < paCount(&dis->files); fd++) { DIS_File *file = paGet(&dis->files, fd); if (file != NULL) { paDrop(&dis->files, fd); bufReset(&file->outgoing); free(file); } } while ((timer = listRemoveHead(&dis->timers)) != NULL) { free(timer); } }
/* * Prepare a call to select() based on the files and timeouts set in <dis>. The necessary parameters * to select() are returned through <nfds>, <rfds>, <wfds> and <tv> (exception-fds should be set to * NULL). <*tv> is set to point to an appropriate timeout value, or NULL if no timeout is to be set. * This function will clear <rfds> and <wfds>, so if callers want to add their own file descriptors, * they should do so after calling this function. This function returns -1 if the first timeout * should already have occurred, otherwise 0. */ int disPrepareSelect(Dispatcher *dis, int *nfds, fd_set *rfds, fd_set *wfds, struct timeval **tv) { int fd; double delta_t; DIS_Timer *timer; *nfds = paCount(&dis->files); FD_ZERO(rfds); FD_ZERO(wfds); P dbgPrint(stderr, "File descriptors:"); for (fd = 0; fd < *nfds; fd++) { DIS_File *file = paGet(&dis->files, fd); if (file == NULL) continue; P fprintf(stderr, " %d", fd); FD_SET(fd, rfds); if (bufLen(&file->outgoing) > 0) FD_SET(fd, wfds); P { fprintf(stderr, " (%s%s)", FD_ISSET(fd, rfds) ? "r" : "", FD_ISSET(fd, wfds) ? "w" : ""); } } P fprintf(stderr, "\n"); P dbgPrint(stderr, "%d pending timers:\n", listLength(&dis->timers)); P for (timer = listHead(&dis->timers); timer; timer = listNext(timer)) { fprintf(stderr, "\t%f seconds\n", timer->t - nowd()); } if ((timer = listHead(&dis->timers)) == NULL) { *tv = NULL; } else if ((delta_t = timer->t - nowd()) < 0) { #if 0 P dbgPrint(stderr, "First timer %f seconds ago, return -1\n", -delta_t); return -1; #endif dis->tv.tv_sec = 0; dis->tv.tv_usec = 0; *tv = &dis->tv; } else { P dbgPrint(stderr, "First timer in %f seconds.\n", delta_t); dis->tv.tv_sec = (int) delta_t; dis->tv.tv_usec = 1000000 * fmod(delta_t, 1.0); *tv = &dis->tv; } return 0; }
/* * Return TRUE if <fd> has been given to <dis> using disOnData(), FALSE otherwise. */ int disOwnsFd(Dispatcher *dis, int fd) { return (paGet(&dis->files, fd) != NULL); }
int main(int argc, char *argv[]) { PointerArray pa = { 0 }; paSet(&pa, 0, (void *) 0x1); make_sure_that(paCount(&pa) == 1); make_sure_that(paGet(&pa, 0) == (void *) 0x1); make_sure_that(paGet(&pa, 1) == NULL); make_sure_that(paGet(&pa, 2) == NULL); paSet(&pa, 2, (void *) 0x3); make_sure_that(paCount(&pa) == 3); make_sure_that(paGet(&pa, 0) == (void *) 0x1); make_sure_that(paGet(&pa, 1) == NULL); make_sure_that(paGet(&pa, 2) == (void *) 0x3); paSet(&pa, 1, (void *) 0x2); make_sure_that(paCount(&pa) == 3); make_sure_that(paGet(&pa, 0) == (void *) 0x1); make_sure_that(paGet(&pa, 1) == (void *) 0x2); make_sure_that(paGet(&pa, 2) == (void *) 0x3); paDrop(&pa, 0); make_sure_that(paCount(&pa) == 3); make_sure_that(paGet(&pa, 0) == NULL); make_sure_that(paGet(&pa, 1) == (void *) 0x2); make_sure_that(paGet(&pa, 2) == (void *) 0x3); paDrop(&pa, 2); make_sure_that(paCount(&pa) == 2); make_sure_that(paGet(&pa, 0) == NULL); make_sure_that(paGet(&pa, 1) == (void *) 0x2); make_sure_that(paGet(&pa, 2) == NULL); paDrop(&pa, 1); make_sure_that(paCount(&pa) == 0); make_sure_that(paGet(&pa, 0) == NULL); make_sure_that(paGet(&pa, 1) == NULL); make_sure_that(paGet(&pa, 2) == NULL); paClear(&pa); }
/* * Discard the first <length> bytes of the incoming buffer for file descriptor <fd> (becuase you've * processed them). */ void nsDiscard(NS *ns, int fd, int length) { NS_Connection *conn = paGet(&ns->connections, fd); bufTrim(&conn->incoming, length, 0); }
/* * Return the number of bytes available in the incoming buffer for <fd>. */ int nsAvailable(NS *ns, int fd) { NS_Connection *conn = paGet(&ns->connections, fd); return bufLen(&conn->incoming); }
/* * Return a pointer to the start of the incoming buffer. */ const char *nsIncoming(NS *ns, int fd) { NS_Connection *conn = paGet(&ns->connections, fd); return bufGet(&conn->incoming); }