Use unlang_interpret_signal to manage cleaning up detached requests instead of having custom code inside the subrequest code
break;
case REQUEST_TYPE_DETACHED:
- intp->funcs.done_detached(request, stack->result, intp->uctx);
+ intp->funcs.done_detached(request, stack->result, intp->uctx); /* Callback will usually free the request */
break;
}
}
if (!fr_cond_assert(stack != NULL)) return;
+ if (!request_is_detachable(request)) return;
+
intp = stack->intp;
intp->funcs.detach(request, intp->uctx);
+
+ if (request_detach(request) < 0) RPEDEBUG("Failed detaching request");
}
/** Send a signal (usually stop) to a request
switch (action) {
case FR_SIGNAL_CANCEL:
- unlang_interpret_request_stop(request); /* Stop gets the request in a consistent state */
- unlang_interpret_request_done(request); /* Done signals the request is complete */
+ /*
+ * Detach the request from the parent to cleanup
+ * any cross-request pointers. This is a noop
+ * if the request is not detachable.
+ */
+ if (request_is_detachable(request)) unlang_interpret_request_detach(request);
/*
- * If we're cancelling a child, detach it from
- * its parent, so we don't leak memory allocated
- * in request_detachable_init.
+ * Get the request into a consistent state,
+ * removing it from any runnable lists.
*/
- if (request_is_detachable(request) && (request_detach(request) < 0)) {
- RPEDEBUG("Failed detaching request on cancel");
- }
+ unlang_interpret_request_stop(request);
+
+ /*
+ * As the request is detached, we call the done_detached
+ * callback which should free the request.
+ */
+ unlang_interpret_request_done(request);
break;
case FR_SIGNAL_DETACH:
- unlang_interpret_request_detach(request); /* Tell our caller that the request is being detached */
- if (request_detach(request) < 0) RPEDEBUG("Failed detaching request");
+ /*
+ * Cleanup any cross-request pointers, and mark the
+ * request as detached. When the request completes it
+ * should by automatically freed.
+ */
+ unlang_interpret_request_detach(request);
break;
default:
static void _request_done_detached(request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx)
{
RDEBUG3("Done synchronous detached request");
+
+ /*
+ * Detached requests have to be freed by us
+ * as nothing else can free them.
+ *
+ * All other requests must be freed by the
+ * code which allocated them.
+ */
+ talloc_free(request);
}
/** We don't need to do anything for internal -> detached
RDEBUG4("<<< interpreter (iteration %i) - %s", iterations,
fr_table_str_by_value(rcode_table, sub_rcode, "<INVALID>"));
- if (sub_request == request) {
- rcode = sub_rcode;
/*
- * Free detached, resumable requests
+ * If the request being run was the original request
+ * then update the rcode we're returning.
*/
- } else if (!sub_request->parent && !unlang_interpret_is_resumable(sub_request)) {
- talloc_free(sub_request); /* Free detached requests */
- }
+ if (sub_request == request) rcode = sub_rcode;
DEBUG3("%u runnable, %u yielded", fr_heap_num_elements(intps->runnable), intps->yielded);
}
RDEBUG("Reached Request-Lifetime. Forcibly stopping request");
- fr_assert(!request->parent); /* must be detached */
-
- /*
- * The request is scheduled and isn't running. Remove it
- * from the backlog.
- */
- if (unlang_request_is_scheduled(request)) {
- unlang_stack_t *stack = request->stack;
- unlang_interpret_t *intp = stack->intp;
-
- fr_assert(request_is_internal(request));
- intp->funcs.done_internal(request, stack->result, intp->uctx); /* Should free the request */
- } else {
- talloc_free(request);
- }
+ unlang_interpret_signal(request, FR_SIGNAL_CANCEL); /* Request should now be freed */
}
/** Initialize a detached child
{
unlang_frame_state_subrequest_t *state;
+ /*
+ * We're already detached so we don't
+ * need to notify the parent we're
+ * waking up, and we don't need to detach
+ * again...
+ */
if (!request->parent) return;
state = talloc_get_type_abort(frame_current(request->parent)->state, unlang_frame_state_subrequest_t);
* Indicate to the parent there's no longer a child
*/
state->child = NULL;
-
+
/*
* Tell the parent to resume
*/