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