chiark / gitweb /
PORT 22
[stressapptest] / src / disk_blocks.cc
1 // Copyright 2008 Google Inc. All Rights Reserved.
2
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6
7 //      http://www.apache.org/licenses/LICENSE-2.0
8
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // Thread-safe container of disk blocks
16
17 // This file must work with autoconf on its public version,
18 // so these includes are correct.
19 #include "disk_blocks.h"
20
21 #include <utility>
22
23 // BlockData
24 BlockData::BlockData() : address_(0), size_(0),
25                          references_(0), initialized_(false),
26                          pattern_(NULL) {
27   pthread_mutex_init(&data_mutex_, NULL);
28 }
29
30 BlockData::~BlockData() {
31   pthread_mutex_destroy(&data_mutex_);
32 }
33
34 void BlockData::set_initialized() {
35   pthread_mutex_lock(&data_mutex_);
36   initialized_ = true;
37   pthread_mutex_unlock(&data_mutex_);
38 }
39
40 bool BlockData::initialized() const {
41   pthread_mutex_lock(&data_mutex_);
42   bool initialized = initialized_;
43   pthread_mutex_unlock(&data_mutex_);
44   return initialized;
45 }
46
47 // DiskBlockTable
48 DiskBlockTable::DiskBlockTable() : sector_size_(0), write_block_size_(0),
49                                    device_name_(""), device_sectors_(0),
50                                    segment_size_(0), size_(0) {
51   pthread_mutex_init(&data_mutex_, NULL);
52   pthread_mutex_init(&parameter_mutex_, NULL);
53   pthread_cond_init(&data_condition_, NULL);
54 }
55
56 DiskBlockTable::~DiskBlockTable() {
57   pthread_mutex_destroy(&data_mutex_);
58   pthread_mutex_destroy(&parameter_mutex_);
59   pthread_cond_destroy(&data_condition_);
60 }
61
62 // 64-bit non-negative random number generator.  Stolen from
63 // depot/google3/base/tracecontext_unittest.cc.
64 int64 DiskBlockTable::Random64() {
65   int64 x = random();
66   x = (x << 30) ^ random();
67   x = (x << 30) ^ random();
68   if (x >= 0)
69     return x;
70   else
71     return -x;
72 }
73
74 uint64 DiskBlockTable::Size() {
75   pthread_mutex_lock(&data_mutex_);
76   uint64 size = size_;
77   pthread_mutex_unlock(&data_mutex_);
78   return size;
79 }
80
81 void DiskBlockTable::InsertOnStructure(BlockData *block) {
82   int64 address = block->address();
83   StorageData *sd = new StorageData();
84   sd->block = block;
85   sd->pos = size_;
86   // Creating new block ...
87   pthread_mutex_lock(&data_mutex_);
88   if (pos_to_addr_.size() <= size_) {
89     pos_to_addr_.insert(pos_to_addr_.end(), address);
90   } else {
91     pos_to_addr_[size_] = address;
92   }
93   addr_to_block_[address] = sd;
94   size_++;
95   pthread_cond_broadcast(&data_condition_);
96   pthread_mutex_unlock(&data_mutex_);
97 }
98
99 int DiskBlockTable::RemoveBlock(BlockData *block) {
100   // For write threads, check the reference counter and remove
101   // it from the structure.
102   int64 address = block->address();
103   AddrToBlockMap::iterator it = addr_to_block_.find(address);
104   int ret = 1;
105   if (it != addr_to_block_.end()) {
106     int curr_pos = it->second->pos;
107     int last_pos = size_ - 1;
108     AddrToBlockMap::iterator last_it = addr_to_block_.find(
109         pos_to_addr_[last_pos]);
110     sat_assert(size_ > 0);
111     sat_assert(last_it != addr_to_block_.end());
112     // Everything is fine, removing block from table.
113     pthread_mutex_lock(&data_mutex_);
114     pos_to_addr_[curr_pos] = pos_to_addr_[last_pos];
115     last_it->second->pos = curr_pos;
116     delete it->second;
117     addr_to_block_.erase(it);
118     size_--;
119     block->DecreaseReferenceCounter();
120     if (block->GetReferenceCounter() == 0)
121       delete block;
122     else if (block->GetReferenceCounter() < 0)
123       ret = 0;
124     pthread_cond_broadcast(&data_condition_);
125     pthread_mutex_unlock(&data_mutex_);
126   } else {
127     ret = 0;
128   }
129   return ret;
130 }
131
132 int DiskBlockTable::ReleaseBlock(BlockData *block) {
133   // If caller is a random thread, just check the reference counter.
134   int ret = 1;
135   pthread_mutex_lock(&data_mutex_);
136   int references = block->GetReferenceCounter();
137   if (references == 1)
138     delete block;
139   else if (references > 0)
140     block->DecreaseReferenceCounter();
141   else
142     ret = 0;
143   pthread_mutex_unlock(&data_mutex_);
144   return ret;
145 }
146
147 BlockData *DiskBlockTable::GetRandomBlock() {
148   struct timespec ts;
149   struct timeval tp;
150   gettimeofday(&tp, NULL);
151   ts.tv_sec  = tp.tv_sec;
152   ts.tv_nsec = tp.tv_usec * 1000;
153   ts.tv_sec += 2;  // Wait for 2 seconds.
154   int result = 0;
155   pthread_mutex_lock(&data_mutex_);
156   while (!size_ && result != ETIMEDOUT) {
157     result = pthread_cond_timedwait(&data_condition_, &data_mutex_, &ts);
158   }
159   if (result == ETIMEDOUT) {
160     pthread_mutex_unlock(&data_mutex_);
161     return NULL;
162   } else {
163     int64 random_number = Random64();
164     int64 random_pos = random_number % size_;
165     int64 address = pos_to_addr_[random_pos];
166     AddrToBlockMap::const_iterator it = addr_to_block_.find(address);
167     sat_assert(it != addr_to_block_.end());
168     BlockData *b = it->second->block;
169     // A block is returned only if its content is written on disk.
170     if (b->initialized()) {
171       b->IncreaseReferenceCounter();
172     } else {
173       b = NULL;
174     }
175     pthread_mutex_unlock(&data_mutex_);
176     return b;
177   }
178 }
179
180 void DiskBlockTable::SetParameters(int sector_size,
181                                    int write_block_size,
182                                    int64 device_sectors,
183                                    int64 segment_size,
184                                    const string& device_name) {
185   sat_assert(size_ == 0);
186   pthread_mutex_lock(&parameter_mutex_);
187   sector_size_ = sector_size;
188   write_block_size_ = write_block_size;
189   device_sectors_ = device_sectors;
190   segment_size_ = segment_size;
191   device_name_ = device_name;
192   pthread_mutex_unlock(&parameter_mutex_);
193 }
194
195 BlockData *DiskBlockTable::GetUnusedBlock(int64 segment) {
196   int64 sector = 0;
197   BlockData *block = new BlockData();
198   bool good_sequence = false;
199   if (block == NULL) {
200     logprintf(0, "Process Error: Unable to allocate memory "
201               "for sector data for disk %s.\n", device_name_.c_str());
202     return NULL;
203   }
204   pthread_mutex_lock(&parameter_mutex_);
205   sat_assert(device_sectors_ != 0);
206   // Align the first sector with the beginning of a write block
207   int num_sectors = write_block_size_ / sector_size_;
208   for (int i = 0; i < kBlockRetry && !good_sequence; i++) {
209     good_sequence = true;
210     // Use the entire disk or a small segment of the disk to allocate the first
211     // sector in the block from.
212     if (segment_size_ == -1) {
213       sector = (Random64() & 0x7FFFFFFFFFFFFFFFLL) % (
214           device_sectors_ / num_sectors);
215       sector *= num_sectors;
216     } else {
217       sector = (Random64() & 0x7FFFFFFFFFFFFFFFLL) % (
218           segment_size_ / num_sectors);
219       sector *= num_sectors;
220       sector += segment * segment_size_;
221       // Make sure the block is within the segment.
222       if (sector + num_sectors > (segment + 1) * segment_size_) {
223         good_sequence = false;
224         continue;
225       }
226     }
227     // Make sure the entire block is in range.
228     if (sector + num_sectors > device_sectors_) {
229       good_sequence = false;
230       continue;
231     }
232     // Check to see if the block is free. Since the blocks are
233     // now aligned to the write_block_size, it is not necessary
234     // to check each sector, just the first block (a sector
235     // overlap will never occur).
236     pthread_mutex_lock(&data_mutex_);
237     if (addr_to_block_.find(sector) != addr_to_block_.end()) {
238       good_sequence = false;
239     }
240     pthread_mutex_unlock(&data_mutex_);
241   }
242
243   if (good_sequence) {
244     block->set_address(sector);
245     block->set_size(write_block_size_);
246     block->IncreaseReferenceCounter();
247     InsertOnStructure(block);
248   } else {
249     // No contiguous sequence of num_sectors sectors was found within
250     // kBlockRetry iterations so return an error value.
251     delete block;
252     block = NULL;
253   }
254   pthread_mutex_unlock(&parameter_mutex_);
255   return block;
256 }