File: 236.c

package info (click to toggle)
storm-lang 0.7.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 52,028 kB
  • sloc: ansic: 261,471; cpp: 140,432; sh: 14,891; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (158 lines) | stat: -rw-r--r-- 4,878 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
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(&timespec, 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;
}