aboutsummaryrefslogtreecommitdiff
path: root/libc/test/integration/src/pthread/pthread_spinlock_test.cpp
blob: 233daf8332fb094f3ced6436a886336b1c510918 (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
133
134
135
136
137
138
139
140
141
142
143
144
145
//===-- Tests for pthread_spinlock ----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "hdr/errno_macros.h"
#include "src/pthread/pthread_create.h"
#include "src/pthread/pthread_join.h"
#include "src/pthread/pthread_spin_destroy.h"
#include "src/pthread/pthread_spin_init.h"
#include "src/pthread/pthread_spin_lock.h"
#include "src/pthread/pthread_spin_trylock.h"
#include "src/pthread/pthread_spin_unlock.h"
#include "test/IntegrationTest/test.h"
#include <pthread.h>

namespace {
void smoke_test() {
  pthread_spinlock_t lock;
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
            0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), 0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
}

void trylock_test() {
  pthread_spinlock_t lock;
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
            0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), 0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), EBUSY);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), 0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
}

void destroy_held_lock_test() {
  pthread_spinlock_t lock;
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
            0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), 0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), EBUSY);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
}

void use_after_destroy_test() {
  pthread_spinlock_t lock;
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
            0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), EINVAL);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), EINVAL);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), EINVAL);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), EINVAL);
}

void unlock_without_holding_test() {
  pthread_spinlock_t lock;
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
            0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), EPERM);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
}

void deadlock_test() {
  pthread_spinlock_t lock;
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
            0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), 0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), EDEADLK);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
}

void null_lock_test() {
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(nullptr, 0), EINVAL);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(nullptr), EINVAL);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(nullptr), EINVAL);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(nullptr), EINVAL);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(nullptr), EINVAL);
}

void pshared_attribute_test() {
  pthread_spinlock_t lock;
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_SHARED),
            0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);

  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
            0);
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);

  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, -1), EINVAL);
}

void multi_thread_test() {
  struct shared_data {
    pthread_spinlock_t lock;
    int count = 0;
  } shared;
  pthread_t thread[10];
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&shared.lock, 0), 0);
  for (int i = 0; i < 10; ++i) {
    ASSERT_EQ(
        LIBC_NAMESPACE::pthread_create(
            &thread[i], nullptr,
            [](void *arg) -> void * {
              auto *data = static_cast<shared_data *>(arg);
              for (int j = 0; j < 1000; ++j) {
                ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&data->lock), 0);
                data->count += j;
                ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&data->lock), 0);
              }
              return nullptr;
            },
            &shared),
        0);
  }
  for (int i = 0; i < 10; ++i) {
    ASSERT_EQ(LIBC_NAMESPACE::pthread_join(thread[i], nullptr), 0);
  }
  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&shared.lock), 0);
  ASSERT_EQ(shared.count, 1000 * 999 * 5);
}

} // namespace

TEST_MAIN() {
  smoke_test();
  trylock_test();
  destroy_held_lock_test();
  use_after_destroy_test();
  unlock_without_holding_test();
  deadlock_test();
  multi_thread_test();
  null_lock_test();
  pshared_attribute_test();
  return 0;
}