[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