sqlite3_blob* BlobLocator::openBlob(const String& streamName, bool readonly, bool throwEx) { Database::Query* query = prepareSelect(); query->reset(); query->bind(1, streamName); sqlite3_blob* blob = NULL; if (query->step()) { const char* db = query->getDatabaseName(0); const char* tbl = query->getTableName(0); const char* column = _blobColumnName.c_str(); int64 rowid = query->getInt64(0); blob = openBlob(db, tbl, column, rowid, readonly, throwEx); } query->reset(); if (blob) return blob; if (throwEx) { NIT_THROW_FMT(EX_NOT_FOUND, "can't find blob '%s'", makeUrl(streamName).c_str()); } return NULL; }
void Blackboard::eventLoop(int _socketListener) { // set the listener socket socketListener = _socketListener; // loop forever while (true) { prepareSelect(); // prepare for select call by collecting every open fd if (WORDY) log("watching %d connections\n", (unsigned int) fdSet.size()); // block until one or more fd need attention or timeout int selectValue = select(maxfd + 1, &fd_r, &fd_w, &fd_x, &select_tv); if (selectValue < 0) { // check that select returned properly, if not, check why if (errno == EBADF) { // lost track of a fd (should never happen) log("invalid fd (closed connection) of a set of %d.\n", (unsigned int) fdSet.size()); // find out which fd we lost track of... // collect them all struct pollfd fds[fdSet.size()]; int i = 0; for (std::map<int, ConnectionDetails>::iterator it = fdSet.begin(); it != fdSet.end(); ++it) { int fd = it->first; fds[i].fd = fd; fds[i].events = POLLWRNORM | POLLRDBAND; ++i; } // check them all int pollret = 0; pollret = poll(fds, i, -1); if (pollret >= 0) { for (int j = 0; j < i; j++) { if (fds[j].revents == POLLNVAL) { log("%#010x was closed but never cleaned, marking it as dirty\n", fds[j].fd); // push the local connection to the deleteQueue for cleaning fdDeleteQueue.push_back(fds[j].fd); } } } else { // everything checks out but select still failed, panic. log("Unidentified non-cleaned fd: Blackboard cannot continue. (%d)\n\n", pollret); exit(1); } cleanClosedConnection(); // clean up anything we found continue; // restart the event loop } errorOnFail(FAIL, "select"); // select failed for some other reason } // if we have a new connection, accept it and add it to the fdSet if (FD_ISSET(socketListener, &fd_r)) { log("%#010x attempts connect... ", socketListener); int newfd = accept(socketListener, NULL, NULL); if (newfd >= 0) { log("got connection %#010x\n", newfd); // once we accepted the connection create a new entry for it. fdSet.insert(std::pair<int, ConnectionDetails > (newfd, ConnectionDetails())); } else { log("connect failed.\n"); } } // TODO: schedule connection handling in a more fair manner // currently: lowest descriptor first, all packets then yield // problems: starvation to newer connections // if packet rate is too fast, we never yield // loop over all the connections we have and check if they need anything for (std::map<int, ConnectionDetails>::iterator it = fdSet.begin(); it != fdSet.end(); ++it) { // assign values for what we are accessing and dealing with int fd = it->first; ConnectionDetails& cd = it->second; // check if we can read anything (or the connection has closed (gracefully or otherwise) if (FD_ISSET(fd, &fd_r)) { if (WORDY) log("%#010x recv\n", fd); Packet vecbuffer; vecbuffer.resize(MAX_BUFFER_SIZE, 0); // allocate memory for a full read int recvResult = recv(fd, &(vecbuffer.front()), vecbuffer.size(), 0); errorOnFail(recvResult < 0, "%#010x recv failed\n"); if (recvResult > 0) { // have something vecbuffer.resize(recvResult); // trim buffer size // TODO: consider a handle queue for packet scheduling handlePacket(fd, vecbuffer); // hand packet off the the handler } else { // recvResult == 0 implies the connection has gone away log("%#010x close\n", fd); close(fd); // close it on our end // we cannot remove the fd from the set just yet so save it to be swept up later fdDeleteQueue.push_back(fd); if (cd.sendQueue.size() > 0) { log("%#010x died with unsent packets\n", fd); } cd.sendQueue.clear(); // clear the sendQueue so even if we can write for some reason, we don't } } // check if we can write if (FD_ISSET(fd, &fd_w)) { // TODO: limit the amount to send while ((cd.sendQueue.size() > 0)) { // have something to send? Packet& packet = cd.sendQueue.front(); // get it if (WORDY) log("%#010x send\n", fd); // try to send it (do not block though) int sendResult = send(fd, &(packet.front()), packet.size(), MSG_DONTWAIT); // was data sent? if so remove it from the sent queue // if not, leave it there if it would block if (sendResult >= 0) { cd.sendQueue.pop_front(); } else { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { break; // would block so try again later } else { errorOnFail(FAIL, "send failed\n"); } } } } } if (fdDeleteQueue.size() > 0) { // IF any process asked to close, clean them // we must erase outside of the iterator to avoid SIGSEGV due to invalidated iterator log("%d connections to be cleaned.\n", (unsigned int) fdDeleteQueue.size()); cleanClosedConnection(); } } }