udi_cancel(3udi)


Cancel a pending asynchronous service call

SYNOPSIS

#include <udi.h>

void udi_cancel (

	udi_cancel_call_t *callback,

	udi_cb_t *gcb );
 
typedef void udi_cancel_call_t (

	udi_cb_t *gcb );
 

ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".

DESCRIPTION Any service request with a pending callback can be canceled by this call, except timer requests (which must be canceled with udi_timer_cancel). The control block must be the same one specified when the service was requested, and must be active (i.e. the callback has not yet been called, regardless of whether or not allocations have actually completed).

udi_cancel must not be used with control blocks that have been passed to channel operations, but some channel operations can be aborted by using udi_channel_op_abort.

When a service request is cancelled, any whole or partially-allocated resources or data structures that would have been returned with that callback upon normal completion will be discarded (i.e. there will be no resource leaks). Further, any resources or data structures that would have been consumed by the original request (e.g. movable structs and objects referenced by transferable handles) will be consumed (and discarded), since there is no way to pass the object back to the original caller. Another way to look at this is that udi_cancel does not provide an undo operation, but rather an abort operation; any objects (such as a data buffer for udi_buf_write) being modified or created by the original request are destroyed by the abort.

Once the request has been cancelled, and any partial allocations released, the specified callback routine will be called instead of the original callback routine from the outstanding request. Ownership of the control block is transferred back to the requestor with this callback, and the control block is available for reuse.

WARNINGS udi_cancel must be called from the region that owned the control block at the time of the original request. It cannot be used to cancel a pending request in another region.

A driver must keep track of its in-progress requests to avoid canceling a different request than intended. See the example below for details. A good rule of thumb is that udi_cancel must not be used to cancel a request without first checking to see if the corresponding callback has been called.

If a driver issues a udi_cancel for a control block that is not active the driver is in error. See the "Driver Faults/Recovery" section of "Execution Model" for an explanation of how the environment may react to this driver error.

Control block usage must follow the rules described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions"

Since ownership of control blocks are transferred away from the driver upon issuing a channel operation, any attempt to use udi_cancel to cancel a channel operation will be considered an error and will be handled as an environment-detected error in accordance with the "Driver Faults/Recovery" section of "Execution Model".

EXAMPLES The first example shows how not to use udi_cancel. The udi_cancel call in ddd_step1 will not necessarily cancel the udi_mem_alloc call that immediately precedes it. In fact, it could even cancel the subsequent udi_cb_alloc request in ddd_step2, or even some further subsequent allocation in the callback sequence. This example is somewhat contrived in that there would typically be some reason the driver is canceling the request; it wouldn't simply do an allocation followed immediately by a cancel, but it illustrates the issues.

void

ddd_step1(ddd_context_t *context)

{

	...

	udi_mem_alloc(ddd_step2, UDI_GCB(cb1), size, 0);

	udi_cancel(ddd_step1a, UDI_GCB(cb1));

}
 
void

ddd_step1a(

	udi_cb_t *gcb) 

{

	/* Something has been canceled,

		but it's unclear what */

	...

}
 
void

ddd_step2(

	udi_cb_t *gcb,

	void *new_mem) 

{

	...

	udi_cb_alloc(ddd_step3, UDI_GCB(cb1), idx, chan);

}
 
void

ddd_step3(

	udi_cb_t *gcb,

	udi_cb_t *new_cb) 

{

	...

}
 

To fix this problem, the driver must first check to see if the corresponding allocation callback has been received before calling udi_cancel. Adding such a check to the above code produces the following, which will cancel the immediately preceding udi_mem_alloc call if and only if the allocation doesn't complete immediately (i.e. isn't complete upon return). (Note that some environments may be designed to never do the callback immediately before returning. So this would not in general be a useful thing to do in the driver, but it does illustrate the issues.)

void

ddd_step1(

	ddd_context_t *context) 

{

	...

	context->mem_alloc_done = FALSE;

	udi_mem_alloc(ddd_step2, UDI_GCB(cb1), size, 0);

	if (!context->mem_alloc_done) 

	    udi_cancel(ddd_step1a, UDI_GCB(cb1));

}
 
void

ddd_step1a(

	udi_cb_t *gcb) 

{

	/* udi_mem_alloc in step1 has been cancelled. */

	...

}
 
void

ddd_step2(

	udi_cb_t *gcb,

	void *new_mem) 

{

	ddd_context_t *context = gcb->context;

	...

	context->mem_alloc_done = TRUE; 

	udi_cb_alloc(ddd_step3, UDI_GCB(cb1), idx, chan);

}
 
void

ddd_step3(

	udi_cb_t *gcb,

	udi_cb_t *new_cb) 

{

	...

}
 

Note that the region serialization rules prevent reentrancy in the region code and therefore prevent the race conditions related to accesses and modifications of the mem_alloc_done variable that would normally need to be considered.


UDI Core Specification Contents