/*

  silcasync.h

  Author: Pekka Riikonen <priikone@silcnet.org>

  Copyright (C) 2005 - 2007 Pekka Riikonen

  The contents of this file are subject to one of the Licenses specified 
  in the COPYING file;  You may not use this file except in compliance 
  with the License.

  The software distributed under the License is distributed on an "AS IS"
  basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
  KIND, either expressed or implied.  See the COPYING file for more
  information.

*/

/****h* silcutil/SILC Async Operation Interface
 *
 * DESCRIPTION
 *
 * SILC Async Operation API is an interface that can be used to control
 * asynchronous operations.  All functions that take callback as argument
 * should return SilcAsyncOperation context.  That context then can be
 * used to control, such as, abort the asynchronous operation.  Using
 * SILC Async Operation API, asynchronous functions can be controlled
 * and aborted safely.
 *
 * The SILC Async Operation API is divided in two levels; the underlaying
 * operation level that implements the asynchronous operation, and the
 * upper layer that can control the asynchronous operation.  The operation
 * layer must guarantee that if the upper layer aborts the asynchronous
 * operation, no callback function will be called back to the upper layer.
 * This must be remembered when implementing the operation layer.
 *
 ***/

#ifndef SILCASYNC_H
#define SILCASYNC_H

/****s* silcutil/SilcAsyncOperationAPI/SilcAsyncOperation
 *
 * NAME
 *
 *    typedef struct SilcAsyncOperationObject *SilcAsyncOperation;
 *
 * DESCRIPTION
 *
 *    The asynchronous operation context allocated by silc_async_alloc.
 *    The layer that implements the asynchronous operation allocates this
 *    context.  The layer that receives this context can use it to control
 *    the underlaying asynchronous operation.  It is also possible to use
 *    a pre-allocated context by using SilcAsyncOperationStruct instead
 *    SilcAsyncOperation.
 *
 ***/
typedef struct SilcAsyncOperationObject *SilcAsyncOperation;

/****s* silcutil/SilcAsyncOperationAPI/SilcAsyncOperationStruct
 *
 * NAME
 *
 *    typedef struct SilcAsyncOperationObject SilcAsyncOperationStruct;
 *
 * DESCRIPTION
 *
 *    The asynchronous operation context that can be used as a pre-allocated
 *    context.  This is initialized with silc_async_init.  It need not
 *    be uninitialized.  The layer that implements the asynchronous
 *    operation initializes this context.  The layer that has access to this
 *    context can use it to control the underlaying asynchronous operation.
 *
 ***/
typedef struct SilcAsyncOperationObject SilcAsyncOperationStruct;

/****f* silcutil/SilcAsyncOperationAPI/SilcAsyncOperationAbort
 *
 * SYNOPSIS
 *
 *    typedef void (*SilcAsyncOperationAbort)(SilcAsyncOperation op,
 *                                            void *context);
 *
 * DESCRIPTION
 *
 *    This callback is called when upper layer calls the silc_async_abort,
 *    and is used to actually perform the abortion of the asynchronous
 *    operation.  The silc_async_free must not be called in this function.
 *
 *    This callback type can also be provided to silc_async_abort function
 *    by the upper layer, if it wants that callback is called to the upper
 *    layer when aborting the operation.
 *
 ***/
typedef void (*SilcAsyncOperationAbort)(SilcAsyncOperation op,
					void *context);

/****f* silcutil/SilcAsyncOperationAPI/SilcAsyncOperationPause
 *
 * SYNOPSIS
 *
 *    typedef SilcBool (*SilcAsyncOperationPause)(SilcAsyncOperation op,
 *                                                SilcBool pause_operation,
 *                                                void *context);
 *
 * DESCRIPTION
 *
 *    This callback is used to halt an operation, if upper layer calls the
 *    silc_async_halt function, or to resume an operation if upper layer
 *    calls the silc_async_resume, after it has earlier halted the operation.
 *    If this callback is implemented it is guaranteed that the asynchronous
 *    operation is not progressed when it is halted.  If the `pause_operation'
 *    is TRUE the operation is halted.  If it is FALSE, then the operation
 *    resumes its execution.  This function returns TRUE if the operation
 *    was (or is going to be) halted or resumed, and FALSE on error.
 *
 ***/
typedef SilcBool (*SilcAsyncOperationPause)(SilcAsyncOperation op,
					    SilcBool pause_operation,
					    void *context);

/* Upper layer functions for managing asynchronous operations.  Layer
   that has received SilcAsyncOperation context can control the async
   operation with these functions. */

/****f* silcutil/SilcAsyncOperationAPI/silc_async_halt
 *
 * SYNOPSIS
 *
 *    SilcBool silc_async_halt(SilcAsyncOperation op);
 *
 * DESCRIPTION
 *
 *    Halt the execution of the asynchronous operation.  If the operation
 *    supports this feature, it is guaranteed that the operation is halted
 *    and its execution is not progressed until the silc_async_resume function
 *    is called.  The operation still can be aborted even if it is halted.
 *    If this function is not supported, calling this has no effect and the
 *    function returns FALSE.  This function is for the upper layer that
 *    controls the asynchronous operation.
 *
 ***/
SilcBool silc_async_halt(SilcAsyncOperation op);

/****f* silcutil/SilcAsyncOperationAPI/silc_async_resume
 *
 * SYNOPSIS
 *
 *    SilcBool silc_async_resume(SilcAsyncOperation op);
 *
 * DESCRIPTION
 *
 *    Resume the execution of the asynchronous operation.  If the halting of
 *    the operation was supported, then this function is used to resume the
 *    execution of the operation after it was halted.  If this function is
 *    not supported, calling this has no effect and the function returns
 *    FALSE.  This function is for the upper layer that controls the
 *    asynchronous operation.
 *
 ***/
SilcBool silc_async_resume(SilcAsyncOperation op);

/****f* silcutil/SilcAsyncOperationAPI/silc_async_abort
 *
 * SYNOPSIS
 *
 *    void silc_async_abort(SilcAsyncOperation op,
 *                          SilcAsyncOperationAbort abort_cb, void *context);
 *
 * DESCRIPTION
 *
 *    This function is used by upper layer that received SilcAsyncOperation
 *    context from an asynchronous function, to abort the asynchronous
 *    operation.  The `op' becomes invalid after this function returns.
 *    It is also guaranteed (assuming the use of this API is implemented
 *    correctly) that some other completion callback is not called after
 *    the operation was aborted.  However, if the caller wants to receive
 *    a callback when aborting the caller may specify the `abort_cb' and
 *    `context' which will be called after the operation is aborted, but
 *    before the `op' becomes invalid.  The `abort_cb' is called immediately
 *    inside this function.
 *
 ***/
void silc_async_abort(SilcAsyncOperation op,
                      SilcAsyncOperationAbort abort_cb, void *context);

/* The operation layer functions.  The layer that performs the async
   operation use these functions. */

/****f* silcutil/SilcAsyncOperationAPI/silc_async_alloc
 *
 * SYNOPSIS
 *
 *    SilcAsyncOperation silc_async_alloc(SilcAsyncOperationAbort abort_cb,
 *                                        SilcAsyncOperationPause pause_cb,
 *                                        void *context);
 *
 * DESCRIPTION
 *
 *    Start asynchronous operation, and assign `abort_cb' callback for it,
 *    which can be used by some upper layer to abort the asynchronous
 *    operation, by calling the silc_async_abort.  The layer which calls
 *    this function must also call silc_async_free when the asynchronous
 *    operation is successfully completed.  If it is aborted by upper layer
 *    then silc_async_free must not be called, since it is called by the
 *    silc_async_abort function.
 *
 *    If the `pause_cb' is provided then the upper layer may also halt and
 *    then later resume the execution of the operation, by calling the
 *    silc_async_halt and silc_async_resume respectively.  If `pause_cb' is
 *    not provided then these functions has no effect for this operation.
 *
 * EXAMPLE
 *
 *    SilcAsyncOperation silc_async_call(Callback callback, void *cb_context)
 *    {
 *      SilcAsyncOperation op;
 *      ...
 *
 *      // Allocate async operation so that caller can control us, like abort
 *      op = silc_async_alloc(silc_async_call_abort, NULL, ctx);
 *
 *      // Start async operation in FSM
 *      silc_fsm_init(&ctx->fsm, ctx, fsm_destructor, ctx, schedule);
 *      silc_fsm_start(&ctx->fsm, first_state);
 *      ...
 *
 *      // Return async operation for upper layer
 *      return op;
 *    }
 *
 ***/
SilcAsyncOperation silc_async_alloc(SilcAsyncOperationAbort abort_cb,
				    SilcAsyncOperationPause pause_cb,
				    void *context);

/****f* silcutil/SilcAsyncOperationAPI/silc_async_init
 *
 * SYNOPSIS
 *
 *    SilcBool silc_async_init(SilcAsyncOperation op,
 *                             SilcAsyncOperationAbort abort_cb,
 *                             SilcAsyncOperationPause pause_cb,
 *                             void *context);
 *
 * DESCRIPTION
 *
 *    Initializes and starts a pre-allocated asynchronous operation context,
 *    and assigns `abort_cb' callback for it, which can be used by some upper
 *    layer to abort the asynchronous operation, by calling the
 *    silc_async_abort.  Since this use pre-allocated context, the function
 *    silc_async_free need not be called.  This function is equivalent
 *    to silc_async_alloc except this does not allocate any memory.  The `op'
 *    needs not be uninitialized.
 *
 *    If the `pause_cb' is provided then the upper layer may also halt and
 *    then later resume the execution of the operation, by calling the
 *    silc_async_halt and silc_async_resume respectively.  If `pause_cb' is
 *    not provided then these functions has no effect for this operation.
 *
 ***/
SilcBool silc_async_init(SilcAsyncOperation op,
			 SilcAsyncOperationAbort abort_cb,
			 SilcAsyncOperationPause pause_cb,
			 void *context);

/****f* silcutil/SilcAsyncOperationAPI/silc_async_free
 *
 * SYNOPSIS
 *
 *    void silc_async_free(SilcAsyncOperation op);
 *
 * DESCRIPTION
 *
 *    Stop the asynchronous operation.  If the asynchronous operation ended
 *    normally (ie. it was not aborted) this function must be called by the
 *    caller who called silc_async_alloc.  The `op' will become invalid after
 *    this and the upper layer must not call silc_async_abort after this
 *    function is called.  The layer that calls this, must call some other
 *    completion callback to the upper layer, so that it knows that the
 *    asynchronous operation is completed.
 *
 ***/
void silc_async_free(SilcAsyncOperation op);

/****f* silcutil/SilcAsyncOperationAPI/silc_async_get_context
 *
 * SYNOPSIS
 *
 *    void *silc_async_get_context(SilcAsyncOperation op);
 *
 * DESCRIPTION
 *
 *    Returns the context that was given to the silc_async_alloc or
 *    silc_async_init.
 *
 ***/
void *silc_async_get_context(SilcAsyncOperation op);

#include "silcasync_i.h"

#endif /* SILCASYNC_H */
