Repository: ggerganov/llama.cpp
File: tools/server/server-context.cpp (C++)

Code before:

                    if (task.is_parent()) {
                        // if this is a parent task, we want to make sure parent + all child tasks can be launched at the same time

                        // the current slot must be either reserved for this task, or free (checked above)
                        GGML_ASSERT(slot->task_id_next == -1 || slot->task_id_next == task.id);
                        slot->task_id_next = task.id;

                        // need to reserve n_children more slots
                        if (try_reserve_child_slots(*slot, task.n_children, task.id)) {
                            // all required slots have been reserved, safe to proceed
                            int task_id = task.id;
                            if (!launch_slots_with_child_tasks(*slot, std::move(task))) {
                                SRV_ERR("failed to launch slots with child tasks, id_task = %d\n", task_id);
                                // task must be dropped on error
                                break;
                            }
                            break;
                        } else {
                            // failed to reserve all required slots, we defer this task for processing later
                            SRV_DBG("failed to reserve %d slots, defer task, id_task = %d\n", task.n_children + 1, task.id);
                            queue_tasks.defer(std::move(task));
                            // note: the current slot + child slots are already reserved for this task
                            break;
                        }
                    }

                    // note: launch_slot_with_task will unreserve the task
                    if (!launch_slot_with_task(*slot, std::move(task))) {
                        SRV_ERR("failed to launch slot with task, id_task = %d\n", task.id);
                        break;
                    }
                } break;
            case SERVER_TASK_TYPE_CANCEL:
                {
                    // release slot linked with the task id
                    for (auto & slot : slots) {
                        if (slot.task && slot.task->id == task.id_target) {
                            slot.release();
                            break;
                        }
                    }
                    unreserve_slots(task.id_target);
                } break;
            case SERVER_TASK_TYPE_NEXT_RESPONSE:
                {
                    // do nothing
                } break;
            case SERVER_TASK_TYPE_METRICS:
                {
                    json slots_data = json::array();

Review comment (refactor):
I am wondering if we can significantly simplify the "reserve" logic by containing it fully within this block. Something like:

```c++
// pseudo code
if (task.is_parent()) {
    // note `try_reserve()` does not need to mutate the slots
    auto reserve_info = try_reserve(slot, task);

    if (reserve_info.ok()) {
        launch_task_with_children(slot, std::move(task), reserve_info);
    } else {
        defer(std::move(task));
    }

    // could be moved in the destructor of `reserve_info` too
    // also, this might just be a noop in this approach since we didn't mutate the slots
    unreserve(reserve_info);

    break;
}
```

I feel like there is no need to maintain the reservation state of the slots beyond attempting to launch the parent and the children.

Code after:
                    res->n_decode_total          = metrics.n_decode_total;
                    res->n_busy_slots_total      = metrics.n_busy_slots_total;

                    if (task.metrics_reset_bucket) {
                        metrics.reset_bucket();
                    }
                    queue_results.send(std::move(res));
                } break;
            case SERVER_TASK_TYPE_SLOT_SAVE:
                {
                    if (!check_no_mtmd(task.id)) {
                        break;
                    }

                    int id_slot = task.slot_action.slot_id;
                    server_slot * slot = get_slot_by_id(id_slot);
                    if (slot == nullptr) {
                        send_error(task, "Invalid slot ID", ERROR_TYPE_INVALID_REQUEST);
                        break;
                    }
                    if (slot->is_processing()) {
                        // if requested slot is unavailable, we defer this task for processing later
                        SRV_DBG("requested slot is unavailable, defer task, id_task = %d\n", task.id);
                        queue_tasks.defer(std::move(task));
                        break;
                    }

                    const size_t token_count = slot->prompt.tokens.size();
                    const int64_t t_start = ggml_time_us();

                    std::string filename = task.slot_action.filename;
                    std::string filepath = task.slot_action.filepath;

                    const llama_tokens & tokens = slot->prompt.tokens.get_text_tokens();
                    const size_t nwrite = llama_state_seq_save_file(ctx, filepath.c_str(), slot->id, tokens.data(), token_count);

                    const int64_t t_end = ggml_time_us();
                    const double t_save_ms = (t_end - t_start) / 1000.0;

                    auto res = std::make_unique<server_task_result_slot_save_load>();
                    res->id       = task.id;
                    res->id_slot  = id_slot;
                    res->filename = filename;
                    res->is_save  = true;
                    res->n_tokens = token_count;
                    res->n_bytes  = nwrite;
                    res->t_ms     = t_save_ms;
                    queue_results.send(std::move(res));
                } break;
            case SERVER_TASK_TYPE_SLOT_RESTORE:
                {