summaryrefslogtreecommitdiffstats
path: root/testsuites/libtests/newlib01/init.c
diff options
context:
space:
mode:
Diffstat (limited to 'testsuites/libtests/newlib01/init.c')
-rw-r--r--testsuites/libtests/newlib01/init.c350
1 files changed, 304 insertions, 46 deletions
diff --git a/testsuites/libtests/newlib01/init.c b/testsuites/libtests/newlib01/init.c
index 383abf41f6..d3ccbddba1 100644
--- a/testsuites/libtests/newlib01/init.c
+++ b/testsuites/libtests/newlib01/init.c
@@ -1,15 +1,28 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
/*
- * Copyright (c) 2014 embedded brains GmbH. All rights reserved.
+ * Copyright (C) 2014, 2022 embedded brains GmbH & Co. KG
*
- * embedded brains GmbH
- * Dornierstr. 4
- * 82178 Puchheim
- * Germany
- * <rtems@embedded-brains.de>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
*
- * The license and distribution terms for this file may be
- * found in the file LICENSE in this distribution or at
- * http://www.rtems.org/license/LICENSE.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
@@ -17,19 +30,28 @@
#endif
#include <stdio.h>
+#include <errno.h>
#include <sys/reent.h>
+#include <sys/stat.h>
#include <rtems.h>
#include <rtems/console.h>
#include <rtems/imfs.h>
#include <rtems/libcsupport.h>
+#include <rtems/sysinit.h>
#include "tmacros.h"
+#ifndef _REENT_CLEANUP
+#define _REENT_CLEANUP(ptr) ((ptr)->__cleanup)
+#endif
+
const char rtems_test_name[] = "NEWLIB 1";
-static const char file_path[] = "/file";
+static const char stdio_file_path[] = "/stdio-file";
+
+static const char non_stdio_file_path[] = "/non-stdio-file";
typedef enum {
INIT,
@@ -42,6 +64,8 @@ typedef struct {
rtems_id main_task_id;
rtems_id worker_task_id;
test_state current;
+ FILE *non_stdio_file;
+ int non_stdio_fd;
} test_context;
static test_context test_instance;
@@ -62,25 +86,71 @@ static void wait(void)
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
-static void worker_task(rtems_task_argument arg)
+/*
+ * Check that rand() is properly initialized and returns the expected sequence
+ * for default seed values. A call to rand() without any previous call to
+ * srand() generates the same sequence as when srand() is first called with a
+ * seed value of 1.
+ */
+static void test_rand(void)
+{
+ int rv;
+
+ rv = rand();
+ rtems_test_assert(rv == 1481765933);
+ rv = rand();
+ rtems_test_assert(rv == 1085377743);
+ rv = rand();
+ rtems_test_assert(rv == 1270216262);
+
+ srand(1);
+ rv = rand();
+ rtems_test_assert(rv == 1481765933);
+ rv = rand();
+ rtems_test_assert(rv == 1085377743);
+ rv = rand();
+ rtems_test_assert(rv == 1270216262);
+}
+
+/*
+ * Check that lrand48() is properly initialized and returns the expected
+ * sequence for default seed values. A call to lrand48() without any previous
+ * call to srand48() uses default constant initializer values set in the _seed
+ * member of struct _rand48.
+ */
+static void test_lrand48(void)
+{
+ long rv;
+
+ rv = lrand48();
+ rtems_test_assert(rv == 851401618);
+ rv = lrand48();
+ rtems_test_assert(rv == 1804928587);
+ rv = lrand48();
+ rtems_test_assert(rv == 758783491);
+}
+
+static void stdio_file_worker(rtems_task_argument arg)
{
test_context *ctx = &test_instance;
- struct _reent *reent = _REENT;
FILE *output;
char buf[1] = { 'x' };
size_t n;
- rtems_test_assert(reent->__sdidinit == 0);
+ test_rand();
+ test_lrand48();
- output = stdout = fopen(&file_path[0], "r+");
+ rtems_test_assert(_REENT_CLEANUP(_REENT) == NULL);
+
+ output = stdout = fopen(&stdio_file_path[0], "r+");
rtems_test_assert(stdout != NULL);
/*
* Check newlib's __sinit does not touch our assigned file pointer.
*/
- rtems_test_assert(reent->__sdidinit == 0);
+ rtems_test_assert(_REENT_CLEANUP(_REENT) == NULL);
rtems_test_assert(fflush(stdout) == 0);
- rtems_test_assert(reent->__sdidinit != 0);
+ rtems_test_assert(_REENT_CLEANUP(_REENT) != NULL);
rtems_test_assert(stdout == output);
n = fwrite(&buf[0], sizeof(buf), 1, stdout);
@@ -93,6 +163,30 @@ static void worker_task(rtems_task_argument arg)
rtems_test_assert(0);
}
+static void non_stdio_file_worker(rtems_task_argument arg)
+{
+ test_context *ctx = &test_instance;
+ FILE *fp;
+ char buf[1] = { 'y' };
+ size_t n;
+ int fd;
+
+ fp = ctx->non_stdio_file = fopen(&non_stdio_file_path[0], "w");
+ rtems_test_assert(fp != NULL);
+
+ /* Get file descriptor of new global file stream, store it in text context */
+ fd = fileno(fp);
+ rtems_test_assert(fd != -1);
+ ctx->non_stdio_fd = fd;
+
+ n = fwrite(&buf[0], sizeof(buf), 1, fp);
+ rtems_test_assert(n == 1);
+
+ wake_up_main(ctx);
+
+ rtems_test_assert(0);
+}
+
static int handler_open(
rtems_libio_t *iop,
const char *path,
@@ -253,31 +347,9 @@ static const IMFS_node_control node_control = IMFS_GENERIC_INITIALIZER(
IMFS_node_destroy_default
);
-static void test(void)
+static void create_and_run_worker(test_context *ctx, rtems_task_entry entry)
{
- test_context *ctx = &test_instance;
rtems_status_code sc;
- int rv;
- rtems_resource_snapshot snapshot;
- FILE *file;
-
- ctx->main_task_id = rtems_task_self();
-
- /* Fill dynamic file pool in Newlib _GLOBAL_REENT */
- file = fopen(CONSOLE_DEVICE_NAME, "r+");
- rtems_test_assert(file != NULL);
- rv = fclose(file);
- rtems_test_assert(rv == 0);
-
- rtems_resource_snapshot_take(&snapshot);
-
- rv = IMFS_make_generic_node(
- &file_path[0],
- S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
- &node_control,
- NULL
- );
- rtems_test_assert(rv == 0);
sc = rtems_task_create(
rtems_build_name('W', 'O', 'R', 'K'),
@@ -289,38 +361,224 @@ static void test(void)
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
- sc = rtems_task_start(ctx->worker_task_id, worker_task, 0);
+ sc = rtems_task_start(ctx->worker_task_id, entry, 0);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
wait();
sc = rtems_task_delete(ctx->worker_task_id);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+/*
+ * Check that a FILE opened by a task and assigned to a stdio stream is closed
+ * during thread termination. Ensure that resources are returned to the system.
+ */
+static void test_stdio_file(test_context *ctx)
+{
+ int rv;
+ rtems_resource_snapshot snapshot;
+
+ rtems_resource_snapshot_take(&snapshot);
+
+ rv = IMFS_make_generic_node(
+ &stdio_file_path[0],
+ S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
+ &node_control,
+ NULL
+ );
+ rtems_test_assert(rv == 0);
- rv = unlink(&file_path[0]);
+ create_and_run_worker(ctx, stdio_file_worker);
+
+ rv = unlink(&stdio_file_path[0]);
rtems_test_assert(rv == 0);
rtems_test_assert(rtems_resource_snapshot_check(&snapshot));
}
+/*
+ * Open a global FILE object from a task but do not assign it to a stdio
+ * stream. The FILE is not closed upon thread termination.
+ */
+static void test_non_stdio_file(test_context *ctx)
+{
+ create_and_run_worker(ctx, non_stdio_file_worker);
+}
+
+/*
+ * This exit handler will be called last among the functions registered with
+ * atexit(). Check that stdio file descriptors are closed. The Newlib cleanup
+ * handler has not yet run, so the stdio FILE objects themselves are still
+ * open.
+ */
+static void check_after_libio_exit(void)
+{
+ test_context *ctx = &test_instance;
+ struct stat unused;
+ int rv;
+
+ errno = 0;
+ rv = fstat(0, &unused);
+ rtems_test_assert(rv == -1);
+ rtems_test_assert(errno == EBADF);
+
+ errno = 0;
+ rv = fstat(1, &unused);
+ rtems_test_assert(rv == -1);
+ rtems_test_assert(errno == EBADF);
+
+ errno = 0;
+ rv = fstat(2, &unused);
+ rtems_test_assert(rv == -1);
+ rtems_test_assert(errno == EBADF);
+
+ rtems_test_assert(stdin->_flags != 0);
+ rtems_test_assert(stdout->_flags != 0);
+ rtems_test_assert(stderr->_flags != 0);
+
+ /*
+ * The non-stdio file and its file descriptor should be still open at this
+ * point.
+ */
+ rv = fstat(ctx->non_stdio_fd, &unused);
+ rtems_test_assert(rv == 0);
+ rtems_test_assert(ctx->non_stdio_file->_flags != 0);
+}
+
+static void register_exit_handler_before_libio_exit(void)
+{
+ int rv;
+
+ rv = atexit(check_after_libio_exit);
+ rtems_test_assert(rv == 0);
+}
+
+/*
+ * Register the exit handler before rtems_libio_exit() so that
+ * check_after_libio_exit() is called after rtems_libio_exit(). The exit()
+ * handlers are in a LIFO list.
+ */
+RTEMS_SYSINIT_ITEM(register_exit_handler_before_libio_exit,
+ RTEMS_SYSINIT_STD_FILE_DESCRIPTORS, RTEMS_SYSINIT_ORDER_FIRST);
+
+/*
+ * At this point, neither the functions registered with atexit() nor the Newlib
+ * cleanup procedures have been called. Therefore, stdio file descriptors
+ * should be open and stdio FILE object flags should be non-zero.
+ */
+static void test_exit_handling(test_context *ctx)
+{
+ struct stat unused;
+ int rv;
+
+ rv = fstat(0, &unused);
+ rtems_test_assert(rv == 0);
+
+ rv = fstat(1, &unused);
+ rtems_test_assert(rv == 0);
+
+ rv = fstat(2, &unused);
+ rtems_test_assert(rv == 0);
+
+ rtems_test_assert(stdin->_flags != 0);
+ rtems_test_assert(stdout->_flags != 0);
+ rtems_test_assert(stderr->_flags != 0);
+
+ /*
+ * The file descriptor of the non-stdio file should still be open; the FILE
+ * object flags should still be non-zero.
+ */
+ rv = fstat(ctx->non_stdio_fd, &unused);
+ rtems_test_assert(rv == 0);
+ rtems_test_assert(ctx->non_stdio_file->_flags != 0);
+
+ /* Run exit handlers and Newlib cleanup procedures */
+ exit(0);
+}
+
static void Init(rtems_task_argument arg)
{
+ test_context *ctx = &test_instance;
+ FILE *file;
+ int rv;
+
TEST_BEGIN();
+ test_rand();
+ test_lrand48();
+
+ ctx->main_task_id = rtems_task_self();
- test();
+ /* Fill dynamic file pool in Newlib */
+ file = fopen(CONSOLE_DEVICE_NAME, "r+");
+ rtems_test_assert(file != NULL);
+ rv = fclose(file);
+ rtems_test_assert(rv == 0);
- TEST_END();
- rtems_test_exit(0);
+ test_stdio_file(ctx);
+ test_non_stdio_file(ctx);
+ test_exit_handling(ctx);
+}
+
+static void fatal_extension(
+ rtems_fatal_source source,
+ bool always_set_to_false,
+ rtems_fatal_code error
+)
+{
+ if (
+ source == RTEMS_FATAL_SOURCE_EXIT
+ && !always_set_to_false
+ && error == 0
+ ) {
+ /*
+ * Final conditions check after exit handlers and Newlib cleanup procedures
+ * have run. File descriptors and FILE objects themselves are closed.
+ */
+ struct stat unused;
+ int rv;
+ test_context *ctx = &test_instance;
+
+ errno = 0;
+ rv = fstat(0, &unused);
+ rtems_test_assert(rv == -1);
+ rtems_test_assert(errno == EBADF);
+
+ errno = 0;
+ rv = fstat(1, &unused);
+ rtems_test_assert(rv == -1);
+ rtems_test_assert(errno == EBADF);
+
+ errno = 0;
+ rv = fstat(2, &unused);
+ rtems_test_assert(rv == -1);
+ rtems_test_assert(errno == EBADF);
+
+ rtems_test_assert(stdin->_flags == 0);
+ rtems_test_assert(stdout->_flags == 0);
+ rtems_test_assert(stderr->_flags == 0);
+
+ /* The non-stdio file and its file descriptor should be closed */
+ errno = 0;
+ rv = fstat(ctx->non_stdio_fd, &unused);
+ rtems_test_assert(rv == -1);
+ rtems_test_assert(errno == EBADF);
+ rtems_test_assert(ctx->non_stdio_file->_flags == 0);
+
+ TEST_END();
+ }
}
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
-#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 4
+#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 5
#define CONFIGURE_MAXIMUM_TASKS 2
-#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
+#define CONFIGURE_INITIAL_EXTENSIONS \
+ { .fatal = fatal_extension }, \
+ RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE