feat(scheduler-snapshot-job-allocation): add dup_job_alloc deep-copy primitive
Task 1.1: new deep-copy for xce_job_alloc_t composed from existing make_alloc/make_host_alloc/make_job_alloc, explicitly preserving each allocation's original from/duration rather than accepting make_alloc's own time(NULL) stamp. TDD: 6 new unit tests (single-host, multi-host, multi-resource-per-host, shared-resource-pool, independence-after-free, from/duration fidelity), all passing under ASAN. Also logs bug-1723 (pre-existing, unrelated leak in two bug-1722 test fixtures, confirmed via git-stash A/B rebuild to be byte-for-byte identical before and after this change) -- documentation only, not fixed here. Requirements: 1.1, 1.4
This commit is contained in:
parent
1b4737360b
commit
097e33ae34
5 changed files with 1023 additions and 418 deletions
|
|
@ -13,5 +13,6 @@ extern xce_job_alloc_t* make_job_alloc(uint64_t, int, xce_host_alloc_t**);
|
|||
extern xce_alloc_t** make_allocs(int, ...);
|
||||
extern xce_host_alloc_t** make_host_allocs(int, ...);
|
||||
extern xce_job_account_t* make_job_account(xce_job_alloc_t*);
|
||||
extern xce_job_alloc_t* dup_job_alloc(const xce_job_alloc_t*);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "unity.h"
|
||||
#include "xce/types.h"
|
||||
#include "xcealloc.h"
|
||||
|
||||
/* generated Cap'n Proto codec — pulls in free_JobAlloc_ptr(), the existing
|
||||
* NULL-safe free path dup_job_alloc()'s output must remain compatible with. */
|
||||
#include "controller.capnp.h"
|
||||
|
||||
/* Heap buffer overflow: make_job_account() allocates account->accounts with
|
||||
* room for exactly 1 xce_host_account_t* (xcealloc.c, calloc(1, ...)) but
|
||||
* its own loop writes account->accounts[i] for i up to jalloc->nhosts-1 --
|
||||
|
|
@ -88,10 +93,375 @@ static void test_make_host_allocs_multi_host_no_overflow(void)
|
|||
TEST_ASSERT_EQUAL_STRING("host1", hallocs[1]->host);
|
||||
}
|
||||
|
||||
/* ── dup_job_alloc() (scheduler-snapshot-job-allocation task 1.1) ───────
|
||||
*
|
||||
* dup_job_alloc() must build a fresh, independent xce_job_alloc_t from a
|
||||
* source one -- composed entirely from make_alloc()/make_host_alloc()/
|
||||
* make_job_alloc() -- so a snapshot-restored job's resource allocation can
|
||||
* be reconstructed without aliasing the snapshot-producing core's own copy
|
||||
* (Req 1.1, 1.4). The output must remain compatible with the existing,
|
||||
* NULL-safe free_JobAlloc_ptr() -- no new free path is introduced.
|
||||
*
|
||||
* Unity here is built without UNITY_INCLUDE_DOUBLE, so xce_alloc_t.value
|
||||
* (a double) is compared via float cast with TEST_ASSERT_EQUAL_FLOAT,
|
||||
* matching the existing convention (scheduler/testsuite/test.c,
|
||||
* controller/testsuite/test_e2e_capnp.c). */
|
||||
|
||||
/* single-host/single-resource copy: the simplest shape. */
|
||||
static void test_dup_job_alloc_single_host_single_resource(void)
|
||||
{
|
||||
xce_alloc_t* a;
|
||||
xce_host_alloc_t* h;
|
||||
xce_host_alloc_t** hallocs;
|
||||
xce_job_alloc_t* src;
|
||||
xce_job_alloc_t* dup;
|
||||
|
||||
a = make_alloc((char*)"slots", (char*)"resv-x", 42, 3.0f, 60);
|
||||
TEST_ASSERT_NOT_NULL(a);
|
||||
|
||||
h = make_host_alloc((char*)"host0", 1, make_allocs(1, a));
|
||||
TEST_ASSERT_NOT_NULL(h);
|
||||
|
||||
hallocs = (xce_host_alloc_t**)calloc(1, sizeof(*hallocs));
|
||||
hallocs[0] = h;
|
||||
|
||||
src = make_job_alloc(7777, 1, hallocs);
|
||||
TEST_ASSERT_NOT_NULL(src);
|
||||
|
||||
dup = dup_job_alloc(src);
|
||||
TEST_ASSERT_NOT_NULL(dup);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT64(src->jobid, dup->jobid);
|
||||
TEST_ASSERT_EQUAL_INT(1, dup->nhosts);
|
||||
TEST_ASSERT_EQUAL_INT(0, dup->nres_shared);
|
||||
TEST_ASSERT_NULL(dup->allocs_shared);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("host0", dup->allocs[0]->host);
|
||||
TEST_ASSERT_EQUAL_INT(1, dup->allocs[0]->nallocs);
|
||||
TEST_ASSERT_EQUAL_STRING("slots", dup->allocs[0]->allocs[0]->resname);
|
||||
TEST_ASSERT_EQUAL_STRING("resv-x", dup->allocs[0]->allocs[0]->reserve);
|
||||
TEST_ASSERT_EQUAL_UINT64(42, dup->allocs[0]->allocs[0]->instid);
|
||||
TEST_ASSERT_EQUAL_FLOAT(3.0f, (float)dup->allocs[0]->allocs[0]->value);
|
||||
|
||||
/* independence: no pointer shared with src anywhere in the tree */
|
||||
TEST_ASSERT_NOT_EQUAL(src, dup);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs, dup->allocs);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0], dup->allocs[0]);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->host, dup->allocs[0]->host);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->allocs, dup->allocs[0]->allocs);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->allocs[0], dup->allocs[0]->allocs[0]);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->allocs[0]->resname,
|
||||
dup->allocs[0]->allocs[0]->resname);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->allocs[0]->reserve,
|
||||
dup->allocs[0]->allocs[0]->reserve);
|
||||
|
||||
free_JobAlloc_ptr(&src);
|
||||
free_JobAlloc_ptr(&dup);
|
||||
}
|
||||
|
||||
/* multi-host copy: 2 hosts, 1 resource each. */
|
||||
static void test_dup_job_alloc_multi_host(void)
|
||||
{
|
||||
xce_alloc_t* a0;
|
||||
xce_alloc_t* a1;
|
||||
xce_host_alloc_t* h0;
|
||||
xce_host_alloc_t* h1;
|
||||
xce_host_alloc_t** hallocs;
|
||||
xce_job_alloc_t* src;
|
||||
xce_job_alloc_t* dup;
|
||||
|
||||
a0 = make_alloc((char*)"slots", NULL, 10, 1.0f, 30);
|
||||
a1 = make_alloc((char*)"slots", NULL, 20, 2.0f, 45);
|
||||
TEST_ASSERT_NOT_NULL(a0);
|
||||
TEST_ASSERT_NOT_NULL(a1);
|
||||
|
||||
h0 = make_host_alloc((char*)"host0", 1, make_allocs(1, a0));
|
||||
h1 = make_host_alloc((char*)"host1", 1, make_allocs(1, a1));
|
||||
TEST_ASSERT_NOT_NULL(h0);
|
||||
TEST_ASSERT_NOT_NULL(h1);
|
||||
|
||||
hallocs = (xce_host_alloc_t**)calloc(2, sizeof(*hallocs));
|
||||
hallocs[0] = h0;
|
||||
hallocs[1] = h1;
|
||||
|
||||
src = make_job_alloc(8888, 2, hallocs);
|
||||
TEST_ASSERT_NOT_NULL(src);
|
||||
|
||||
dup = dup_job_alloc(src);
|
||||
TEST_ASSERT_NOT_NULL(dup);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(2, dup->nhosts);
|
||||
TEST_ASSERT_EQUAL_STRING("host0", dup->allocs[0]->host);
|
||||
TEST_ASSERT_EQUAL_STRING("host1", dup->allocs[1]->host);
|
||||
TEST_ASSERT_EQUAL_UINT64(10, dup->allocs[0]->allocs[0]->instid);
|
||||
TEST_ASSERT_EQUAL_UINT64(20, dup->allocs[1]->allocs[0]->instid);
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs, dup->allocs);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0], dup->allocs[0]);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[1], dup->allocs[1]);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->host, dup->allocs[0]->host);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[1]->host, dup->allocs[1]->host);
|
||||
|
||||
free_JobAlloc_ptr(&src);
|
||||
free_JobAlloc_ptr(&dup);
|
||||
}
|
||||
|
||||
/* multi-resource-per-host copy: 1 host, 2 resources. */
|
||||
static void test_dup_job_alloc_multi_resource_per_host(void)
|
||||
{
|
||||
xce_alloc_t* a0;
|
||||
xce_alloc_t* a1;
|
||||
xce_host_alloc_t* h0;
|
||||
xce_host_alloc_t** hallocs;
|
||||
xce_job_alloc_t* src;
|
||||
xce_job_alloc_t* dup;
|
||||
|
||||
a0 = make_alloc((char*)"cpu", NULL, 1, 4.0f, 60);
|
||||
a1 = make_alloc((char*)"mem", NULL, 2, 8.0f, 60);
|
||||
TEST_ASSERT_NOT_NULL(a0);
|
||||
TEST_ASSERT_NOT_NULL(a1);
|
||||
|
||||
h0 = make_host_alloc((char*)"host0", 2, make_allocs(2, a0, a1));
|
||||
TEST_ASSERT_NOT_NULL(h0);
|
||||
|
||||
hallocs = (xce_host_alloc_t**)calloc(1, sizeof(*hallocs));
|
||||
hallocs[0] = h0;
|
||||
|
||||
src = make_job_alloc(9999, 1, hallocs);
|
||||
TEST_ASSERT_NOT_NULL(src);
|
||||
|
||||
dup = dup_job_alloc(src);
|
||||
TEST_ASSERT_NOT_NULL(dup);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(2, dup->allocs[0]->nallocs);
|
||||
TEST_ASSERT_EQUAL_STRING("cpu", dup->allocs[0]->allocs[0]->resname);
|
||||
TEST_ASSERT_EQUAL_STRING("mem", dup->allocs[0]->allocs[1]->resname);
|
||||
TEST_ASSERT_EQUAL_FLOAT(4.0f, (float)dup->allocs[0]->allocs[0]->value);
|
||||
TEST_ASSERT_EQUAL_FLOAT(8.0f, (float)dup->allocs[0]->allocs[1]->value);
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->allocs, dup->allocs[0]->allocs);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->allocs[0], dup->allocs[0]->allocs[0]);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->allocs[1], dup->allocs[0]->allocs[1]);
|
||||
|
||||
free_JobAlloc_ptr(&src);
|
||||
free_JobAlloc_ptr(&dup);
|
||||
}
|
||||
|
||||
/* shared-resource-pool copy, combined with multi-host + multi-resource-per-
|
||||
* host, as the comprehensive "byte-for-byte equivalent, shares no pointers"
|
||||
* proof called out in the task's observable completion criterion. */
|
||||
static void test_dup_job_alloc_shared_resource_pool_full_equivalence(void)
|
||||
{
|
||||
xce_alloc_t* h0r0;
|
||||
xce_alloc_t* h0r1;
|
||||
xce_alloc_t* h1r0;
|
||||
xce_host_alloc_t* h0;
|
||||
xce_host_alloc_t* h1;
|
||||
xce_host_alloc_t** hallocs;
|
||||
xce_alloc_t* s0;
|
||||
xce_alloc_t* s1;
|
||||
xce_job_alloc_t* src;
|
||||
xce_job_alloc_t* dup;
|
||||
|
||||
h0r0 = make_alloc((char*)"cpu", (char*)"resv-a", 100, 2.0f, 60);
|
||||
h0r1 = make_alloc((char*)"mem", (char*)"resv-b", 101, 4.0f, 90);
|
||||
h1r0 = make_alloc((char*)"cpu", NULL, 200, 8.0f, 120);
|
||||
TEST_ASSERT_NOT_NULL(h0r0);
|
||||
TEST_ASSERT_NOT_NULL(h0r1);
|
||||
TEST_ASSERT_NOT_NULL(h1r0);
|
||||
|
||||
h0 = make_host_alloc((char*)"host0", 2, make_allocs(2, h0r0, h0r1));
|
||||
h1 = make_host_alloc((char*)"host1", 1, make_allocs(1, h1r0));
|
||||
TEST_ASSERT_NOT_NULL(h0);
|
||||
TEST_ASSERT_NOT_NULL(h1);
|
||||
|
||||
hallocs = (xce_host_alloc_t**)calloc(2, sizeof(*hallocs));
|
||||
hallocs[0] = h0;
|
||||
hallocs[1] = h1;
|
||||
|
||||
src = make_job_alloc(4242, 2, hallocs);
|
||||
TEST_ASSERT_NOT_NULL(src);
|
||||
|
||||
s0 = make_alloc((char*)"license", NULL, 300, 1.0f, 30);
|
||||
s1 = make_alloc((char*)"license2", (char*)"resv-c", 301, 3.0f, 45);
|
||||
TEST_ASSERT_NOT_NULL(s0);
|
||||
TEST_ASSERT_NOT_NULL(s1);
|
||||
|
||||
src->nres_shared = 2;
|
||||
src->allocs_shared = make_allocs(2, s0, s1);
|
||||
|
||||
dup = dup_job_alloc(src);
|
||||
TEST_ASSERT_NOT_NULL(dup);
|
||||
|
||||
/* --- byte-for-byte content equivalence --- */
|
||||
TEST_ASSERT_EQUAL_UINT64(src->jobid, dup->jobid);
|
||||
TEST_ASSERT_EQUAL_INT(src->nhosts, dup->nhosts);
|
||||
TEST_ASSERT_EQUAL_INT(src->nres_shared, dup->nres_shared);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("host0", dup->allocs[0]->host);
|
||||
TEST_ASSERT_EQUAL_INT(2, dup->allocs[0]->nallocs);
|
||||
TEST_ASSERT_EQUAL_STRING("cpu", dup->allocs[0]->allocs[0]->resname);
|
||||
TEST_ASSERT_EQUAL_STRING("resv-a", dup->allocs[0]->allocs[0]->reserve);
|
||||
TEST_ASSERT_EQUAL_UINT64(100, dup->allocs[0]->allocs[0]->instid);
|
||||
TEST_ASSERT_EQUAL_FLOAT(2.0f, (float)dup->allocs[0]->allocs[0]->value);
|
||||
TEST_ASSERT_EQUAL_STRING("mem", dup->allocs[0]->allocs[1]->resname);
|
||||
TEST_ASSERT_EQUAL_STRING("resv-b", dup->allocs[0]->allocs[1]->reserve);
|
||||
TEST_ASSERT_EQUAL_UINT64(101, dup->allocs[0]->allocs[1]->instid);
|
||||
TEST_ASSERT_EQUAL_FLOAT(4.0f, (float)dup->allocs[0]->allocs[1]->value);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("host1", dup->allocs[1]->host);
|
||||
TEST_ASSERT_EQUAL_INT(1, dup->allocs[1]->nallocs);
|
||||
TEST_ASSERT_EQUAL_STRING("cpu", dup->allocs[1]->allocs[0]->resname);
|
||||
TEST_ASSERT_EQUAL_STRING("", dup->allocs[1]->allocs[0]->reserve);
|
||||
TEST_ASSERT_EQUAL_UINT64(200, dup->allocs[1]->allocs[0]->instid);
|
||||
TEST_ASSERT_EQUAL_FLOAT(8.0f, (float)dup->allocs[1]->allocs[0]->value);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(dup->allocs_shared);
|
||||
TEST_ASSERT_EQUAL_STRING("license", dup->allocs_shared[0]->resname);
|
||||
TEST_ASSERT_EQUAL_UINT64(300, dup->allocs_shared[0]->instid);
|
||||
TEST_ASSERT_EQUAL_FLOAT(1.0f, (float)dup->allocs_shared[0]->value);
|
||||
TEST_ASSERT_EQUAL_STRING("license2", dup->allocs_shared[1]->resname);
|
||||
TEST_ASSERT_EQUAL_STRING("resv-c", dup->allocs_shared[1]->reserve);
|
||||
TEST_ASSERT_EQUAL_UINT64(301, dup->allocs_shared[1]->instid);
|
||||
TEST_ASSERT_EQUAL_FLOAT(3.0f, (float)dup->allocs_shared[1]->value);
|
||||
|
||||
/* --- no pointer anywhere in the copy is shared with the source --- */
|
||||
TEST_ASSERT_NOT_EQUAL(src, dup);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs, dup->allocs);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs_shared, dup->allocs_shared);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0], dup->allocs[0]);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[1], dup->allocs[1]);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->host, dup->allocs[0]->host);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[1]->host, dup->allocs[1]->host);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->allocs, dup->allocs[0]->allocs);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->allocs[0], dup->allocs[0]->allocs[0]);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->allocs[1], dup->allocs[0]->allocs[1]);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[1]->allocs[0], dup->allocs[1]->allocs[0]);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->allocs[0]->resname,
|
||||
dup->allocs[0]->allocs[0]->resname);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs[0]->allocs[0]->reserve,
|
||||
dup->allocs[0]->allocs[0]->reserve);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs_shared[0], dup->allocs_shared[0]);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs_shared[1], dup->allocs_shared[1]);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs_shared[0]->resname,
|
||||
dup->allocs_shared[0]->resname);
|
||||
TEST_ASSERT_NOT_EQUAL(src->allocs_shared[1]->reserve,
|
||||
dup->allocs_shared[1]->reserve);
|
||||
|
||||
free_JobAlloc_ptr(&src);
|
||||
free_JobAlloc_ptr(&dup);
|
||||
}
|
||||
|
||||
/* independence proof: mutate the source's strings/values in place, then
|
||||
* free the source entirely, and confirm the copy is unaffected either way
|
||||
* -- the direct test of the Req 1.4 "independent of the snapshot-producing
|
||||
* instance" guarantee. */
|
||||
static void test_dup_job_alloc_independence_after_mutate_and_free(void)
|
||||
{
|
||||
xce_alloc_t* a;
|
||||
xce_host_alloc_t* h;
|
||||
xce_host_alloc_t** hallocs;
|
||||
xce_job_alloc_t* src;
|
||||
xce_job_alloc_t* dup;
|
||||
|
||||
a = make_alloc((char*)"slots", (char*)"resv-x", 55, 5.0f, 70);
|
||||
TEST_ASSERT_NOT_NULL(a);
|
||||
|
||||
h = make_host_alloc((char*)"host0", 1, make_allocs(1, a));
|
||||
TEST_ASSERT_NOT_NULL(h);
|
||||
|
||||
hallocs = (xce_host_alloc_t**)calloc(1, sizeof(*hallocs));
|
||||
hallocs[0] = h;
|
||||
|
||||
src = make_job_alloc(1234, 1, hallocs);
|
||||
TEST_ASSERT_NOT_NULL(src);
|
||||
|
||||
dup = dup_job_alloc(src);
|
||||
TEST_ASSERT_NOT_NULL(dup);
|
||||
|
||||
/* mutate source strings in place (same length -- strdup'd buffers are
|
||||
* sized exactly, no room to grow) and mutate source numeric fields. */
|
||||
strcpy(src->allocs[0]->host, "hostX");
|
||||
strcpy(src->allocs[0]->allocs[0]->resname, "slotX");
|
||||
src->allocs[0]->allocs[0]->value = 999.0;
|
||||
src->allocs[0]->allocs[0]->instid = 999;
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("host0", dup->allocs[0]->host);
|
||||
TEST_ASSERT_EQUAL_STRING("slots", dup->allocs[0]->allocs[0]->resname);
|
||||
TEST_ASSERT_EQUAL_FLOAT(5.0f, (float)dup->allocs[0]->allocs[0]->value);
|
||||
TEST_ASSERT_EQUAL_UINT64(55, dup->allocs[0]->allocs[0]->instid);
|
||||
|
||||
/* now free the source entirely; the copy must remain fully valid */
|
||||
free_JobAlloc_ptr(&src);
|
||||
TEST_ASSERT_NULL(src);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("host0", dup->allocs[0]->host);
|
||||
TEST_ASSERT_EQUAL_STRING("slots", dup->allocs[0]->allocs[0]->resname);
|
||||
TEST_ASSERT_EQUAL_STRING("resv-x", dup->allocs[0]->allocs[0]->reserve);
|
||||
TEST_ASSERT_EQUAL_FLOAT(5.0f, (float)dup->allocs[0]->allocs[0]->value);
|
||||
TEST_ASSERT_EQUAL_UINT64(55, dup->allocs[0]->allocs[0]->instid);
|
||||
|
||||
free_JobAlloc_ptr(&dup);
|
||||
}
|
||||
|
||||
/* fidelity check: make_alloc() itself stamps ->from = time(NULL) internally
|
||||
* (xcealloc.c) -- a naive copy that only called make_alloc() and stopped
|
||||
* would silently reset every restored allocation's start-time to copy-time.
|
||||
* dup_job_alloc() must explicitly carry over the source's original from/
|
||||
* duration instead. */
|
||||
static void test_dup_job_alloc_from_duration_fidelity(void)
|
||||
{
|
||||
xce_alloc_t* a;
|
||||
xce_host_alloc_t* h;
|
||||
xce_host_alloc_t** hallocs;
|
||||
xce_job_alloc_t* src;
|
||||
xce_job_alloc_t* dup;
|
||||
uint64_t old_from;
|
||||
uint64_t now;
|
||||
|
||||
a = make_alloc((char*)"slots", NULL, 1, 1.0f, 0);
|
||||
TEST_ASSERT_NOT_NULL(a);
|
||||
|
||||
/* overwrite make_alloc()'s own time(NULL) stamp with a fixed value far
|
||||
* in the past, and a distinctive (negative -- "indefinite", per the
|
||||
* scheduler.c convention of duration=-1) duration. */
|
||||
old_from = (uint64_t)1000000;
|
||||
a->from = old_from;
|
||||
a->duration = -1;
|
||||
|
||||
h = make_host_alloc((char*)"host0", 1, make_allocs(1, a));
|
||||
TEST_ASSERT_NOT_NULL(h);
|
||||
|
||||
hallocs = (xce_host_alloc_t**)calloc(1, sizeof(*hallocs));
|
||||
hallocs[0] = h;
|
||||
|
||||
src = make_job_alloc(1, 1, hallocs);
|
||||
TEST_ASSERT_NOT_NULL(src);
|
||||
|
||||
now = (uint64_t)time(NULL);
|
||||
dup = dup_job_alloc(src);
|
||||
TEST_ASSERT_NOT_NULL(dup);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT64(old_from, dup->allocs[0]->allocs[0]->from);
|
||||
TEST_ASSERT_EQUAL_INT64(-1, dup->allocs[0]->allocs[0]->duration);
|
||||
|
||||
/* explicitly confirm the copy was NOT re-stamped to copy-time */
|
||||
TEST_ASSERT_NOT_EQUAL(now, dup->allocs[0]->allocs[0]->from);
|
||||
|
||||
free_JobAlloc_ptr(&src);
|
||||
free_JobAlloc_ptr(&dup);
|
||||
}
|
||||
|
||||
void test_xcealloc(void)
|
||||
{
|
||||
printf("\n>> xcealloc test\n\n");
|
||||
|
||||
RUN_TEST(test_make_job_account_multi_host_no_overflow);
|
||||
RUN_TEST(test_make_host_allocs_multi_host_no_overflow);
|
||||
RUN_TEST(test_dup_job_alloc_single_host_single_resource);
|
||||
RUN_TEST(test_dup_job_alloc_multi_host);
|
||||
RUN_TEST(test_dup_job_alloc_multi_resource_per_host);
|
||||
RUN_TEST(test_dup_job_alloc_shared_resource_pool_full_equivalence);
|
||||
RUN_TEST(test_dup_job_alloc_independence_after_mutate_and_free);
|
||||
RUN_TEST(test_dup_job_alloc_from_duration_fidelity);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,6 +167,124 @@ xce_job_alloc_t* make_job_alloc(uint64_t jobid, int nhost,
|
|||
return jalloc;
|
||||
}
|
||||
|
||||
/* dup_job_alloc() (scheduler-snapshot-job-allocation task 1.1) -- deep-copy
|
||||
* a job's resource allocation, independent of the source, for safe
|
||||
* cross-core snapshot restoration (Req 1.1, 1.4). Composed entirely from
|
||||
* make_alloc()/make_host_alloc()/make_job_alloc() below; on any allocation
|
||||
* failure partway through, returns NULL rather than a partial/inconsistent
|
||||
* result -- matching this file's existing convention (make_host_account()/
|
||||
* make_job_account() do not unwind partial state on OOM either; a leak on
|
||||
* this rare path is accepted, not a defect). */
|
||||
|
||||
static xce_alloc_t* dup_alloc(const xce_alloc_t* src)
|
||||
{
|
||||
xce_alloc_t* alloc = NULL;
|
||||
|
||||
if (src == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* make_alloc() stamps ->from = time(NULL) internally rather than
|
||||
* accepting the caller's original value, so from/duration are
|
||||
* explicitly re-stamped from src below after construction. */
|
||||
alloc = make_alloc(src->resname, src->reserve, src->instid,
|
||||
(float)src->value, 0);
|
||||
if (alloc == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
alloc->from = src->from;
|
||||
alloc->duration = src->duration;
|
||||
|
||||
return alloc;
|
||||
}
|
||||
|
||||
static xce_host_alloc_t* dup_host_alloc(const xce_host_alloc_t* src)
|
||||
{
|
||||
xce_alloc_t** allocs = NULL;
|
||||
xce_host_alloc_t* halloc = NULL;
|
||||
int i;
|
||||
|
||||
if (src == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (src->nallocs > 0) {
|
||||
allocs = (xce_alloc_t**)calloc((size_t)src->nallocs,
|
||||
sizeof(*allocs));
|
||||
if (allocs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < src->nallocs; i++) {
|
||||
allocs[i] = dup_alloc(src->allocs[i]);
|
||||
if (allocs[i] == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
halloc = make_host_alloc(src->host, src->nallocs, allocs);
|
||||
|
||||
return halloc;
|
||||
}
|
||||
|
||||
xce_job_alloc_t* dup_job_alloc(const xce_job_alloc_t* src)
|
||||
{
|
||||
xce_host_alloc_t** hallocs = NULL;
|
||||
xce_alloc_t** shared = NULL;
|
||||
xce_job_alloc_t* jalloc = NULL;
|
||||
int i;
|
||||
|
||||
if (src == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (src->nhosts > 0) {
|
||||
hallocs = (xce_host_alloc_t**)calloc((size_t)src->nhosts,
|
||||
sizeof(*hallocs));
|
||||
if (hallocs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < src->nhosts; i++) {
|
||||
hallocs[i] = dup_host_alloc(src->allocs[i]);
|
||||
if (hallocs[i] == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jalloc = make_job_alloc(src->jobid, src->nhosts, hallocs);
|
||||
if (jalloc == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* make_job_alloc() always zeroes nres_shared/allocs_shared itself, so
|
||||
* the shared-resource pool is populated here, after construction --
|
||||
* the same "patch after construct" pattern used for from/duration
|
||||
* above. */
|
||||
if (src->nres_shared > 0) {
|
||||
shared = (xce_alloc_t**)calloc((size_t)src->nres_shared,
|
||||
sizeof(*shared));
|
||||
if (shared == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < src->nres_shared; i++) {
|
||||
shared[i] = dup_alloc(src->allocs_shared[i]);
|
||||
if (shared[i] == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
jalloc->allocs_shared = shared;
|
||||
jalloc->nres_shared = src->nres_shared;
|
||||
}
|
||||
|
||||
return jalloc;
|
||||
}
|
||||
|
||||
xce_job_account_t* make_job_account(xce_job_alloc_t* jalloc)
|
||||
{
|
||||
xce_job_account_t* account = NULL;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue