* gpgme.texi (Cancellation): Document gpgme_cancel_async.
gpgme/
2008-06-27 Marcus Brinkmann <marcus@g10code.de>
* context.h: Include "sema.h".
(struct gpgme_context): New members lock and canceled.
* gpgme.c (gpgme_new): Initialize lock.
(gpgme_release): Destroy lock.
(gpgme_cancel_async): New function.
* op-support.c (_gpgme_op_reset): Reset the canceled flag.
* wait-global.c (gpgme_wait): Check cancel flag before processing
any I/O callbacks.
* wait-private.c (_gpgme_wait_on_condition): Likewise.
* wait-user.c (_gpgme_user_io_cb_handler): Likewise.
+2008-06-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.texi (Cancellation): Document gpgme_cancel_async.
+
2008-06-25 Werner Koch <wk@g10code.com>
* gpgme.texi (Listing Keys): Updated example to the current API.
@cindex aborting operations
@cindex cancelling operations
-Sometimes you do not want to wait for an operation to finish. If you
-use external I/O callbacks, you can cancel a pending operation.
-However, you must ensure that no other thread is currently using the
-context in which the operation you want to cancel runs. This includes
-callback handlers. So your external event loop must either be halted
-or otherwise it must be guaranteed that no installed I/O callbacks are
-run for this context.
+Sometimes you do not want to wait for an operation to finish.
+@acronym{GPGME} provides two different functions to achieve that. The
+function @code{gpgme_cancel} takes effect immediately. When it
+returns, the operation is effectively canceled. However, it has some
+limitations and can not be used with synchronous operations. In
+contrast, the function @code{gpgme_cancel_async} can be used with any
+context and from any thread, but it is not guaranteed to take effect
+immediately. Instead, cancellation occurs at the next possible time
+(typically the next time I/O occurs in the target context).
@deftypefun gpgme_ctx_t gpgme_cancel (@w{gpgme_ctx_t @var{ctx}})
The function @code{gpgme_cancel} attempts to cancel a pending
case the state of @var{ctx} is not modified).
@end deftypefun
+
+@deftypefun gpgme_ctx_t gpgme_cancel_async (@w{gpgme_ctx_t @var{ctx}})
+The function @code{gpgme_cancel} attempts to cancel a pending
+operation in the context @var{ctx}. This can be called by any thread
+at any time after starting an operation on the context, but will not
+take effect immediately. The actual cancellation happens at the next
+time GPGME processes I/O in that context.
+
+The function returns an error code if the cancellation failed (in this
+case the state of @var{ctx} is not modified).
+@end deftypefun
+
@c **********************************************************
@c ******************* Appendices *************************
@c **********************************************************
+2008-06-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * context.h: Include "sema.h".
+ (struct gpgme_context): New members lock and canceled.
+ * gpgme.c (gpgme_new): Initialize lock.
+ (gpgme_release): Destroy lock.
+ (gpgme_cancel_async): New function.
+ * op-support.c (_gpgme_op_reset): Reset the canceled flag.
+ * wait-global.c (gpgme_wait): Check cancel flag before processing
+ any I/O callbacks.
+ * wait-private.c (_gpgme_wait_on_condition): Likewise.
+ * wait-user.c (_gpgme_user_io_cb_handler): Likewise.
+
2008-06-26 Werner Koch <wk@g10code.com>
* w32-util.c (_gpgme_mkstemp): Replace sprint by stpcpy.
#include "gpgme.h"
#include "engine.h"
#include "wait.h"
+#include "sema.h"
\f
/* Operations might require to remember arbitrary information and data
be performed (sequentially). */
struct gpgme_context
{
+ DECLARE_LOCK (lock);
+
+ /* True if the context was canceled asynchronously. */
+ int canceled;
+
/* The engine info for this context. */
gpgme_engine_info_t engine_info;
if (!ctx)
return TRACE_ERR (gpg_error_from_errno (errno));
+ INIT_LOCK (ctx->lock);
+
_gpgme_engine_info_copy (&ctx->engine_info);
if (!ctx->engine_info)
{
return TRACE_ERR (0);
}
+
+/* Cancel a pending operation asynchronously. */
+gpgme_error_t
+gpgme_cancel_async (gpgme_ctx_t ctx)
+{
+ gpgme_error_t err;
+ TRACE_BEG (DEBUG_CTX, "gpgme_cancel_async", ctx);
+
+ LOCK (ctx->lock);
+ ctx->canceled = 1;
+ UNLOCK (ctx->lock);
+
+ return TRACE_ERR (0);
+}
+
+
/* Release all resources associated with the given context. */
void
gpgme_release (gpgme_ctx_t ctx)
if (ctx->lc_messages)
free (ctx->lc_messages);
_gpgme_engine_info_release (ctx->engine_info);
+ DESTROY_LOCK (ctx->lock);
free (ctx);
}
/* Cancel a pending asynchronous operation. */
gpgme_error_t gpgme_cancel (gpgme_ctx_t ctx);
+/* Cancel a pending operation asynchronously. */
+gpgme_error_t gpgme_cancel_async (gpgme_ctx_t ctx);
+
\f
struct _gpgme_invalid_key
{
type &= 255;
_gpgme_release_result (ctx);
+ LOCK (ctx->lock);
+ ctx->canceled = 0;
+ UNLOCK (ctx->lock);
if (ctx->engine && no_reset)
reuse_engine = 1;
if (fdt.fds[i].fd != -1 && fdt.fds[i].signaled)
{
gpgme_ctx_t ictx;
- gpgme_error_t err;
+ gpgme_error_t err = 0;
struct wait_item_s *item;
assert (nr);
ictx = item->ctx;
assert (ictx);
- err = _gpgme_run_io_cb (&fdt.fds[i], 0);
+ LOCK (ctx->lock);
+ if (ctx->canceled)
+ err = gpg_error (GPG_ERR_CANCELED);
+ UNLOCK (ctx->lock);
+
+ if (!err)
+ err = _gpgme_run_io_cb (&fdt.fds[i], 0);
if (err)
{
/* An error occured. Close all fds in this context,
assert (nr);
nr--;
- err = _gpgme_run_io_cb (&ctx->fdt.fds[i], 0);
+ LOCK (ctx->lock);
+ if (ctx->canceled)
+ err = gpg_error (GPG_ERR_CANCELED);
+ UNLOCK (ctx->lock);
+
+ if (!err)
+ err = _gpgme_run_io_cb (&ctx->fdt.fds[i], 0);
if (err)
{
/* An error occured. Close all fds in this context,
gpgme_error_t
_gpgme_user_io_cb_handler (void *data, int fd)
{
- gpgme_error_t err;
+ gpgme_error_t err = 0;
struct tag *tag = (struct tag *) data;
gpgme_ctx_t ctx;
ctx = tag->ctx;
assert (ctx);
- err = _gpgme_run_io_cb (&ctx->fdt.fds[tag->idx], 0);
+ LOCK (ctx->lock);
+ if (ctx->canceled)
+ err = gpg_error (GPG_ERR_CANCELED);
+ UNLOCK (ctx->lock);
+
+ if (! err)
+ err = _gpgme_run_io_cb (&ctx->fdt.fds[tag->idx], 0);
if (err)
{
unsigned int idx;