Skip to content

Commit f5f90e9

Browse files
authored
Merge branch 'master' into paaggarwal/add-timed-test-suite-to-all-tests
2 parents 4041ef7 + f946b90 commit f5f90e9

9 files changed

Lines changed: 866 additions & 294 deletions

win32/devdoc/job_object_helper_requirements.md

Lines changed: 91 additions & 22 deletions
Large diffs are not rendered by default.

win32/inc/c_pal/job_object_helper.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515

1616
#include "c_pal/thandle.h"
1717

18+
// Passing JOB_OBJECT_HELPER_DISABLE_CPU_RATE_CONTROL as percent_cpu disables CPU rate control (removes the throttle)
19+
#define JOB_OBJECT_HELPER_DISABLE_CPU_RATE_CONTROL 0
20+
21+
// Passing JOB_OBJECT_HELPER_DISABLE_MEMORY_LIMIT as percent_physical_memory removes memory limits
22+
#define JOB_OBJECT_HELPER_DISABLE_MEMORY_LIMIT 0
23+
1824
typedef struct JOB_OBJECT_HELPER_TAG JOB_OBJECT_HELPER;
1925
THANDLE_TYPE_DECLARE(JOB_OBJECT_HELPER);
2026

win32/src/job_object_helper.c

Lines changed: 226 additions & 127 deletions
Large diffs are not rendered by default.
Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,30 @@
11
#Copyright (c) Microsoft. All rights reserved.
22
#Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4-
# Negative tests (no job objects created in current process)
5-
set(theseTestsName job_object_helper_negative_int)
6-
set(${theseTestsName}_test_files
7-
${theseTestsName}.c
4+
set(int_test_names
5+
job_object_helper_negative_int
6+
job_object_helper_create_int
7+
job_object_helper_memory_int
8+
job_object_helper_multi_process_int
9+
job_object_helper_singleton_int
10+
job_object_helper_reconfigure_int
811
)
9-
set(${theseTestsName}_c_files)
10-
set(${theseTestsName}_h_files)
11-
build_test_artifacts(${theseTestsName} "tests/c_pal/win32" ADDITIONAL_LIBS c_pal)
1212

13-
# Basic creation test (creates job object in current process - 1 test per process)
14-
set(theseTestsName job_object_helper_create_int)
15-
set(${theseTestsName}_test_files
16-
${theseTestsName}.c
13+
# Tests that need the helper tester executable built first
14+
set(tests_needing_tester
15+
job_object_helper_create_int
16+
job_object_helper_multi_process_int
1717
)
18-
set(${theseTestsName}_c_files)
19-
set(${theseTestsName}_h_files)
20-
build_test_artifacts(${theseTestsName} "tests/c_pal/win32" ADDITIONAL_LIBS c_pal)
21-
add_dependencies(${theseTestsName}_exe_${CMAKE_PROJECT_NAME} job_object_helper_tester)
2218

23-
# Memory limits test (creates job object in current process - 1 test per process)
24-
set(theseTestsName job_object_helper_memory_int)
25-
set(${theseTestsName}_test_files
26-
${theseTestsName}.c
27-
)
28-
set(${theseTestsName}_c_files)
29-
set(${theseTestsName}_h_files)
30-
build_test_artifacts(${theseTestsName} "tests/c_pal/win32" ADDITIONAL_LIBS c_pal)
31-
32-
# Multi-process tests (creates job objects in child processes only)
33-
set(theseTestsName job_object_helper_multi_process_int)
34-
set(${theseTestsName}_test_files
35-
${theseTestsName}.c
36-
)
37-
set(${theseTestsName}_c_files)
38-
set(${theseTestsName}_h_files)
39-
build_test_artifacts(${theseTestsName} "tests/c_pal/win32" ADDITIONAL_LIBS c_pal)
40-
add_dependencies(${theseTestsName}_exe_${CMAKE_PROJECT_NAME} job_object_helper_tester)
19+
foreach(theseTestsName IN LISTS int_test_names)
20+
set(${theseTestsName}_test_files
21+
${theseTestsName}.c
22+
)
23+
set(${theseTestsName}_c_files)
24+
set(${theseTestsName}_h_files)
25+
build_test_artifacts(${theseTestsName} "tests/c_pal/win32" ADDITIONAL_LIBS c_pal)
4126

42-
# Singleton/CPU compounding test (creates job object in current process - 1 test per process)
43-
set(theseTestsName job_object_helper_singleton_int)
44-
set(${theseTestsName}_test_files
45-
${theseTestsName}.c
46-
)
47-
set(${theseTestsName}_c_files)
48-
set(${theseTestsName}_h_files)
49-
build_test_artifacts(${theseTestsName} "tests/c_pal/win32" ADDITIONAL_LIBS c_pal)
27+
if(${theseTestsName} IN_LIST tests_needing_tester)
28+
add_dependencies(${theseTestsName}_exe_${CMAKE_PROJECT_NAME} job_object_helper_tester)
29+
endif()
30+
endforeach()

win32/tests/job_object_helper_int/job_object_helper_create_int.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ TEST_FUNCTION(test_job_object_helper_set_job_limits_to_current_process)
5454
(void)snprintf(job_name, sizeof(job_name), TEST_JOB_NAME_PREFIX "%" PRI_UUID_T "", UUID_T_VALUES(job_name_uuid));
5555
LogInfo("Running test with job name: %s...", job_name);
5656

57-
THANDLE(JOB_OBJECT_HELPER) result = job_object_helper_set_job_limits_to_current_process(job_name, 50, 1);
58-
ASSERT_IS_NOT_NULL(result);
57+
THANDLE(JOB_OBJECT_HELPER) job_object_helper = job_object_helper_set_job_limits_to_current_process(job_name, 50, 1);
58+
ASSERT_IS_NOT_NULL(job_object_helper);
5959

6060
/* Check that the job object was created */
6161
HANDLE job_object = OpenJobObjectA(JOB_OBJECT_QUERY, FALSE, job_name);
@@ -94,8 +94,9 @@ TEST_FUNCTION(test_job_object_helper_set_job_limits_to_current_process)
9494
ASSERT_ARE_EQUAL(size_t, job_info.ProcessMemoryLimit / MEGABYTE, one_percent_of_total_memory_in_MB, "Job object should have process memory limit set to 1%% of total physical memory");
9595

9696
/* Performing the same action from the same process shall not change anything */
97-
THANDLE(JOB_OBJECT_HELPER) result_1 = job_object_helper_set_job_limits_to_current_process(job_name, 50, 1);
98-
ASSERT_IS_NOT_NULL(result_1);
97+
THANDLE(JOB_OBJECT_HELPER) job_object_helper_duplicate = job_object_helper_set_job_limits_to_current_process(job_name, 50, 1);
98+
ASSERT_IS_NOT_NULL(job_object_helper_duplicate);
99+
ASSERT_ARE_EQUAL(void_ptr, job_object_helper, job_object_helper_duplicate, "Duplicate call with same params should return same singleton");
99100

100101
job_object = OpenJobObjectA(JOB_OBJECT_QUERY, FALSE, job_name);
101102
ASSERT_IS_NOT_NULL(job_object);
@@ -113,8 +114,8 @@ TEST_FUNCTION(test_job_object_helper_set_job_limits_to_current_process)
113114
ASSERT_ARE_EQUAL(size_t, job_info.JobMemoryLimit / MEGABYTE, one_percent_of_total_memory_in_MB, "Job object should have job memory limit set to 1%% of total physical memory");
114115
ASSERT_ARE_EQUAL(size_t, job_info.ProcessMemoryLimit / MEGABYTE, one_percent_of_total_memory_in_MB, "Job object should have process memory limit set to 1%% of total physical memory");
115116

116-
THANDLE_ASSIGN(JOB_OBJECT_HELPER)(&result, NULL);
117-
THANDLE_ASSIGN(JOB_OBJECT_HELPER)(&result_1, NULL);
117+
THANDLE_ASSIGN(JOB_OBJECT_HELPER)(&job_object_helper, NULL);
118+
THANDLE_ASSIGN(JOB_OBJECT_HELPER)(&job_object_helper_duplicate, NULL);
118119
}
119120

120121
END_TEST_SUITE(TEST_SUITE_NAME_FROM_CMAKE)

win32/tests/job_object_helper_int/job_object_helper_negative_int.c

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -40,43 +40,55 @@ TEST_FUNCTION_CLEANUP(cleanup)
4040
job_object_helper_deinit_for_test();
4141
}
4242

43-
/*Tests_SRS_JOB_OBJECT_HELPER_88_001: [ If percent_cpu is 100 and percent_physical_memory is 100, job_object_helper_set_job_limits_to_current_process shall return NULL without creating a job object (100% CPU and 100% memory are effectively no limits). ]*/
44-
TEST_FUNCTION(job_object_helper_set_job_limits_to_current_process_with_100_percent_cpu_and_memory_returns_null)
43+
TEST_FUNCTION(job_object_helper_set_job_limits_to_current_process_returns_null_when_no_effective_limits_and_null_job_name)
4544
{
46-
/* 100% CPU with 100% memory is effectively no limit.
45+
/* Both (0,0) and (100,100) are effectively no limits.
4746
The function should return NULL without creating a job object,
48-
avoiding any risk of compounding if called again. */
47+
avoiding any risk of compounding if called again.
48+
Uses empty string (unnamed job object). */
4949

50-
///act
51-
THANDLE(JOB_OBJECT_HELPER) result = job_object_helper_set_job_limits_to_current_process("", MAX_CPU_PERCENT, MAX_MEMORY_PERCENT);
50+
uint32_t cpu_limits[2] = { 0, MAX_CPU_PERCENT };
51+
uint32_t memory_limits[2] = { 0, MAX_MEMORY_PERCENT };
5252

53-
///assert
54-
ASSERT_IS_NULL(result, "100%% CPU with 100%% memory should return NULL (no job object needed)");
53+
for (int i = 0; i < MU_COUNT_ARRAY_ITEMS(cpu_limits); ++i)
54+
{
55+
///act
56+
THANDLE(JOB_OBJECT_HELPER) job_object_helper = job_object_helper_set_job_limits_to_current_process(NULL, cpu_limits[i], memory_limits[i]);
57+
58+
///assert
59+
ASSERT_IS_NULL(job_object_helper, "(%" PRIu32 ", %" PRIu32 ") should return NULL (no job object needed)", cpu_limits[i], memory_limits[i]);
60+
}
5561
}
5662

57-
/*Tests_SRS_JOB_OBJECT_HELPER_88_001: [ If percent_cpu is 100 and percent_physical_memory is 100, job_object_helper_set_job_limits_to_current_process shall return NULL without creating a job object (100% CPU and 100% memory are effectively no limits). ]*/
58-
TEST_FUNCTION(job_object_helper_set_job_limits_to_current_process_with_100_percent_cpu_and_memory_does_not_create_job_object)
63+
TEST_FUNCTION(job_object_helper_set_job_limits_to_current_process_returns_null_when_no_effective_limits_and_named_job_object)
5964
{
60-
/* 100% CPU with 100% memory is effectively no limit.
61-
The function should return NULL and no job object should be created. */
65+
/* Both (0,0) and (100,100) are effectively no limits.
66+
The function should return NULL and no job object should be created.
67+
Uses a named job object so we can verify via OpenJobObjectA that no OS object was created. */
68+
69+
uint32_t cpu_limits[2] = { 0, MAX_CPU_PERCENT };
70+
uint32_t memory_limits[2] = { 0, MAX_MEMORY_PERCENT };
6271

63-
///arrange
64-
UUID_T job_name_uuid;
65-
(void)uuid_produce(&job_name_uuid);
72+
for (int i = 0; i < MU_COUNT_ARRAY_ITEMS(cpu_limits); ++i)
73+
{
74+
///arrange
75+
UUID_T job_name_uuid;
76+
(void)uuid_produce(&job_name_uuid);
6677

67-
char job_name[64];
68-
(void)snprintf(job_name, sizeof(job_name), TEST_JOB_NAME_PREFIX "%" PRI_UUID_T "", UUID_T_VALUES(job_name_uuid));
69-
LogInfo("Running test with job name: %s...", job_name);
78+
char job_name[64];
79+
(void)snprintf(job_name, sizeof(job_name), TEST_JOB_NAME_PREFIX "%" PRI_UUID_T "", UUID_T_VALUES(job_name_uuid));
80+
LogInfo("Running test with job name: %s, cpu=%" PRIu32 ", memory=%" PRIu32 "...", job_name, cpu_limits[i], memory_limits[i]);
7081

71-
///act
72-
THANDLE(JOB_OBJECT_HELPER) result = job_object_helper_set_job_limits_to_current_process(job_name, MAX_CPU_PERCENT, MAX_MEMORY_PERCENT);
82+
///act
83+
THANDLE(JOB_OBJECT_HELPER) job_object_helper = job_object_helper_set_job_limits_to_current_process(job_name, cpu_limits[i], memory_limits[i]);
7384

74-
///assert
75-
ASSERT_IS_NULL(result, "100%% CPU with 100%% memory should return NULL (no job object needed)");
85+
///assert
86+
ASSERT_IS_NULL(job_object_helper, "(%" PRIu32 ", %" PRIu32 ") should return NULL (no job object needed)", cpu_limits[i], memory_limits[i]);
7687

77-
/* Verify that no job object was actually created */
78-
HANDLE job_object = OpenJobObjectA(JOB_OBJECT_QUERY, FALSE, job_name);
79-
ASSERT_IS_NULL(job_object, "No job object should have been created for 100%% CPU and 100%% memory");
88+
/* Verify that no job object was actually created */
89+
HANDLE job_object = OpenJobObjectA(JOB_OBJECT_QUERY, FALSE, job_name);
90+
ASSERT_IS_NULL(job_object, "No job object should have been created for (%" PRIu32 ", %" PRIu32 ")", cpu_limits[i], memory_limits[i]);
91+
}
8092
}
8193

8294
END_TEST_SUITE(TEST_SUITE_NAME_FROM_CMAKE)
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
// Copyright(C) Microsoft Corporation.All rights reserved.
2+
3+
4+
#include <stdlib.h>
5+
#include <inttypes.h>
6+
7+
#include "windows.h"
8+
9+
#include "macro_utils/macro_utils.h"
10+
11+
#include "testrunnerswitcher.h"
12+
13+
#include "c_pal/gballoc_hl.h"
14+
#include "c_pal/gballoc_hl_redirect.h"
15+
#include "c_pal/job_object_helper.h"
16+
#include "c_pal/uuid.h"
17+
18+
/* Complete the opaque struct definition so integration tests can access
19+
the internal job_object HANDLE for QueryInformationJobObject calls
20+
(needed for unnamed job objects where OpenJobObjectA is not available). */
21+
struct JOB_OBJECT_HELPER_TAG {
22+
HANDLE job_object;
23+
};
24+
25+
26+
#define MEGABYTE ((size_t)1024 * 1024)
27+
#define TEST_JOB_NAME_PREFIX "job_test_reconfig_"
28+
#define INITIAL_CPU_PERCENT 50
29+
#define INITIAL_MEMORY_PERCENT 1
30+
#define RECONFIGURED_CPU_PERCENT 30
31+
#define RECONFIGURED_MEMORY_PERCENT 2
32+
33+
BEGIN_TEST_SUITE(TEST_SUITE_NAME_FROM_CMAKE)
34+
35+
TEST_SUITE_INITIALIZE(suite_init)
36+
{
37+
}
38+
39+
TEST_SUITE_CLEANUP(suite_cleanup)
40+
{
41+
}
42+
43+
TEST_FUNCTION_INITIALIZE(init)
44+
{
45+
}
46+
47+
TEST_FUNCTION_CLEANUP(cleanup)
48+
{
49+
job_object_helper_deinit_for_test();
50+
}
51+
52+
TEST_FUNCTION(job_object_helper_set_job_limits_to_current_process_reconfigures_limits_for_named_job_object)
53+
{
54+
/* This test verifies that:
55+
* 1. A job object is created with initial limits
56+
* 2. The limits can be reconfigured in-place via a second call
57+
* 3. The reconfigured limits are applied to the existing job object
58+
*/
59+
60+
UUID_T job_name_uuid;
61+
(void)uuid_produce(&job_name_uuid);
62+
63+
char job_name[64];
64+
(void)snprintf(job_name, sizeof(job_name), TEST_JOB_NAME_PREFIX "%" PRI_UUID_T "", UUID_T_VALUES(job_name_uuid));
65+
LogInfo("Running reconfigure test with job name: %s...", job_name);
66+
67+
// Step 1: Create the singleton with initial limits (50% CPU, 1% memory)
68+
THANDLE(JOB_OBJECT_HELPER) initial_job_object_helper = job_object_helper_set_job_limits_to_current_process(job_name, INITIAL_CPU_PERCENT, INITIAL_MEMORY_PERCENT);
69+
ASSERT_IS_NOT_NULL(initial_job_object_helper);
70+
71+
// Verify initial CPU rate
72+
HANDLE job_object = OpenJobObjectA(JOB_OBJECT_QUERY, FALSE, job_name);
73+
ASSERT_IS_NOT_NULL(job_object, "Failed to open job object");
74+
75+
JOBOBJECT_CPU_RATE_CONTROL_INFORMATION cpu_info = { 0 };
76+
DWORD return_length = 0;
77+
BOOL query_result = QueryInformationJobObject(job_object, JobObjectCpuRateControlInformation, &cpu_info, sizeof(cpu_info), &return_length);
78+
ASSERT_IS_TRUE(query_result, "Failed to query CPU rate info");
79+
ASSERT_ARE_EQUAL(uint32_t, INITIAL_CPU_PERCENT * 100, cpu_info.CpuRate, "Initial CPU rate should be %" PRIu32 "", INITIAL_CPU_PERCENT * 100);
80+
LogInfo("Initial CPU rate verified: %" PRIu32 "", cpu_info.CpuRate);
81+
82+
// Verify initial memory limit
83+
MEMORYSTATUSEX mem_status;
84+
mem_status.dwLength = sizeof(mem_status);
85+
ASSERT_IS_TRUE(GlobalMemoryStatusEx(&mem_status));
86+
SIZE_T expected_initial_memory_in_MB = INITIAL_MEMORY_PERCENT * mem_status.ullTotalPhys / 100 / MEGABYTE;
87+
88+
JOBOBJECT_EXTENDED_LIMIT_INFORMATION ext_info = { 0 };
89+
query_result = QueryInformationJobObject(job_object, JobObjectExtendedLimitInformation, &ext_info, sizeof(ext_info), &return_length);
90+
ASSERT_IS_TRUE(query_result, "Failed to query extended limit info");
91+
ASSERT_ARE_EQUAL(size_t, expected_initial_memory_in_MB, ext_info.JobMemoryLimit / MEGABYTE, "Initial job memory limit should match");
92+
LogInfo("Initial memory limit verified: %zu MB", ext_info.JobMemoryLimit / MEGABYTE);
93+
94+
(void)CloseHandle(job_object);
95+
96+
// Step 2: Reconfigure to new limits (30% CPU, 2% memory)
97+
THANDLE(JOB_OBJECT_HELPER) reconfigured_job_object_helper = job_object_helper_set_job_limits_to_current_process(job_name, RECONFIGURED_CPU_PERCENT, RECONFIGURED_MEMORY_PERCENT);
98+
ASSERT_IS_NOT_NULL(reconfigured_job_object_helper, "Reconfigure should succeed");
99+
ASSERT_ARE_EQUAL(void_ptr, initial_job_object_helper, reconfigured_job_object_helper, "Reconfigured call should return same singleton");
100+
101+
// Step 3: Verify reconfigured CPU rate
102+
job_object = OpenJobObjectA(JOB_OBJECT_QUERY, FALSE, job_name);
103+
ASSERT_IS_NOT_NULL(job_object, "Failed to open job object after reconfigure");
104+
105+
(void)memset(&cpu_info, 0, sizeof(cpu_info));
106+
query_result = QueryInformationJobObject(job_object, JobObjectCpuRateControlInformation, &cpu_info, sizeof(cpu_info), &return_length);
107+
ASSERT_IS_TRUE(query_result, "Failed to query CPU rate info after reconfigure");
108+
ASSERT_ARE_EQUAL(uint32_t, RECONFIGURED_CPU_PERCENT * 100, cpu_info.CpuRate, "Reconfigured CPU rate should be %" PRIu32 "", RECONFIGURED_CPU_PERCENT * 100);
109+
LogInfo("Reconfigured CPU rate verified: %" PRIu32 "", cpu_info.CpuRate);
110+
111+
// Verify reconfigured memory limit
112+
SIZE_T expected_reconfigured_memory_in_MB = RECONFIGURED_MEMORY_PERCENT * mem_status.ullTotalPhys / 100 / MEGABYTE;
113+
114+
(void)memset(&ext_info, 0, sizeof(ext_info));
115+
query_result = QueryInformationJobObject(job_object, JobObjectExtendedLimitInformation, &ext_info, sizeof(ext_info), &return_length);
116+
ASSERT_IS_TRUE(query_result, "Failed to query extended limit info after reconfigure");
117+
ASSERT_ARE_EQUAL(size_t, expected_reconfigured_memory_in_MB, ext_info.JobMemoryLimit / MEGABYTE, "Reconfigured job memory limit should match");
118+
LogInfo("Reconfigured memory limit verified: %zu MB", ext_info.JobMemoryLimit / MEGABYTE);
119+
120+
(void)CloseHandle(job_object);
121+
122+
// Cleanup
123+
THANDLE_ASSIGN(JOB_OBJECT_HELPER)(&initial_job_object_helper, NULL);
124+
THANDLE_ASSIGN(JOB_OBJECT_HELPER)(&reconfigured_job_object_helper, NULL);
125+
}
126+
127+
TEST_FUNCTION(job_object_helper_set_job_limits_to_current_process_reconfigures_limits_for_unnamed_job_object)
128+
{
129+
/* This test verifies reconfiguration works for unnamed job objects (NULL job_name).
130+
* Since OpenJobObjectA cannot be used with unnamed objects, we access the
131+
* internal job_object HANDLE from the THANDLE to query limits directly.
132+
*/
133+
134+
LogInfo("Running reconfigure test with NULL job name (unnamed job object)...");
135+
136+
// Step 1: Create the singleton with initial limits
137+
THANDLE(JOB_OBJECT_HELPER) initial_job_object_helper = job_object_helper_set_job_limits_to_current_process(NULL, INITIAL_CPU_PERCENT, INITIAL_MEMORY_PERCENT);
138+
ASSERT_IS_NOT_NULL(initial_job_object_helper);
139+
140+
// Verify initial CPU rate via internal handle
141+
JOBOBJECT_CPU_RATE_CONTROL_INFORMATION cpu_info = { 0 };
142+
DWORD return_length = 0;
143+
BOOL query_result = QueryInformationJobObject(initial_job_object_helper->job_object, JobObjectCpuRateControlInformation, &cpu_info, sizeof(cpu_info), &return_length);
144+
ASSERT_IS_TRUE(query_result, "Failed to query CPU rate info");
145+
ASSERT_ARE_EQUAL(uint32_t, INITIAL_CPU_PERCENT * 100, cpu_info.CpuRate, "Initial CPU rate should be %" PRIu32 "", INITIAL_CPU_PERCENT * 100);
146+
LogInfo("Initial CPU rate verified: %" PRIu32 "", cpu_info.CpuRate);
147+
148+
// Verify initial memory limit
149+
MEMORYSTATUSEX mem_status;
150+
mem_status.dwLength = sizeof(mem_status);
151+
ASSERT_IS_TRUE(GlobalMemoryStatusEx(&mem_status));
152+
SIZE_T expected_initial_memory_in_MB = INITIAL_MEMORY_PERCENT * mem_status.ullTotalPhys / 100 / MEGABYTE;
153+
154+
JOBOBJECT_EXTENDED_LIMIT_INFORMATION ext_info = { 0 };
155+
query_result = QueryInformationJobObject(initial_job_object_helper->job_object, JobObjectExtendedLimitInformation, &ext_info, sizeof(ext_info), &return_length);
156+
ASSERT_IS_TRUE(query_result, "Failed to query extended limit info");
157+
ASSERT_ARE_EQUAL(size_t, expected_initial_memory_in_MB, ext_info.JobMemoryLimit / MEGABYTE, "Initial job memory limit should match");
158+
LogInfo("Initial memory limit verified: %zu MB", ext_info.JobMemoryLimit / MEGABYTE);
159+
160+
// Step 2: Reconfigure to new limits
161+
THANDLE(JOB_OBJECT_HELPER) reconfigured_job_object_helper = job_object_helper_set_job_limits_to_current_process(NULL, RECONFIGURED_CPU_PERCENT, RECONFIGURED_MEMORY_PERCENT);
162+
ASSERT_IS_NOT_NULL(reconfigured_job_object_helper, "Reconfigure should succeed");
163+
ASSERT_ARE_EQUAL(void_ptr, initial_job_object_helper, reconfigured_job_object_helper, "Reconfigured call should return same singleton");
164+
165+
// Step 3: Verify reconfigured CPU rate
166+
(void)memset(&cpu_info, 0, sizeof(cpu_info));
167+
query_result = QueryInformationJobObject(reconfigured_job_object_helper->job_object, JobObjectCpuRateControlInformation, &cpu_info, sizeof(cpu_info), &return_length);
168+
ASSERT_IS_TRUE(query_result, "Failed to query CPU rate info after reconfigure");
169+
ASSERT_ARE_EQUAL(uint32_t, RECONFIGURED_CPU_PERCENT * 100, cpu_info.CpuRate, "Reconfigured CPU rate should be %" PRIu32 "", RECONFIGURED_CPU_PERCENT * 100);
170+
LogInfo("Reconfigured CPU rate verified: %" PRIu32 "", cpu_info.CpuRate);
171+
172+
// Verify reconfigured memory limit
173+
SIZE_T expected_reconfigured_memory_in_MB = RECONFIGURED_MEMORY_PERCENT * mem_status.ullTotalPhys / 100 / MEGABYTE;
174+
175+
(void)memset(&ext_info, 0, sizeof(ext_info));
176+
query_result = QueryInformationJobObject(reconfigured_job_object_helper->job_object, JobObjectExtendedLimitInformation, &ext_info, sizeof(ext_info), &return_length);
177+
ASSERT_IS_TRUE(query_result, "Failed to query extended limit info after reconfigure");
178+
ASSERT_ARE_EQUAL(size_t, expected_reconfigured_memory_in_MB, ext_info.JobMemoryLimit / MEGABYTE, "Reconfigured job memory limit should match");
179+
LogInfo("Reconfigured memory limit verified: %zu MB", ext_info.JobMemoryLimit / MEGABYTE);
180+
181+
// Cleanup
182+
THANDLE_ASSIGN(JOB_OBJECT_HELPER)(&initial_job_object_helper, NULL);
183+
THANDLE_ASSIGN(JOB_OBJECT_HELPER)(&reconfigured_job_object_helper, NULL);
184+
}
185+
186+
END_TEST_SUITE(TEST_SUITE_NAME_FROM_CMAKE)

0 commit comments

Comments
 (0)