2002-07-03 Marcus Brinkmann <marcus@g10code.de>

* gpgme.texi (Run Control): Update this section.
	(Waiting For Completion): Likewise for this subsection.
	(Cancelling an Operation): Likewise for this subsection.
	(Using External Event Loops): New subsection with several
	subsubsections.
This commit is contained in:
Marcus Brinkmann 2002-07-03 02:22:38 +00:00
parent d7b78c0987
commit ea0acccf2b
2 changed files with 579 additions and 14 deletions

View File

@ -1,3 +1,11 @@
2002-07-03 Marcus Brinkmann <marcus@g10code.de>
* gpgme.texi (Run Control): Update this section.
(Waiting For Completion): Likewise for this subsection.
(Cancelling an Operation): Likewise for this subsection.
(Using External Event Loops): New subsection with several
subsubsections.
2002-06-28 Marcus Brinkmann <marcus@g10code.de>
* gpgme.texi (Multi Threading): Remove item about the need to

View File

@ -185,6 +185,15 @@ Run Control
* Waiting For Completion:: Waiting until an operation is completed.
* Cancelling an Operation:: Interrupting a running operation.
* Hooking Up Into Idle Time:: Doing something when nothing has to be done.
* Using External Event Loops:: Advanced control over what happens when.
Using External Event Loops
* I/O Callback Interface:: How I/O callbacks are registered.
* Registering I/O Callbacks:: How to use I/O callbacks for a context.
* I/O Callback Example:: An example how to use I/O callbacks.
* I/O Callback Example GTK+:: How to integrate @acronym{GPGME} in GTK+.
* I/O Callback Example GDK:: How to integrate @acronym{GPGME} in GDK.
@end detailmenu
@end menu
@ -2690,15 +2699,21 @@ available.
@cindex run control
@cindex cryptographic operation, running
Some basic support for running operations asynchronously is available
in @acronym{GPGME}. You can use it to set up a context completely up
to initiating the desired operation, but delay performing it to a
later point.
@acronym{GPGME} supports running operations synchronously and
asynchronously. You can use asynchronous operation to set up a
context up to initiating the desired operation, but delay performing
it to a later point.
Furthermore, you can use an external event loop to control exactly
when @acronym{GPGME} runs. This ensures that @acronym{GPGME} only
runs when necessary and also prevents it from blocking for a long
time.
@menu
* Waiting For Completion:: Waiting until an operation is completed.
* Cancelling an Operation:: Interrupting a running operation.
* Hooking Up Into Idle Time:: Doing something when nothing has to be done.
* Using External Event Loops:: Advanced control over what happens when.
@end menu
@ -2708,21 +2723,35 @@ later point.
@cindex wait for completion
@deftypefun GpgmeCtx gpgme_wait (@w{GpgmeCtx @var{ctx}}, @w{GpgmeError *@var{status}}, @w{int @var{hang}})
The function @code{gpgme_wait} does continue the pending operation
within the context @var{ctx}. In particular, it ensures the data
exchange between @acronym{GPGME} and the crypto backend and watches
over the run time status of the backend process.
The function @code{gpgme_wait} continues the pending operation within
the context @var{ctx}. In particular, it ensures the data exchange
between @acronym{GPGME} and the crypto backend and watches over the
run time status of the backend process.
If @var{hang} is true, the function does not return until the
operation is completed or cancelled. Otherwise the function will not
block for a long time.
The error status of the finished operation is returned in
@var{status}.
The error status of the finished operation is returned in @var{status}
if @code{gpgme_wait} does not return @code{NULL}.
The @var{ctx} argument can be @code{NULL}. In that case,
@code{gpgme_wait} waits for any context to complete its operation.
@code{gpgme_wait} can be used only in conjunction with any context
that has a pending operation initiated with one of the
@code{gpgme_op_*_start} functions except @code{gpgme_op_keylist_start}
and @code{gpgme_op_trustlist_start} (for which you should use the
corresponding @code{gpgme_op_*_next} functions). If @var{ctx} is
@code{NULL}, all of such contexts are waited upon and possibly
returned. Synchronous operations running in parallel, as well as key
and trust item list operations, do not affect @code{gpgme_wait}.
In a multi-threaded environment, only one thread should ever call
@code{gpgme_wait} at any time, irregardless if @var{ctx} is specified
or not. This means that all calls to this function should be fully
synchronized by locking primitives.
The function returns the @var{ctx} of the context which has finished
the operation.
@end deftypefun
@ -2735,10 +2764,12 @@ the operation.
@deftypefun void gpgme_cancel (@w{GpgmeCtx @var{ctx}})
The function @code{gpgme_cancel} tries to cancel the pending
operation. The function @code{gpgme_wait} might notice the
cancellation flag and return. It is currently not guaranteed to work
under all circumstances. It's current primary purpose is to prevent
asking for a passphrase again in the passphrase callback.
operation. A running synchronous operation in the context or the
function @code{gpgme_wait} with this context as its @var{ctx} argument
might notice the cancellation flag and return. It is currently not
guaranteed to work under all circumstances. Its current primary
purpose is to prevent asking for a passphrase again in the passphrase
callback.
@end deftypefun
@ -2766,6 +2797,532 @@ registered yet.
@end deftypefun
@node Using External Event Loops
@subsection Using External Event Loops
@cindex event loop, external
@acronym{GPGME} hides the complexity of the communication between the
library and the crypto engine. The price of this convenience is that
the calling thread can block arbitrary long waiting for the data
returned by the crypto engine. In single-threaded programs, in
particular if they are interactive, this is an unwanted side-effect.
OTOH, if @code{gpgme_wait} is used without the @var{hang} option being
enabled, it might be called unnecessarily often, wasting CPU time that
could be used otherwise.
The I/O callback interface described in this section lets the user
take control over what happens when. @acronym{GPGME} will provide the
user with the file descriptors that should be monitored, and the
callback functions that should be invoked when a file descriptor is
ready for reading or writing. It is then the user's responsibility to
decide when to check the file descriptors and when to invoke the
callback functions. Usually this is done in an event loop, that also
checks for events in other parts of the program. If the callback
functions are only called when the file descriptors are ready,
@acronym{GPGME} will never block. This gives the user mroe control
over the program flow, and allows to perform other tasks when
@acronym{GPGME} would block otherwise.
By using this advanced mechanism, @acronym{GPGME} can be integrated
smoothly into GUI toolkits like GTK+ even for single-threaded
programs.
@menu
* I/O Callback Interface:: How I/O callbacks are registered.
* Registering I/O Callbacks:: How to use I/O callbacks for a context.
* I/O Callback Example:: An example how to use I/O callbacks.
* I/O Callback Example GTK+:: How to use @acronym{GPGME} with GTK+.
* I/O Callback Example GDK:: How to use @acronym{GPGME} with GDK.
@end menu
@node I/O Callback Interface
@subsubsection I/O Callback Interface
@deftp {Data type} {void (*GpgmeIOCb) (@w{void *@var{data}}, @w{int @var{fd}})}
@tindex GpgmeIOCb
The @code{GpgmeIOCb} type is the type of functions which
@acronym{GPGME} wants to register as I/O callback handlers using the
@code{GpgmeRegisterIOCb} functions provided by the user.
@var{data} and @var{fd} are provided by @acronym{GPGME} when the I/O
callback handler is registered, and should be passed through to the
handler when it is invoked by the user because it noticed activity on
the file descriptor @var{fd}.
@end deftp
@deftp {Data type} {GpgmeError (*GpgmeRegisterIOCb) (@w{void *@var{data}}, @w{int @var{fd}}, @w{int @var{dir}}, @w{GpgmeIOCb @var{fnc}}, @w{void *@var{fnc_data}}, @w{void **@var{tag}})}
@tindex GpgmeRegisterIOCb
The @code{GpgmeRegisterIOCb} type is the type of functions which can
be called by @acronym{GPGME} to register an I/O callback funtion
@var{fnc} for the file descriptor @var{fd} with the user.
@var{fnc_data} should be passed as the first argument to @var{fnc}
when the handler is invoked (the second argument should be @var{fd}).
If @var{dir} is 0, @var{fnc} should be called by the user when
@var{fd} is ready for writing. If @var{dir} is 1, @var{fnc} should be
called when @var{fd} is ready for reading.
@var{data} was provided by the user when registering the
@code{GpgmeRegisterIOCb} function with @acronym{GPGME} and will always
be passed as the first argument when registering a callback function.
For example, the user can use this to determine the event loop to
which the file descriptor should be added.
@acronym{GPGME} will call this function when a crypto operation is
initiated in a context for which the user has registered I/O callback
handler functions with @code{gpgme_set_io_cbs}. It can also call this
function when it is in an I/O callback handler for a file descriptor
associated to this context.
The user should return a unique handle in @var{tag} identifying this
I/O callback registration, which will be passed to the
@code{GpgmeRegisterIOCb} function without interpretation when the file
descriptor should not be monitored anymore.
@end deftp
@deftp {Data type} {void (*GpgmeRemoveIOCb) (@w{void *@var{tag}})}
The @code{GpgmeRemoveIOCb} type is the type of functions which can be
called by @acronym{GPGME} to remove an I/O callback handler that was
registered before. @var{tag} is the handle that was returned by the
@code{GpgmeRegisterIOCb} for this I/O callback.
@acronym{GPGME} can call this function when a crypto operation is in
an I/O callback. It will also call this function when the context is
destroyed while an operation is pending.
@end deftp
@deftp {Data type} {enum GpgmeEventIO}
@tindex GpgmeEventIO
The @code{GpgmeEventIO} type specifies the type of an event that is
reported to the user by @acronym{GPGME} as a consequence of an I/O
operation. The following events are defined:
@table @code
@item GPGME_EVENT_DONE
The operation is finished, the last I/O callback for this operation
was removed. The accompanying @var{type_data} points to a
@code{GpgmeError} variable that contains the status of the operation
that finished. This event is signalled after the last I/O callback
has been removed.
@item GPGME_EVENT_NEXT_KEY
In a @code{gpgme_op_keylist_start} operation, the next key was
received from the crypto engine. The accompanying @var{type_data} is
a @code{GpgmeKey} variable that contains the key with one reference
for the user.
@item GPGME_EVENT_NEXT_TRUSTITEM
In a @code{gpgme_op_trustlist_start} operation, the next trust item
was received from the crypto engine. The accompanying @var{type_data}
is a @code{GpgmeTrustItem} variable that contains the trust item with
one reference for the user.
@end table
@end deftp
@deftp {Data type} {void (*GpgmeEventIOCb) (@w{void *@var{data}}, @w{GpgmeEventIO @var{type}}, @w{void *@var{type_data}})}
The @code{GpgmeEventIOCb} type is the type of functions which can be
called by @acronym{GPGME} to signal an event for an operation running
in a context which has I/O callback functions registered by the user.
@var{data} was provided by the user when registering the
@code{GpgmeEventIOCb} function with @acronym{GPGME} and will always be
passed as the first argument when registering a callback function.
For example, the user can use this to determine the context in which
this event has occured.
@var{type} will specify the type of event that has occured.
@var{type_data} specifies the event further, as described in the above
list of possible @code{GpgmeEventIO} types.
@acronym{GPGME} can call this function in an I/O callback handler.
@end deftp
@node Registering I/O Callbacks
@subsubsection Registering I/O Callbacks
@deftp {Data type} {struct GpgmeIOCbs}
@tindex GpgmeEventIO
This structure is used to store the I/O callback interface functions
described in the previous section. It has the following members:
@table @code
@item GpgmeRegisterIOCb add
This is the function called by @acronym{GPGME} to register an I/O
callback handler. It must be specified.
@item void *add_data
This is passed as the first argument to the @code{add} function when
it is called by @acronym{GPGME}. For example, it can be used to
determine the event loop to which the file descriptor should be added.
@item GpgmeRemoveIOCb remove
This is the function called by @acronym{GPGME} to remove an I/O
callback handler. It must be specified.
@item GpgmeEventIOCb event
This is the function called by @acronym{GPGME} to signal an event for
an operation. It is optional, but if you don't specify it, you can
not retrieve the return value of the operation.
@item void *event_data
This is passed as the first argument to the @code{event} function when
it is called by @acronym{GPGME}. For example, it can be used to
determine the context in which the event has occured.
@end table
@end deftp
@deftypefun void gpgme_set_io_cbs (@w{GpgmeCtx @var{ctx}}, @w{struct GpgmeIOCbs *@var{io_cbs}})
The function @code{gpgme_set_io_cbs} enables the I/O callback
interface for the context @var{ctx}. The I/O callback functions are
specified by @var{io_cbs}.
If @var{io_cbs}->@code{add} is @code{NULL}, the I/O callback interface
is disabled for the context, and normal operation is restored.
@end deftypefun
@deftypefun void gpgme_get_io_cbs (@w{GpgmeCtx @var{ctx}}, @w{struct GpgmeIOCbs *@var{io_cbs}})
The function @code{gpgme_get_io_cbs} returns the I/O callback
functions set with @code{gpgme_set_io_cbs} in @var{io_cbs}.
@end deftypefun
@node I/O Callback Example
@subsubsection I/O Callback Example
To actually use an external event loop, you have to implement the I/O
callback functions that are used by @acronym{GPGME} to register and
unregister file descriptors. Furthermore, you have to actually
monitor these file descriptors for activity and call the appropriate
I/O callbacks.
The following example illustrates how to do that. The example uses
locking to show in which way the the callbacks and the event loop can
run concurrently. For the event loop, we use a fixed array. For a
real-world implementation, you should use a dynamically sized
structure because the number of file descriptors needed for a crypto
operation in @acronym{GPGME} is not predictable.
@example
#include <pthread.h>
#include <sys/types.h>
#include <gpgme.h>
/* The following structure holds the result of a crypto operation. */
struct op_result
@{
int done;
GpgmeError err;
@};
/* The following structure holds the data associated with one I/O
callback. */
struct one_fd
@{
int fd;
int dir;
GpgmeIOCb fnc;
void *fnc_data;
@};
struct event_loop
@{
pthread_mutex_t lock;
#define MAX_FDS 32
/* Unused slots are marked with FD being -1. */
struct one_fd fds[MAX_FDS];
@};
@end example
The following functions implement the I/O callback interface.
@example
GpgmeError
add_io_cb (void *data, int fd, int dir, GpgmeIOCb fnc, void *fnc_data,
void **r_tag)
@{
struct event_loop *loop = data;
struct one_fd *fds = loop->fds;
int i;
pthread_mutex_lock (&loop->lock);
for (i = 0; i < MAX_FDS; i++)
@{
if (fds[i].fd == -1)
@{
fds[i].fd = fd;
fds[i].dir = dir;
fds[i].fnc = fnc;
fds[i].fnc_data = fnc_data;
break;
@}
@}
pthread_mutex_unlock (&loop->lock);
if (i == MAX_FDS)
return GPGME_General_Error;
*r_tag = &fds[i];
return 0;
@}
void
remove_io_cb (void *tag)
@{
struct one_fd *fd = tag;
pthread_mutex_lock (&loop->lock);
fd->fd = -1;
pthread_mutex_unlock (&loop->lock);
@}
void
event_io_cb (void *data, GpgmeEventIO type, void *type_data)
@{
struct op_result *result = data;
GpgmeError *err = data;
/* We don't support list operations here. */
if (type == GPGME_EVENT_DONE)
@{
result->done = 1;
result->err = *data;
@}
@}
@end example
The final missing piece is the event loop, which will be presented
next. We only support waiting for the success of a single operation.
@example
int
do_select (struct event_loop *loop)
@{
fd_set rfds;
fd_set wfds;
int i, n;
int any = 0;
pthread_mutex_lock (&loop->lock);
FD_ZERO (&rfds);
FD_ZERO (&wfds);
for (i = 0; i < FDLIST_MAX; i++)
if (fdlist[i].fd != -1)
FD_SET (fdlist[i].fd, fdlist[i].dir ? &rfds : &wfds);
pthread_mutex_unlock (&loop->unlock);
do
@{
n = select (FD_SETSIZE, &rfds, &wfds, NULL, 0);
@}
while (n < 0 && errno == EINTR);
if (n < 0)
return n; /* Error or timeout. */
pthread_mutex_lock (&loop->lock);
for (i = 0; i < FDLIST_MAX && n; i++)
@{
if (fdlist[i].fd != -1)
@{
if (FD_ISSET (fdlist[i].fd, fdlist[i].dir ? &rfds : &wfds))
@{
assert (n);
n--;
any = 1;
/* The I/O callback handler can register/remove callbacks,
so we have to unlock the file descriptor list. */
pthread_mutex_unlock (&loop->lock);
(*fdlist[i].fnc) (fdlist[i].fnc_data, fdlist[i].fd);
pthread_mutex_lock (&loop->lock);
@}
@}
@}
pthread_mutex_unlock (&loop->lock);
return any;
@}
void
wait_for_op (struct event_loop *loop, struct op_result *result)
@{
int ret;
do
@{
ret = do_select (loop);
@}
while (ret >= 0 && !result->done);
return ret;
@}
@end example
The main function shows how to put it all together.
@example
int
main (int argc, char *argv[])
@{
struct event_loop loop;
struct op_result result;
GpgmeCtx ctx;
GpgmeError err;
GpgmeData sig, text;
GpgmeSigStat status;
int i;
struct GpgmeIOCbs io_cbs =
@{
add_io_cb,
&loop,
remove_io_cb,
event_io_cb,
&result
@};
/* Initialize the loop structure. */
loop.lock = PTHREAD_MUTEX_INITIALIZER;
for (i = 0; i < MAX_FDS; i++)
loop->fds[i].fd = -1;
/* Initialize the result structure. */
result.done = 0;
err = gpgme_data_new_from_file (&sig, "signature", 1);
if (!err)
err = gpgme_data_new_from_file (&text, "text", 1);
if (!err)
err = gpgme_new (&ctx);
if (!err)
@{
gpgme_set_io_cbs (ctx, &io_cbs);
err = gpgme_op_verify_start (ctx, sig, text, &status);
@}
if (err)
@{
fprintf (stderr, "gpgme error: %s\n", gpgme_strerror (err));
exit (1);
@}
wait_for_op (&loop, &result);
if (!result.done)
@{
fprintf (stderr, "select error\n");
exit (1);
@}
if (!result.err)
@{
fprintf (stderr, "verification failed: %s\n", gpgme_strerror (result.err));
exit (1);
@}
/* Evaluate STATUS. */
@dots{}
return 0;
@}
@end example
@node I/O Callback Example GTK+
@subsubsection I/O Callback Example GTK+
@cindex GTK+, using @acronym{GPGME} with
The I/O callback interface can be used to integrate @acronym{GPGME}
with the GTK+ event loop. The following code snippets shows how this
can be done using the appropriate register and remove I/O callback
functions. In this example, the private data of the register I/O
callback function is unused. The event notifications is missing
because it does not require any GTK+ specific setup.
@example
#include <gtk/gtk.h>
struct my_gpgme_io_cb
@{
GpgmeIOCb fnc;
void *fnc_data;
guint input_handler_id
@};
void
my_gpgme_io_cb (gpointer data, gint source, GdkInputCondition condition)
@{
struct my_gpgme_io_cb *iocb = data;
(*(iocb->fnc)) (iocb->data, source);
@}
void
my_gpgme_remove_io_cb (void *data)
@{
struct my_gpgme_io_cb *iocb = data;
gtk_input_remove (data->input_handler_id);
@}
void
my_gpgme_register_io_callback (void *data, int fd, int dir, GpgmeIOCb fnc,
void *fnc_data, void **tag)
@{
struct my_gpgme_io_cb *iocb = g_malloc (sizeof (struct my_gpgme_io_cb));
iocb->fnc = fnc;
iocb->data = fnc_data;
iocb->input_handler_id = gtk_input_add_full (fd, dir
? GDK_INPUT_READ
: GDK_INPUT_WRITE,
my_gpgme_io_callback,
0, iocb, NULL);
*tag = iocb;
return 0;
@}
@end example
@node I/O Callback Example GDK
@subsubsection I/O Callback Example GDK
@cindex GDK, using @acronym{GPGME} with
The I/O callback interface can also be used to integrate
@acronym{GPGME} with the GDK event loop. The following code snippets
shows how this can be done using the appropriate register and remove
I/O callback functions. In this example, the private data of the
register I/O callback function is unused. The event notifications is
missing because it does not require any GDK specific setup.
It is very similar to the GTK+ example in the previous section.
@example
#include <gdk/gdk.h>
struct my_gpgme_io_cb
@{
GpgmeIOCb fnc;
void *fnc_data;
gint tag;
@};
void
my_gpgme_io_cb (gpointer data, gint source, GdkInputCondition condition)
@{
struct my_gpgme_io_cb *iocb = data;
(*(iocb->fnc)) (iocb->data, source);
@}
void
my_gpgme_remove_io_cb (void *data)
@{
struct my_gpgme_io_cb *iocb = data;
gdk_input_remove (data->tag);
@}
void
my_gpgme_register_io_callback (void *data, int fd, int dir, GpgmeIOCb fnc,
void *fnc_data, void **tag)
@{
struct my_gpgme_io_cb *iocb = g_malloc (sizeof (struct my_gpgme_io_cb));
iocb->fnc = fnc;
iocb->data = fnc_data;
iocb->tag = gtk_input_add_full (fd, dir ? GDK_INPUT_READ : GDK_INPUT_WRITE,
my_gpgme_io_callback, iocb, NULL);
*tag = iocb;
return 0;
@}
@end example
@include gpl.texi