/*
TEST_HEADER
id = $Id$
summary = regression test for GitHub issues #9 and #10
language = c
link = testlib.o myfmt.o
parameters =
END_HEADER
*/
#define _POSIX_C_SOURCE 199309L /* for nanosleep */
#include "testlib.h"
#if !defined(MPS_OS_FR) && !defined(MPS_OS_LI)
static void test(void *stack_pointer)
{
/* nothing to do on non-POSIX systems */
}
#else
#include "mpsavm.h"
#include "mpscamc.h"
#include "myfmt.h"
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <unistd.h>
/* On POSIX systems, the MPS suspends threads by sending them a signal
* (see PThreadextSuspend in pthrdext.c). If the signal was delivered
* while the mutator was blocked in a system call like open() or
* read() then the system call could formerly fail with EINTR.
*
* The test creates a thread. The thread registers itself with the
* MPS, posts on a semaphore to synchronize with the main thread, and
* then blocks itself in a read() system call. The main thread then
* calls mps_arena_collect() to cause the MPS to suspend all other
* threads. If the issue is not fixed, then the read() call on the
* thread fails with EINTR. Alternatively, if the issue is fixed, the
* read() call is automatically restarted, the main thread writes to
* the other end of the pipe so that the read() call completes, and we
* tear everything down.
*/
typedef struct test_data_s {
mps_arena_t arena; /* The arena. */
sem_t *sem; /* Semaphore to post once registered. */
int fd; /* File descriptor to read from. */
} test_data_t;
static void *thread_fn(void *arg)
{
test_data_t *data = arg;
mps_thr_t thread;
int e;
char c;
die(mps_thread_reg(&thread, data->arena), "mps_thread_reg");
e = sem_post(data->sem);
asserts(e == 0, "sem_post: %s", strerror(errno));
errno = ETOOMANYREFS;
/* There is a race here: if MPS goes ahead with the collection on
* the main thread and manages to signal this thread before the
* read() system call is entered, then the test condition is not
* exercised (no opportunity for read() to fail with EINTR). This is
* an acceptable race because it doesn't make the test fail, it just
* fails to exercise the test condition.
*
* POSIX doesn't provide us with a portable way to ensure that the
* thread has blocked in the system call before starting the
* collection; on Linux we could poll /proc/TID/stat until the
* thread enters state S (sleeping in an interruptible wait) but
* this seems like a lot of trouble for a nice-to-have feature.
*/
e = read(data->fd, &c, 1);
asserts(e == 1, "read: %d: %s", e, strerror(errno));
/* Check that the MPS's signal handlers did not modify errno. */
asserts(errno == ETOOMANYREFS, "errno=%d", errno);
mps_thread_dereg(thread);
return NULL;
}
static void test(void *stack_pointer)
{
mps_arena_t arena;
mps_fmt_t format;
mps_pool_t pool;
int pipefd[2];
int e;
sem_t sem;
test_data_t data;
pthread_t pthread;
struct timespec timespec = { 0, 1000000 };
die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "mps_arena_create_k");
die(mps_fmt_create_A(&format, arena, &fmtA), "create format");
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format);
die(mps_pool_create_k(&pool, arena, mps_class_amc(), args), "mps_pool_create_k");
} MPS_ARGS_END(args);
/* Create data structures for synchronizing with the thread. */
e = pipe(pipefd);
asserts(e == 0, "pipe: %s", strerror(errno));
e = sem_init(&sem, 0, 0);
asserts(e == 0, "sem_init: %s", strerror(errno));
data.arena = arena;
data.sem = &sem;
data.fd = pipefd[0];
/* Start the thread. */
e = pthread_create(&pthread, NULL, thread_fn, &data);
asserts(e == 0, "pthread_create: %s", strerror(e));
/* Wait for the thread to register itself. */
e = sem_wait(&sem);
asserts(e == 0, "sem_wait: %s", strerror(errno));
/* Delay to give the thread more time to block in read(). */
e = nanosleep(×pec, NULL);
asserts(e == 0, "nanosleep: %s", strerror(errno));
/* Run a collection: this causes the MPS to suspend the thread via
* ArenaCollect, ArenaStartCollect, TraceStartCollectAll,
* TraceCondemnEnd, ShieldHold, shieldSuspend, ThreadRingSuspend, and
* PThreadextSuspend. */
mps_arena_collect(arena);
/* Write to the pipe so that the thread can complete its read. */
e = write(pipefd[1], "x", 1);
asserts(e == 1, "write: %s", strerror(errno));
/* Wait for the thread to complete. */
e = pthread_join(pthread, NULL);
asserts(e == 0, "pthread_join: %s", strerror(e));
/* Tear down MPS data structures. */
mps_arena_park(arena);
mps_pool_destroy(pool);
mps_fmt_destroy(format);
mps_arena_destroy(arena);
}
#endif
int main(void)
{
run_test(test);
pass();
return 0;
}