Home | History | Annotate | Download | only in cctest
      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 using i::SamplingCircularQueue;
     10 
     11 
     12 TEST(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 
     79 namespace {
     80 
     81 class 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 
    114 TEST(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