| // Copyright 2022 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #include "pw_thread/thread_iteration.h" |
| |
| #include <cstddef> |
| #include <string_view> |
| |
| #include "gtest/gtest.h" |
| #include "pw_span/span.h" |
| #include "pw_string/util.h" |
| #include "pw_sync/thread_notification.h" |
| #include "pw_thread/test_threads.h" |
| #include "pw_thread/thread.h" |
| #include "pw_thread/thread_info.h" |
| |
| namespace pw::thread::freertos { |
| namespace { |
| |
| sync::ThreadNotification lock_start; |
| sync::ThreadNotification lock_end; |
| |
| void ForkedThreadEntry(void*) { |
| // Release start lock to allow test thread to continue execution. |
| lock_start.release(); |
| while (true) { |
| // Return only when end lock released by test thread. |
| if (lock_end.try_acquire()) { |
| return; |
| } |
| } |
| } |
| |
| // Tests thread iteration API by: |
| // - Forking a test thread. |
| // - Using iteration API to iterate over all running threads. |
| // - Compares name of forked thread and current thread. |
| // - Confirms thread exists and is iterated over. |
| TEST(ThreadIteration, ForkOneThread) { |
| const auto& options = *static_cast<const pw::thread::freertos::Options*>( |
| &thread::test::TestOptionsThread0()); |
| thread::Thread t(options, ForkedThreadEntry); |
| |
| // Blocked until thread t releases start lock. |
| lock_start.acquire(); |
| |
| struct { |
| bool thread_exists; |
| span<const std::byte> name; |
| } temp_struct; |
| |
| temp_struct.thread_exists = false; |
| // Max permissible length of task name including null byte. |
| static constexpr size_t buffer_size = configMAX_TASK_NAME_LEN; |
| |
| std::string_view string(string::ClampedCString(options.name(), buffer_size)); |
| temp_struct.name = as_bytes(span(string)); |
| |
| // Callback that confirms forked thread is checked by the iterator. |
| auto cb = [&temp_struct](const ThreadInfo& thread_info) { |
| // Compare sizes accounting for null byte. |
| if (thread_info.thread_name().has_value()) { |
| for (size_t i = 0; i < thread_info.thread_name().value().size(); i++) { |
| // Compare character by character of span. |
| if ((unsigned char)thread_info.thread_name().value().data()[i] != |
| (unsigned char)temp_struct.name.data()[i]) { |
| return true; |
| } |
| } |
| temp_struct.thread_exists = true; |
| } |
| // Signal to stop iteration. |
| return false; |
| }; |
| |
| thread::ForEachThread(cb); |
| |
| // Signal to forked thread that execution is complete. |
| lock_end.release(); |
| |
| // Clean up the test thread context. |
| #if PW_THREAD_JOINING_ENABLED |
| t.join(); |
| #else |
| t.detach(); |
| thread::test::WaitUntilDetachedThreadsCleanedUp(); |
| #endif // PW_THREAD_JOINING_ENABLED |
| |
| EXPECT_TRUE(temp_struct.thread_exists); |
| } |
| |
| } // namespace |
| } // namespace pw::thread::freertos |