mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-01 20:29:00 +00:00
Merge 7aac6c5cb8 into 37a1f0e068
This commit is contained in:
commit
5984c49703
16 changed files with 589 additions and 33 deletions
|
|
@ -114,6 +114,11 @@ ifeq ($(CONFIG_ARCH_HAVE_FORK),y)
|
|||
else
|
||||
@echo "export ac_cv_func_fork=\"no\"" >> $@
|
||||
endif
|
||||
ifneq ($(CONFIG_ARCH_HAVE_VFORK)$(CONFIG_ARCH_HAVE_FORK),)
|
||||
@echo "export ac_cv_func_vfork=\"yes\"" >> $@
|
||||
else
|
||||
@echo "export ac_cv_func_vfork=\"no\"" >> $@
|
||||
endif
|
||||
ifeq ($(CONFIG_SYSTEM_SYSTEM),y)
|
||||
@echo "export ac_cv_func_system=\"yes\"" >> $@
|
||||
else
|
||||
|
|
@ -135,7 +140,10 @@ endif
|
|||
|
||||
$(SETUP_LOCAL):
|
||||
$(Q) ( cp $(SETUP_LOCAL).in $(SETUP_LOCAL))
|
||||
ifneq ($(CONFIG_ARCH_HAVE_FORK),y)
|
||||
# _posixsubprocess is the fork-then-exec path, so vfork() is enough for it;
|
||||
# os.fork() itself needs a real fork() and is governed by ac_cv_func_fork
|
||||
# above.
|
||||
ifeq ($(CONFIG_ARCH_HAVE_FORK)$(CONFIG_ARCH_HAVE_VFORK),)
|
||||
@echo "_posixsubprocess" >> $@
|
||||
endif
|
||||
ifneq ($(CONFIG_LIBC_DLFCN),y)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,10 @@
|
|||
/* #undef USE_CYASSL */
|
||||
|
||||
/* Define to 1 if you have the `fork' function. */
|
||||
|
||||
#ifdef CONFIG_ARCH_HAVE_FORK
|
||||
#define LWS_HAVE_FORK
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DISABLE_ENVIRON
|
||||
/* Define to 1 if you have the `getenv' function. */
|
||||
|
|
@ -111,10 +114,14 @@
|
|||
/* #undef LWS_HAVE_VFORK_H */
|
||||
|
||||
/* Define to 1 if `fork' works. */
|
||||
#ifdef CONFIG_ARCH_HAVE_FORK
|
||||
#define LWS_HAVE_WORKING_FORK
|
||||
#endif
|
||||
|
||||
/* Define to 1 if `vfork' works. */
|
||||
#if defined(CONFIG_ARCH_HAVE_VFORK) || defined(CONFIG_ARCH_HAVE_FORK)
|
||||
#define LWS_HAVE_WORKING_VFORK
|
||||
#endif
|
||||
|
||||
/* Define to 1 if execvpe() exists */
|
||||
#define LWS_HAVE_EXECVPE
|
||||
|
|
|
|||
|
|
@ -171,6 +171,11 @@ if(CONFIG_LIBUV)
|
|||
${LIBUV_TEST_DIR}/run-tests.c ${LIBUV_TEST_DIR}/runner.c
|
||||
${LIBUV_TEST_DIR}/runner-unix.c ${LIBUV_TEST_DIR}/echo-server.c)
|
||||
file(GLOB TEST_CSRCS ${LIBUV_TEST_DIR}/test-*.c)
|
||||
|
||||
# See system/libuv/Makefile.
|
||||
|
||||
list(REMOVE_ITEM TEST_CSRCS ${LIBUV_TEST_DIR}/test-fork.c
|
||||
${LIBUV_TEST_DIR}/test-pipe-close-stdout-read-stdin.c)
|
||||
list(APPEND LIBUV_UTILS_TEST_SRCS ${TEST_CSRCS})
|
||||
nuttx_add_application(
|
||||
NAME
|
||||
|
|
|
|||
|
|
@ -144,7 +144,15 @@ CSRCS += runner.c
|
|||
CSRCS += runner-unix.c
|
||||
CSRCS += echo-server.c
|
||||
|
||||
CSRCS += $(wildcard libuv/test/test-*.c)
|
||||
# test-fork.c and test-pipe-close-stdout-read-stdin.c call fork(). Every
|
||||
# test they define is already excluded from the task list on NuttX by
|
||||
# 0001-libuv-port-for-nuttx.patch, so they are dead code here.
|
||||
|
||||
LIBUV_TEST_CSRCS = $(wildcard libuv/test/test-*.c)
|
||||
LIBUV_TEST_CSRCS := $(filter-out libuv/test/test-fork.c,$(LIBUV_TEST_CSRCS))
|
||||
LIBUV_TEST_CSRCS := $(filter-out libuv/test/test-pipe-close-stdout-read-stdin.c,$(LIBUV_TEST_CSRCS))
|
||||
|
||||
CSRCS += $(LIBUV_TEST_CSRCS)
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_LIBUV_UTILS_BENCHMARK),)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
config TESTING_NAND_SIM
|
||||
boolean "NAND Flash Simulator"
|
||||
depends on MTD_NAND_RAM && ENABLE_ALL_SIGNALS
|
||||
depends on TASK_FORK || (ARCH_HAVE_FORK && !ARCH_HAVE_TASK_FORK)
|
||||
default n
|
||||
---help---
|
||||
Enable the NAND Flash Simulator device.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@
|
|||
****************************************************************************/
|
||||
|
||||
#include <nuttx/debug.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <nuttx/drivers/drivers.h>
|
||||
#include <nuttx/mtd/nand.h>
|
||||
|
|
@ -140,9 +142,16 @@ int main(int argc, FAR char *argv[])
|
|||
int ret;
|
||||
pid_t pid;
|
||||
|
||||
/* Daemon */
|
||||
/* task_fork() rather than fork(): this wants a clone that outlives the
|
||||
* caller and shares its memory. The fallback below covers a nuttx that
|
||||
* does not have task_fork() yet.
|
||||
*/
|
||||
|
||||
pid = fork();
|
||||
#ifndef CONFIG_TASK_FORK
|
||||
# define task_fork() fork()
|
||||
#endif
|
||||
|
||||
pid = task_fork();
|
||||
|
||||
if (pid > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ static void test_case_overflow(void **state)
|
|||
assert_int_equal(open_count, close_count);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ARCH_HAVE_VFORK) || defined(CONFIG_ARCH_HAVE_FORK)
|
||||
static void test_case_vfork(void **state)
|
||||
{
|
||||
int fd = open("/dev/null", O_RDONLY);
|
||||
|
|
@ -112,6 +113,7 @@ static void test_case_vfork(void **state)
|
|||
|
||||
android_fdsan_close_with_tag(fd, 0xbadc0de);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
|
|
@ -129,7 +131,9 @@ int main(int argc, FAR char *argv[])
|
|||
cmocka_unit_test(test_case_unowned_tagged_close),
|
||||
cmocka_unit_test(test_case_owned_tagged_close),
|
||||
cmocka_unit_test(test_case_overflow),
|
||||
#if defined(CONFIG_ARCH_HAVE_VFORK) || defined(CONFIG_ARCH_HAVE_FORK)
|
||||
cmocka_unit_test(test_case_vfork),
|
||||
#endif
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
|
|
|
|||
|
|
@ -86,6 +86,12 @@ if(CONFIG_TESTING_LTP)
|
|||
list(APPEND BLACKWORDS "pthread_spin_init" "pthread_spin_destroy"
|
||||
"pthread_spin_trylock")
|
||||
endif()
|
||||
|
||||
# See testing/ltp/Makefile.
|
||||
|
||||
if(NOT CONFIG_ARCH_HAVE_FORK AND NOT CONFIG_FORK_IS_TASK_FORK)
|
||||
list(APPEND BLACKWORDS "[^v_]fork(")
|
||||
endif()
|
||||
list(
|
||||
APPEND
|
||||
BLACKWORDS
|
||||
|
|
|
|||
|
|
@ -44,6 +44,13 @@ BLACKWORDS += "pthread_spin_destroy"
|
|||
BLACKWORDS += "pthread_spin_trylock"
|
||||
endif
|
||||
|
||||
# Where NuttX does not declare fork(), a test that calls it cannot be built.
|
||||
# The pattern spares vfork() and task_fork(), which remain available.
|
||||
|
||||
ifeq ($(CONFIG_ARCH_HAVE_FORK)$(CONFIG_FORK_IS_TASK_FORK),)
|
||||
BLACKWORDS += "[^v_]fork("
|
||||
endif
|
||||
|
||||
BLACKWORDS += "CHILD_MAX"
|
||||
BLACKWORDS += "setpgid("
|
||||
BLACKWORDS += "PTHREAD_SCOPE_PROCESS"
|
||||
|
|
|
|||
|
|
@ -143,10 +143,20 @@ if(CONFIG_TESTING_OSTEST)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_ARCH_HAVE_FORK)
|
||||
if(CONFIG_SCHED_WAITPID)
|
||||
list(APPEND SRCS vfork.c)
|
||||
endif()
|
||||
# See testing/ostest/ostest.h for the one transitional exception.
|
||||
|
||||
if(CONFIG_TASK_FORK)
|
||||
list(APPEND SRCS task_fork.c)
|
||||
elseif(CONFIG_ARCH_HAVE_FORK AND NOT CONFIG_ARCH_HAVE_TASK_FORK)
|
||||
list(APPEND SRCS task_fork.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ARCH_HAVE_VFORK)
|
||||
list(APPEND SRCS vfork.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ARCH_HAVE_FORK AND CONFIG_ARCH_HAVE_VFORK)
|
||||
list(APPEND SRCS fork.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ARCH_SETJMP_H)
|
||||
|
|
|
|||
|
|
@ -144,10 +144,27 @@ CSRCS += sigev_thread.c
|
|||
endif
|
||||
endif
|
||||
|
||||
# Each test is built where the primitive it tests exists. See ostest.h for the
|
||||
# one transitional exception.
|
||||
|
||||
ifeq ($(CONFIG_TASK_FORK),y)
|
||||
CSRCS += task_fork.c
|
||||
else ifeq ($(CONFIG_ARCH_HAVE_TASK_FORK),)
|
||||
# A nuttx with no ARCH_HAVE_TASK_FORK at all is the pre-split one, where
|
||||
# fork() is task_fork(). This branch comes out with the split.
|
||||
ifeq ($(CONFIG_ARCH_HAVE_FORK),y)
|
||||
ifeq ($(CONFIG_SCHED_WAITPID),y)
|
||||
CSRCS += task_fork.c
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_ARCH_HAVE_VFORK),y)
|
||||
CSRCS += vfork.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_ARCH_HAVE_FORK),y)
|
||||
ifeq ($(CONFIG_ARCH_HAVE_VFORK),y)
|
||||
CSRCS += fork.c
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_ARCH_SETJMP_H),y)
|
||||
|
|
|
|||
276
testing/ostest/fork.c
Normal file
276
testing/ostest/fork.c
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
/****************************************************************************
|
||||
* apps/testing/ostest/fork.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ostest.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define FORK_HEAPSIZE 256
|
||||
#define FORK_PARENTMARK 0x5a
|
||||
#define FORK_CHILDMARK 0xa5
|
||||
|
||||
/* Distinct values written through a pointer to a stack local, to check that
|
||||
* the child's stack is at the same virtual address as the parent's.
|
||||
*/
|
||||
|
||||
#define FORK_STACKPARENT 0x1234
|
||||
#define FORK_STACKCHILD 0x5678
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
/* .data and .bss, which a fork() child must get its own copy of */
|
||||
|
||||
static volatile int g_forkdata = 1;
|
||||
static volatile int g_forkbss;
|
||||
static FAR unsigned char *g_forkheap;
|
||||
|
||||
/* Pointer to a local in fork_test()'s frame, taken before fork(). In .data
|
||||
* rather than on the stack: the compiler could rematerialise a local
|
||||
* pointer from the current stack pointer, which would test nothing.
|
||||
*/
|
||||
|
||||
static FAR volatile int *g_forkstackptr;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: fork_child
|
||||
*
|
||||
* Description:
|
||||
* Everything here is forbidden to a vfork() child and permitted to a
|
||||
* fork() child. Returns the child's exit status.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int fork_child(void)
|
||||
{
|
||||
FAR char *scratch;
|
||||
|
||||
/* The parent must see none of this. */
|
||||
|
||||
g_forkdata = 2;
|
||||
g_forkbss = 2;
|
||||
memset(g_forkheap, FORK_CHILDMARK, FORK_HEAPSIZE);
|
||||
|
||||
/* A vfork() child may not call these; a fork() child may. */
|
||||
|
||||
scratch = malloc(64);
|
||||
if (scratch == NULL)
|
||||
{
|
||||
printf("fork_test: ERROR Child could not malloc()\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
strlcpy(scratch, "child", 64);
|
||||
printf("fork_test: Child running independently (%s)\n", scratch);
|
||||
free(scratch);
|
||||
|
||||
/* Give the parent time to make its own writes, so that if the two shared
|
||||
* memory we would see the parent's values below rather than our own.
|
||||
*/
|
||||
|
||||
usleep(200 * 1000);
|
||||
|
||||
if (g_forkdata != 2 || g_forkbss != 2)
|
||||
{
|
||||
printf("fork_test: ERROR Child saw the parent's writes: "
|
||||
"data=%d bss=%d\n", g_forkdata, g_forkbss);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (g_forkheap[0] != FORK_CHILDMARK ||
|
||||
g_forkheap[FORK_HEAPSIZE - 1] != FORK_CHILDMARK)
|
||||
{
|
||||
printf("fork_test: ERROR Child saw the parent's heap writes\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: fork_test
|
||||
*
|
||||
* Description:
|
||||
* Verify the defining property of POSIX fork(): the child gets its own
|
||||
* copy of the parent's memory, in both directions. That is the exact
|
||||
* opposite of what task_fork_test() checks. Also verifies that none of
|
||||
* vfork()'s restrictions apply.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int fork_test(void)
|
||||
{
|
||||
pid_t pid;
|
||||
int status = 0;
|
||||
int ret = 0;
|
||||
|
||||
/* volatile so it is really in memory and the compare is not folded. */
|
||||
|
||||
volatile int stackvar = FORK_STACKPARENT;
|
||||
|
||||
printf("fork_test: Started\n");
|
||||
|
||||
/* Publish its address before forking. See g_forkstackptr. */
|
||||
|
||||
g_forkstackptr = &stackvar;
|
||||
|
||||
g_forkdata = 1;
|
||||
g_forkbss = 1;
|
||||
|
||||
g_forkheap = malloc(FORK_HEAPSIZE);
|
||||
if (g_forkheap == NULL)
|
||||
{
|
||||
printf("fork_test: ERROR Failed to allocate the heap probe\n");
|
||||
ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(g_forkheap, FORK_PARENTMARK, FORK_HEAPSIZE);
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0)
|
||||
{
|
||||
/* The child's stack is at the parent's virtual addresses, so this
|
||||
* pointer names the child's own live local. A relocated stack would
|
||||
* write into the copy of the parent's instead.
|
||||
*/
|
||||
|
||||
*g_forkstackptr = FORK_STACKCHILD;
|
||||
if (stackvar != FORK_STACKCHILD)
|
||||
{
|
||||
printf("fork_test: ERROR Child stack was relocated: wrote %d "
|
||||
"through %p, local at %p reads %d\n",
|
||||
FORK_STACKCHILD, g_forkstackptr, &stackvar, stackvar);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
/* Returns from fork_child() and from this branch -- both illegal
|
||||
* for a vfork() child.
|
||||
*/
|
||||
|
||||
_exit(fork_child());
|
||||
}
|
||||
else if (pid < 0)
|
||||
{
|
||||
printf("fork_test: ERROR fork() failed: %d\n", errno);
|
||||
free(g_forkheap);
|
||||
ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Parent runs concurrently: write now, check isolation afterwards. */
|
||||
|
||||
g_forkdata = 3;
|
||||
g_forkbss = 3;
|
||||
memset(g_forkheap, FORK_PARENTMARK, FORK_HEAPSIZE);
|
||||
|
||||
#ifdef CONFIG_SCHED_WAITPID
|
||||
/* Wait for the child to be done before comparing memory. waitpid() blocks
|
||||
* on a child that is still alive whether or not its exit status will be
|
||||
* retained, so this synchronises either way; ECHILD simply means the child
|
||||
* had already finished, which is just as good. It is not a failure:
|
||||
* ostest_main() sets SA_NOCLDWAIT on SIGCHLD for the whole run, so an
|
||||
* exited child's status is not kept even where CONFIG_SCHED_CHILD_STATUS
|
||||
* is enabled.
|
||||
*/
|
||||
|
||||
if (waitpid(pid, &status, 0) != pid && errno != ECHILD)
|
||||
{
|
||||
printf("fork_test: ERROR waitpid() failed: %d\n", errno);
|
||||
free(g_forkheap);
|
||||
ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
sleep(1);
|
||||
#endif
|
||||
|
||||
if (g_forkdata != 3 || g_forkbss != 3)
|
||||
{
|
||||
printf("fork_test: ERROR Parent saw the child's writes: "
|
||||
"data=%d bss=%d (expected 3, 3)\n", g_forkdata, g_forkbss);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (g_forkheap[0] != FORK_PARENTMARK ||
|
||||
g_forkheap[FORK_HEAPSIZE - 1] != FORK_PARENTMARK)
|
||||
{
|
||||
printf("fork_test: ERROR Parent saw the child's heap writes\n");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
/* The child wrote to the same stack address; the parent must not see it */
|
||||
|
||||
if (stackvar != FORK_STACKPARENT)
|
||||
{
|
||||
printf("fork_test: ERROR Parent saw the child's stack write: "
|
||||
"%d (expected %d)\n", stackvar, FORK_STACKPARENT);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCHED_WAITPID
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
{
|
||||
printf("fork_test: ERROR Child reported failure, status 0x%04x\n",
|
||||
status);
|
||||
ret = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
free(g_forkheap);
|
||||
g_forkheap = NULL;
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
ASSERT(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
printf("fork_test: Parent and child had independent memory\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -282,12 +282,58 @@ void priority_inheritance(void);
|
|||
|
||||
void sched_lock_test(void);
|
||||
|
||||
/* The fork family **********************************************************/
|
||||
|
||||
/* Until nuttx has the three separate primitives, ARCH_HAVE_FORK stands in
|
||||
* for task_fork(): today's fork() *is* task_fork(), so the test that has
|
||||
* always covered that primitive keeps running under its own name. A nuttx
|
||||
* without ARCH_HAVE_TASK_FORK is the pre-split one, and only there does the
|
||||
* substitution apply -- once the split has landed, TASK_FORK says whether
|
||||
* task_fork() was built, and nothing stands in for it.
|
||||
*
|
||||
* vfork_test() and fork_test() deliberately have no such mapping. They
|
||||
* check semantics a pre-split nuttx does not describe -- the parent
|
||||
* suspension and the private copy -- and ARCH_HAVE_VFORK is the evidence
|
||||
* that the split has landed.
|
||||
*
|
||||
* This block comes out with the split.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_TASK_FORK) || \
|
||||
(defined(CONFIG_ARCH_HAVE_FORK) && !defined(CONFIG_ARCH_HAVE_TASK_FORK))
|
||||
# define OSTEST_HAVE_TASK_FORK 1
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_HAVE_VFORK
|
||||
# define OSTEST_HAVE_VFORK 1
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_HAVE_FORK) && defined(CONFIG_ARCH_HAVE_VFORK)
|
||||
# define OSTEST_HAVE_FORK 1
|
||||
#endif
|
||||
|
||||
#if defined(OSTEST_HAVE_TASK_FORK) && !defined(CONFIG_TASK_FORK)
|
||||
# define task_fork() fork()
|
||||
#endif
|
||||
|
||||
/* task_fork.c **************************************************************/
|
||||
|
||||
#ifdef OSTEST_HAVE_TASK_FORK
|
||||
int task_fork_test(void);
|
||||
#endif
|
||||
|
||||
/* vfork.c ******************************************************************/
|
||||
|
||||
#if defined(CONFIG_ARCH_HAVE_FORK) && defined(CONFIG_SCHED_WAITPID)
|
||||
#ifdef OSTEST_HAVE_VFORK
|
||||
int vfork_test(void);
|
||||
#endif
|
||||
|
||||
/* fork.c *******************************************************************/
|
||||
|
||||
#ifdef OSTEST_HAVE_FORK
|
||||
int fork_test(void);
|
||||
#endif
|
||||
|
||||
/* setjmp.c *****************************************************************/
|
||||
|
||||
void setjmp_test(void);
|
||||
|
|
|
|||
|
|
@ -224,6 +224,29 @@ static int user_main(int argc, char *argv[])
|
|||
g_mmbefore = mallinfo();
|
||||
g_mmprevious = g_mmbefore;
|
||||
|
||||
/* Run the three fork primitives first. They exercise the lowest-level
|
||||
* machinery in the suite, so a fault takes the process down rather than
|
||||
* reporting a failure -- better to learn that in seconds.
|
||||
*/
|
||||
|
||||
#ifdef OSTEST_HAVE_TASK_FORK
|
||||
printf("\nuser_main: task_fork() test\n");
|
||||
task_fork_test();
|
||||
check_test_memory_usage();
|
||||
#endif
|
||||
|
||||
#ifdef OSTEST_HAVE_VFORK
|
||||
printf("\nuser_main: vfork() test\n");
|
||||
vfork_test();
|
||||
check_test_memory_usage();
|
||||
#endif
|
||||
|
||||
#ifdef OSTEST_HAVE_FORK
|
||||
printf("\nuser_main: fork() test\n");
|
||||
fork_test();
|
||||
check_test_memory_usage();
|
||||
#endif
|
||||
|
||||
printf("\nuser_main: Begin argument test\n");
|
||||
printf("user_main: Started with argc=%d\n", argc);
|
||||
|
||||
|
|
@ -637,12 +660,6 @@ static int user_main(int argc, char *argv[])
|
|||
check_test_memory_usage();
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_HAVE_FORK) && defined(CONFIG_SCHED_WAITPID) && \
|
||||
!defined(CONFIG_ARCH_SIM)
|
||||
printf("\nuser_main: vfork() test\n");
|
||||
vfork_test();
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SMP) && defined(CONFIG_BUILD_FLAT)
|
||||
printf("\nuser_main: smp call test\n");
|
||||
smp_call_test();
|
||||
|
|
|
|||
98
testing/ostest/task_fork.c
Normal file
98
testing/ostest/task_fork.c
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/****************************************************************************
|
||||
* apps/testing/ostest/task_fork.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ostest.h"
|
||||
|
||||
#ifdef OSTEST_HAVE_TASK_FORK
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static volatile bool g_taskforkchild;
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: task_fork_test
|
||||
*
|
||||
* Description:
|
||||
* Verify the defining property of task_fork(): the child shares the
|
||||
* parent's memory, so the parent observes the child's write. This is the
|
||||
* test that used to be called the "vfork" test. A real fork() child's
|
||||
* write would be invisible here; see fork_test().
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int task_fork_test(void)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
printf("task_fork_test: Started\n");
|
||||
|
||||
g_taskforkchild = false;
|
||||
pid = task_fork();
|
||||
if (pid == 0)
|
||||
{
|
||||
/* Shared memory, so the parent sees this. Runs concurrently. */
|
||||
|
||||
g_taskforkchild = true;
|
||||
exit(0);
|
||||
}
|
||||
else if (pid < 0)
|
||||
{
|
||||
printf("task_fork_test: ERROR task_fork() failed: %d\n", errno);
|
||||
ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
|
||||
if (!g_taskforkchild)
|
||||
{
|
||||
printf("task_fork_test: ERROR Child %d did not run, or its write to "
|
||||
"shared memory was not visible\n", pid);
|
||||
ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("task_fork_test: Child %d ran successfully\n", pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* OSTEST_HAVE_TASK_FORK */
|
||||
|
|
@ -31,36 +31,49 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ostest.h"
|
||||
|
||||
#if defined(CONFIG_ARCH_HAVE_FORK) && defined(CONFIG_SCHED_WAITPID)
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static volatile bool g_vforkchild;
|
||||
/* Set and cleared by the parent only: a vfork() child may not write it. */
|
||||
|
||||
static volatile bool g_vforkrunning;
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: vfork_test
|
||||
*
|
||||
* Description:
|
||||
* Verify the defining property of vfork(): the parent is suspended until
|
||||
* the child _exit()s or exec()s. The child does only what POSIX
|
||||
* permits -- _exit(), not exit(), which would flush stdio in the parent's
|
||||
* address space. Since the child may not write memory, the observable is
|
||||
* its exit status: an unsuspended parent would reach waitpid() first.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int vfork_test(void)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
g_vforkchild = false;
|
||||
printf("vfork_test: Started\n");
|
||||
|
||||
g_vforkrunning = true;
|
||||
|
||||
pid = vfork();
|
||||
if (pid == 0)
|
||||
{
|
||||
/* There is not very much that the child is permitted to do. Perhaps
|
||||
* it can just set g_vforkchild.
|
||||
*/
|
||||
/* The only thing a vfork() child may do is leave. */
|
||||
|
||||
g_vforkchild = true;
|
||||
exit(0);
|
||||
_exit(42);
|
||||
}
|
||||
else if (pid < 0)
|
||||
{
|
||||
|
|
@ -68,22 +81,46 @@ int vfork_test(void)
|
|||
ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
|
||||
/* Reached only once the child has exited or exec'ed. */
|
||||
|
||||
g_vforkrunning = false;
|
||||
|
||||
#ifdef CONFIG_SCHED_WAITPID
|
||||
{
|
||||
sleep(1);
|
||||
if (g_vforkchild)
|
||||
int status = 0;
|
||||
pid_t ret;
|
||||
|
||||
ret = waitpid(pid, &status, 0);
|
||||
|
||||
/* Two answers are correct, and which one comes back is a property of
|
||||
* the configuration: a retained status must be exit(42), and ECHILD
|
||||
* is equally good evidence -- it says the child was already gone when
|
||||
* we asked. ostest_main() sets SA_NOCLDWAIT for the whole run, so
|
||||
* testing CONFIG_SCHED_CHILD_STATUS alone is not enough.
|
||||
*/
|
||||
|
||||
if (ret == pid)
|
||||
{
|
||||
printf("vfork_test: Child %d ran successfully\n", pid);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 42)
|
||||
{
|
||||
printf("vfork_test: ERROR Child %d status 0x%04x, expected "
|
||||
"exit(42)\n", pid, status);
|
||||
ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (ret >= 0 || errno != ECHILD)
|
||||
{
|
||||
printf("vfork_test: ERROR Child %d did not run\n", pid);
|
||||
printf("vfork_test: ERROR waitpid() returned %d (%d), expected "
|
||||
"the child's status or ECHILD\n", ret, errno);
|
||||
ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("vfork_test: Child %d ran and exited before the parent resumed\n",
|
||||
pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ARCH_HAVE_FORK && CONFIG_SCHED_WAITPID */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue