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