mcache.cc

Go to the documentation of this file.
00001 /* -*-  Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
00002 
00003 /*
00004  * mcache.cc
00005  * Copyright (C) 1997 by the University of Southern California
00006  * $Id: mcache.cc,v 1.13 2005/09/18 23:33:35 tomh Exp $
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License,
00010  * version 2, as published by the Free Software Foundation.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License along
00018  * with this program; if not, write to the Free Software Foundation, Inc.,
00019  * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
00020  *
00021  *
00022  * The copyright of this module includes the following
00023  * linking-with-specific-other-licenses addition:
00024  *
00025  * In addition, as a special exception, the copyright holders of
00026  * this module give you permission to combine (via static or
00027  * dynamic linking) this module with free software programs or
00028  * libraries that are released under the GNU LGPL and with code
00029  * included in the standard release of ns-2 under the Apache 2.0
00030  * license or under otherwise-compatible licenses with advertising
00031  * requirements (or modified versions of such code, with unchanged
00032  * license).  You may copy and distribute such a system following the
00033  * terms of the GNU GPL for this module and the licenses of the
00034  * other code concerned, provided that you include the source code of
00035  * that other code when and as the GNU GPL requires distribution of
00036  * source code.
00037  *
00038  * Note that people who make modified versions of this module
00039  * are not obligated to grant this special exception for their
00040  * modified versions; it is their choice whether to do so.  The GNU
00041  * General Public License gives permission to release a modified
00042  * version without this exception; this exception also makes it
00043  * possible to release a modified version which carries forward this
00044  * exception.
00045  *
00046  */
00047 
00048 //
00049 // Multimedia cache implementation
00050 //
00051 // $Header: /nfs/jade/vint/CVSROOT/ns-2/webcache/mcache.cc,v 1.13 2005/09/18 23:33:35 tomh Exp $
00052 
00053 #include <assert.h>
00054 #include <stdio.h>
00055 
00056 #include "rap/media-app.h"
00057 #include "mcache.h"
00058 
00059 
00060 MediaPage::MediaPage(const char *n, int s, double mt, double et, 
00061              double a, int l) :
00062     ClientPage(n, s, mt, et, a), num_layer_(l), locked_(0), realsize_(0)
00063 {
00064     for (int i = 0; i < num_layer_; i++) {
00065         hc_[i] = new HitCount(this, i);
00066         flags_[i] = 0;
00067     }
00068 }
00069 
00070 MediaPage::~MediaPage()
00071 {
00072     int i; 
00073     for (i = 0; i < num_layer_; i++) {
00074         // Delete hit count list
00075         // These hit count records should have already been removed
00076         // from the cache's hit count list. 
00077         assert((hc_[i]->prev() == NULL) && (hc_[i]->next() == NULL));
00078         delete hc_[i];
00079         // Delete media segment list
00080         layer_[i].destroy();
00081     }
00082 }
00083 
00084 void MediaPage::print_info(char *buf) 
00085 {
00086     ClientPage::print_info(buf);
00087     buf += strlen(buf);
00088     sprintf(buf, " pgtype MEDIA layer %d", num_layer_);
00089 }
00090 
00091 // Make the page full with stream data
00092 void MediaPage::create()
00093 {
00094     assert((num_layer_ >= 0) && (num_layer_ < MAX_LAYER));
00095     int i, sz = size_ / num_layer_;
00096     for (i = 0; i < num_layer_; i++) {
00097         // Delete whatever that was there. 
00098         layer_[i].destroy();
00099         add_segment(i, MediaSegment(0, sz));
00100         set_complete_layer(i);
00101     }
00102     realsize_ = size_;
00103 }
00104 
00105 void MediaPage::add_segment(int layer, const MediaSegment& s) 
00106 {
00107     assert((layer >= 0) && (layer < MAX_LAYER));
00108     layer_[layer].add(s);
00109     realsize_ += s.datasize();
00110     if (s.is_last())
00111         set_complete_layer(layer);
00112 }
00113 
00114 int MediaPage::is_complete()
00115 {
00116     // Consider a page finished when all NON-EMPTY layers are 
00117     // marked as "completed"
00118     for (int i = 0; i < num_layer_; i++)
00119         if (!is_complete_layer(i) && (layer_[i].length() > 0))
00120             return 0;
00121     return 1;
00122 }
00123 
00124 void MediaPage::set_complete()
00125 {
00126     for (int i = 0; i < num_layer_; i++)
00127         set_complete_layer(i);
00128 }
00129 
00130 // Used for cache replacement
00131 int MediaPage::evict_tail_segment(int layer, int size)
00132 {
00133     if (is_locked() || is_tlocked())
00134         return 0;
00135 
00136     assert((layer >= 0) && (layer < MAX_LAYER));
00137     //#ifdef MCACHE_DEBUG
00138 #if 0
00139     char buf[20];
00140     name(buf);
00141     fprintf(stderr, "Page %s evicted layer %d: ", buf, layer);
00142 #endif
00143     int sz = layer_[layer].evict_tail(size);
00144     realsize_ -= sz;
00145     //#ifdef MCACHE_DEBUG
00146 #if 0
00147     fprintf(stderr, "\n");
00148 #endif
00149     return sz;
00150 }
00151 
00152 
00153 
00154 //----------------------------------------------------------------------
00155 // Classes related to a multimedia client page pool
00156 //
00157 // HitCountList and MClientPagePool
00158 //----------------------------------------------------------------------
00159 
00160 void HitCountList::update(HitCount *h)
00161 {
00162     HitCount *tmp = h->prev();
00163     if ((tmp != NULL) && (tmp->hc() < h->hc())) {
00164         // Hit count increased, need to move this one up
00165         detach(h);
00166         while ((tmp != NULL) && (tmp->hc() < h->hc())) {
00167             if ((tmp->page() == h->page()) &&
00168                 (tmp->layer() < h->layer()))
00169                 // XXX Don't violate layer encoding within the
00170                 // same page!
00171                 break;
00172             tmp = tmp->prev();
00173         }
00174         if (tmp == NULL) 
00175             // Insert it at the head
00176             insert(h, head_);
00177         else 
00178             append(h, tmp);
00179     } else if ((h->next() != NULL) && (h->hc() < h->next()->hc())) {
00180         // Hit count decreased, need to move this one down
00181         tmp = h->next();
00182         detach(h);
00183         while ((tmp != NULL) && (h->hc() < tmp->hc())) {
00184             if ((h->page() == tmp->page()) && 
00185                 (h->layer() < tmp->layer()))
00186                 // XXX Don't violate layer encoding within 
00187                 // the same page!
00188                 break;
00189             tmp = tmp->next();
00190         }
00191         if (tmp == NULL)
00192             // At the tail
00193             append(h, tail_);
00194         else
00195             insert(h, tmp);
00196     }
00197     // We may end up with two cases here:
00198     //
00199     // (1) tmp->hc()>h->hc() && tmp->layer()<h->layer(). This is
00200     // the normal case, where both hit count ordering and layer 
00201     // ordering are preserved;
00202     //
00203     // (2) tmp->hc()>h->hc() && tmp->layer()>h->layer(). In this
00204     // case, we should move h BEFORE tmp so that the layer 
00205     // ordering is not violated. We basically order the list using 
00206     // layer number as primary key, and use hit count as secondary
00207     // key. 
00208     // Note that the hit count ordering is only violated when more packets 
00209     // in layer i are dropped than those in layer i+1.
00210 }
00211 
00212 // Check the integrity of the resulting hit count list
00213 void HitCountList::check_integrity()
00214 {
00215     HitCount *p = (HitCount*)head_, *q;
00216     while (p != NULL) {
00217         q = p->next();
00218         while (q != NULL) {
00219             // Check layer ordering 
00220             if ((p->page() == q->page()) && 
00221                 (p->layer() > q->layer())) {
00222                 fprintf(stderr, "Wrong hit count list.\n");
00223                 abort();
00224             }
00225             q = q->next();
00226         }
00227         p = p->next();
00228     }
00229 }
00230 
00231 void HitCountList::add(HitCount *h)
00232 {
00233     HitCount *tmp = (HitCount*)head_;
00234 
00235     // XXX First, ensure that the layer ordering within the same page
00236     // is not violated!!
00237     while ((tmp != NULL) && (tmp->hc() > h->hc())) {
00238         if ((tmp->page() == h->page()) && (tmp->layer() > h->layer()))
00239             break;
00240         tmp = tmp->next();
00241     }
00242     // Then order according to layer number
00243     while ((tmp != NULL) && (tmp->hc() == h->hc()) && 
00244            (tmp->layer() < h->layer()))
00245         tmp = tmp->next();
00246 
00247     if (tmp == NULL) {
00248         if (head_ == NULL) 
00249             head_ = tail_ = h;
00250         else
00251             append(h, tail_);
00252         return;
00253     } else if ((tmp == head_) && 
00254            ((tmp->hc() < h->hc()) || (tmp->layer() > h->layer()))) {
00255         insert(h, head_);
00256         return;
00257     }
00258 
00259     // Now tmp->hc()<=h->hc(), or tmp->hc()>h->hc() but 
00260     // tmp->layer()>h->layer(), insert h BEFORE tmp
00261     insert(h, tmp);
00262 }
00263 
00264 // Debug only
00265 void HitCountList::print() 
00266 {
00267     HitCount *p = (HitCount *)head_;
00268     int i = 0;
00269     char buf[20];
00270     while (p != NULL) {
00271         p->page()->name(buf);
00272             fprintf(stderr, "(%s %d %f) ", buf, p->layer(), p->hc());
00273         if (++i % 4 == 0)
00274             printf("\n");
00275         p = p->next();
00276     }
00277     if (i % 4 != 0)
00278         fprintf(stderr, "\n");
00279 }
00280 
00281 //------------------------------
00282 // Multimedia client page pool
00283 //------------------------------
00284 static class MClientPagePoolClass : public TclClass {
00285 public:
00286         MClientPagePoolClass() : TclClass("PagePool/Client/Media") {}
00287         TclObject* create(int, const char*const*) {
00288         return (new MClientPagePool());
00289     }
00290 } class_mclientpagepool_agent;
00291 
00292 MClientPagePool::MClientPagePool() : 
00293     used_size_(0), repl_style_(FINEGRAIN)
00294 {
00295     bind("max_size_", &max_size_);
00296     used_size_ = 0;
00297 }
00298 
00299 int MClientPagePool::command(int argc, const char*const* argv)
00300 {
00301     if (argc == 3) 
00302         if (strcmp(argv[1], "set-repl-style") == 0) {
00303             // Set replacement style
00304             // <obj> set-repl-style <style>
00305             if (strcmp(argv[2], "FINEGRAIN") == 0) 
00306                 repl_style_ = FINEGRAIN;
00307             else if (strcmp(argv[2], "ATOMIC") == 0)
00308                 repl_style_ = ATOMIC;
00309             else {
00310                 fprintf(stderr, "Unknown style %s", argv[3]);
00311                 return (TCL_ERROR);
00312             }
00313             return (TCL_OK);
00314         }
00315     return ClientPagePool::command(argc, argv);
00316 }
00317 
00318 void MClientPagePool::hc_update(const char *name, int max_layer)
00319 {
00320     MediaPage *pg = (MediaPage*)get_page(name);
00321     assert(pg != NULL);
00322 
00323     int i;
00324     HitCount *h;
00325     // First we update the hit count of each layer of the given page
00326     for (i = 0; i <= max_layer; i++)
00327         pg->hit_layer(i);
00328     // Then we update the position of these hit count records
00329     for (i = 0; i <= max_layer; i++) {
00330         h = pg->get_hit_count(i);
00331         hclist_.update(h);
00332     }
00333 #if 1
00334     hclist_.check_integrity();
00335 #endif
00336 }
00337 
00338 // Add a segment to an object, and adjust hit counts accordingly
00339 // XXX Call cache replacement algorithm if necessary
00340 int MClientPagePool::add_segment(const char* name, int layer, 
00341                  const MediaSegment& s)
00342 {
00343     MediaPage* pg = (MediaPage *)get_page(name);
00344     if (pg == NULL)
00345         return -1;
00346     if (layer >= pg->num_layer()) {
00347         if (s.datasize() == 0)
00348             return 0;
00349         else {
00350             fprintf(stderr, 
00351                 "MClientPagePool: cannot add a new layer.\n");
00352             abort();
00353         }
00354     }
00355 
00356     // Check space availability
00357     if (used_size_ + s.datasize() > max_size_) {
00358         // If atomic replacement is used, page size is deducted in
00359         // remove_page(). If fine-grain is used, evicted size is 
00360         // deducted in repl_finegrain().
00361         cache_replace(pg, s.datasize());
00362         //#ifdef MCACHE_DEBUG
00363 #if 0
00364         fprintf(stderr, 
00365             "Replaced for page %s segment (%d %d) layer %d\n",
00366             name, s.start(), s.end(), layer);
00367 #endif
00368     } 
00369     // Add new page. When we are doing atomic replacement, the size that
00370     // we evicted may be larger than what we add.
00371     used_size_ += s.datasize();
00372 
00373     // If this layer was not 'in' before, add its hit count block
00374     if (pg->layer_size(layer) == 0)
00375         hclist_.add(pg->get_hit_count(layer));
00376 
00377     // Add new segment
00378     pg->add_segment(layer, s);
00379 
00380     return 0;
00381 }
00382 
00383 void MClientPagePool::fill_page(const char* pgname)
00384 {
00385     MediaPage *pg = (MediaPage*)get_page(pgname);
00386     used_size_ -= pg->realsize();
00387     // Lock this page before we do any replacement. 
00388     pg->lock();
00389     pg->create();
00390     // If we cannot hold the nominal size of the page, do replacement
00391     if (used_size_ + pg->size() > max_size_)
00392         // Size deduction has already been done in remove_page()
00393         cache_replace(pg, pg->size());
00394     used_size_ += pg->size();
00395     pg->unlock();
00396 }
00397 
00398 ClientPage* MClientPagePool::enter_page(int argc, const char*const* argv)
00399 {
00400     double mt = -1, et, age = -1, noc = 0;
00401     int size = -1, media_page = 0, layer = -1;
00402     for (int i = 3; i < argc; i+=2) {
00403         if (strcmp(argv[i], "modtime") == 0)
00404             mt = strtod(argv[i+1], NULL);
00405         else if (strcmp(argv[i], "size") == 0) 
00406             size = atoi(argv[i+1]);
00407         else if (strcmp(argv[i], "age") == 0)
00408             age = strtod(argv[i+1], NULL);
00409         else if (strcmp(argv[i], "noc") == 0)
00410             // non-cacheable flag
00411             noc = 1;
00412         else if (strcmp(argv[i], "pgtype") == 0) {
00413             if (strcmp(argv[i+1], "MEDIA") == 0)
00414                 media_page = 1;
00415         } else if (strcmp(argv[i], "layer") == 0)
00416             layer = atoi(argv[i+1]);
00417     }
00418     // XXX allow mod time < 0 and age < 0!
00419     if ((size < 0) || (media_page && (layer <= 0))) {
00420         fprintf(stderr, "%s: wrong page information %s\n",
00421             name_, argv[2]);
00422         return NULL;
00423     }
00424     et = Scheduler::instance().clock();
00425     ClientPage *pg;
00426     if (media_page)
00427         pg = new MediaPage(argv[2], size, mt, et, age, layer);
00428     else 
00429         pg = new ClientPage(argv[2], size, mt, et, age);
00430     if (add_page(pg) < 0) {
00431         delete pg;
00432         return NULL;
00433     }
00434     if (noc) 
00435         pg->set_uncacheable();
00436     if (media_page) 
00437         ((MediaPage *)pg)->lock();
00438     return pg;
00439 }
00440 
00441 int MClientPagePool::cache_replace(ClientPage *pg, int size)
00442 {
00443     switch (repl_style_) {
00444     case FINEGRAIN:
00445         return repl_finegrain(pg, size);
00446     case ATOMIC:
00447 #if 0
00448         char tmp[128];
00449         pg->name(tmp);
00450         fprintf(stderr, "Replaced for page %s size %d\n", tmp, size);
00451         fprintf(stderr, "Used size %d, max size %d\n", used_size_, 
00452             max_size_);
00453 #endif
00454         return repl_atomic(pg, size);
00455     default:
00456         fprintf(stderr, "Corrupted replacement style.\n");
00457         abort();
00458     }
00459     // To make msvc happy
00460     return -1;
00461 }
00462 
00463 int MClientPagePool::repl_atomic(ClientPage*, int size)
00464 {
00465     // XXX We use standard LRU to determine the stream to be kicked out.
00466     // The major problem is that we do not keep discrete hit counts. 
00467     // We solve the problem by using hit counts of the base layer as 
00468     // a close approximate. Because whenever a stream is accessed, 
00469     // it's assumed that the client bw can always afford the base layer,
00470     // this should be a fairly good approximation. 
00471 
00472     HitCount *h, *p;
00473     int sz, totalsz = 0;
00474     // Repeatedly get rid of streams until get enough space
00475     h = (HitCount*)hclist_.tail();
00476     while (h != NULL) {
00477         if (h->layer() != 0) {
00478             // We only look for the base layer
00479             h = h->prev();
00480             continue;
00481         }
00482         MediaPage *pg = (MediaPage *)h->page();
00483         // Don't touch locked pages
00484         if (pg->is_tlocked() || pg->is_locked()) {
00485             h = h->prev();
00486             continue;
00487         }
00488         sz = pg->realsize();
00489         totalsz += sz;
00490         char tmp[HTTP_MAXURLLEN];
00491         pg->name(tmp);
00492         // Before we delete, find the previous hit count record that
00493         // does not belong to this page. 
00494         p = h->prev(); 
00495         while ((p != NULL) && (p->page() == h->page()))
00496             p = p->prev();
00497         h = p;
00498         // XXX Manually remove hit count before deleting it
00499         for (int i = 0; i < pg->num_layer(); i++) {
00500             p = pg->get_hit_count(i);
00501             hclist_.detach(p);
00502         }
00503         // Delete the page, together with its media segment list
00504 #if 0
00505         fprintf(stderr, "At time %g, atomic replacement evicted page %s\n", 
00506             Scheduler::instance().clock(), tmp); 
00507         fprintf(stderr, "Hit count list: \n");
00508         hclist_.print();
00509         fprintf(stderr,"----------------------------------------\n\n");
00510 #endif      
00511         remove_page(tmp);
00512         if (sz >= size)
00513             return totalsz;
00514         // Continue to evict to meet the space requirement
00515         size -= sz;
00516     }
00517     fprintf(stderr, "Cache replacement cannot get enough space.\n");
00518     abort();
00519     return 0; // Make msvc happy
00520 }
00521 
00522 int MClientPagePool::repl_finegrain(ClientPage *, int size)
00523 {
00524     // Traverse through hit count table, evict segments from the tail
00525     // of a layer with minimum hit counts
00526     HitCount *h, *p;
00527     int sz, totalsz = 0;
00528 
00529     // Repeatedly evict pages/segments until get enough space
00530     h = (HitCount*)hclist_.tail();
00531     while (h != NULL) {
00532         MediaPage *pg = (MediaPage *)h->page();
00533         // XXX Don't touch locked pages
00534         if (pg->is_tlocked() || pg->is_locked()) {
00535             h = h->prev();
00536             continue;
00537         }
00538         // Try to get "size" space by evicting other segments
00539         sz = pg->evict_tail_segment(h->layer(), size);
00540         // Decrease the cache used space
00541         used_size_ -= sz;
00542         totalsz += sz;
00543         // If we have not got enough space, we must have got rid of 
00544         // the entire layer
00545         assert((sz == size) || 
00546                ((sz < size) && (pg->layer_size(h->layer()) == 0)));
00547 
00548         // If we don't have anything of this layer left, get rid of 
00549         // the hit count record. 
00550         // XXX Must do this BEFORE removing the page
00551         p = h;
00552         h = h->prev();
00553         if (pg->layer_size(p->layer()) == 0) {
00554             // XXX Should NEVER delete a hit count record!!
00555             // A hit count record is ONLY deleted when the page
00556             // is deleted (evicted from cache: ~MediaPage())
00557             hclist_.detach(p);
00558             p->reset();
00559         }
00560         // Furthermore, if the page has nothing left, get rid of it
00561         if (pg->realsize() == 0) {
00562             // NOTE: we do not manually remove hit counts of 
00563             // this page because if its realsize is 0, all 
00564             // hit count records must have already been 
00565             // detached from the page. 
00566             char tmp[HTTP_MAXURLLEN];
00567             pg->name(tmp);
00568 #if 0
00569             fprintf(stderr, "At time %g, fine-grain evicted page %s\n",
00570                 Scheduler::instance().clock(), tmp);
00571             fprintf(stderr, "Hit count list: \n");
00572             hclist_.print();
00573             fprintf(stderr,
00574                 "---------------------------------------\n\n");
00575 #endif
00576             // Then the hit count record will be deleted in here
00577             remove_page(tmp);
00578         }
00579         // If we've got enough space, return; otherwise continue
00580         if (sz >= size)
00581             return totalsz;
00582         size -= sz; // Evict to fill the rest
00583     }
00584     fprintf(stderr, "Cache replacement cannot get enough space.\n");
00585     abort();
00586     return 0; // Make msvc happy
00587 }
00588 
00589 // Clean all hit count record of a page regardless of whether it's in the 
00590 // hit count list. Used when hclist_ is not used at all, e.g., by MediaClient.
00591 int MClientPagePool::force_remove(const char *name)
00592 {
00593     // XXX Bad hack. Needs to integrate this into ClientPagePool.
00594     ClientPage *pg = (ClientPage*)get_page(name);
00595     // We should not remove a non-existent page!!
00596     assert(pg != NULL);
00597     if (pg->type() == MEDIA) {
00598         HitCount *p;
00599         MediaPage *q = (MediaPage*)pg;
00600         used_size_ -= q->realsize();
00601         for (int i = 0; i < q->num_layer(); i++) {
00602             p = q->get_hit_count(i);
00603             hclist_.detach(p);
00604         }
00605     } else if (pg->type() == HTML)
00606         used_size_ -= pg->size();
00607     return ClientPagePool::remove_page(name);
00608 }
00609 
00610 int MClientPagePool::remove_page(const char *name)
00611 {
00612     // XXX Bad hack. Needs to integrate this into ClientPagePool.
00613     ClientPage *pg = (ClientPage*)get_page(name);
00614     // We should not remove a non-existent page!!
00615     assert(pg != NULL);
00616     if (pg->type() == MEDIA)
00617         used_size_ -= ((MediaPage *)pg)->realsize();
00618     else if (pg->type() == HTML)
00619         used_size_ -= pg->size();
00620     return ClientPagePool::remove_page(name);
00621 }
00622 
00623 
00624 
00625 //------------------------------------------------------------
00626 // MediaPagePool
00627 // Generate requests and pages for clients and servers 
00628 //------------------------------------------------------------
00629 static class MediaPagePoolClass : public TclClass {
00630 public:
00631         MediaPagePoolClass() : TclClass("PagePool/Media") {}
00632         TclObject* create(int, const char*const*) {
00633         return (new MediaPagePool());
00634     }
00635 } class_mediapagepool_agent;
00636 
00637 MediaPagePool::MediaPagePool() : PagePool()
00638 {
00639     size_ = NULL;
00640     duration_ = 0;
00641     layer_ = 1;
00642 }
00643 
00644 // For now, only one page, fixed size, fixed layer
00645 int MediaPagePool::command(int argc, const char*const* argv)
00646 {
00647     Tcl& tcl = Tcl::instance();
00648 
00649     if (argc == 2) {
00650         if (strcmp(argv[1], "get-poolsize") == 0) { 
00651             tcl.resultf("%d", num_pages_);
00652             return TCL_OK;
00653         } else if (strcmp(argv[1], "get-start-time") == 0) {
00654             tcl.resultf("%.17g", start_time_);
00655             return TCL_OK;
00656         } else if (strcmp(argv[1], "get-duration") == 0) {
00657             tcl.resultf("%d", duration_);
00658             return TCL_OK;
00659         }
00660     } else if (argc == 3) {
00661         if (strcmp(argv[1], "gen-pageid") == 0) {
00662             // Generating requested page id
00663             if (rvReq_ == NULL) {
00664                 tcl.add_errorf("no page id ranvar.");
00665                 return TCL_ERROR;
00666             }
00667             int p = (int)rvReq_->value();
00668             assert((p >= 0) && (p < num_pages_));
00669             tcl.resultf("%d", p);
00670             return TCL_OK;
00671         } else if (strcmp(argv[1], "is-media-page") == 0) {
00672             // XXX Currently all pages are media pages. Should
00673             // be able to allow both normal pages and media pages
00674             // in the future
00675             tcl.result("1");
00676             return TCL_OK;
00677         } else if (strcmp(argv[1], "get-layer") == 0) {
00678             // XXX Currently all pages have the same number of 
00679             // layers. Should be able to change this in future.
00680             tcl.resultf("%d", layer_); 
00681             return TCL_OK;
00682         } else if (strcmp(argv[1], "set-start-time") == 0) {
00683             double st = strtod(argv[2], NULL);
00684             start_time_ = st;
00685             end_time_ = st + duration_;
00686             return TCL_OK;
00687         } else if (strcmp(argv[1], "set-duration") == 0) {
00688             // XXX Need this info to set page mod time!!
00689             duration_ = atoi(argv[2]);
00690             end_time_ = start_time_ + duration_;
00691             return TCL_OK;
00692         } else if (strcmp(argv[1], "gen-init-modtime") == 0) {
00693             // XXX We are not interested in page consistency here,
00694             // so never change this page.
00695             tcl.resultf("%d", -1);
00696             return TCL_OK;
00697         } else if (strcmp(argv[1], "gen-size") == 0) {
00698             int pagenum = atoi(argv[2]);
00699             if (pagenum >= num_pages_) {
00700                 tcl.add_errorf("Invalid page id %d", pagenum);
00701                 return TCL_ERROR;
00702             }
00703             tcl.resultf("%d", size_[pagenum]);
00704             return TCL_OK;
00705         } else if (strcmp(argv[1], "set-layer") == 0) {
00706             layer_ = atoi(argv[2]);
00707             return TCL_OK;
00708         } else if (strcmp(argv[1], "set-num-pages") == 0) {
00709             if (size_ != NULL) {
00710                 tcl.add_errorf("can't change number of pages");
00711                 return TCL_ERROR;
00712             }
00713             num_pages_ = atoi(argv[2]);
00714             size_ = new int[num_pages_];
00715             return TCL_OK;
00716         } else if (strcmp(argv[1], "ranvar-req") == 0) {
00717             rvReq_ = (RandomVariable*)TclObject::lookup(argv[2]);
00718             return TCL_OK;
00719         }
00720     } else if (argc == 4) {
00721         if (strcmp(argv[1], "gen-modtime") == 0) {
00722             // This should never be called, because we never
00723             // deals with page modifications!!
00724             fprintf(stderr, "%s: gen-modtime called!\n", name());
00725             abort();
00726         } else if (strcmp(argv[1], "set-pagesize") == 0) {
00727             // <pagepool> set-pagesize <pagenum> <size>
00728             int pagenum = atoi(argv[2]);
00729             if (pagenum >= num_pages_) {
00730                 tcl.add_errorf("Invalid page id %d", pagenum);
00731                 return TCL_ERROR;
00732             }
00733             size_[pagenum] = atoi(argv[3]);
00734             return TCL_OK;
00735         }
00736     }
00737     return PagePool::command(argc, argv);
00738 }
00739 
00740 
00741 
00742 //----------------------------------------------------------------------
00743 // PagePool that generates requests using the SURGE model
00744 //
00745 // Part of the code by Paul Barford (barford@cs.bu.edu).
00746 // Copyright (c) 1997 Trustees of Boston University
00747 //
00748 // Allow two options: (1) setting if all pages are media page or normal
00749 // HTTP pages; (2) average page size
00750 //----------------------------------------------------------------------
00751 //  static class SurgePagePoolClass : public TclClass {
00752 //  public:
00753 //          SurgePagePoolClass() : TclClass("PagePool/Surge") {}
00754 //          TclObject* create(int, const char*const*) {
00755 //          return (new SurgePagePool());
00756 //      }
00757 //  } class_surgepagepool_agent;
00758 
00759 //  SurgePagePool::SurgePagePool() : PagePool()
00760 //  {
00761 //  }
00762 
00763 
00764 
00765 //----------------------------------------------------------------------
00766 // Multimedia web applications: cache, etc.
00767 //----------------------------------------------------------------------
00768 
00769 static class MediaCacheClass : public TclClass {
00770 public:
00771     MediaCacheClass() : TclClass("Http/Cache/Media") {}
00772     TclObject* create(int, const char*const*) {
00773         return (new MediaCache());
00774     }
00775 } class_mediacache;
00776 
00777 // By default we use online prefetching
00778 MediaCache::MediaCache() : pref_style_(ONLINE_PREF)
00779 {
00780     cmap_ = new Tcl_HashTable;
00781     Tcl_InitHashTable(cmap_, TCL_ONE_WORD_KEYS);
00782 }
00783 
00784 MediaCache::~MediaCache()
00785 {
00786     Tcl_HashEntry *he;
00787     Tcl_HashSearch hs;
00788     if (cmap_) {
00789         for (he = Tcl_FirstHashEntry(cmap_, &hs);  he != NULL;
00790              he = Tcl_NextHashEntry(&hs))
00791             delete (RegInfo*)Tcl_GetHashValue(he);
00792         Tcl_DeleteHashTable(cmap_);
00793         delete cmap_;
00794     }
00795 }
00796 
00797 AppData* MediaCache::get_data(int& size, AppData* req)
00798 {
00799     assert(req != NULL);
00800     if (req->type() != MEDIA_REQUEST) {
00801         return HttpApp::get_data(size, req);
00802     }
00803 
00804     MediaRequest *r = (MediaRequest *)req;
00805 
00806     // Get statistics block for the requestor
00807     Tcl_HashEntry *he = 
00808         Tcl_FindHashEntry(cmap_, (const char *)(r->app()));
00809     assert(he != NULL);
00810     RegInfo *ri = (RegInfo *)Tcl_GetHashValue(he);
00811 
00812     // Process request
00813     if (r->request() == MEDIAREQ_GETSEG) {
00814         // Get a new data segment
00815         MediaPage* pg = (MediaPage*)pool_->get_page(r->name());
00816         assert(pg != NULL);
00817         MediaSegment s1(r->st(), r->et());
00818         MediaSegment s2 = pg->next_overlap(r->layer(), s1);
00819         HttpMediaData *p;
00820         if (s2.datasize() == 0) {
00821             // No more data available for this layer, allocate
00822             // an ADU with data size 0 to signal the end
00823             // of transmission for this layer
00824             size = 0;
00825             p = new HttpMediaData(name(), r->name(),
00826                           r->layer(), 0, 0);
00827         } else {
00828             size = s2.datasize();
00829             p = new HttpMediaData(name(), r->name(),
00830                        r->layer(), s2.start(), s2.end());
00831         }
00832         // XXX If we are still receiving the stream, don't 
00833         // ever say that this is the last segment. If the 
00834         // page is not locked, it's still possible that we
00835         // return a NULL segment because the requested one
00836         // is not available. Don't set the 'LAST' flag in this 
00837         // case.
00838         if (s2.is_last()) {
00839             p->set_last();
00840             if (!pg->is_locked() && (s2.datasize() == 0) && 
00841                 (r->layer() == 0)) 
00842                 p->set_finish();
00843         }
00844 
00845         //----------------------------------------
00846         // Update statistics of this connection
00847         //----------------------------------------
00848         // Update the highest layer that this client has requested
00849         if (ri->hl_ < r->layer())
00850             ri->hl_ = r->layer();
00851         if (size > 0) {
00852             // Update total delivered bytes
00853             ri->db_[r->layer()] += size;
00854             // Update prefetched bytes that've been delivered
00855             ri->eb_[r->layer()] += ri->pref_size(r->layer(), s2);
00856         }
00857         return p;
00858     } else if (r->request() == MEDIAREQ_CHECKSEG) {
00859         // If we are not doing online prefetching, return nothing
00860         if (pref_style_ != ONLINE_PREF)
00861             return NULL;
00862         // Check the availability of a new data segment
00863         // And refetch if it is not available
00864         MediaPage* pg = (MediaPage*)pool_->get_page(r->name());
00865         assert(pg != NULL);
00866         if (pg->is_locked()) 
00867             // If we are during the first retrieval, don't prefetch
00868             return NULL;
00869         MediaSegmentList ul = pg->is_available(r->layer(),
00870                       MediaSegment(r->st(),r->et()));
00871         if (ul.length() == 0)
00872             // All segments are available
00873             return NULL;
00874         // Otherwise do prefetching on these "holes"
00875         char *buf = ul.dump2buf();
00876         Tcl::instance().evalf("%s pref-segment %s %s %d %s", name(), 
00877                       r->app()->name(), r->name(), 
00878                       r->layer(), buf);
00879 //          log("E PREF p %s l %d %s\n", r->name(), r->layer(), buf);
00880         delete []buf;
00881         ul.destroy();
00882 
00883         // Update the highest layer that this client has requested
00884         Tcl_HashEntry *he = 
00885             Tcl_FindHashEntry(cmap_, (const char *)(r->app()));
00886         assert(he != NULL);
00887         RegInfo *ri = (RegInfo *)Tcl_GetHashValue(he);
00888         if (ri->hl_ < r->layer())
00889             ri->hl_ = r->layer();
00890         return NULL;
00891     }
00892         
00893     fprintf(stderr, 
00894         "MediaCache %s gets an unknown MediaRequest type %d\n",
00895         name(), r->request());
00896     abort();
00897     return NULL; // Make msvc happy
00898 }
00899 
00900 // Add received media segment into page pool
00901 void MediaCache::process_data(int size, AppData* data) 
00902 {
00903     switch (data->type()) {
00904     case MEDIA_DATA: {
00905         HttpMediaData* d = (HttpMediaData*)data;
00906         // Cache this segment, do replacement if necessary
00907         if (mpool()->add_segment(d->page(), d->layer(), 
00908                      MediaSegment(*d)) == -1) {
00909             fprintf(stderr, "MediaCache %s gets a segment for an "
00910                 "unknown page %s\n", name(), d->page());
00911             abort();
00912         }
00913         if (d->is_pref()) {
00914             // Update total prefetched bytes
00915             Tcl_HashEntry *he = Tcl_FindHashEntry(cmap_, 
00916                         (const char*)(d->conid()));
00917             // Client-cache-server disconnection procedure:
00918             // (1) client disconnects from cache, then
00919             // (2) cache disconnects from server and shuts down 
00920             //     prefetching channel. 
00921             // Therefore, after client disconnects, the cache 
00922             // may still receive a few prefetched segments. 
00923             // Ignore those because we no longer keep statistics
00924             // about the torn-down connection.
00925             if (he != NULL) {
00926                 RegInfo *ri = (RegInfo *)Tcl_GetHashValue(he);
00927                 ri->add_pref(d->layer(), MediaSegment(*d));
00928                 ri->pb_[d->layer()] += d->datasize();
00929             }
00930         }
00931         // XXX debugging only
00932 #if 1
00933         log("E RSEG p %s l %d s %d e %d z %d f %d\n", 
00934             d->page(), d->layer(), d->st(), d->et(), d->datasize(),
00935             d->is_pref());
00936 #endif
00937         break;
00938     } 
00939     default:
00940         HttpCache::process_data(size, data);
00941     }
00942 }
00943 
00944 int MediaCache::command(int argc, const char*const* argv) 
00945 {
00946     Tcl& tcl = Tcl::instance();
00947     if (argc == 2) {
00948         if (strcmp(argv[1], "get-pref-style") == 0) {
00949             switch (pref_style_) {
00950             case NOPREF:
00951                 tcl.result("NOPREF");
00952                 break;
00953             case ONLINE_PREF:
00954                 tcl.result("ONLINE_PREF");
00955                 break;
00956             case OFFLINE_PREF:
00957                 tcl.result("OFFLINE_PREF");
00958                 break;
00959             default:
00960                 fprintf(stderr, 
00961                     "Corrupted prefetching style %d", 
00962                     pref_style_);
00963                 return TCL_ERROR;
00964             }
00965             return TCL_OK;
00966         }
00967     } else if (argc == 3) {
00968         if (strcmp(argv[1], "offline-complete") == 0) {
00969             // Delete whatever segments in the given page, 
00970             // make it complete. Used by offline prefetching
00971             ClientPage *pg = mpool()->get_page(argv[2]);
00972             if (pg == NULL)
00973                 // XXX It's possible that we've already kicked
00974                 // it out of the cache. Do nothing.
00975                 return TCL_OK;
00976             assert(pg->type() == MEDIA);
00977             assert(!((MediaPage*)pg)->is_locked());
00978             mpool()->fill_page(argv[2]);
00979             return TCL_OK;
00980         } else if (strcmp(argv[1], "set-pref-style") == 0) {
00981             // Set prefetching style
00982             // <obj> set-pref-style <style>
00983             //
00984             // style can be: NOPREF, ONLINE_PREF, OFFLINE_PREF
00985             if (strcmp(argv[2], "NOPREF") == 0) 
00986                 pref_style_ = NOPREF;
00987             else if (strcmp(argv[2], "ONLINE_PREF") == 0) 
00988                 pref_style_ = ONLINE_PREF;
00989             else if (strcmp(argv[2], "OFFLINE_PREF") == 0) 
00990                 pref_style_ = OFFLINE_PREF;
00991             else {
00992                 fprintf(stderr, "Wrong prefetching style %s",
00993                     argv[2]);
00994                 return TCL_ERROR;
00995             }
00996             return TCL_OK;
00997         } else if (strcmp(argv[1], "dump-page") == 0) {
00998             // Dump segments of a given page
00999             ClientPage *p=(ClientPage*)mpool()->get_page(argv[2]);
01000             if (p->type() != MEDIA)
01001                 // Do nothing for non-media pages
01002                 return TCL_OK;
01003             MediaPage *pg = (MediaPage *)p;
01004             char *buf;
01005             for (int i = 0; i < pg->num_layer(); i++) {
01006                 buf = pg->print_layer(i);
01007                 if (strlen(buf) > 0)
01008                     log("E SEGS p %s l %d %s\n", argv[2], 
01009                         i, buf);
01010                 delete []buf;
01011             }
01012             return TCL_OK;
01013         } else if (strcmp(argv[1], "stream-received") == 0) {
01014             // We've got the entire page, unlock it
01015             MediaPage *pg = (MediaPage*)mpool()->get_page(argv[2]);
01016             assert(pg != NULL);
01017             pg->unlock();
01018             // XXX Should we clear all "last" flag of segments??
01019 #ifdef MCACHE_DEBUG
01020             // Printing out current buffer status of the page
01021             char *buf;
01022             for (int i = 0; i < pg->num_layer(); i++) {
01023                 buf = pg->print_layer(i);
01024                 log("E SEGS p %s l %d %s\n", argv[2], i, buf);
01025                 delete []buf;
01026             }
01027 #endif
01028             // Show cache free size
01029             log("E SIZ n %d z %d t %d\n", mpool()->num_pages(),
01030                 mpool()->usedsize(), mpool()->maxsize());
01031             return TCL_OK;
01032         }
01033     } else if (argc == 5) {
01034         if (strcmp(argv[1], "register-client") == 0) {
01035             // <server> register-client <app> <client> <pageid>
01036             TclObject *a = TclObject::lookup(argv[2]);
01037             assert(a != NULL);
01038             int newEntry;
01039             Tcl_HashEntry *he = Tcl_CreateHashEntry(cmap_, 
01040                     (const char *)a, &newEntry);
01041             if (he == NULL) {
01042                 tcl.add_errorf("cannot create hash entry");
01043                 return TCL_ERROR;
01044             }
01045             if (!newEntry) {
01046                 tcl.add_errorf("duplicate connection");
01047                 return TCL_ERROR;
01048             }
01049             RegInfo *p = new RegInfo;
01050             p->client_ = (HttpApp*)TclObject::lookup(argv[3]);
01051             assert(p->client_ != NULL);
01052             strcpy(p->name_, argv[4]);
01053             Tcl_SetHashValue(he, (ClientData)p);
01054 
01055             // Lock the page while transmitting it to a client
01056             MediaPage *pg = (MediaPage*)mpool()->get_page(argv[4]);
01057             assert((pg != NULL) && (pg->type() == MEDIA));
01058             pg->tlock();
01059 
01060             return TCL_OK;
01061         } else if (strcmp(argv[1], "unregister-client") == 0) {
01062             // <server> unregister-client <app> <client> <pageid>
01063             TclObject *a = TclObject::lookup(argv[2]);
01064             assert(a != NULL);
01065             Tcl_HashEntry *he = 
01066                 Tcl_FindHashEntry(cmap_, (const char*)a);
01067             if (he == NULL) {
01068                 tcl.add_errorf("cannot find hash entry");
01069                 return TCL_ERROR;
01070             }
01071             RegInfo *ri = (RegInfo*)Tcl_GetHashValue(he);
01072             // Update hit count
01073             mpool()->hc_update(argv[4], ri->hl_);
01074 #ifdef MCACHE_DEBUG
01075             printf("Cache %d hit counts: \n", id_);
01076             mpool()->dump_hclist();
01077 #endif
01078             // Dump per-connection statistics
01079             for (int i = 0; i <= ri->hl_; i++)
01080                 log("E STAT p %s l %d d %d e %d p %d\n",
01081                     ri->name_, i, ri->db_[i], ri->eb_[i], 
01082                     ri->pb_[i]);
01083             delete ri;
01084             Tcl_DeleteHashEntry(he);
01085 
01086             // Lock the page while transmitting it to a client
01087             MediaPage *pg = (MediaPage*)mpool()->get_page(argv[4]);
01088             assert((pg != NULL) && (pg->type() == MEDIA));
01089             pg->tunlock();
01090 
01091             return TCL_OK;
01092         }
01093     }
01094 
01095     return HttpCache::command(argc, argv);
01096 }
01097 
01098 
01099 
01100 //----------------------------------------------------------------------
01101 // Media web client 
01102 //   Use C++ interface to records quality of received stream.
01103 // NOTE: 
01104 //   It has OTcl inheritance, but no C++ inheritance!
01105 //----------------------------------------------------------------------
01106 
01107 static class HttpMediaClientClass : public TclClass {
01108 public:
01109     HttpMediaClientClass() : TclClass("Http/Client/Media") {}
01110     TclObject* create(int, const char*const*) {
01111         return (new MediaClient());
01112     }
01113 } class_httpmediaclient;
01114 
01115 // Records the quality of stream received
01116 void MediaClient::process_data(int size, AppData* data)
01117 {
01118     assert(data != NULL);
01119 
01120     switch (data->type()) {
01121     case MEDIA_DATA: {
01122         HttpMediaData* d = (HttpMediaData*)data;
01123         // XXX Don't pass any data to page pool!!
01124         if (mpool()->add_segment(d->page(), d->layer(), 
01125                      MediaSegment(*d)) == -1) {
01126             fprintf(stderr, 
01127   "MediaCache %s gets a segment for an unknown page %s\n", name(), d->page());
01128 //              abort();
01129         }
01130         // Note: we store the page only to produce some statistics
01131         // later so that we need not do postprocessing of traces.
01132 #if 1
01133             log("C RSEG p %s l %d s %d e %d z %d\n", 
01134                 d->page(), d->layer(), d->st(), d->et(), d->datasize());
01135 #endif
01136         break;
01137     }
01138     default:
01139         HttpClient::process_data(size, data);
01140     }
01141 }
01142 
01143 int MediaClient::command(int argc, const char*const* argv)
01144 {
01145     if (argc == 3) {
01146         if (strcmp(argv[1], "stream-received") == 0) {
01147             // XXX This is the place to do statistics collection
01148             // about quality of received stream.
01149             // 
01150             // Dump delivered quality log
01151             MediaPage *pg = (MediaPage*)mpool()->get_page(argv[2]);
01152             assert(pg != NULL);
01153             // Printing out current buffer status of the page
01154             char *buf;
01155             for (int i = 0; i < pg->num_layer(); i++) {
01156                 buf = pg->print_layer(i);
01157                 if (strlen(buf) > 0) 
01158                     log("C SEGS p %s l %d %s\n", 
01159                         argv[2], i, buf);
01160                 delete []buf;
01161             }
01162             // then delete the stream from buffer
01163             mpool()->force_remove(argv[2]);
01164             return TCL_OK;
01165         }
01166     }
01167     return HttpClient::command(argc, argv);
01168 }
01169 
01170 
01171 
01172 //----------------------------------------------------------------------
01173 // Multimedia web server
01174 //----------------------------------------------------------------------
01175 
01176 static class MediaServerClass : public TclClass {
01177 public:
01178     MediaServerClass() : TclClass("Http/Server/Media") {}
01179     TclObject* create(int, const char*const*) {
01180         return (new MediaServer());
01181     }
01182 } class_mediaserver;
01183 
01184 MediaServer::MediaServer() : HttpServer() 
01185 {
01186     long keySizeInBytes = sizeof (PageID);
01187     long keySizeInSizeOfInt;
01188     if ((keySizeInBytes % sizeof (int)) == 0) {
01189         keySizeInSizeOfInt = keySizeInBytes / sizeof (int);
01190     } else {
01191         keySizeInSizeOfInt = keySizeInBytes / sizeof (int) + 1;
01192     }
01193     pref_ = new Tcl_HashTable;
01194     Tcl_InitHashTable(pref_, keySizeInSizeOfInt);
01195     cmap_ = new Tcl_HashTable;
01196     Tcl_InitHashTable(cmap_, TCL_ONE_WORD_KEYS);
01197 }
01198 
01199 MediaServer::~MediaServer() 
01200 {
01201     Tcl_HashEntry *he;
01202     Tcl_HashSearch hs;
01203     if (pref_ != NULL) {
01204         for (he = Tcl_FirstHashEntry(pref_, &hs);  he != NULL;
01205              he = Tcl_NextHashEntry(&hs)) {
01206             PrefInfo *pi = (PrefInfo*)Tcl_GetHashValue(he);
01207             pi->sl_->destroy();
01208             delete pi->sl_;
01209         }
01210         Tcl_DeleteHashTable(pref_);
01211         delete pref_;
01212     }
01213     if (cmap_ != NULL) {
01214         for (he = Tcl_FirstHashEntry(cmap_, &hs);  he != NULL;
01215              he = Tcl_NextHashEntry(&hs))
01216             delete (RegInfo*)Tcl_GetHashValue(he);
01217         Tcl_DeleteHashTable(cmap_);
01218         delete cmap_;
01219     }
01220 }
01221 
01222 // Return the next segment to be sent to a particular application
01223 MediaSegment MediaServer::get_next_segment(MediaRequest *r, Application*& ci)
01224 {
01225     MediaPage* pg = (MediaPage*)pool_->get_page(r->name());
01226     assert(pg != NULL);
01227 
01228     // XXX Extremely hacky way to map media app names to 
01229     // HTTP connections. Should maintain another hash table for this.
01230     RegInfo *ri = get_reginfo(r->app());
01231     assert(ri != NULL);
01232     PrefInfoQ* q = get_piq(r->name(), ri->client_);
01233 
01234     // We are not on the prefetching list, send a normal data segment
01235     if ((q == NULL) || (q->is_empty())) {
01236         MediaSegment s1(r->st(), r->et());
01237         return pg->next_overlap(r->layer(), s1);
01238     }
01239 
01240     // Cycle through the prefetched segments that we need to send
01241     int found = 0;
01242     int searched = 0;
01243     PrefInfo *pi; 
01244     while (!found) {
01245         PrefInfoE *pe = q->dequeue();
01246         pi = pe->data();
01247         q->enqueue(pe);
01248         // If there's a pending segment in any layer, send it
01249         for (int i = 0; i < pg->num_layer(); i++) 
01250             if (pi->sl_[i].length() > 0) 
01251                 found = 1;
01252         // If no pending prefetched segments, return empty
01253         if (searched++ == q->size()) 
01254             return MediaSegment(0, 0);
01255     }
01256 
01257     // Send a segment from the prefetching list. Only use the data size
01258     // included in the request.
01259     MediaSegmentList *p = pi->sl_;
01260     // Set return conid
01261     ci = pi->conid_;
01262 
01263     // Find one available segment in prefetching list if there is none
01264     // in the given layer
01265     int l = r->layer(), i = 0;
01266     MediaSegment res;
01267     while ((res.datasize() == 0) && (i < pg->num_layer())) {
01268         // next() doesn't work. Need a method that returns the 
01269         // *FIRST* non-empty segment which satisfies the size 
01270         // constraint.
01271         res = p[l].get_nextseg(MediaSegment(0, r->datasize()));
01272         i++;
01273         l = (l+1) % pg->num_layer();
01274     }
01275     // XXX We must do boundary check of the prefetched segments to make
01276     // sure that the start and end offsets are valid!
01277     if (res.start() < 0) 
01278         res.set_start(0);
01279     if (res.end() > pg->layer_size(l))
01280         res.set_end(pg->layer_size(l));
01281     if (res.datasize() > 0) {
01282         // XXX We may end up getting data from another layer!!
01283         l = (l-1+pg->num_layer()) % pg->num_layer();
01284         if (l != r->layer())
01285             r->set_layer(l);
01286         // We may not be able to get the specified data size, due 
01287         // to arbitrary stream lengths
01288         //assert(res.datasize() == r->datasize());
01289         p[r->layer()].evict_head(r->datasize());
01290     }
01291     // Set the prefetching flag of this segment
01292     res.set_pref();
01293     return res;
01294 }
01295 
01296 // Similar to MediaCache::get_data(), but ignore segment availability checking
01297 AppData* MediaServer::get_data(int& size, AppData *req)
01298 {
01299     assert((req != NULL) && (req->type() == MEDIA_REQUEST));
01300     MediaRequest *r = (MediaRequest *)req;
01301     Application* conid = NULL;
01302 
01303     if (r->request() == MEDIAREQ_GETSEG) {
01304         // Get a new data segment
01305         MediaSegment s2 = get_next_segment(r, conid);
01306         HttpMediaData *p;
01307         if (s2.datasize() == 0) {
01308             // No more data available for this layer, most likely
01309             // it's because this layer is finished.
01310             size = 0;
01311             p = new HttpMediaData(name(), r->name(),
01312                           r->layer(), 0, 0);
01313         } else {
01314             size = s2.datasize();
01315             p = new HttpMediaData(name(), r->name(),
01316                        r->layer(), s2.start(), s2.end());
01317         }
01318         if (s2.is_last()) {
01319             p->set_last();
01320             // Tear down the connection after we've sent the last
01321             // segment of the base layer and are requested again.
01322             if ((s2.datasize() == 0) && (r->layer() == 0))
01323                 p->set_finish();
01324         }
01325         if (s2.is_pref()) {
01326             // Add connection id into returned data
01327             p->set_conid(conid);
01328             p->set_pref();
01329         }
01330         return p;
01331     } else if (r->request() == MEDIAREQ_CHECKSEG) 
01332         // We don't need to return anything, so just NULL
01333         return NULL;
01334     else {
01335         fprintf(stderr, 
01336                "MediaServer %s gets an unknown MediaRequest type %d\n",
01337             name(), r->request());
01338         abort();
01339     }
01340     /*NOTREACHED*/
01341     return NULL; // Make msvc happy
01342 }
01343 
01344 int MediaServer::command(int argc, const char*const* argv)
01345 {
01346     Tcl& tcl = Tcl::instance();
01347     if (argc == 3) {
01348         if (strcmp(argv[1], "is-media-page") == 0) {
01349             ClientPage *pg = pool_->get_page(argv[2]);
01350             if (pg && (pg->type() == MEDIA))
01351                 tcl.result("1");
01352             else 
01353                 tcl.result("0");
01354             return TCL_OK;
01355         }
01356     } else if (argc == 5) { 
01357         if (strcmp(argv[1], "stop-prefetching") == 0) {
01358             /*
01359              * <server> stop-prefetching <Client> <conid> <pagenum>
01360              */
01361             HttpApp *app = static_cast <HttpApp *> (TclObject::lookup(argv[2]));
01362             assert(app != NULL);
01363             int id = atoi (argv[4]);
01364             PageID pageId (app, id);
01365             Tcl_HashEntry *he = 
01366                 Tcl_FindHashEntry(pref_, (const char*)&pageId);
01367             if (he == NULL) {
01368                 tcl.add_errorf(
01369                   "Server %d cannot stop prefetching!\n", id_);
01370                 return TCL_ERROR;
01371             }
01372             TclObject *conId = TclObject::lookup(argv[3]);
01373             assert(conId != NULL);
01374             PrefInfoQ *q = (PrefInfoQ*)Tcl_GetHashValue(he);
01375             PrefInfoE *pe = find_prefinfo(q, (Application*)conId);
01376             assert(pe != NULL);
01377             PrefInfo *pi = pe->data();
01378             MediaSegmentList *p = pi->sl_;
01379             assert(p != NULL);
01380             for (int i = 0; i < MAX_LAYER; i++)
01381                 p[i].destroy();
01382             delete []p;
01383             delete pi;
01384             q->detach(pe);
01385             delete pe;
01386             // If no more prefetching streams left for this client,
01387             // delete all the information.
01388             // Return 0 means that we still have prefetching 
01389             // clients left, don't tear down the channel yet. 
01390             // Otherwise return 1. 
01391             int res = 0;
01392             if (q->is_empty()) {
01393                 delete q;
01394                 Tcl_DeleteHashEntry(he);
01395                 res = 1;
01396             }
01397             tcl.resultf("%d", res);
01398             return (TCL_OK);
01399         } else if (strcmp(argv[1], "register-client") == 0) {
01400             // <cache> register-client <app> <client> <pageid>
01401             TclObject *a = TclObject::lookup(argv[2]);
01402             assert(a != NULL);
01403             int newEntry;
01404             Tcl_HashEntry *he = Tcl_CreateHashEntry(cmap_, 
01405                     (const char *)a, &newEntry);
01406             if (he == NULL) {
01407                 tcl.add_errorf("cannot create hash entry");
01408                 return TCL_ERROR;
01409             }
01410             if (!newEntry) {
01411                 tcl.add_errorf("duplicate connection");
01412                 return TCL_ERROR;
01413             }
01414             RegInfo *p = new RegInfo;
01415             p->client_ = (HttpApp*)TclObject::lookup(argv[3]);
01416             assert(p->client_ != NULL);
01417             strcpy(p->name_, argv[4]);
01418             Tcl_SetHashValue(he, (ClientData)p);
01419             return TCL_OK;
01420         } else if (strcmp(argv[1], "unregister-client") == 0) {
01421             // <cache> unregister-client <app> <client> <pageid>
01422             TclObject *a = TclObject::lookup(argv[2]);
01423             assert(a != NULL);
01424             Tcl_HashEntry *he = 
01425                 Tcl_FindHashEntry(cmap_, (const char*)a);
01426             if (he == NULL) {
01427                 tcl.add_errorf("cannot find hash entry");
01428                 return TCL_ERROR;
01429             }
01430             RegInfo *p = (RegInfo*)Tcl_GetHashValue(he);
01431             delete p;
01432             Tcl_DeleteHashEntry(he);
01433             return TCL_OK;
01434         }
01435     } else {
01436         if (strcmp(argv[1], "enter-page") == 0) {
01437             ClientPage *pg = pool_->enter_page(argc, argv);
01438             if (pg == NULL)
01439                 return TCL_ERROR;
01440             if (pg->type() == MEDIA) 
01441                 ((MediaPage*)pg)->create();
01442             // Unlock the page after creation
01443             ((MediaPage*)pg)->unlock(); 
01444             return TCL_OK;
01445         } else if (strcmp(argv[1], "register-prefetch") == 0) {
01446             /*
01447              * <server> register-prefetch <client> <pagenum> 
01448              *  <conid> <layer> {<segments>}
01449              * Registers a list of segments to be prefetched by 
01450              * <client>, where each <segment> is a pair of 
01451              * (start, end). <pagenum> should be pageid without 
01452              * preceding [server:] prefix.
01453              * 
01454              * <conid> is the OTcl name of the original client 
01455              * who requested the page. This is used for the cache
01456              * to get statistics about a particular connection.
01457              * 
01458              * <client> is the requestor of the stream.
01459              */
01460             HttpApp *app = static_cast <HttpApp *> (TclObject::lookup(argv[2]));
01461             assert(app != NULL);
01462             int id = atoi (argv[3]);
01463             PageID pageId (app, id);
01464             int newEntry = 1;
01465             Tcl_HashEntry *he = Tcl_CreateHashEntry(pref_, 
01466                     (const char*)&pageId, &newEntry);
01467             if (he == NULL) {
01468                 fprintf(stderr, "Cannot create entry.\n");
01469                 return TCL_ERROR;
01470             }
01471             PrefInfo *pi;
01472             PrefInfoE *pe;
01473             PrefInfoQ *q; 
01474             MediaSegmentList *p;
01475             TclObject *conId = TclObject::lookup(argv[4]);
01476             if (newEntry) {
01477                 q = new PrefInfoQ;
01478                 Tcl_SetHashValue(he, (ClientData)q);
01479                 pe = NULL;
01480             } else {
01481                 q = (PrefInfoQ *)Tcl_GetHashValue(he);
01482                 pe = find_prefinfo(q, (Application*)conId);
01483             }
01484             if (pe == NULL) {
01485                 pi = new PrefInfo;
01486                 pi->conid_ = (Application*)conId;
01487                 p = pi->sl_ = new MediaSegmentList[MAX_LAYER];
01488                 q->enqueue(new PrefInfoE(pi));
01489             } else {
01490                 pi = pe->data();
01491                 p = pi->sl_;
01492             }
01493             assert((pi != NULL) && (p != NULL));
01494             // Preempt all old requests because they 
01495             // cannot reach the cache "in time"
01496             int layer = atoi(argv[5]);
01497             p[layer].destroy();
01498             // Add segments into prefetching list
01499             assert(argc % 2 == 0);
01500             for (int i = 6; i < argc; i+=2)
01501                 p[layer].add(MediaSegment(atoi(argv[i]), 
01502                               atoi(argv[i+1])));
01503             return TCL_OK;
01504         }
01505     }
01506             
01507     return HttpServer::command(argc, argv);
01508 }

Generated on Tue Mar 6 16:47:47 2007 for ns2 Network Simulator 2.29 by  doxygen 1.4.6