From 778629e24775ffa415c5122db5c16af939b9a71b Mon Sep 17 00:00:00 2001 From: Yong Cong Sin Date: Fri, 14 Jun 2024 19:55:55 +0800 Subject: [PATCH] libc: minimal: implement putc_unlocked & putchar_unlocked Add a simple implementation for putc_unlocked() & putchar_unlocked(). Signed-off-by: Yong Cong Sin --- .../portability/posix/option_groups/index.rst | 4 +- lib/libc/Kconfig | 2 + lib/libc/common/CMakeLists.txt | 2 + lib/libc/common/Kconfig | 2 + lib/libc/common/source/CMakeLists.txt | 4 ++ lib/libc/common/source/Kconfig | 4 ++ lib/libc/common/source/stdio/CMakeLists.txt | 5 +++ lib/libc/common/source/stdio/Kconfig | 19 ++++++++ lib/libc/common/source/stdio/getc_unlocked.c | 24 +++++++++++ lib/libc/common/source/stdio/putc_unlocked.c | 19 ++++++++ lib/libc/minimal/include/stdio.h | 5 +++ lib/posix/options/file_locking.c | 1 + tests/posix/common/src/file_locking.c | 43 +++++++++++++++++++ 13 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 lib/libc/common/source/CMakeLists.txt create mode 100644 lib/libc/common/source/Kconfig create mode 100644 lib/libc/common/source/stdio/CMakeLists.txt create mode 100644 lib/libc/common/source/stdio/Kconfig create mode 100644 lib/libc/common/source/stdio/getc_unlocked.c create mode 100644 lib/libc/common/source/stdio/putc_unlocked.c diff --git a/doc/services/portability/posix/option_groups/index.rst b/doc/services/portability/posix/option_groups/index.rst index 47127adb042d88..8c5d846b01d9be 100644 --- a/doc/services/portability/posix/option_groups/index.rst +++ b/doc/services/portability/posix/option_groups/index.rst @@ -569,8 +569,8 @@ This table lists service support status in Zephyr for `POSIX_FD_MGMT`: funlockfile(), yes getc_unlocked(), getchar_unlocked(), - putc_unlocked(), - putchar_unlocked(), + putc_unlocked(), yes + putchar_unlocked(), yes .. _posix_option_group_memory_protection: diff --git a/lib/libc/Kconfig b/lib/libc/Kconfig index 8794c8a898bc81..94d7bc14e7cd7c 100644 --- a/lib/libc/Kconfig +++ b/lib/libc/Kconfig @@ -76,6 +76,8 @@ config MINIMAL_LIBC imply COMMON_LIBC_CALLOC imply COMMON_LIBC_REALLOCARRAY select POSIX_FILE_LOCKING if POSIX_THREAD_SAFE_FUNCTIONS + select COMMON_LIBC_PUTC_UNLOCKED if POSIX_THREAD_SAFE_FUNCTIONS + select COMMON_LIBC_GETC_UNLOCKED if POSIX_THREAD_SAFE_FUNCTIONS help Build with minimal C library. diff --git a/lib/libc/common/CMakeLists.txt b/lib/libc/common/CMakeLists.txt index 64fe33d1c4ad35..1a822a96d6c278 100644 --- a/lib/libc/common/CMakeLists.txt +++ b/lib/libc/common/CMakeLists.txt @@ -17,5 +17,7 @@ zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_THRD source/thrd/tss.c ) +add_subdirectory(source) + # Prevent compiler from optimizing calloc into an infinite recursive call zephyr_library_compile_options($) diff --git a/lib/libc/common/Kconfig b/lib/libc/common/Kconfig index ea0d6869435c4d..25387699941904 100644 --- a/lib/libc/common/Kconfig +++ b/lib/libc/common/Kconfig @@ -82,3 +82,5 @@ config COMMON_LIBC_THRD default y help Common implementation of C11 API. + +rsource "source/Kconfig" diff --git a/lib/libc/common/source/CMakeLists.txt b/lib/libc/common/source/CMakeLists.txt new file mode 100644 index 00000000000000..d80868a73ee2eb --- /dev/null +++ b/lib/libc/common/source/CMakeLists.txt @@ -0,0 +1,4 @@ +# Copyright (c) 2024 Meta Platforms +# SPDX-License-Identifier: Apache-2.0 + +add_subdirectory_ifdef(CONFIG_COMMON_LIBC_STDIO stdio) diff --git a/lib/libc/common/source/Kconfig b/lib/libc/common/source/Kconfig new file mode 100644 index 00000000000000..4bc4ea4d185184 --- /dev/null +++ b/lib/libc/common/source/Kconfig @@ -0,0 +1,4 @@ +# Copyright (c) 2024 Meta Platforms +# SPDX-License-Identifier: Apache-2.0 + +rsource "stdio/Kconfig" diff --git a/lib/libc/common/source/stdio/CMakeLists.txt b/lib/libc/common/source/stdio/CMakeLists.txt new file mode 100644 index 00000000000000..922c590e81a589 --- /dev/null +++ b/lib/libc/common/source/stdio/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (c) 2024 Meta Platforms +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_GETC_UNLOCKED getc_unlocked.c) +zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_PUTC_UNLOCKED putc_unlocked.c) diff --git a/lib/libc/common/source/stdio/Kconfig b/lib/libc/common/source/stdio/Kconfig new file mode 100644 index 00000000000000..fe21143e912259 --- /dev/null +++ b/lib/libc/common/source/stdio/Kconfig @@ -0,0 +1,19 @@ +# Copyright (c) 2024 Meta Platforms +# SPDX-License-Identifier: Apache-2.0 + +config COMMON_LIBC_STDIO + bool + help + common function implementation in stdio.h + +config COMMON_LIBC_GETC_UNLOCKED + bool + select COMMON_LIBC_STDIO + help + common implementation of getc_unlocked() & getchar_unlocked(). + +config COMMON_LIBC_PUTC_UNLOCKED + bool + select COMMON_LIBC_STDIO + help + common implementation of putc_unlocked() & putchar_unlocked(). diff --git a/lib/libc/common/source/stdio/getc_unlocked.c b/lib/libc/common/source/stdio/getc_unlocked.c new file mode 100644 index 00000000000000..94d97e70102edd --- /dev/null +++ b/lib/libc/common/source/stdio/getc_unlocked.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 Meta Platforms + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +int getc_unlocked(FILE *stream) +{ + ARG_UNUSED(stream); + + errno = ENOSYS; + + return EOF; +} + +int getchar_unlocked(void) +{ + return getc_unlocked(stdin); +} diff --git a/lib/libc/common/source/stdio/putc_unlocked.c b/lib/libc/common/source/stdio/putc_unlocked.c new file mode 100644 index 00000000000000..5bd6ecf6cd4469 --- /dev/null +++ b/lib/libc/common/source/stdio/putc_unlocked.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 Meta Platforms + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include + +int putc_unlocked(int c, FILE *stream) +{ + return zephyr_fputc(c, stream); +} + +int putchar_unlocked(int c) +{ + return putc_unlocked(c, stdout); +} diff --git a/lib/libc/minimal/include/stdio.h b/lib/libc/minimal/include/stdio.h index 995bf255ece8f3..e179b7c9a71e6f 100644 --- a/lib/libc/minimal/include/stdio.h +++ b/lib/libc/minimal/include/stdio.h @@ -63,6 +63,11 @@ size_t fwrite(const void *ZRESTRICT ptr, size_t size, size_t nitems, #define putc(c, stream) fputc(c, stream) #define putchar(c) putc(c, stdout) +#if defined(CONFIG_COMMON_LIBC_PUTC_UNLOCKED) || defined(__DOXYGEN__) +int putc_unlocked(int c, FILE *stream); +int putchar_unlocked(int c); +#endif /* CONFIG_COMMON_LIBC_PUTC_UNLOCKED || __DOXYGEN__ */ + #if defined(CONFIG_POSIX_FILE_LOCKING) || defined(__DOXYGEN__) void flockfile(FILE *file); int ftrylockfile(FILE *file); diff --git a/lib/posix/options/file_locking.c b/lib/posix/options/file_locking.c index 755d1194828b59..9493f2c7330a9b 100644 --- a/lib/posix/options/file_locking.c +++ b/lib/posix/options/file_locking.c @@ -6,6 +6,7 @@ #include +#include #include void zvfs_flockfile(int fd); diff --git a/tests/posix/common/src/file_locking.c b/tests/posix/common/src/file_locking.c index ade9ecf908471c..2c6d06f99685f4 100644 --- a/tests/posix/common/src/file_locking.c +++ b/tests/posix/common/src/file_locking.c @@ -102,6 +102,49 @@ ZTEST(file_locking, test_file_locking) z_free_fd(POINTER_TO_INT(file)); } +void put_thread(void *p1, void *p2, void *p3) +{ + FILE *file = p1; + + /* Lock held in main thread */ + zassert_not_ok(ftrylockfile(file)); + + /* Wait for the lock */ + flockfile(file); + zassert_equal(putc_unlocked('S', file), 'S'); + zassert_equal(putchar('T'), 'T'); + funlockfile(file); +} + +ZTEST(file_locking, test_stdio) +{ + FILE *file = INT_TO_POINTER(z_alloc_fd(NULL, NULL)); + struct k_thread test_thread; + int priority = k_thread_priority_get(k_current_get()); + + /* Lock the file before creating the test thread */ + flockfile(file); + + k_thread_create(&test_thread, test_stack, K_THREAD_STACK_SIZEOF(test_stack), + put_thread, file, INT_TO_POINTER(true), NULL, priority, 0, + K_NO_WAIT); + + /* Allow the test thread to run */ + k_msleep(100); + /* The test thread should be waiting for the lock */ + zassert_equal(k_thread_join(&test_thread, K_MSEC(10)), -EAGAIN); + + /* Main thread has the lock, either version should work */ + zassert_equal(putc('T', file), 'T'); + zassert_equal(putchar_unlocked('E'), 'E'); + + /* We are done with the file here, unlock it so that test thread can run */ + funlockfile(file); + zassert_equal(k_thread_join(&test_thread, K_MSEC(100)), 0); + + z_free_fd(POINTER_TO_INT(file)); +} + #else /** * PicoLIBC doesn't support these functions in its header