| | 1 | /* |
| | 2 | * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. |
| | 3 | * |
| | 4 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 5 | * on using and copying this software. |
| | 6 | */ |
| | 7 | /* |
| | 8 | Name |
| | 9 | vmpool.cpp - constant pool - in-memory (non-swapping) implementation |
| | 10 | Function |
| | 11 | |
| | 12 | Notes |
| | 13 | |
| | 14 | Modified |
| | 15 | 10/20/98 MJRoberts - Creation |
| | 16 | */ |
| | 17 | |
| | 18 | #include <stdlib.h> |
| | 19 | #include <memory.h> |
| | 20 | |
| | 21 | #include "t3std.h" |
| | 22 | #include "vmpool.h" |
| | 23 | |
| | 24 | |
| | 25 | /* ------------------------------------------------------------------------ */ |
| | 26 | /* |
| | 27 | * In-memory pool implementation. This pool implementation pre-loads |
| | 28 | * all available pages in the pool and keeps the complete pool in memory |
| | 29 | * at all times. |
| | 30 | */ |
| | 31 | |
| | 32 | /* |
| | 33 | * delete the pool's resources - this is called from our destructor, and |
| | 34 | * can also be called explicitly to reset the pool |
| | 35 | */ |
| | 36 | void CVmPoolInMem::terminate_nv() |
| | 37 | { |
| | 38 | /* free any pages we allocated from the backing store */ |
| | 39 | free_backing_pages(); |
| | 40 | |
| | 41 | /* free all of the dynamic object handles */ |
| | 42 | while (dyn_head_ != 0) |
| | 43 | { |
| | 44 | CVmPoolDynObj *nxt; |
| | 45 | |
| | 46 | /* note the next object */ |
| | 47 | nxt = dyn_head_->get_next(); |
| | 48 | |
| | 49 | /* delete this object */ |
| | 50 | t3free(dyn_head_); |
| | 51 | |
| | 52 | /* move on to the next one */ |
| | 53 | dyn_head_ = nxt; |
| | 54 | } |
| | 55 | |
| | 56 | /* we no longer have anything in the list */ |
| | 57 | dyn_head_ = dyn_tail_ = 0; |
| | 58 | |
| | 59 | /* we no longer have any dynamic pages */ |
| | 60 | first_dyn_page_ = 0; |
| | 61 | } |
| | 62 | |
| | 63 | /* |
| | 64 | * free pages that we allocated from the backing store |
| | 65 | */ |
| | 66 | void CVmPoolInMem::free_backing_pages() |
| | 67 | { |
| | 68 | size_t i; |
| | 69 | pool_ofs_t ofs; |
| | 70 | CVmPool_pg *info; |
| | 71 | |
| | 72 | /* |
| | 73 | * delete any dynamically-allocated pages (these are not managed by |
| | 74 | * the backing store) |
| | 75 | */ |
| | 76 | for (i = first_dyn_page_, info = &pages_[i] ; i < page_slots_ ; |
| | 77 | ++i, ++info) |
| | 78 | { |
| | 79 | /* if this slot was allocated, delete it */ |
| | 80 | if (info->mem != 0) |
| | 81 | { |
| | 82 | /* free the memory */ |
| | 83 | t3free((char *)info->mem); |
| | 84 | |
| | 85 | /* note that we've freed it */ |
| | 86 | info->mem = 0; |
| | 87 | } |
| | 88 | } |
| | 89 | |
| | 90 | /* if there's no backing store, there's nothing to do */ |
| | 91 | if (backing_store_ == 0) |
| | 92 | return; |
| | 93 | |
| | 94 | /* |
| | 95 | * Run through the page array and delete each allocated page. Since |
| | 96 | * we allocate pages through the backing store, delete pages through |
| | 97 | * the backing store. |
| | 98 | */ |
| | 99 | for (i = 0, info = pages_, ofs = 0 ; i < first_dyn_page_ ; |
| | 100 | ++i, ++info, ofs += page_size_) |
| | 101 | { |
| | 102 | /* if this slot was allocated, delete it */ |
| | 103 | if (info->mem != 0) |
| | 104 | { |
| | 105 | /* delete the page */ |
| | 106 | backing_store_->vmpbs_free_page(info->mem, ofs, page_size_); |
| | 107 | |
| | 108 | /* forget it */ |
| | 109 | info->mem = 0; |
| | 110 | } |
| | 111 | } |
| | 112 | } |
| | 113 | |
| | 114 | /* |
| | 115 | * initialize |
| | 116 | */ |
| | 117 | void CVmPoolInMem::attach_backing_store(CVmPoolBackingStore *backing_store) |
| | 118 | { |
| | 119 | size_t ofs; |
| | 120 | size_t i; |
| | 121 | CVmPool_pg *info; |
| | 122 | |
| | 123 | /* do the normal initialization to allocate the page slots */ |
| | 124 | CVmPoolPaged::attach_backing_store(backing_store); |
| | 125 | |
| | 126 | /* |
| | 127 | * set the initial dynamic page index - this will be the first page |
| | 128 | * after all of the fixed pages we load from the backing store |
| | 129 | */ |
| | 130 | first_dyn_page_ = page_slots_; |
| | 131 | |
| | 132 | /* load all of the pages */ |
| | 133 | for (i = 0, info = pages_, ofs = 0 ; i < page_slots_ ; |
| | 134 | ++i, ++info, ofs += page_size_) |
| | 135 | { |
| | 136 | size_t load_size; |
| | 137 | |
| | 138 | /* determine how much memory we really need for this page */ |
| | 139 | load_size = backing_store_->vmpbs_get_page_size(ofs, page_size_); |
| | 140 | |
| | 141 | /* allocate and load the page */ |
| | 142 | info->mem = backing_store_->vmpbs_alloc_and_load_page( |
| | 143 | ofs, page_size_, load_size); |
| | 144 | } |
| | 145 | } |
| | 146 | |
| | 147 | /* |
| | 148 | * Detach from the backing store |
| | 149 | */ |
| | 150 | void CVmPoolInMem::detach_backing_store() |
| | 151 | { |
| | 152 | /* release the backing pages */ |
| | 153 | free_backing_pages(); |
| | 154 | |
| | 155 | /* inherit default */ |
| | 156 | CVmPoolPaged::detach_backing_store(); |
| | 157 | } |
| | 158 | |
| | 159 | /* ------------------------------------------------------------------------ */ |
| | 160 | /* |
| | 161 | * Dynamic interface implementation |
| | 162 | */ |
| | 163 | |
| | 164 | /* |
| | 165 | * Allocate space in the dynamic pool |
| | 166 | */ |
| | 167 | CVmPoolDynObj *CVmPoolInMem::dynpool_alloc(size_t len) |
| | 168 | { |
| | 169 | CVmPoolDynObj *cur; |
| | 170 | |
| | 171 | /* |
| | 172 | * if the requested size exceeds the page size, we can't allocate |
| | 173 | * it, since each request must fit within a single page |
| | 174 | */ |
| | 175 | if (len > page_size_) |
| | 176 | return 0; |
| | 177 | |
| | 178 | /* |
| | 179 | * First, see if we can find a free pool object that we've already |
| | 180 | * allocated that will fill the request |
| | 181 | */ |
| | 182 | for (cur = dyn_head_ ; cur != 0 ; cur = cur->get_next()) |
| | 183 | { |
| | 184 | /* if this object is free, and it's big enough, use it */ |
| | 185 | if (cur->is_free() && cur->get_len() >= len) |
| | 186 | { |
| | 187 | /* |
| | 188 | * if this object is at least a little bigger than the |
| | 189 | * request, create a new object to hold the balance of this |
| | 190 | * object, so that the balance can be allocated separately |
| | 191 | */ |
| | 192 | if (cur->get_len() > len + 63) |
| | 193 | { |
| | 194 | CVmPoolDynObj *new_obj; |
| | 195 | |
| | 196 | /* |
| | 197 | * create a new object, using the space remaining in the |
| | 198 | * old object after the requested amount of space |
| | 199 | */ |
| | 200 | new_obj = new CVmPoolDynObj(cur->get_ofs() + len, |
| | 201 | cur->get_len() - len); |
| | 202 | |
| | 203 | /* reduce the old object to the requested size */ |
| | 204 | cur->set_len(len); |
| | 205 | |
| | 206 | /* link the new object in after the old object */ |
| | 207 | insert_dyn(cur, new_obj); |
| | 208 | |
| | 209 | /* the new object is free */ |
| | 210 | new_obj->set_free(TRUE); |
| | 211 | } |
| | 212 | |
| | 213 | /* this object is now in use */ |
| | 214 | cur->set_free(FALSE); |
| | 215 | |
| | 216 | /* return the object we found */ |
| | 217 | return cur; |
| | 218 | } |
| | 219 | } |
| | 220 | |
| | 221 | /* |
| | 222 | * We didn't find any free memory in any existing pages where we |
| | 223 | * could fill the request. So, we must allocate a new page. First, |
| | 224 | * allocate a new page slot. |
| | 225 | */ |
| | 226 | alloc_page_slots(page_slots_ + 1); |
| | 227 | |
| | 228 | /* allocate space for the page data */ |
| | 229 | pages_[page_slots_ - 1].mem = (char *)t3malloc(page_size_); |
| | 230 | |
| | 231 | /* |
| | 232 | * if the requested size wouldn't leave much additional space on the |
| | 233 | * page, simply give the entire page to the new object |
| | 234 | */ |
| | 235 | if (len + 63 >= page_size_) |
| | 236 | len = page_size_; |
| | 237 | |
| | 238 | /* create a new dynamic pool handle for the new object */ |
| | 239 | cur = new CVmPoolDynObj(get_page_start_ofs(page_slots_ - 1), len); |
| | 240 | |
| | 241 | /* link it in at the end of the list */ |
| | 242 | append_dyn(cur); |
| | 243 | |
| | 244 | /* |
| | 245 | * if there's any space left over, create yet another object to |
| | 246 | * cover the free space remaining on the page |
| | 247 | */ |
| | 248 | if (len < page_size_) |
| | 249 | { |
| | 250 | CVmPoolDynObj *f; |
| | 251 | |
| | 252 | /* create a new dynamic pool handle for the new object */ |
| | 253 | f = new CVmPoolDynObj(get_page_start_ofs(page_slots_ - 1) + len, |
| | 254 | page_size_ - len); |
| | 255 | |
| | 256 | /* mark it as free */ |
| | 257 | f->set_free(TRUE); |
| | 258 | |
| | 259 | /* link it in at the end of the list */ |
| | 260 | append_dyn(f); |
| | 261 | } |
| | 262 | |
| | 263 | /* return the new object */ |
| | 264 | return cur; |
| | 265 | } |
| | 266 | |
| | 267 | /* |
| | 268 | * Delete a dynamic object |
| | 269 | */ |
| | 270 | void CVmPoolInMem::dynpool_delete(CVmPoolDynObj *obj) |
| | 271 | { |
| | 272 | CVmPoolDynObj *cur; |
| | 273 | size_t pg; |
| | 274 | |
| | 275 | /* mark this object as free */ |
| | 276 | obj->set_free(TRUE); |
| | 277 | |
| | 278 | /* note the page containing this object */ |
| | 279 | pg = get_page_for_ofs(obj->get_ofs()); |
| | 280 | |
| | 281 | /* |
| | 282 | * Combine this object with any consecutive objects in the same |
| | 283 | * page. First, look for objects before this object. |
| | 284 | */ |
| | 285 | for (cur = obj->get_prev() ; |
| | 286 | cur != 0 && cur->is_free() |
| | 287 | && get_page_for_ofs(cur->get_ofs()) == pg ; |
| | 288 | cur = cur->get_prev()) |
| | 289 | { |
| | 290 | /* |
| | 291 | * cur precedes obj, is on the same page, and is also free - |
| | 292 | * combine cur and obj into cur |
| | 293 | */ |
| | 294 | cur->set_len(cur->get_len() + obj->get_len()); |
| | 295 | |
| | 296 | /* unlink obj from the list, since it's no longer needed */ |
| | 297 | unlink_dyn(obj); |
| | 298 | |
| | 299 | /* delete the original object */ |
| | 300 | delete obj; |
| | 301 | |
| | 302 | /* forget about obj - cur now encompasses it */ |
| | 303 | obj = cur; |
| | 304 | } |
| | 305 | |
| | 306 | /* |
| | 307 | * Now combine any consecutive objects that follow |
| | 308 | */ |
| | 309 | for (cur = obj->get_next() ; |
| | 310 | cur != 0 && cur->is_free() |
| | 311 | && get_page_for_ofs(cur->get_ofs()) == pg ; ) |
| | 312 | { |
| | 313 | /* |
| | 314 | * cur follows obj, is on the same page, and is also free - |
| | 315 | * combine cur and obj into obj |
| | 316 | */ |
| | 317 | obj->set_len(obj->get_len() + cur->get_len()); |
| | 318 | |
| | 319 | /* unlink cur from the list, since it's no longer needed */ |
| | 320 | unlink_dyn(cur); |
| | 321 | |
| | 322 | /* delete the second object */ |
| | 323 | delete cur; |
| | 324 | |
| | 325 | /* move on to the next object */ |
| | 326 | cur = obj->get_next(); |
| | 327 | } |
| | 328 | } |
| | 329 | |
| | 330 | /* |
| | 331 | * Compress the dynamic portion of the pool |
| | 332 | */ |
| | 333 | void CVmPoolInMem::dynpool_compress() |
| | 334 | { |
| | 335 | // $$$ |
| | 336 | } |
| | 337 | |
| | 338 | /* |
| | 339 | * Append a dynamic handle to the end of our list |
| | 340 | */ |
| | 341 | void CVmPoolInMem::append_dyn(CVmPoolDynObj *obj) |
| | 342 | { |
| | 343 | /* this goes at the end, so there's nothing after this object */ |
| | 344 | obj->set_next(0); |
| | 345 | |
| | 346 | /* the old tail will precede this object in the list */ |
| | 347 | obj->set_prev(dyn_tail_); |
| | 348 | |
| | 349 | /* |
| | 350 | * if the list is empty, this is the new head; otherwise, set the |
| | 351 | * old tail's forward pointer to the new item |
| | 352 | */ |
| | 353 | if (dyn_tail_ == 0) |
| | 354 | dyn_head_ = obj; |
| | 355 | else |
| | 356 | dyn_tail_->set_next(obj); |
| | 357 | |
| | 358 | /* this is now the tail item */ |
| | 359 | dyn_tail_ = obj; |
| | 360 | } |
| | 361 | |
| | 362 | /* |
| | 363 | * Unlink a dynamic handle from the list |
| | 364 | */ |
| | 365 | void CVmPoolInMem::unlink_dyn(CVmPoolDynObj *obj) |
| | 366 | { |
| | 367 | /* |
| | 368 | * if there's a previous item, set its forward pointer; otherwise, |
| | 369 | * advance the list head past the item we're unlinking |
| | 370 | */ |
| | 371 | if (obj->get_prev() != 0) |
| | 372 | obj->get_prev()->set_next(obj->get_next()); |
| | 373 | else |
| | 374 | dyn_head_ = obj->get_next(); |
| | 375 | |
| | 376 | /* |
| | 377 | * if there's a following item, set its back pointer; otherwise, |
| | 378 | * move the list tail back over the item we're unlinking |
| | 379 | */ |
| | 380 | if (obj->get_next() != 0) |
| | 381 | obj->get_next()->set_prev(obj); |
| | 382 | else |
| | 383 | dyn_tail_ = obj->get_prev(); |
| | 384 | } |
| | 385 | |
| | 386 | /* |
| | 387 | * Insert a dynamic handle into the list after the given object |
| | 388 | */ |
| | 389 | void CVmPoolInMem::insert_dyn(CVmPoolDynObj *obj, CVmPoolDynObj *new_obj) |
| | 390 | { |
| | 391 | /* the old following object (after obj) will now follow new_obj */ |
| | 392 | new_obj->set_next(obj->get_next()); |
| | 393 | |
| | 394 | /* obj will precede new_obj */ |
| | 395 | new_obj->set_prev(obj); |
| | 396 | |
| | 397 | /* new_obj will follow the old object */ |
| | 398 | obj->set_next(new_obj); |
| | 399 | |
| | 400 | /* |
| | 401 | * if another object follows, set its back pointer to point to the |
| | 402 | * new object; otherwise, set the list tail pointer |
| | 403 | */ |
| | 404 | if (new_obj->get_next() != 0) |
| | 405 | new_obj->get_next()->set_prev(new_obj); |
| | 406 | else |
| | 407 | dyn_tail_ = new_obj; |
| | 408 | } |