chiark / gitweb /
Update stressapptest to 1.0.3.
[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 #include <utility>
18
19 // This file must work with autoconf on its public version,
20 // so these includes are correct.
21 #include "disk_blocks.h"
22
23 DiskBlockTable::DiskBlockTable() {
24   nelems_ = 0;
25   pthread_mutex_init(&data_mutex_, NULL);
26   pthread_mutex_init(&parameter_mutex_, NULL);
27   pthread_cond_init(&data_condition_, NULL);
28 }
29
30 DiskBlockTable::~DiskBlockTable() {
31   CleanTable();
32   pthread_mutex_destroy(&data_mutex_);
33   pthread_mutex_destroy(&parameter_mutex_);
34   pthread_cond_destroy(&data_condition_);
35 }
36
37 void DiskBlockTable::CleanTable() {
38   pthread_mutex_lock(&data_mutex_);
39   for (map<int64, StorageData*>::iterator it =
40            addr_to_block_.begin(); it != addr_to_block_.end(); ++it) {
41     delete it->second;
42   }
43   addr_to_block_.erase(addr_to_block_.begin(), addr_to_block_.end());
44   nelems_ = 0;
45   pthread_cond_broadcast(&data_condition_);
46   pthread_mutex_unlock(&data_mutex_);
47 }
48
49 // 64-bit non-negative random number generator.  Stolen from
50 // depot/google3/base/tracecontext_unittest.cc.
51 int64 DiskBlockTable::Random64() {
52   int64 x = random();
53   x = (x << 30) ^ random();
54   x = (x << 30) ^ random();
55   if (x >= 0)
56     return x;
57   else
58     return -x;
59 }
60
61 int64 DiskBlockTable::NumElems() {
62   unsigned int nelems;
63   pthread_mutex_lock(&data_mutex_);
64   nelems = nelems_;
65   pthread_mutex_unlock(&data_mutex_);
66   return nelems;
67 }
68
69 void DiskBlockTable::InsertOnStructure(BlockData *block) {
70   int64 address = block->GetAddress();
71   StorageData *sd = new StorageData();
72   sd->block = block;
73   sd->pos = nelems_;
74   // Creating new block ...
75   pthread_mutex_lock(&data_mutex_);
76   if (pos_to_addr_.size() <= nelems_) {
77     pos_to_addr_.insert(pos_to_addr_.end(), address);
78   } else {
79     pos_to_addr_[nelems_] = address;
80   }
81   addr_to_block_.insert(std::make_pair(address, sd));
82   nelems_++;
83   pthread_cond_broadcast(&data_condition_);
84   pthread_mutex_unlock(&data_mutex_);
85 }
86
87 int DiskBlockTable::RemoveBlock(BlockData *block) {
88   // For write threads, check the reference counter and remove
89   // it from the structure.
90   int64 address = block->GetAddress();
91   AddrToBlockMap::iterator it = addr_to_block_.find(address);
92   int ret = 1;
93   if (it != addr_to_block_.end()) {
94     int curr_pos = it->second->pos;
95     int last_pos = nelems_ - 1;
96     AddrToBlockMap::iterator last_it = addr_to_block_.find(
97         pos_to_addr_[last_pos]);
98     sat_assert(nelems_ > 0);
99     sat_assert(last_it != addr_to_block_.end());
100     // Everything is fine, updating ...
101     pthread_mutex_lock(&data_mutex_);
102     pos_to_addr_[curr_pos] = pos_to_addr_[last_pos];
103     last_it->second->pos = curr_pos;
104     delete it->second;
105     addr_to_block_.erase(it);
106     nelems_--;
107     block->DecreaseReferenceCounter();
108     if (block->GetReferenceCounter() == 0)
109       delete block;
110     pthread_cond_broadcast(&data_condition_);
111     pthread_mutex_unlock(&data_mutex_);
112   } else {
113     ret = 0;
114   }
115   return ret;
116 }
117
118 int DiskBlockTable::ReleaseBlock(BlockData *block) {
119   // If is a random thread, just check the reference counter.
120   int ret = 1;
121   pthread_mutex_lock(&data_mutex_);
122   int references = block->GetReferenceCounter();
123   if (references > 0) {
124     if (references == 1)
125       delete block;
126     else
127       block->DecreaseReferenceCounter();
128   } else {
129     ret = 0;
130   }
131   pthread_mutex_unlock(&data_mutex_);
132   return ret;
133 }
134
135 BlockData *DiskBlockTable::GetRandomBlock() {
136   struct timespec ts;
137   struct timeval tp;
138   int result = 0;
139   gettimeofday(&tp, NULL);
140   ts.tv_sec  = tp.tv_sec;
141   ts.tv_nsec = tp.tv_usec * 1000;
142   ts.tv_sec += 2;  // Wait for 2 seconds.
143   pthread_mutex_lock(&data_mutex_);
144   while (!nelems_ && result != ETIMEDOUT) {
145     result = pthread_cond_timedwait(&data_condition_, &data_mutex_, &ts);
146   }
147   if (result == ETIMEDOUT) {
148     pthread_mutex_unlock(&data_mutex_);
149     return NULL;
150   } else {
151     int64 random_number = Random64();
152     int64 random_pos = random_number % nelems_;
153     int64 address = pos_to_addr_[random_pos];
154     AddrToBlockMap::const_iterator it = addr_to_block_.find(address);
155     sat_assert(it != addr_to_block_.end());
156     BlockData *b = it->second->block;
157     // A block is returned only if its content is written on disk.
158     if (b->BlockIsInitialized()) {
159       b->IncreaseReferenceCounter();
160     } else {
161       b = NULL;
162     }
163     pthread_mutex_unlock(&data_mutex_);
164     return b;
165   }
166 }
167
168 void DiskBlockTable::SetParameters(
169     int sector_size, int write_block_size, int64 device_sectors,
170     int64 segment_size, string device_name) {
171   pthread_mutex_lock(&parameter_mutex_);
172   sector_size_ = sector_size;
173   write_block_size_ = write_block_size;
174   device_sectors_ = device_sectors;
175   segment_size_ = segment_size;
176   device_name_ = device_name;
177   CleanTable();
178   pthread_mutex_unlock(&parameter_mutex_);
179 }
180
181 BlockData *DiskBlockTable::GetUnusedBlock(int64 segment) {
182   int64 sector = 0;
183   BlockData *block = new BlockData();
184
185   bool good_sequence = false;
186   int num_sectors;
187
188   if (block == NULL) {
189     logprintf(0, "Process Error: Unable to allocate memory "
190               "for sector data for disk %s.\n", device_name_.c_str());
191     return NULL;
192   }
193
194   pthread_mutex_lock(&parameter_mutex_);
195
196   sat_assert(device_sectors_ != 0);
197
198   // Align the first sector with the beginning of a write block
199   num_sectors = write_block_size_ / sector_size_;
200
201   for (int i = 0; i < kBlockRetry && !good_sequence; i++) {
202     good_sequence = true;
203
204     // Use the entire disk or a small segment of the disk to allocate the first
205     // sector in the block from.
206
207     if (segment_size_ == -1) {
208       sector = (Random64() & 0x7FFFFFFFFFFFFFFFLL) % (
209           device_sectors_ / num_sectors);
210       sector *= num_sectors;
211     } else {
212       sector = (Random64() & 0x7FFFFFFFFFFFFFFFLL) % (
213           segment_size_ / num_sectors);
214       sector *= num_sectors;
215       sector += segment * segment_size_;
216
217       // Make sure the block is within the segment.
218       if (sector + num_sectors > (segment + 1) * segment_size_) {
219         good_sequence = false;
220         continue;
221       }
222     }
223     // Make sure the entire block is in range.
224     if (sector + num_sectors > device_sectors_) {
225       good_sequence = false;
226       continue;
227     }
228     // Check to see if the block is free. Since the blocks are
229     // now aligned to the write_block_size, it is not necessary
230     // to check each sector, just the first block (a sector
231     // overlap will never occur).
232
233     pthread_mutex_lock(&data_mutex_);
234     if (addr_to_block_.find(sector) != addr_to_block_.end()) {
235       good_sequence = false;
236     }
237     pthread_mutex_unlock(&data_mutex_);
238   }
239
240   if (good_sequence) {
241     block->SetParameters(sector, write_block_size_);
242     block->IncreaseReferenceCounter();
243     InsertOnStructure(block);
244   } else {
245     // No contiguous sequence of num_sectors sectors was found within
246     // kBlockRetry iterations so return an error value.
247     delete block;
248     block = NULL;
249   }
250   pthread_mutex_unlock(&parameter_mutex_);
251
252   return block;
253 }
254
255 // BlockData
256
257 BlockData::BlockData() {
258   addr_ = 0;
259   size_ = 0;
260   references_ = 0;
261   initialized_ = false;
262   pthread_mutex_init(&data_mutex_, NULL);
263 }
264
265 BlockData::~BlockData() {
266   pthread_mutex_destroy(&data_mutex_);
267 }
268
269 void BlockData::SetParameters(int64 address, int64 size) {
270   addr_ = address;
271   size_ = size;
272 }
273
274 void BlockData::IncreaseReferenceCounter() {
275   references_++;
276 }
277
278 void BlockData::DecreaseReferenceCounter() {
279   references_--;
280 }
281
282 int BlockData::GetReferenceCounter() {
283   return references_;
284 }
285
286 void BlockData::SetBlockAsInitialized() {
287   pthread_mutex_lock(&data_mutex_);
288   initialized_ = true;
289   pthread_mutex_unlock(&data_mutex_);
290 }
291
292 bool BlockData::BlockIsInitialized() {
293   pthread_mutex_lock(&data_mutex_);
294   bool initialized = initialized_;
295   pthread_mutex_unlock(&data_mutex_);
296   return initialized;
297 }
298
299 int64 BlockData::GetAddress() {
300   return addr_;
301 }
302
303 int64 BlockData::GetSize() {
304   return size_;
305 }
306
307 Pattern *BlockData::GetPattern() {
308   return pattern_;
309 }
310
311 void BlockData::SetPattern(Pattern *p) {
312   pattern_ = p;
313 }