/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ /* * mcache.cc * Copyright (C) 1997 by the University of Southern California * $Id: mcache.cc,v 1.13 2005/09/18 23:33:35 tomh Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * * The copyright of this module includes the following * linking-with-specific-other-licenses addition: * * In addition, as a special exception, the copyright holders of * this module give you permission to combine (via static or * dynamic linking) this module with free software programs or * libraries that are released under the GNU LGPL and with code * included in the standard release of ns-2 under the Apache 2.0 * license or under otherwise-compatible licenses with advertising * requirements (or modified versions of such code, with unchanged * license). You may copy and distribute such a system following the * terms of the GNU GPL for this module and the licenses of the * other code concerned, provided that you include the source code of * that other code when and as the GNU GPL requires distribution of * source code. * * Note that people who make modified versions of this module * are not obligated to grant this special exception for their * modified versions; it is their choice whether to do so. The GNU * General Public License gives permission to release a modified * version without this exception; this exception also makes it * possible to release a modified version which carries forward this * exception. * */ // // Multimedia cache implementation // // $Header: /nfs/jade/vint/CVSROOT/ns-2/webcache/mcache.cc,v 1.13 2005/09/18 23:33:35 tomh Exp $ #include #include #include "rap/media-app.h" #include "mcache.h" MediaPage::MediaPage(const char *n, int s, double mt, double et, double a, int l) : ClientPage(n, s, mt, et, a), num_layer_(l), locked_(0), realsize_(0) { for (int i = 0; i < num_layer_; i++) { hc_[i] = new HitCount(this, i); flags_[i] = 0; } } MediaPage::~MediaPage() { int i; for (i = 0; i < num_layer_; i++) { // Delete hit count list // These hit count records should have already been removed // from the cache's hit count list. assert((hc_[i]->prev() == NULL) && (hc_[i]->next() == NULL)); delete hc_[i]; // Delete media segment list layer_[i].destroy(); } } void MediaPage::print_info(char *buf) { ClientPage::print_info(buf); buf += strlen(buf); sprintf(buf, " pgtype MEDIA layer %d", num_layer_); } // Make the page full with stream data void MediaPage::create() { assert((num_layer_ >= 0) && (num_layer_ < MAX_LAYER)); int i, sz = size_ / num_layer_; for (i = 0; i < num_layer_; i++) { // Delete whatever that was there. layer_[i].destroy(); add_segment(i, MediaSegment(0, sz)); set_complete_layer(i); } realsize_ = size_; } void MediaPage::add_segment(int layer, const MediaSegment& s) { assert((layer >= 0) && (layer < MAX_LAYER)); layer_[layer].add(s); realsize_ += s.datasize(); if (s.is_last()) set_complete_layer(layer); } int MediaPage::is_complete() { // Consider a page finished when all NON-EMPTY layers are // marked as "completed" for (int i = 0; i < num_layer_; i++) if (!is_complete_layer(i) && (layer_[i].length() > 0)) return 0; return 1; } void MediaPage::set_complete() { for (int i = 0; i < num_layer_; i++) set_complete_layer(i); } // Used for cache replacement int MediaPage::evict_tail_segment(int layer, int size) { if (is_locked() || is_tlocked()) return 0; assert((layer >= 0) && (layer < MAX_LAYER)); //#ifdef MCACHE_DEBUG #if 0 char buf[20]; name(buf); fprintf(stderr, "Page %s evicted layer %d: ", buf, layer); #endif int sz = layer_[layer].evict_tail(size); realsize_ -= sz; //#ifdef MCACHE_DEBUG #if 0 fprintf(stderr, "\n"); #endif return sz; } //---------------------------------------------------------------------- // Classes related to a multimedia client page pool // // HitCountList and MClientPagePool //---------------------------------------------------------------------- void HitCountList::update(HitCount *h) { HitCount *tmp = h->prev(); if ((tmp != NULL) && (tmp->hc() < h->hc())) { // Hit count increased, need to move this one up detach(h); while ((tmp != NULL) && (tmp->hc() < h->hc())) { if ((tmp->page() == h->page()) && (tmp->layer() < h->layer())) // XXX Don't violate layer encoding within the // same page! break; tmp = tmp->prev(); } if (tmp == NULL) // Insert it at the head insert(h, head_); else append(h, tmp); } else if ((h->next() != NULL) && (h->hc() < h->next()->hc())) { // Hit count decreased, need to move this one down tmp = h->next(); detach(h); while ((tmp != NULL) && (h->hc() < tmp->hc())) { if ((h->page() == tmp->page()) && (h->layer() < tmp->layer())) // XXX Don't violate layer encoding within // the same page! break; tmp = tmp->next(); } if (tmp == NULL) // At the tail append(h, tail_); else insert(h, tmp); } // We may end up with two cases here: // // (1) tmp->hc()>h->hc() && tmp->layer()layer(). This is // the normal case, where both hit count ordering and layer // ordering are preserved; // // (2) tmp->hc()>h->hc() && tmp->layer()>h->layer(). In this // case, we should move h BEFORE tmp so that the layer // ordering is not violated. We basically order the list using // layer number as primary key, and use hit count as secondary // key. // Note that the hit count ordering is only violated when more packets // in layer i are dropped than those in layer i+1. } // Check the integrity of the resulting hit count list void HitCountList::check_integrity() { HitCount *p = (HitCount*)head_, *q; while (p != NULL) { q = p->next(); while (q != NULL) { // Check layer ordering if ((p->page() == q->page()) && (p->layer() > q->layer())) { fprintf(stderr, "Wrong hit count list.\n"); abort(); } q = q->next(); } p = p->next(); } } void HitCountList::add(HitCount *h) { HitCount *tmp = (HitCount*)head_; // XXX First, ensure that the layer ordering within the same page // is not violated!! while ((tmp != NULL) && (tmp->hc() > h->hc())) { if ((tmp->page() == h->page()) && (tmp->layer() > h->layer())) break; tmp = tmp->next(); } // Then order according to layer number while ((tmp != NULL) && (tmp->hc() == h->hc()) && (tmp->layer() < h->layer())) tmp = tmp->next(); if (tmp == NULL) { if (head_ == NULL) head_ = tail_ = h; else append(h, tail_); return; } else if ((tmp == head_) && ((tmp->hc() < h->hc()) || (tmp->layer() > h->layer()))) { insert(h, head_); return; } // Now tmp->hc()<=h->hc(), or tmp->hc()>h->hc() but // tmp->layer()>h->layer(), insert h BEFORE tmp insert(h, tmp); } // Debug only void HitCountList::print() { HitCount *p = (HitCount *)head_; int i = 0; char buf[20]; while (p != NULL) { p->page()->name(buf); fprintf(stderr, "(%s %d %f) ", buf, p->layer(), p->hc()); if (++i % 4 == 0) printf("\n"); p = p->next(); } if (i % 4 != 0) fprintf(stderr, "\n"); } //------------------------------ // Multimedia client page pool //------------------------------ static class MClientPagePoolClass : public TclClass { public: MClientPagePoolClass() : TclClass("PagePool/Client/Media") {} TclObject* create(int, const char*const*) { return (new MClientPagePool()); } } class_mclientpagepool_agent; MClientPagePool::MClientPagePool() : used_size_(0), repl_style_(FINEGRAIN) { bind("max_size_", &max_size_); used_size_ = 0; } int MClientPagePool::command(int argc, const char*const* argv) { if (argc == 3) if (strcmp(argv[1], "set-repl-style") == 0) { // Set replacement style // set-repl-style