[PATCH] tevent: add another example
Stefan (metze) Metzmacher
metze at samba.org
Fri Jul 18 00:57:48 MDT 2014
Hi Ralph,
some comments inline...
> +A simple example of nested subrequests can be found in the file
> +examples/multiply.c. It implements n * m multiplication by sending n *
> +add_subreq(m) subrequests.
> +
> A comprehensive example of nested subrequests can be found in the file
> examples/echo_server.c. It implements a complete, self-contained echo server with no
> dependencies but libevent and libtalloc.
> diff --git a/lib/tevent/examples/multiply.c b/lib/tevent/examples/multiply.c
> new file mode 100644
> index 0000000..57b71be
> --- /dev/null
> +++ b/lib/tevent/examples/multiply.c
> @@ -0,0 +1,258 @@
> +/**
> + ** NOTE! The following liberal license applies to this sample file only.
> + ** This does NOT imply that all of Samba is released under this license.
> + **
> + ** This file is meant as a starting point for libtevent users to be used
> + ** in any program linking against the LGPL licensed libtevent.
> + **/
> +
> +/*
> + * This file is being made available by the Samba Team under the following
> + * license:
> + *
> + * Permission to use, copy, modify, and distribute this sample file for any
> + * purpose is hereby granted without fee.
> + *
> + * This work 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.
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <netinet/in.h>
> +#include <sys/types.h>
> +#include <sys/socket.h>
> +#include <errno.h>
> +#include <unistd.h>
> +
> +#include "tevent.h"
> +#include "talloc.h"
> +
> +/**
> + * @brief Helper function to get a useful unix error from tevent_req
> + */
> +static bool tevent_req_is_unix_error(struct tevent_req *req, int *perrno)
> +{
> + enum tevent_req_state state;
> + uint64_t err;
> +
> + if (!tevent_req_is_error(req, &state, &err)) {
> + return false;
> + }
> + switch (state) {
> + case TEVENT_REQ_TIMED_OUT:
> + *perrno = ETIMEDOUT;
> + break;
> + case TEVENT_REQ_NO_MEMORY:
> + *perrno = ENOMEM;
> + break;
> + case TEVENT_REQ_USER_ERROR:
> + *perrno = err;
> + break;
> + default:
> + *perrno = EINVAL;
> + break;
> + }
> + return true;
> +}
> +
> +struct add_state {
> + long int sum;
> +};
> +
> +/*
> + * a "terminal" request withouth subrequests
> + */
> +static struct tevent_req *add_send(TALLOC_CTX *mem_ctx,
> + struct tevent_context *ev,
> + int sum, int operand)
> +{
> + struct tevent_req *req, *subreq;
> + struct add_state *state;
> +
> + req = tevent_req_create(mem_ctx, &state, struct add_state);
> + if (req == NULL) {
> + return NULL;
> + }
> +
> + /*
> + * No further async subrequests, computation simply takes
> + * place here
> + */
> + state->sum += operand;
> +
> + /*
> + * The event is already finished, tevent_req_post() to mark
> + * the event as finished before the caller could set the
> + * callback.
> + */
You need a tevent_req_done(req); here.
Otherwise add_recv() will return false.
> + return tevent_req_post(req, ev);
> +}
> +
> +/*
> + * Usually an async request would either have a handler function, when
> + * dealing with external event sources like file descriptors or
> + * signals. Or a *_done function as callback for subrequest that
> + * finished, cf mul_done() below.
> + */
This comments is confusing before the add_recv function.
You can have a large comment explaining the whole file
on top of the file.
> +/*
> + * returns the result of the add_send() request
> + */
> +static bool add_recv(struct tevent_req *req, long int *psum, int *perr)
> +{
> + struct add_state *state = tevent_req_data(req, struct add_state);
> + int err;
> +
> + if (!tevent_req_is_unix_error(req, &err)) {
> + *perr = err;
> + return false;
> + }
> +
> + *psum = state->sum;
> + return true;
> +}
> +
> +struct mul_state {
> + struct tevent_context *ev;
> + int num1;
> + int num2;
> + long result;
> +};
> +
> +static void mul_done(struct tevent_req *subreq);
> +
> +/*
> + * Create a multiplication request
> + *
> + * Decompose the n * m multiplication into a series of n * add_send(m)
> + * requests
> + */
> +static struct tevent_req *mul_send(TALLOC_CTX *mem_ctx,
> + struct tevent_context *ev,
> + int num1, int num2)
> +{
> + struct tevent_req *req, *subreq;
> + struct mul_state *state;
> +
> + req = tevent_req_create(mem_ctx, &state, struct mul_state);
> + if (req == NULL) {
> + return NULL;
> + }
> +
> + state->ev = ev;
> + state->num1 = num1;
> + state->num2 = num2;
> + state->result = 0;
> +
> + if (!num1 || !num2) {
> + tevent_req_done(req);
This seems to be wrong, after functions like tevent_req_done()
you're only allowed to directly return. Because we're in the _send()
function we need to return by calling return tevent_req_post(req, ev);
> + }
If we would have a mul_add_next() function (see below)
we could use
mul_add_next(req);
if (!tevent_req_is_in_progress(req)) {
return tevent_req_post(req, ev);
}
return req;
}
> + subreq = add_send(state, state->ev, state->result, state->num2);
> + if (tevent_req_nomem(subreq, req)) {
> + return tevent_req_post(req, ev);
> + }
> + tevent_req_set_callback(subreq, mul_done, req);
> + return req;
> +}
> +
> +/*
> + * This is the callback for add_send() subrequests. It's called
> + * mul_done() by convention, which doesn't mean the mul request is
> + * done/finished.
This is because it might be called multiple time.
> + * If it helps, read the XXX_done() as XXX_process_subreq_result()
mul_add_done() might be better name, as it indicates the one add_*()
operation
is done.
> + */
As this is called multiple time
> +static void mul_done(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
> + struct mul_state *state = tevent_req_data(req, struct mul_state);
> + bool ok;
> + long int result;
> + int err;
> +
> + ok = add_recv(subreq, &result, &err);
> + TALLOC_FREE(subreq);
> + if (!ok) {
> + tevent_req_error(req, err);
> + return;
> + }
> +
> + state->result += result;
> + state->num1--;
You could split the lines below into mul_add_next() function.
This would be moved before the mul_add_done() function then.
static void mul_add_next(struct tevent_req *req)
{
struct mul_state *state = tevent_req_data(req, struct mul_state);
> + if (state->num1 == 0) {
> + tevent_req_done(req);
> + return;
> + }
Having this in mul_add_next() will further simplify the mul_send() function.
if (state->num2 == 0) {
tevent_req_done(req);
return;
}
> + subreq = add_send(state, state->ev, state->result, state->num2);
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, mul_done, req);
> + return;
> +}
> +
> +/*
> + * Function called for retrieving the result of the request
> + */
> +static bool mul_recv(struct tevent_req *req, long int *psum, int *perr)
> +{
> + struct mul_state *state = tevent_req_data(req, struct mul_state);
> + int err;
> +
> + if (tevent_req_is_unix_error(req, &err)) {
> + *perr = err;
> + return false;
> + }
> +
> + *psum = state->result;
> + return true;
> +}
> +
> +int main(int argc, const char **argv)
> +{
> + int num1, num2, err;
> + struct tevent_context *ev;
> + struct tevent_req *req;
> + long int product;
> +
> + if (argc != 3) {
> + fprintf(stderr, "Usage: %s <number> <number>\n", argv[0]);
> + exit(1);
> + }
> +
> + num1 = atoi(argv[1]);
> + num2 = atoi(argv[2]);
> +
> + ev = tevent_context_init(NULL);
> + if (ev == NULL) {
> + fprintf(stderr, "tevent_context_init failed\n");
> + exit(1);
> + }
> +
> + req = mul_send(ev, ev, num1, num2);
> + if (req == NULL) {
> + fprintf(stderr, "mul_send failed\n");
> + exit(1);
> + }
Please use
ok = event_req_poll(req, ev);
if (!ok) {
> + if (!tevent_req_poll(req, ev)) {
> + perror("tevent_req_poll() failed");
> + exit(1);
> + }
Please use
ok = mul_recv(req, &product, &err);
if (!ok) {
> + if (!mul_recv(req, &product, &err)) {
> + fprintf(stderr, "mul_recv failed: %s\n", strerror(err));
> + exit(1);
> + }
> + TALLOC_FREE(req);
> +
> + printf("%d * %d = %ld\n", num1, num2, product);
> +
> + return 0;
> +}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 246 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20140718/9a349cc0/attachment.pgp>
More information about the samba-technical
mailing list