Main Page | Namespace List | Class Hierarchy | Alphabetical List | Compound List | File List | Compound Members | File Members

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

Generated on Tue Apr 20 12:14:23 2004 for NS2.26SourcesOriginal by doxygen 1.3.3