Exemple #1
0
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;
}
Exemple #2
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;
}
Exemple #3
0
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);
    }
}
Exemple #4
0
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;
}
Exemple #5
0
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;
}
Exemple #6
0
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 */
    }
}
Exemple #7
0
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;
    }
}
Exemple #8
0
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);
}
Exemple #9
0
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;
}
Exemple #10
0
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;
}
Exemple #11
0
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;
}
Exemple #12
0
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;
        }
    }
}
Exemple #13
0
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;
}
Exemple #14
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;
}
Exemple #15
0
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 */
    }
}
Exemple #16
0
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;
}