blob: 3fa49bfaf3d2cdef923ee7d6a7fc07027fc34158 [file] [log] [blame]
Steve Block6ded16b2010-05-10 14:33:55 +01001// Copyright 2010 the V8 project authors. All rights reserved.
2//
3// Tests of circular queues.
4
5#include "v8.h"
6#include "circular-queue-inl.h"
7#include "cctest.h"
8
9namespace i = v8::internal;
10
11using i::CircularQueue;
12using i::SamplingCircularQueue;
13
14
15TEST(SingleRecordCircularQueue) {
16 typedef int Record;
17 CircularQueue<Record> cq(sizeof(Record) * 2);
18 CHECK(cq.IsEmpty());
19 cq.Enqueue(1);
20 CHECK(!cq.IsEmpty());
21 Record rec = 0;
22 cq.Dequeue(&rec);
23 CHECK_EQ(1, rec);
24 CHECK(cq.IsEmpty());
25}
26
27
28TEST(MultipleRecordsCircularQueue) {
29 typedef int Record;
30 const int kQueueSize = 10;
31 CircularQueue<Record> cq(sizeof(Record) * (kQueueSize + 1));
32 CHECK(cq.IsEmpty());
33 cq.Enqueue(1);
34 CHECK(!cq.IsEmpty());
35 for (int i = 2; i <= 5; ++i) {
36 cq.Enqueue(i);
37 CHECK(!cq.IsEmpty());
38 }
39 Record rec = 0;
40 for (int i = 1; i <= 4; ++i) {
41 CHECK(!cq.IsEmpty());
42 cq.Dequeue(&rec);
43 CHECK_EQ(i, rec);
44 }
45 for (int i = 6; i <= 12; ++i) {
46 cq.Enqueue(i);
47 CHECK(!cq.IsEmpty());
48 }
49 for (int i = 5; i <= 12; ++i) {
50 CHECK(!cq.IsEmpty());
51 cq.Dequeue(&rec);
52 CHECK_EQ(i, rec);
53 }
54 CHECK(cq.IsEmpty());
55}
56
57
58TEST(SamplingCircularQueue) {
59 typedef SamplingCircularQueue::Cell Record;
60 const int kRecordsPerChunk = 4;
61 SamplingCircularQueue scq(sizeof(Record),
62 kRecordsPerChunk * sizeof(Record),
63 3);
64
65 // Check that we are using non-reserved values.
66 CHECK_NE(SamplingCircularQueue::kClear, 1);
67 CHECK_NE(SamplingCircularQueue::kEnd, 1);
68 // Fill up the first chunk.
69 CHECK_EQ(NULL, scq.StartDequeue());
70 for (Record i = 1; i < 1 + kRecordsPerChunk; ++i) {
71 Record* rec = reinterpret_cast<Record*>(scq.Enqueue());
72 CHECK_NE(NULL, rec);
73 *rec = i;
74 CHECK_EQ(NULL, scq.StartDequeue());
75 }
76
77 // Fill up the second chunk. Consumption must still be unavailable.
78 CHECK_EQ(NULL, scq.StartDequeue());
79 for (Record i = 10; i < 10 + kRecordsPerChunk; ++i) {
80 Record* rec = reinterpret_cast<Record*>(scq.Enqueue());
81 CHECK_NE(NULL, rec);
82 *rec = i;
83 CHECK_EQ(NULL, scq.StartDequeue());
84 }
85
86 Record* rec = reinterpret_cast<Record*>(scq.Enqueue());
87 CHECK_NE(NULL, rec);
88 *rec = 20;
89 // Now as we started filling up the third chunk, consumption
90 // must become possible.
91 CHECK_NE(NULL, scq.StartDequeue());
92
93 // Consume the first chunk.
94 for (Record i = 1; i < 1 + kRecordsPerChunk; ++i) {
95 Record* rec = reinterpret_cast<Record*>(scq.StartDequeue());
96 CHECK_NE(NULL, rec);
97 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(*rec));
98 CHECK_EQ(rec, reinterpret_cast<Record*>(scq.StartDequeue()));
99 scq.FinishDequeue();
100 CHECK_NE(rec, reinterpret_cast<Record*>(scq.StartDequeue()));
101 }
102 // Now consumption must not be possible, as consumer now polls
103 // the first chunk for emptinness.
104 CHECK_EQ(NULL, scq.StartDequeue());
105
106 scq.FlushResidualRecords();
107 // From now, consumer no more polls ahead of the current chunk,
108 // so it's possible to consume the second chunk.
109 CHECK_NE(NULL, scq.StartDequeue());
110 // Consume the second chunk
111 for (Record i = 10; i < 10 + kRecordsPerChunk; ++i) {
112 Record* rec = reinterpret_cast<Record*>(scq.StartDequeue());
113 CHECK_NE(NULL, rec);
114 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(*rec));
115 CHECK_EQ(rec, reinterpret_cast<Record*>(scq.StartDequeue()));
116 scq.FinishDequeue();
117 CHECK_NE(rec, reinterpret_cast<Record*>(scq.StartDequeue()));
118 }
119 // Consumption must still be possible as the first cell of the
120 // last chunk is not clean.
121 CHECK_NE(NULL, scq.StartDequeue());
122}
123
124
125namespace {
126
127class ProducerThread: public i::Thread {
128 public:
129 typedef SamplingCircularQueue::Cell Record;
130
131 ProducerThread(SamplingCircularQueue* scq,
132 int records_per_chunk,
133 Record value,
134 i::Semaphore* finished)
135 : scq_(scq),
136 records_per_chunk_(records_per_chunk),
137 value_(value),
138 finished_(finished) { }
139
140 virtual void Run() {
141 for (Record i = value_; i < value_ + records_per_chunk_; ++i) {
142 Record* rec = reinterpret_cast<Record*>(scq_->Enqueue());
143 CHECK_NE(NULL, rec);
144 *rec = i;
145 }
146
147 finished_->Signal();
148 }
149
150 private:
151 SamplingCircularQueue* scq_;
152 const int records_per_chunk_;
153 Record value_;
154 i::Semaphore* finished_;
155};
156
157} // namespace
158
159TEST(SamplingCircularQueueMultithreading) {
160 // Emulate multiple VM threads working 'one thread at a time.'
161 // This test enqueues data from different threads. This corresponds
162 // to the case of profiling under Linux, where signal handler that
163 // does sampling is called in the context of different VM threads.
164
165 typedef ProducerThread::Record Record;
166 const int kRecordsPerChunk = 4;
167 SamplingCircularQueue scq(sizeof(Record),
168 kRecordsPerChunk * sizeof(Record),
169 3);
170 i::Semaphore* semaphore = i::OS::CreateSemaphore(0);
171 // Don't poll ahead, making possible to check data in the buffer
172 // immediately after enqueuing.
173 scq.FlushResidualRecords();
174
175 // Check that we are using non-reserved values.
176 CHECK_NE(SamplingCircularQueue::kClear, 1);
177 CHECK_NE(SamplingCircularQueue::kEnd, 1);
178 ProducerThread producer1(&scq, kRecordsPerChunk, 1, semaphore);
179 ProducerThread producer2(&scq, kRecordsPerChunk, 10, semaphore);
180 ProducerThread producer3(&scq, kRecordsPerChunk, 20, semaphore);
181
182 CHECK_EQ(NULL, scq.StartDequeue());
183 producer1.Start();
184 semaphore->Wait();
185 for (Record i = 1; i < 1 + kRecordsPerChunk; ++i) {
186 Record* rec = reinterpret_cast<Record*>(scq.StartDequeue());
187 CHECK_NE(NULL, rec);
188 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(*rec));
189 CHECK_EQ(rec, reinterpret_cast<Record*>(scq.StartDequeue()));
190 scq.FinishDequeue();
191 CHECK_NE(rec, reinterpret_cast<Record*>(scq.StartDequeue()));
192 }
193
194 CHECK_EQ(NULL, scq.StartDequeue());
195 producer2.Start();
196 semaphore->Wait();
197 for (Record i = 10; i < 10 + kRecordsPerChunk; ++i) {
198 Record* rec = reinterpret_cast<Record*>(scq.StartDequeue());
199 CHECK_NE(NULL, rec);
200 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(*rec));
201 CHECK_EQ(rec, reinterpret_cast<Record*>(scq.StartDequeue()));
202 scq.FinishDequeue();
203 CHECK_NE(rec, reinterpret_cast<Record*>(scq.StartDequeue()));
204 }
205
206 CHECK_EQ(NULL, scq.StartDequeue());
207 producer3.Start();
208 semaphore->Wait();
209 for (Record i = 20; i < 20 + kRecordsPerChunk; ++i) {
210 Record* rec = reinterpret_cast<Record*>(scq.StartDequeue());
211 CHECK_NE(NULL, rec);
212 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(*rec));
213 CHECK_EQ(rec, reinterpret_cast<Record*>(scq.StartDequeue()));
214 scq.FinishDequeue();
215 CHECK_NE(rec, reinterpret_cast<Record*>(scq.StartDequeue()));
216 }
217
218 CHECK_EQ(NULL, scq.StartDequeue());
219
220 delete semaphore;
221}