static gsize _tgentransfer_flushOut(TGenTransfer* transfer) { TGEN_ASSERT(transfer); if(!transfer->writeBuffer) { return 0; } gchar* position = &(transfer->writeBuffer->str[transfer->writeBufferOffset]); gsize length = transfer->writeBuffer->len - transfer->writeBufferOffset; gssize bytes = tgentransport_write(transfer->transport, position, length); if(bytes < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { _tgentransfer_changeState(transfer, TGEN_XFER_ERROR); _tgentransfer_changeError(transfer, TGEN_XFER_ERR_WRITE); tgen_critical("write(): transport %s transfer %s error %i: %s", tgentransport_toString(transfer->transport), _tgentransfer_toString(transfer), errno, g_strerror(errno)); } else if(bytes == 0) { _tgentransfer_changeState(transfer, TGEN_XFER_ERROR); _tgentransfer_changeError(transfer, TGEN_XFER_ERR_WRITE); tgen_critical("write(): transport %s transfer %s closed unexpectedly", tgentransport_toString(transfer->transport), _tgentransfer_toString(transfer)); } else if(bytes > 0) { transfer->writeBufferOffset += bytes; if(transfer->writeBufferOffset >= (transfer->writeBuffer->len - 1)) { transfer->writeBufferOffset = 0; g_string_free(transfer->writeBuffer, TRUE); transfer->writeBuffer = NULL; } transfer->bytes.totalWrite += bytes; return (gsize) bytes; } return 0; }
GQueue* tgengraph_getNextActions(TGenGraph* g, TGenAction* action) { TGEN_ASSERT(g); /* given an action, get all of the next actions in the dependency graph */ gpointer key = tgenaction_getKey(action); igraph_integer_t srcVertexIndex = (igraph_integer_t) GPOINTER_TO_INT(key); /* initialize a vector to hold the result neighbor vertices for this action */ igraph_vector_t* resultNeighborVertices = g_new0(igraph_vector_t, 1); /* initialize with 0 entries, since we dont know how many neighbors we have */ gint result = igraph_vector_init(resultNeighborVertices, 0); if(result != IGRAPH_SUCCESS) { tgen_critical("igraph_vector_init return non-success code %i", result); g_free(resultNeighborVertices); return FALSE; } /* now get all outgoing 1-hop neighbors of the given action */ result = igraph_neighbors(g->graph, resultNeighborVertices, srcVertexIndex, IGRAPH_OUT); if(result != IGRAPH_SUCCESS) { tgen_critical("igraph_neighbors return non-success code %i", result); igraph_vector_destroy(resultNeighborVertices); g_free(resultNeighborVertices); return NULL; } /* handle the results */ glong nVertices = igraph_vector_size(resultNeighborVertices); tgen_debug("found %li neighbors to vertex %i", nVertices, (gint)srcVertexIndex); GQueue* nextActions = g_queue_new(); for (gint i = 0; i < nVertices; i++) { igraph_integer_t dstVertexIndex = igraph_vector_e(resultNeighborVertices, i); TGenAction* nextAction = _tgengraph_getAction(g, dstVertexIndex); if(nextAction) { g_queue_push_tail(nextActions, nextAction); } } /* cleanup */ igraph_vector_destroy(resultNeighborVertices); g_free(resultNeighborVertices); return nextActions; }
void tgenio_loopOnce(TGenIO* io) { TGEN_ASSERT(io); /* storage for collecting events from our epoll descriptor */ struct epoll_event epevs[100]; memset(epevs, 0, 100*sizeof(struct epoll_event)); /* collect all events that are ready */ gint nfds = epoll_wait(io->epollD, epevs, 100, 0); if(nfds == -1) { tgen_critical("epoll_wait(): epoll %i returned %i error %i: %s", io->epollD, nfds, errno, g_strerror(errno)); } if(nfds <= 0) { return; } /* activate correct component for every descriptor that's ready. */ for (gint i = 0; i < nfds; i++) { gboolean in = (epevs[i].events & EPOLLIN) ? TRUE : FALSE; gboolean out = (epevs[i].events & EPOLLOUT) ? TRUE : FALSE; TGenIOChild* child = g_hash_table_lookup(io->children, &epevs[i].data.fd); _tgenio_helper(io, child, in, out); } }
gboolean tgenio_register(TGenIO* io, gint descriptor, TGenIO_notifyEventFunc notify, TGenIO_notifyCheckTimeoutFunc checkTimeout, gpointer data, GDestroyNotify destructData) { TGEN_ASSERT(io); if(g_hash_table_lookup(io->children, &descriptor)) { _tgenio_deregister(io, descriptor); } /* start watching */ struct epoll_event ee; memset(&ee, 0, sizeof(struct epoll_event)); ee.events = EPOLLIN|EPOLLOUT; ee.data.fd = descriptor; gint result = epoll_ctl(io->epollD, EPOLL_CTL_ADD, descriptor, &ee); if (result != 0) { tgen_critical("epoll_ctl(): epoll %i socket %i returned %i error %i: %s", io->epollD, descriptor, result, errno, g_strerror(errno)); return FALSE; } TGenIOChild* child = _tgeniochild_new(descriptor, notify, checkTimeout, data, destructData); g_hash_table_replace(io->children, &child->descriptor, child); return TRUE; }
static GError* _tgengraph_parseSynchronizeVertex(TGenGraph* g, const gchar* idStr, igraph_integer_t vertexIndex) { TGEN_ASSERT(g); tgen_debug("found vertex %li (%s)", (glong)vertexIndex, idStr); /* Count up the total incoming edges */ /* initialize a vector to hold the result neighbor vertices for this action */ igraph_vector_t* resultNeighborVertices = g_new0(igraph_vector_t, 1); /* initialize with 0 entries, since we dont know how many neighbors we have */ gint result = igraph_vector_init(resultNeighborVertices, 0); if(result != IGRAPH_SUCCESS) { tgen_critical("igraph_vector_init return non-success code %i", result); g_free(resultNeighborVertices); return FALSE; } /* now get all incoming 1-hop neighbors of the given action */ result = igraph_neighbors(g->graph, resultNeighborVertices, vertexIndex, IGRAPH_IN); if(result != IGRAPH_SUCCESS) { tgen_critical("igraph_neighbors return non-success code %i", result); igraph_vector_destroy(resultNeighborVertices); g_free(resultNeighborVertices); return NULL; } /* handle the results */ glong totalIncoming = igraph_vector_size(resultNeighborVertices); tgen_debug("found %li neighbors to vertex %i", totalIncoming, (gint)vertexIndex); /* cleanup */ igraph_vector_destroy(resultNeighborVertices); g_free(resultNeighborVertices); GError* error = NULL; TGenAction* a = tgenaction_newSynchronizeAction(totalIncoming, &error); if(a) { _tgengraph_storeAction(g, a, vertexIndex); } return error; }
static void _tgentransfer_readResponse(TGenTransfer* transfer) { TGEN_ASSERT(transfer); if(_tgentransfer_getLine(transfer)) { /* we have read the entire command from the other end */ gboolean hasError = FALSE; transfer->time.response = g_get_monotonic_time(); gchar* line = g_string_free(transfer->readBuffer, FALSE); transfer->readBuffer = NULL; gchar** parts = g_strsplit(line, " ", 0); if(parts[0] == NULL || parts[1] == NULL) { tgen_critical("error parsing command '%s'", transfer->readBuffer->str); hasError = TRUE; } else { g_assert(!transfer->remoteName); transfer->remoteName = g_strdup(parts[0]); transfer->remoteCount = (gsize)g_ascii_strtoull(parts[1], NULL, 10); if(transfer->remoteCount == 0) { tgen_critical("error parsing command ID '%s'", parts[1]); hasError = TRUE; } } g_strfreev(parts); g_free(line); /* payload phase is next unless there was an error parsing */ if(hasError) { _tgentransfer_changeState(transfer, TGEN_XFER_ERROR); _tgentransfer_changeError(transfer, TGEN_XFER_ERR_READ); } else { /* we need to update our string with the new command info */ _tgentransfer_resetString(transfer); _tgentransfer_changeState(transfer, TGEN_XFER_PAYLOAD); if(transfer->state == TGEN_TYPE_PUT) { transfer->events |= TGEN_EVENT_WRITE; } } } else { /* unable to receive entire command, wait for next chance to read */ } }
static void _tgentransfer_readPayload(TGenTransfer* transfer) { TGEN_ASSERT(transfer); guchar buffer[65536]; /* keep reading until blocked */ while(TRUE) { gsize length = MIN(65536, (transfer->size - transfer->bytes.payloadRead)); if(length > 0) { /* we need to read more payload */ gssize bytes = tgentransport_read(transfer->transport, buffer, length); if(bytes < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { _tgentransfer_changeState(transfer, TGEN_XFER_ERROR); _tgentransfer_changeError(transfer, TGEN_XFER_ERR_READ); tgen_critical("read(): transport %s transfer %s error %i: %s", tgentransport_toString(transfer->transport), _tgentransfer_toString(transfer), errno, g_strerror(errno)); } else if(bytes == 0) { _tgentransfer_changeState(transfer, TGEN_XFER_ERROR); _tgentransfer_changeError(transfer, TGEN_XFER_ERR_READ); tgen_critical("read(): transport %s transfer %s closed unexpectedly", tgentransport_toString(transfer->transport), _tgentransfer_toString(transfer)); } else if(bytes > 0) { if(transfer->bytes.payloadRead == 0) { transfer->time.firstPayloadByte = g_get_monotonic_time(); } transfer->bytes.payloadRead += bytes; transfer->bytes.totalRead += bytes; g_checksum_update(transfer->payloadChecksum, buffer, bytes); continue; } } else { /* payload done, send the checksum next */ _tgentransfer_changeState(transfer, TGEN_XFER_CHECKSUM); transfer->time.lastPayloadByte = g_get_monotonic_time(); } break; } }
TGenTransport* tgentransport_newActive(TGenPeer* proxy, TGenPeer* peer, TGenTransport_notifyBytesFunc notify, gpointer data, GDestroyNotify destructData) { gint64 started = g_get_monotonic_time(); /* create the socket and get a socket descriptor */ gint socketD = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); gint64 created = g_get_monotonic_time(); if (socketD < 0) { tgen_critical("socket(): returned %i error %i: %s", socketD, errno, g_strerror(errno)); return NULL; } /* connect to another host */ struct sockaddr_in master; memset(&master, 0, sizeof(master)); master.sin_family = AF_INET; /* if there is a proxy, we connect there; otherwise connect to the peer */ TGenPeer* connectee = proxy ? proxy : peer; /* its safe to do lookups on whoever we are directly connecting to. */ tgenpeer_performLookups(connectee); master.sin_addr.s_addr = tgenpeer_getNetworkIP(connectee); master.sin_port = tgenpeer_getNetworkPort(connectee); gint result = connect(socketD, (struct sockaddr *) &master, sizeof(master)); /* nonblocking sockets means inprogress is ok */ if (result < 0 && errno != EINPROGRESS) { tgen_critical("connect(): socket %i returned %i error %i: %s", socketD, result, errno, g_strerror(errno)); close(socketD); return NULL; } return _tgentransport_newHelper(socketD, started, created, proxy, peer, notify, data, destructData); }
static gboolean _tgentransfer_getLine(TGenTransfer* transfer) { TGEN_ASSERT(transfer); /* create a new buffer if we have not done that yet */ if(!transfer->readBuffer) { transfer->readBuffer = g_string_new(NULL); } gchar c; gssize bytes = 1; while(bytes > 0) { bytes = tgentransport_read(transfer->transport, &c, 1); if(bytes < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { _tgentransfer_changeState(transfer, TGEN_XFER_ERROR); _tgentransfer_changeError(transfer, TGEN_XFER_ERR_READ); tgen_critical("read(): transport %s transfer %s error %i: %s", tgentransport_toString(transfer->transport), _tgentransfer_toString(transfer), errno, g_strerror(errno)); } else if(bytes == 0) { _tgentransfer_changeState(transfer, TGEN_XFER_ERROR); _tgentransfer_changeError(transfer, TGEN_XFER_ERR_READ); tgen_critical("read(): transport %s transfer %s closed unexpectedly", tgentransport_toString(transfer->transport), _tgentransfer_toString(transfer)); } else if(bytes == 1) { transfer->bytes.totalRead += 1; if(c == '\n') { tgen_debug("finished receiving line: '%s'", transfer->readBuffer->str); return TRUE; } g_string_append_c(transfer->readBuffer, c); } } return FALSE; }
static igraph_t* _tgengraph_loadNewGraph(const gchar* path) { /* get the file */ FILE* graphFile = fopen(path, "r"); if(!graphFile) { tgen_critical("fopen returned NULL, problem opening graph file path '%s'", path); return FALSE; } tgen_info("reading graphml action graph at '%s'...", path); igraph_t* graph = g_new0(igraph_t, 1); gint result = igraph_read_graph_graphml(graph, graphFile, 0); fclose(graphFile); if(result != IGRAPH_SUCCESS) { tgen_critical("igraph_read_graph_graphml return non-success code %i", result); g_free(graph); return NULL; } tgen_info("successfully read graphml action graph at '%s'", path); return graph; }
TGenIO* tgenio_new() { /* create an epoll descriptor so we can manage events */ gint epollD = epoll_create(1); if (epollD < 0) { tgen_critical("epoll_create(): returned %i error %i: %s", epollD, errno, g_strerror(errno)); return NULL; } /* allocate the new server object and return it */ TGenIO* io = g_new0(TGenIO, 1); io->magic = TGEN_MAGIC; io->refcount = 1; io->children = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, (GDestroyNotify)_tgeniochild_free); io->epollD = epollD; return io; }
static TGenEvent _tgentransfer_runTransportEventLoop(TGenTransfer* transfer, TGenEvent events) { TGenEvent retEvents = tgentransport_onEvent(transfer->transport, events); if(retEvents == TGEN_EVENT_NONE) { /* proxy failed */ tgen_critical("proxy connection failed, transfer cannot begin"); _tgentransfer_changeState(transfer, TGEN_XFER_ERROR); _tgentransfer_changeError(transfer, TGEN_XFER_ERR_PROXY); _tgentransfer_log(transfer, FALSE); return TGEN_EVENT_DONE; } else { transfer->time.lastProgress = g_get_monotonic_time(); if(retEvents & TGEN_EVENT_DONE) { /* proxy is connected and ready, now its our turn */ return TGEN_EVENT_READ|TGEN_EVENT_WRITE; } else { /* proxy in progress */ return retEvents; } } }
gdouble tgengraph_getEdgeWeight(TGenGraph* g, TGenAction* srcAction, TGenAction* dstAction) { TGEN_ASSERT(g); /* given choose action, get the weights of the edges connected to it */ gpointer srcKey = tgenaction_getKey(srcAction); igraph_integer_t srcVertexIndex = (igraph_integer_t) GPOINTER_TO_INT(srcKey); gpointer dstKey = tgenaction_getKey(dstAction); igraph_integer_t dstVertexIndex = (igraph_integer_t) GPOINTER_TO_INT(dstKey); igraph_integer_t edgeIndex; gint result = igraph_get_eid(g->graph, &edgeIndex, srcVertexIndex, dstVertexIndex, IGRAPH_DIRECTED, TRUE); if(result != IGRAPH_SUCCESS) { tgen_critical("igraph_get_eid return non-success code %i", result); return FALSE; } gdouble* weight = _tgengraph_getWeight(g, edgeIndex); return (weight != NULL) ? *weight : 0.0; }
TGenGraph* tgengraph_new(gchar* path) { if(!path || !g_file_test(path, G_FILE_TEST_IS_REGULAR|G_FILE_TEST_EXISTS)) { tgen_critical("path '%s' to tgen config graph is not valid or does not exist", path); return NULL; } TGenGraph* g = g_new0(TGenGraph, 1); g->magic = TGEN_MAGIC; g->actions = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)tgenaction_unref); g->weights = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); g->graphPath = path ? _tgengraph_getHomePath(path) : NULL; GError* error = NULL; gboolean exists = g_file_test(g->graphPath, G_FILE_TEST_IS_REGULAR|G_FILE_TEST_EXISTS); if(!exists) { error = g_error_new(G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "graph file does not exist at path '%s'", g->graphPath); } if(!error && g->graphPath) { tgen_lock(); /* use the built-in C attribute handler */ igraph_attribute_table_t* oldHandler = igraph_i_set_attribute_table(&igraph_cattribute_table); g->graph = _tgengraph_loadNewGraph(g->graphPath); if(!g->graph) { error = g_error_new(G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "unable to read graph at path '%s'", g->graphPath); } /* parse edges first for choose, needs hash table of weights filled for error handling */ if(!error) { error = _tgengraph_parseGraphProperties(g); } if(!error) { error = _tgengraph_parseGraphEdges(g); } if(!error) { error = _tgengraph_parseGraphVertices(g); } /* replace the old handler */ igraph_i_set_attribute_table(oldHandler); tgen_unlock(); } if(error) { tgen_critical("error (%i) while loading graph: %s", error->code, error->message); g_error_free(error); tgengraph_free(g); return NULL; } tgen_message("successfully loaded graphml file '%s' and validated actions: " "graph is %s with %u %s, %u %s, and %u %s", g->graphPath, g->isConnected ? "weakly connected" : "disconnected", (guint)g->clusterCount, g->clusterCount == 1 ? "cluster" : "clusters", (guint)g->vertexCount, g->vertexCount == 1 ? "vertex" : "vertices", (guint)g->edgeCount, g->edgeCount == 1 ? "edge" : "edges"); return g; }
static void _tgentransfer_readCommand(TGenTransfer* transfer) { TGEN_ASSERT(transfer); if(_tgentransfer_getLine(transfer)) { /* we have read the entire command from the other end */ gboolean hasError = FALSE; transfer->time.command = g_get_monotonic_time(); gchar* line = g_string_free(transfer->readBuffer, FALSE); transfer->readBuffer = NULL; if(g_ascii_strncasecmp(line, TGEN_AUTH_PW, 20)) { /* password doesn't match */ tgen_info("transfer authentication error: passwords don't match") hasError = TRUE; _tgentransfer_changeState(transfer, TGEN_XFER_ERROR); _tgentransfer_changeError(transfer, TGEN_XFER_ERR_AUTH); } else { /* password matches, lets parse the rest of the string */ gchar** parts = g_strsplit(line, " ", 0); if(parts[0] == NULL || parts[1] == NULL || parts[2] == NULL || parts[3] == NULL || parts[4] == NULL || parts[5] == NULL) { tgen_critical("error parsing command '%s'", line); hasError = TRUE; } else { g_assert(!transfer->remoteName); transfer->remoteName = g_strdup(parts[1]); /* we are not the commander so we should not have an id yet */ g_assert(transfer->id == NULL); transfer->id = g_strdup(parts[2]); transfer->remoteCount = (gsize)g_ascii_strtoull(parts[3], NULL, 10); if(transfer->remoteCount == 0) { tgen_critical("error parsing command ID '%s'", parts[3]); hasError = TRUE; } if(!g_ascii_strncasecmp(parts[4], "GET", 3)) { /* they are trying to GET, then we need to PUT to them */ transfer->type = TGEN_TYPE_PUT; /* we read command, but now need to write payload */ transfer->events |= TGEN_EVENT_WRITE; } else if(!g_ascii_strncasecmp(parts[4], "PUT", 3)) { /* they want to PUT, so we will GET from them */ transfer->type = TGEN_TYPE_GET; } else { tgen_critical("error parsing command type '%s'", parts[4]); hasError = TRUE; } transfer->size = (gsize)g_ascii_strtoull(parts[5], NULL, 10); if(transfer->size == 0) { tgen_critical("error parsing command size '%s'", parts[5]); hasError = TRUE; } } g_strfreev(parts); g_free(line); /* payload phase is next unless there was an error parsing */ if(hasError) { _tgentransfer_changeState(transfer, TGEN_XFER_ERROR); _tgentransfer_changeError(transfer, TGEN_XFER_ERR_READ); } else { /* we need to update our string with the new command info */ _tgentransfer_resetString(transfer); _tgentransfer_changeState(transfer, TGEN_XFER_RESPONSE); transfer->events |= TGEN_EVENT_WRITE; } } } else { /* unable to receive entire command, wait for next chance to read */ } }
TGenTimer* tgentimer_new(guint64 milliseconds, gboolean isPersistent, TGenTimer_notifyExpiredFunc notify, gpointer data1, gpointer data2, GDestroyNotify destructData1, GDestroyNotify destructData2) { /* if they dont want to be notified of timer expirations, there is no point */ if(!notify) { return NULL; } /* create the timer descriptor */ int timerD = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK); if (timerD < 0) { tgen_critical("timerfd_create(): returned %i error %i: %s", timerD, errno, g_strerror(errno)); return NULL; } /* create the timer info */ struct itimerspec arm; guint64 seconds = milliseconds / 1000; guint64 nanoseconds = (milliseconds % 1000) * 1000000; /* a timer with 0 delay will cause timerfd to disarm, so we use a 1 nano * delay instead, in order to execute the event as close to now as possible */ if(seconds == 0 && nanoseconds == 0) { nanoseconds = 1; } /* set the initial expiration */ arm.it_value.tv_sec = seconds; arm.it_value.tv_nsec = nanoseconds; if(isPersistent) { /* timer continuously repeats */ arm.it_interval.tv_sec = seconds; arm.it_interval.tv_nsec = nanoseconds; } else { /* timer never repeats */ arm.it_interval.tv_sec = 0; arm.it_interval.tv_nsec = 0; } /* arm the timer, flags=0 -> relative time, NULL -> ignore previous setting */ gint result = timerfd_settime(timerD, 0, &arm, NULL); if (result < 0) { tgen_critical("timerfd_settime(): returned %i error %i: %s", result, errno, g_strerror(errno)); return NULL; } /* allocate the new server object and return it */ TGenTimer* timer = g_new0(TGenTimer, 1); timer->magic = TGEN_MAGIC; timer->refcount = 1; timer->notify = notify; timer->data1 = data1; timer->data2 = data2; timer->destructData1 = destructData1; timer->destructData2 = destructData2; timer->timerD = timerD; timer->isPersistent = isPersistent; return timer; }