• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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