File: test_pthread_sbrk.cpp

package info (click to toggle)
emscripten 3.1.6~dfsg-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 114,112 kB
  • sloc: ansic: 583,052; cpp: 391,943; javascript: 79,361; python: 54,180; sh: 49,997; pascal: 4,658; makefile: 3,426; asm: 2,191; lisp: 1,869; ruby: 488; cs: 142
file content (143 lines) | stat: -rw-r--r-- 4,818 bytes parent folder | download
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
// Copyright 2016 The Emscripten Authors.  All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License.  Both these licenses can be
// found in the LICENSE file.

#include <pthread.h>
#include <emscripten.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>

#define NUM_THREADS 8
#define NUM_ALLOCATIONS 10240
#if ABORTING_MALLOC
#define ALLOCATION_SIZE 1280 // Malloc aborts, so allocate a bit less of memory so all fits
#else
#define ALLOCATION_SIZE 2560 // Malloc doesn't abort, allocate a bit more memory to test graceful allocation failures
#endif

#define RESULT_OK 0
#define RESULT_EXPECTED_FAILS 1
#define RESULT_BAD_FAIL 2

// Use barriers to make each thread synchronize their execution points, to maximize the possibility of seeing race conditions
// if those might occur.
static pthread_barrier_t barrierWaitToAlloc;
static pthread_barrier_t barrierWaitToVerify;
static pthread_barrier_t barrierWaitToFree;

// Use a mutex for logging.
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

static void *thread_start(void *arg)
{
#if DEBUG
  pthread_mutex_lock( &mutex );
  printf("thread started, will try %d allocations of size %d\n", NUM_ALLOCATIONS, ALLOCATION_SIZE);
  pthread_mutex_unlock( &mutex );
#endif

  int id = (int)(intptr_t)(arg)+1;
  int return_code = RESULT_OK;

  uint8_t *allocated_buffers[NUM_ALLOCATIONS] = {};

  int some_allocations_failed = 0;
  size_t allocated = 0;

  pthread_barrier_wait(&barrierWaitToAlloc); // Halt until all threads reach here, then proceed synchronously.
  for(int i = 0; i < NUM_ALLOCATIONS; ++i)
  {
    allocated_buffers[i] = (uint8_t*)malloc(ALLOCATION_SIZE);
    if (allocated_buffers[i]) {
      memset(allocated_buffers[i], id, ALLOCATION_SIZE);
      allocated += ALLOCATION_SIZE;
    } else
      some_allocations_failed = 1;
  }
#if DEBUG
  pthread_mutex_lock( &mutex );
  printf("total allocations: %u (%d of size %d tried), some failed? %d\n", allocated, NUM_ALLOCATIONS, ALLOCATION_SIZE, some_allocations_failed);
  pthread_mutex_unlock( &mutex );
#endif
  pthread_barrier_wait(&barrierWaitToVerify); // Halt until all threads reach here, then proceed synchronously.
  int reported_once = 0;
  for(int i = 0; i < NUM_ALLOCATIONS; ++i)
  {
    if (!allocated_buffers[i]) continue;
    for(int j = 0; j < ALLOCATION_SIZE; ++j)
      if (allocated_buffers[i][j] != id)
      {
        ++return_code; // Failed! (but run to completion so that the barriers will all properly proceed without hanging)
        if (!reported_once) {
          EM_ASM(console.error('Memory corrupted! mem[i]: ' + $0 + ' != ' + $1 + ', i: ' + $2 + ', j: ' + $3), allocated_buffers[i][j], id, i, j);
          reported_once = 1; // Avoid print flood that makes debugging hard.
        }
      }
  }

  pthread_barrier_wait(&barrierWaitToFree); // Halt until all threads reach here, then proceed synchronously.
  for(int i = 0; i < NUM_ALLOCATIONS; ++i)
    free(allocated_buffers[i]);

#if ABORTING_MALLOC
  if (some_allocations_failed)
    return_code = RESULT_BAD_FAIL; // We expect allocations not to fail (if they did, shouldn't reach here, but we should have aborted)
#else
  if (some_allocations_failed)
    return_code = RESULT_EXPECTED_FAILS; // We expect to be allocating so much memory that some of the allocations fail.
  // Otherwise, the fails might happen in another thread, that's cool.
#endif
#if DEBUG
  pthread_mutex_lock( &mutex );
  printf("the pthread return code: %d\n", return_code);
  pthread_mutex_unlock( &mutex );
#endif
  pthread_exit((void*)return_code);
}

int main()
{
  printf("starting test, aborting? %d\n", ABORTING_MALLOC);

  int ret = pthread_barrier_init(&barrierWaitToAlloc, NULL, NUM_THREADS);
  assert(ret == 0);
  ret = pthread_barrier_init(&barrierWaitToVerify, NULL, NUM_THREADS);
  assert(ret == 0);
  ret = pthread_barrier_init(&barrierWaitToFree, NULL, NUM_THREADS);
  assert(ret == 0);

  pthread_t thr[8/*NUM_THREADS*/];
  for(int i = 0; i < NUM_THREADS; ++i)
  {
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setstacksize(&attr, NUM_ALLOCATIONS*80);
    ret = pthread_create(&thr[i], &attr, thread_start, (void*)(i));
    assert(ret == 0);
  }

  int seen_expected_fails = 0;

  for(int i = 0; i < NUM_THREADS; ++i) {
    int res = 0;
    ret = pthread_join(thr[i], (void**)&res);
    assert(ret == 0);
    assert(res != RESULT_BAD_FAIL);
    if (res == RESULT_EXPECTED_FAILS) {
      seen_expected_fails = 1;
    }
    if (res) printf("Thread %d failed with return code %d.\n", i, res);
  }
#if !ABORTING_MALLOC
  if (!seen_expected_fails) {
    printf("Expected to see fails, but saw none :(\n");
    return 2;
  }
#endif

  printf("Test finished\n");
  return 0;
}