/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ /* * mcache.h * Copyright (C) 1997 by the University of Southern California * $Id: mcache.h,v 1.8 2005/09/18 23:33:35 tomh Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * * The copyright of this module includes the following * linking-with-specific-other-licenses addition: * * In addition, as a special exception, the copyright holders of * this module give you permission to combine (via static or * dynamic linking) this module with free software programs or * libraries that are released under the GNU LGPL and with code * included in the standard release of ns-2 under the Apache 2.0 * license or under otherwise-compatible licenses with advertising * requirements (or modified versions of such code, with unchanged * license). You may copy and distribute such a system following the * terms of the GNU GPL for this module and the licenses of the * other code concerned, provided that you include the source code of * that other code when and as the GNU GPL requires distribution of * source code. * * Note that people who make modified versions of this module * are not obligated to grant this special exception for their * modified versions; it is their choice whether to do so. The GNU * General Public License gives permission to release a modified * version without this exception; this exception also makes it * possible to release a modified version which carries forward this * exception. * */ // // Multimedia caches // // $Header: /nfs/jade/vint/CVSROOT/ns-2/webcache/mcache.h,v 1.8 2005/09/18 23:33:35 tomh Exp $ #ifndef ns_mcache_h #define ns_mcache_h #include "config.h" #include "agent.h" #include "pagepool.h" #include "http.h" #include "rap/media-app.h" //---------------------------------------------------------------------- // Priority list for hit counts at each layer //---------------------------------------------------------------------- class HitCount : public DoubleListElem { public: HitCount(ClientPage *pg, short layer) : DoubleListElem(), pg_(pg), layer_(layer), hc_(0) {} void update(float hc) { hc_ += hc; } float hc() { return hc_; } void reset() { hc_ = 0; } ClientPage* page() { return pg_; } short layer() { return layer_; } HitCount* next() { return (HitCount *)DoubleListElem::next(); } HitCount* prev() { return (HitCount *)DoubleListElem::prev(); } private: ClientPage* pg_; // page short layer_; // layer id float hc_; // hit count }; class HitCountList : public DoubleList { public: HitCountList() : DoubleList() {} void update(HitCount *h); // Update layer hit count void add(HitCount *h); // Add a new layer HitCount* detach_tail() { HitCount* tmp = (HitCount *)tail_; if (tmp) { tail_ = tail_->prev(); tmp->detach(); } return tmp; } // Debug only void print(); void check_integrity(); }; //---------------------------------------------------------------------- // Multimedia web objects //---------------------------------------------------------------------- class MediaPage : public ClientPage { public: MediaPage(const char *n, int s, double mt, double et, double a, int l); virtual ~MediaPage(); virtual WebPageType type() const { return MEDIA; } virtual void print_info(char *buf); int num_layer() const { return num_layer_; } HitCount* get_hit_count(int layer) { assert((layer >= 0) && (layer < num_layer_)); return hc_[layer]; } void hit_layer(int layer) { assert((layer >= 0) && (layer < num_layer_)); hc_[layer]->update((double)(layer_[layer].length()*num_layer_) / (double)size_); } int layer_size(int layer) { assert((layer >= 0) && (layer < num_layer_)); return layer_[layer].length(); } void add_segment(int layer, const MediaSegment& s); int evict_tail_segment(int layer, int size); void add_layer(int layer) { assert((layer >= 0) && (layer < num_layer_)); num_layer_ = (num_layer_ < layer) ? layer : num_layer_; } char* print_layer(int layer) { assert((layer >= 0) && (layer < num_layer_)); return layer_[layer].dump2buf(); } MediaSegmentList is_available(int layer, const MediaSegment& s) { assert((layer >= 0) && (layer < MAX_LAYER)); return layer_[layer].check_holes(s); } // Return a media segment which is the closest one after 's'. // Used by MediaApps to request data. // Do NOT check if layer >= num_layer_. If it's empty, // an empty MediaSegment will be returned. MediaSegment next_available(int layer, const MediaSegment& s) { assert((layer >= 0) && (layer < MAX_LAYER)); return layer_[layer].get_nextseg(s); } MediaSegment next_overlap(int layer, const MediaSegment& s) { assert((layer >= 0) && (layer < MAX_LAYER)); MediaSegment s1 = layer_[layer].get_nextseg(s); if ((s1.end() <= s.start()) || (s1.start() >= s.end())) { MediaSegment s; if (s1.is_last()) s.set_last(); return s; } else return s1; } enum {FETCHLOCK = 1, XMITLOCK = 2}; // 1st type of lock: it is being fetched from the server void lock() { locked_ |= FETCHLOCK; } void unlock() { locked_ &= ~FETCHLOCK; } int is_locked() { return (locked_ & FETCHLOCK); } // 2nd type of lock: it is being transmitted to a client void tlock() { locked_ |= XMITLOCK; } void tunlock() { locked_ &= ~XMITLOCK; } int is_tlocked() { return (locked_ & XMITLOCK); } // Whether all layers are marked as "completed" int is_complete(); // Set all layers as "completed" void set_complete(); // Given the number of layers, evenly distribute the size among all // layers and create all segments. void create(); int realsize() const { return realsize_; } protected: void set_complete_layer(int layer) { flags_[layer] = 1; } int is_complete_layer(int layer) { return flags_[layer] == 1; } MediaSegmentList layer_[MAX_LAYER]; int flags_[MAX_LAYER]; // If 1, marks the layer as completed HitCount* hc_[MAX_LAYER]; int num_layer_; int locked_; // If 1, then no segment can be replaced int realsize_; // The size of stream data in this page. }; // XXX Later we should add a generic replacement algorithm hook into // ClientPagePool. But now we'll leave it there and get this media // page pool work first. // ClientPagePool enhanced with support for multimedia objects, and // with replacement algorithms class MClientPagePool : public ClientPagePool { public: MClientPagePool(); virtual ClientPage* enter_page(int argc, const char*const* argv); virtual int remove_page(const char *name); virtual int force_remove(const char *name); int add_segment(const char *name, int layer, const MediaSegment& s); void hc_update(const char *name, int max_layer); int maxsize() { return max_size_; } int usedsize() { return used_size_; } void fill_page(const char* pgname); // Debug only void dump_hclist() { hclist_.print(); } protected: virtual int command(int argc, const char*const* argv); virtual int cache_replace(ClientPage* page, int size); // Fine-grain replacement int repl_finegrain(ClientPage* p, int size); int repl_atomic(ClientPage* p, int size); // XXX Should change to quad_t, or use MB as unit int max_size_; // PagePool size int used_size_; // Available space size HitCountList hclist_; // Replacement style enum { FINEGRAIN, ATOMIC } repl_style_; }; // Provide page data and generate requests for servers and clients class MediaPagePool : public PagePool { public: MediaPagePool(); protected: virtual int command(int argc, const char*const* argv); int layer_; // Number of layers of pages int *size_; // Page sizes RandomVariable *rvReq_; }; //-------------------- // Multimedia caches //-------------------- // Plain multimedia cache, which can interface with RapAgent class MediaCache : public HttpCache { public: MediaCache(); ~MediaCache(); // Handle app-specific data in C++ virtual void process_data(int size, AppData* data); // Handle data request from RAP virtual AppData* get_data(int& size, AppData* data); protected: virtual int command(int argc, const char*const* argv); // Do we need to maintain links to MediaApp?? I doubt not // MediaApp* mapp_; // An array of incoming/outgoing RAP agents MClientPagePool* mpool() { return (MClientPagePool *)pool_; } // Information and statistics related to clients struct RegInfo { RegInfo() : client_(NULL), hl_(-1) { memset(pb_, 0, sizeof(unsigned int)*MAX_LAYER); memset(db_, 0, sizeof(unsigned int)*MAX_LAYER); memset(eb_, 0, sizeof(unsigned int)*MAX_LAYER); } ~RegInfo() { for (int i = 0; i < MAX_LAYER; i++) pref_[i].destroy(); } char name_[20]; HttpApp* client_; int hl_; // Highest layer this client has asked // Prefetched bytes unsigned int pb_[MAX_LAYER]; // Prefetched bytes that were delivered unsigned int eb_[MAX_LAYER]; // Total delivered bytes unsigned int db_[MAX_LAYER]; MediaSegmentList pref_[MAX_LAYER]; // Return the number of prefetched bytes in the given segment void add_pref(int layer, const MediaSegment& s) { assert((layer >= 0) && (layer < MAX_LAYER)); pref_[layer].add(s); } int pref_size(int layer, const MediaSegment &s) const { assert((layer >= 0) && (layer < MAX_LAYER)); return pref_[layer].overlap_size(s); } }; Tcl_HashTable *cmap_; // client map // Prefetching/No-prefetching/Offline-prefetching flag enum {NOPREF, ONLINE_PREF, OFFLINE_PREF} pref_style_; }; //---------------------------------------------------------------------- // Multimedia web client //---------------------------------------------------------------------- class MediaClient : public HttpClient { public: MediaClient() : HttpClient() {} virtual void process_data(int size, AppData* data); virtual int command(int argc, const char*const* argv); private: MClientPagePool* mpool() { return (MClientPagePool *)pool_; } }; // Helper data structure template class Queue; // forward declaration template class QueueElem { public: QueueElem(T* d) : next_(0), data_(d) {} QueueElem* next() const { return next_; } T* data() const { return data_; } void detachData() { data_ = NULL; } void append(QueueElem* e) { e->next_ = next_; next_ = e; } protected: QueueElem* next_; T* data_; friend class Queue; }; template class Queue { public: Queue() : head_(0), tail_(0), size_(0) {} virtual ~Queue() { QueueElem *p = head_, *q; while (p != NULL) { q = p; p = p->next(); delete q; } head_ = NULL; } virtual void reset() { QueueElem *p = head_, *q; while (p != NULL) { q = p; p = p->next(); delete q; } head_ = NULL; } virtual void destroy() { QueueElem *p = head_, *q; while (p != NULL) { q = p; p = p->next(); delete q->data(); delete q; } head_ = NULL; } void enqueue(QueueElem *e) { if (tail_ == 0) head_ = tail_ = e; else { tail_->append(e); tail_ = e; } size_++; } QueueElem* dequeue() { QueueElem *p = head_; if (head_ != 0) head_ = head_->next(); if (head_ == 0) tail_ = 0; p->next_ = 0; size_--; if (size_ == 0) assert((head_ == 0) && (tail_ == 0)); return p; } void detach(QueueElem* e) { assert(head_ != 0); if (head_ == e) { dequeue(); return; } QueueElem *p = head_; while (p != NULL) { if (p->next_ != e) p = p->next_; else break; } assert(p != NULL); p->next_ = e->next_; if (tail_ == e) tail_ = p; size_--; if (size_ == 0) assert((head_ == 0) && (tail_ == 0)); } QueueElem* getHead() { return head_; } int is_empty() const { return (size_ == 0); } int size() const { return size_; } protected: QueueElem *head_, *tail_; int size_; }; //---------------------------------------------------------------------- // Multimedia server //---------------------------------------------------------------------- class MediaServer : public HttpServer { public: MediaServer(); ~MediaServer(); virtual AppData* get_data(int& size, AppData* d); protected: virtual int command(int argc, const char*const* argv); MediaSegment get_next_segment(MediaRequest *r, Application*& ci); // Prefetching list struct RegInfo { char name_[20]; HttpApp* client_; }; struct PrefInfo { MediaSegmentList* sl_; Application* conid_; }; typedef Queue PrefInfoQ; typedef QueueElem PrefInfoE; PrefInfoE* find_prefinfo(PrefInfoQ* q, Application* conid) { for (PrefInfoE *e = q->getHead(); e != NULL; e = e->next()) if (e->data()->conid_ == conid) return e; return NULL; } Tcl_HashTable *pref_; // Mapping : to PrefInfoQ Tcl_HashTable *cmap_; // Mapping MediaApps to clients PrefInfoQ* get_piq(const char* pgname, HttpApp* client) { PageID id; ClientPage::split_name(pgname, id); id.s_ = client; Tcl_HashEntry* he = Tcl_FindHashEntry(pref_, (const char*)&id); if (he == NULL) return NULL; return (PrefInfoQ*)Tcl_GetHashValue(he); } RegInfo* get_reginfo(Application* app) { Tcl_HashEntry *he = Tcl_FindHashEntry(cmap_, (const char *)app); if (he == NULL) { fprintf(stderr, "Unknown connection!\n"); abort(); } return (RegInfo *)Tcl_GetHashValue(he); } }; #endif // ns_mcache_h