summaryrefslogtreecommitdiff
path: root/tests/threadkey.c
blob: 3a318d87e1b49ad013b8be152a9d13a6eca42d43 (plain)
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

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#ifndef GC_THREADS
# define GC_THREADS
#endif

#define GC_NO_THREAD_REDIRECTS 1

#include "gc.h"

#include <stdio.h>
#include <stdlib.h>

#if (!defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS) \
     || defined(__native_client__)) && !defined(SKIP_THREADKEY_TEST)
  /* FIXME: Skip this test on Solaris for now.  The test may fail on    */
  /* other targets as well.  Currently, tested only on Linux, Cygwin    */
  /* and Darwin.                                                        */
# define SKIP_THREADKEY_TEST
#endif

#ifdef SKIP_THREADKEY_TEST

int main(void)
{
  printf("test skipped\n");
  return 0;
}

#else

#include <errno.h> /* for EAGAIN */
#include <pthread.h>
#include <string.h>

pthread_key_t key;

#ifdef GC_SOLARIS_THREADS
  /* pthread_once_t key_once = { PTHREAD_ONCE_INIT }; */
#else
  pthread_once_t key_once = PTHREAD_ONCE_INIT;
#endif

void * entry (void *arg)
{
  pthread_setspecific(key,
                      (void *)GC_HIDE_POINTER(GC_STRDUP("hello, world")));
  return arg;
}

void * GC_CALLBACK on_thread_exit_inner (struct GC_stack_base * sb, void * arg)
{
  int res = GC_register_my_thread (sb);
  pthread_t t;
  int creation_res;     /* Used to suppress a warning about     */
                        /* unchecked pthread_create() result.   */
  pthread_attr_t attr;

  if (pthread_attr_init(&attr) != 0
      || pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) {
    fprintf(stderr, "Thread attribute init or setdetachstate failed\n");
    exit(2);
  }
  creation_res = GC_pthread_create(&t, &attr, entry, NULL);
  (void)pthread_attr_destroy(&attr);
  if (res == GC_SUCCESS)
    GC_unregister_my_thread ();

  return arg ? (void*)(GC_word)creation_res : 0;
}

void on_thread_exit (void *v)
{
  GC_call_with_stack_base (on_thread_exit_inner, v);
}

void make_key (void)
{
  pthread_key_create (&key, on_thread_exit);
}

#ifndef NTHREADS
# define NTHREADS 5
#endif

#define NTHREADS_INNER (NTHREADS * 6) /* number of threads to create */

int main(void)
{
  int i;

  GC_INIT();
  if (GC_get_find_leak())
    printf("This test program is not designed for leak detection mode\n");
# ifdef GC_SOLARIS_THREADS
    pthread_key_create (&key, on_thread_exit);
# else
    pthread_once (&key_once, make_key);
# endif
  for (i = 0; i < NTHREADS_INNER; i++) {
    pthread_t t;
    void *res;
    int code = GC_pthread_create(&t, NULL, entry, NULL);

    if (code != 0) {
      fprintf(stderr, "Thread #%d creation failed: %s\n", i, strerror(code));
      if (i > 0 && EAGAIN == code) break;
      exit(2);
    }

    if ((i & 1) != 0) {
      code = GC_pthread_join(t, &res);
      if (code != 0) {
        fprintf(stderr, "Thread #%d join failed: %s\n", i, strerror(code));
        exit(2);
      }
    } else {
      code = GC_pthread_detach(t);
      if (code != 0) {
        fprintf(stderr, "Thread #%d detach failed: %s\n", i, strerror(code));
        exit(2);
      }
    }
  }
  printf("SUCCEEDED\n");
  return 0;
}

#endif /* !SKIP_THREADKEY_TEST */