| //===-- Unittests for sched_{set,get}{scheduler,param} --------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "src/__support/libc_errno.h" | |
| #include "src/sched/sched_get_priority_max.h" | |
| #include "src/sched/sched_get_priority_min.h" | |
| #include "src/sched/sched_getparam.h" | |
| #include "src/sched/sched_getscheduler.h" | |
| #include "src/sched/sched_setparam.h" | |
| #include "src/sched/sched_setscheduler.h" | |
| #include "src/unistd/getuid.h" | |
| #include "test/UnitTest/ErrnoCheckingTest.h" | |
| #include "test/UnitTest/Test.h" | |
| #include "hdr/sched_macros.h" | |
| #include "hdr/types/struct_sched_param.h" | |
| // We Test: | |
| // SCHED_OTHER, SCHED_FIFO, SCHED_RR | |
| // | |
| // TODO: Missing two tests. | |
| // 1) Missing permissions -> EPERM. Maybe doable by finding | |
| // another pid that exists and changing its policy, but that | |
| // seems risky. Maybe something with fork/clone would work. | |
| // | |
| // 2) Unkown pid -> ESRCH. Probably safe to choose a large range | |
| // number or scanning current pids and getting one that doesn't | |
| // exist, but again seems like it may risk actually changing | |
| // sched policy on a running task. | |
| // | |
| // Linux specific test could also include: | |
| // SCHED_ISO, SCHED_DEADLINE | |
| class SchedTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest { | |
| public: | |
| void testSched(int policy, bool is_mandatory) { | |
| int init_policy = LIBC_NAMESPACE::sched_getscheduler(0); | |
| ASSERT_GE(init_policy, 0); | |
| ASSERT_ERRNO_SUCCESS(); | |
| int max_priority = LIBC_NAMESPACE::sched_get_priority_max(policy); | |
| ASSERT_GE(max_priority, 0); | |
| ASSERT_ERRNO_SUCCESS(); | |
| int min_priority = LIBC_NAMESPACE::sched_get_priority_min(policy); | |
| ASSERT_GE(min_priority, 0); | |
| ASSERT_ERRNO_SUCCESS(); | |
| struct sched_param param = {min_priority}; | |
| // Negative pid | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(-1, policy, ¶m), -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_getscheduler(-1), -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| // Invalid Policy | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy | 128, ¶m), -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| // Out of bounds priority | |
| param.sched_priority = min_priority - 1; | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, ¶m), -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| param.sched_priority = max_priority + 1; | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, ¶m), -1); | |
| // A bit hard to test as depending on user privileges we can run into | |
| // different issues. | |
| ASSERT_TRUE(libc_errno == EINVAL || libc_errno == EPERM); | |
| libc_errno = 0; | |
| param.sched_priority = min_priority; | |
| // Success/unsupported policy/missing permissions. | |
| int setscheduler_result = | |
| LIBC_NAMESPACE::sched_setscheduler(0, policy, ¶m); | |
| ASSERT_TRUE(setscheduler_result == 0 || setscheduler_result == -1); | |
| ASSERT_TRUE( | |
| setscheduler_result != -1 | |
| ? (libc_errno == 0) | |
| : ((!is_mandatory && libc_errno == EINVAL) || libc_errno == EPERM)); | |
| libc_errno = 0; | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_getscheduler(0), | |
| setscheduler_result != -1 ? policy : init_policy); | |
| ASSERT_ERRNO_SUCCESS(); | |
| // Out of bounds priority | |
| param.sched_priority = -1; | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, ¶m), -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| param.sched_priority = max_priority + 1; | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, ¶m), -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| for (int priority = min_priority; priority <= max_priority; ++priority) { | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, ¶m), 0); | |
| ASSERT_ERRNO_SUCCESS(); | |
| int init_priority = param.sched_priority; | |
| param.sched_priority = priority; | |
| // Negative pid | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(-1, ¶m), -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(-1, ¶m), -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| // Success/unsupported policy/missing permissions | |
| int setparam_result = LIBC_NAMESPACE::sched_setparam(0, ¶m); | |
| ASSERT_TRUE(setparam_result == 0 || setparam_result == -1); | |
| ASSERT_TRUE(setparam_result != -1 | |
| ? (libc_errno == 0) | |
| : ((setscheduler_result == -1 && libc_errno == EINVAL) || | |
| libc_errno == EPERM)); | |
| libc_errno = 0; | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, ¶m), 0); | |
| ASSERT_ERRNO_SUCCESS(); | |
| ASSERT_EQ(param.sched_priority, | |
| setparam_result != -1 ? priority : init_priority); | |
| } | |
| // Null test | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_setscheduler(0, policy, nullptr), -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| } | |
| }; | |
| #define LIST_SCHED_TESTS(policy, can_set) \ | |
| using LlvmLibcSchedTest = SchedTest; \ | |
| TEST_F(LlvmLibcSchedTest, Sched_##policy) { testSched(policy, can_set); } | |
| // Mandated by POSIX. | |
| LIST_SCHED_TESTS(SCHED_OTHER, true) | |
| LIST_SCHED_TESTS(SCHED_FIFO, true) | |
| LIST_SCHED_TESTS(SCHED_RR, true) | |
| // Linux extensions. | |
| LIST_SCHED_TESTS(SCHED_BATCH, true) | |
| LIST_SCHED_TESTS(SCHED_IDLE, true) | |
| TEST(LlvmLibcSchedParamAndSchedulerTest, NullParamTest) { | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_setparam(0, nullptr), -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| ASSERT_EQ(LIBC_NAMESPACE::sched_getparam(0, nullptr), -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| } |