/* * scamper_writebuf.c: use in combination with select to send without blocking * * $Id: scamper_writebuf.c,v 1.16 2007/05/10 01:59:40 mjl Exp $ * * Matthew Luckie * * Copyright (C) 2004-2007 The University of Waikato * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #if defined(__APPLE__) #include #endif #include #include #include #include #include #include #include #include #include #include #if defined(DMALLOC) #include #endif #include "scamper_fds.h" #include "scamper_writebuf.h" #include "mjl_list.h" #include "utils.h" struct scamper_writebuf { scamper_fd_t *fdn; slist_t *iovs; int error; }; /* * scamper_writebuf_callback * * this function is called by the scamper_fd code whenever the fd is ready to * write to. */ static void scamper_writebuf_callback(int fd, void *param) { scamper_writebuf_t *wb = (scamper_writebuf_t *)param; struct msghdr msg; struct iovec *iov; uint8_t *bytes; ssize_t size; slist_node_t *node; int i, iovs; assert(scamper_fd_fd_get(wb->fdn) == fd); /* * if this callback was called, but there is no outstanding data to * send, then withdraw the entry from the fd monitoring module */ if((iovs = slist_count(wb->iovs)) == 0) { scamper_fd_write_pause(wb->fdn); return; } /* * if there is only one iovec, or we can't allocate an array large enough * for the backlog, then just send the first without allocating the * array. otherwise, fill the array with the iovecs to send. */ if(iovs == 1 || (iov = malloc(iovs * sizeof(struct iovec))) == NULL) { iov = slist_head_get(wb->iovs); iovs = 1; } else { node = slist_head_node(wb->iovs); for(i=0; i 1) { free(iov); } /* * if we encountered an error, then note the error for the next time * scamper_writebuf_send is called */ if(size == -1) { if(errno != EAGAIN) { wb->error = errno; } return; } /* free up the iovecs that have been sent */ while(size > 0) { node = slist_head_node(wb->iovs); iov = slist_node_item(node); /* if the whole iovec was used then it can be free'd */ if(iov->iov_len <= (size_t)size) { size -= iov->iov_len; free(iov->iov_base); free(iov); slist_head_pop(wb->iovs); continue; } /* if this iovec was only partially sent, then shift the vec */ bytes = iov->iov_base; memmove(iov->iov_base, bytes + size, iov->iov_len - size); iov->iov_len -= size; break; } /* if all the iovecs are sent, withdraw the fd monitor */ if(slist_count(wb->iovs) == 0) { scamper_fd_write_pause(wb->fdn); } return; } /* * scamper_writebuf_flush * * the caller wants anything buffered to be flushed now. probably because * the caller wants to close the fd afterwards. */ int scamper_writebuf_flush(scamper_writebuf_t *wb) { int fd = scamper_fd_fd_get(wb->fdn); assert(wb != NULL); scamper_writebuf_callback(fd, wb); return 0; } /* * scamper_writebuf_send * * register an iovec to send when it can be sent without blocking the * rest of scamper. */ int scamper_writebuf_send(scamper_writebuf_t *wb, void *data, size_t len) { struct iovec *iov; /* make sure there is data to send */ if(len < 1) { return 0; } /* * an error occured last time sendmsg(2) was called which makes this * writebuf invalid */ if(wb->error != 0) { return -1; } /* allocate the iovec and fill it out */ if((iov = malloc(sizeof(struct iovec))) == NULL) { return -1; } if((iov->iov_base = malloc(len)) == NULL) { free(iov); return -1; } memcpy(iov->iov_base, data, len); iov->iov_len = len; /* put the iovec at the tail of iovecs to send */ if(slist_tail_push(wb->iovs, iov) == NULL) { free(iov->iov_base); free(iov); return -1; } scamper_fd_write_unpause(wb->fdn); return 0; } /* * scamper_writebuf_free * */ void scamper_writebuf_free(scamper_writebuf_t *wb) { struct iovec *iov; if(wb == NULL) { return; } if(wb->iovs != NULL) { while((iov = slist_head_pop(wb->iovs)) != NULL) { free(iov->iov_base); free(iov); } slist_free(wb->iovs); } free(wb); return; } /* * scamper_writebuf_alloc * */ scamper_writebuf_t *scamper_writebuf_alloc(scamper_fd_t *fdn) { scamper_writebuf_t *wb = NULL; if((wb = malloc_zero(sizeof(scamper_writebuf_t))) == NULL) { goto err; } if((wb->iovs = slist_alloc()) == NULL) { goto err; } scamper_fd_write_set(fdn, scamper_writebuf_callback, wb); wb->fdn = fdn; return wb; err: scamper_writebuf_free(wb); return NULL; }