mcache.cc
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:43k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
  2. /*
  3.  * mcache.cc
  4.  * Copyright (C) 1997 by the University of Southern California
  5.  * $Id: mcache.cc,v 1.13 2005/09/18 23:33:35 tomh Exp $
  6.  *
  7.  * This program is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU General Public License,
  9.  * version 2, as published by the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License along
  17.  * with this program; if not, write to the Free Software Foundation, Inc.,
  18.  * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  19.  *
  20.  *
  21.  * The copyright of this module includes the following
  22.  * linking-with-specific-other-licenses addition:
  23.  *
  24.  * In addition, as a special exception, the copyright holders of
  25.  * this module give you permission to combine (via static or
  26.  * dynamic linking) this module with free software programs or
  27.  * libraries that are released under the GNU LGPL and with code
  28.  * included in the standard release of ns-2 under the Apache 2.0
  29.  * license or under otherwise-compatible licenses with advertising
  30.  * requirements (or modified versions of such code, with unchanged
  31.  * license).  You may copy and distribute such a system following the
  32.  * terms of the GNU GPL for this module and the licenses of the
  33.  * other code concerned, provided that you include the source code of
  34.  * that other code when and as the GNU GPL requires distribution of
  35.  * source code.
  36.  *
  37.  * Note that people who make modified versions of this module
  38.  * are not obligated to grant this special exception for their
  39.  * modified versions; it is their choice whether to do so.  The GNU
  40.  * General Public License gives permission to release a modified
  41.  * version without this exception; this exception also makes it
  42.  * possible to release a modified version which carries forward this
  43.  * exception.
  44.  *
  45.  */
  46. //
  47. // Multimedia cache implementation
  48. //
  49. // $Header: /cvsroot/nsnam/ns-2/webcache/mcache.cc,v 1.13 2005/09/18 23:33:35 tomh Exp $
  50. #include <assert.h>
  51. #include <stdio.h>
  52. #include "rap/media-app.h"
  53. #include "mcache.h"
  54. MediaPage::MediaPage(const char *n, int s, double mt, double et, 
  55.      double a, int l) :
  56. ClientPage(n, s, mt, et, a), num_layer_(l), locked_(0), realsize_(0)
  57. {
  58. for (int i = 0; i < num_layer_; i++) {
  59. hc_[i] = new HitCount(this, i);
  60. flags_[i] = 0;
  61. }
  62. }
  63. MediaPage::~MediaPage()
  64. {
  65. int i; 
  66. for (i = 0; i < num_layer_; i++) {
  67. // Delete hit count list
  68. // These hit count records should have already been removed
  69. // from the cache's hit count list. 
  70. assert((hc_[i]->prev() == NULL) && (hc_[i]->next() == NULL));
  71. delete hc_[i];
  72. // Delete media segment list
  73. layer_[i].destroy();
  74. }
  75. }
  76. void MediaPage::print_info(char *buf) 
  77. {
  78. ClientPage::print_info(buf);
  79. buf += strlen(buf);
  80. sprintf(buf, " pgtype MEDIA layer %d", num_layer_);
  81. }
  82. // Make the page full with stream data
  83. void MediaPage::create()
  84. {
  85. assert((num_layer_ >= 0) && (num_layer_ < MAX_LAYER));
  86. int i, sz = size_ / num_layer_;
  87. for (i = 0; i < num_layer_; i++) {
  88. // Delete whatever that was there. 
  89. layer_[i].destroy();
  90. add_segment(i, MediaSegment(0, sz));
  91. set_complete_layer(i);
  92. }
  93. realsize_ = size_;
  94. }
  95. void MediaPage::add_segment(int layer, const MediaSegment& s) 
  96. {
  97. assert((layer >= 0) && (layer < MAX_LAYER));
  98. layer_[layer].add(s);
  99. realsize_ += s.datasize();
  100. if (s.is_last())
  101. set_complete_layer(layer);
  102. }
  103. int MediaPage::is_complete()
  104. {
  105. // Consider a page finished when all NON-EMPTY layers are 
  106. // marked as "completed"
  107. for (int i = 0; i < num_layer_; i++)
  108. if (!is_complete_layer(i) && (layer_[i].length() > 0))
  109. return 0;
  110. return 1;
  111. }
  112. void MediaPage::set_complete()
  113. {
  114. for (int i = 0; i < num_layer_; i++)
  115. set_complete_layer(i);
  116. }
  117. // Used for cache replacement
  118. int MediaPage::evict_tail_segment(int layer, int size)
  119. {
  120. if (is_locked() || is_tlocked())
  121. return 0;
  122. assert((layer >= 0) && (layer < MAX_LAYER));
  123. //#ifdef MCACHE_DEBUG
  124. #if 0
  125. char buf[20];
  126. name(buf);
  127. fprintf(stderr, "Page %s evicted layer %d: ", buf, layer);
  128. #endif
  129. int sz = layer_[layer].evict_tail(size);
  130. realsize_ -= sz;
  131. //#ifdef MCACHE_DEBUG
  132. #if 0
  133. fprintf(stderr, "n");
  134. #endif
  135. return sz;
  136. }
  137. //----------------------------------------------------------------------
  138. // Classes related to a multimedia client page pool
  139. //
  140. // HitCountList and MClientPagePool
  141. //----------------------------------------------------------------------
  142. void HitCountList::update(HitCount *h)
  143. {
  144. HitCount *tmp = h->prev();
  145. if ((tmp != NULL) && (tmp->hc() < h->hc())) {
  146. // Hit count increased, need to move this one up
  147. detach(h);
  148. while ((tmp != NULL) && (tmp->hc() < h->hc())) {
  149. if ((tmp->page() == h->page()) &&
  150.     (tmp->layer() < h->layer()))
  151. // XXX Don't violate layer encoding within the
  152. // same page!
  153. break;
  154. tmp = tmp->prev();
  155. }
  156. if (tmp == NULL) 
  157. // Insert it at the head
  158. insert(h, head_);
  159. else 
  160. append(h, tmp);
  161. } else if ((h->next() != NULL) && (h->hc() < h->next()->hc())) {
  162. // Hit count decreased, need to move this one down
  163. tmp = h->next();
  164. detach(h);
  165. while ((tmp != NULL) && (h->hc() < tmp->hc())) {
  166. if ((h->page() == tmp->page()) && 
  167.     (h->layer() < tmp->layer()))
  168. // XXX Don't violate layer encoding within 
  169. // the same page!
  170. break;
  171. tmp = tmp->next();
  172. }
  173. if (tmp == NULL)
  174. // At the tail
  175. append(h, tail_);
  176. else
  177. insert(h, tmp);
  178. }
  179. // We may end up with two cases here:
  180. //
  181. // (1) tmp->hc()>h->hc() && tmp->layer()<h->layer(). This is
  182. // the normal case, where both hit count ordering and layer 
  183. // ordering are preserved;
  184. //
  185. // (2) tmp->hc()>h->hc() && tmp->layer()>h->layer(). In this
  186. // case, we should move h BEFORE tmp so that the layer 
  187. // ordering is not violated. We basically order the list using 
  188. // layer number as primary key, and use hit count as secondary
  189. // key. 
  190. // Note that the hit count ordering is only violated when more packets 
  191. // in layer i are dropped than those in layer i+1.
  192. }
  193. // Check the integrity of the resulting hit count list
  194. void HitCountList::check_integrity()
  195. {
  196. HitCount *p = (HitCount*)head_, *q;
  197. while (p != NULL) {
  198. q = p->next();
  199. while (q != NULL) {
  200. // Check layer ordering 
  201. if ((p->page() == q->page()) && 
  202.     (p->layer() > q->layer())) {
  203. fprintf(stderr, "Wrong hit count list.n");
  204. abort();
  205. }
  206. q = q->next();
  207. }
  208. p = p->next();
  209. }
  210. }
  211. void HitCountList::add(HitCount *h)
  212. {
  213. HitCount *tmp = (HitCount*)head_;
  214. // XXX First, ensure that the layer ordering within the same page
  215. // is not violated!!
  216. while ((tmp != NULL) && (tmp->hc() > h->hc())) {
  217. if ((tmp->page() == h->page()) && (tmp->layer() > h->layer()))
  218. break;
  219. tmp = tmp->next();
  220. }
  221. // Then order according to layer number
  222. while ((tmp != NULL) && (tmp->hc() == h->hc()) && 
  223.        (tmp->layer() < h->layer()))
  224. tmp = tmp->next();
  225. if (tmp == NULL) {
  226. if (head_ == NULL) 
  227. head_ = tail_ = h;
  228. else
  229. append(h, tail_);
  230. return;
  231. } else if ((tmp == head_) && 
  232.    ((tmp->hc() < h->hc()) || (tmp->layer() > h->layer()))) {
  233. insert(h, head_);
  234. return;
  235. }
  236. // Now tmp->hc()<=h->hc(), or tmp->hc()>h->hc() but 
  237. // tmp->layer()>h->layer(), insert h BEFORE tmp
  238. insert(h, tmp);
  239. }
  240. // Debug only
  241. void HitCountList::print() 
  242. {
  243. HitCount *p = (HitCount *)head_;
  244. int i = 0;
  245. char buf[20];
  246. while (p != NULL) {
  247. p->page()->name(buf);
  248.         fprintf(stderr, "(%s %d %f) ", buf, p->layer(), p->hc());
  249. if (++i % 4 == 0)
  250. printf("n");
  251. p = p->next();
  252. }
  253. if (i % 4 != 0)
  254. fprintf(stderr, "n");
  255. }
  256. //------------------------------
  257. // Multimedia client page pool
  258. //------------------------------
  259. static class MClientPagePoolClass : public TclClass {
  260. public:
  261.         MClientPagePoolClass() : TclClass("PagePool/Client/Media") {}
  262.         TclObject* create(int, const char*const*) {
  263. return (new MClientPagePool());
  264. }
  265. } class_mclientpagepool_agent;
  266. MClientPagePool::MClientPagePool() : 
  267. used_size_(0), repl_style_(FINEGRAIN)
  268. {
  269. bind("max_size_", &max_size_);
  270. used_size_ = 0;
  271. }
  272. int MClientPagePool::command(int argc, const char*const* argv)
  273. {
  274. if (argc == 3) 
  275. if (strcmp(argv[1], "set-repl-style") == 0) {
  276. // Set replacement style
  277. // <obj> set-repl-style <style>
  278. if (strcmp(argv[2], "FINEGRAIN") == 0) 
  279. repl_style_ = FINEGRAIN;
  280. else if (strcmp(argv[2], "ATOMIC") == 0)
  281. repl_style_ = ATOMIC;
  282. else {
  283. fprintf(stderr, "Unknown style %s", argv[3]);
  284. return (TCL_ERROR);
  285. }
  286. return (TCL_OK);
  287. }
  288. return ClientPagePool::command(argc, argv);
  289. }
  290. void MClientPagePool::hc_update(const char *name, int max_layer)
  291. {
  292. MediaPage *pg = (MediaPage*)get_page(name);
  293. assert(pg != NULL);
  294. int i;
  295. HitCount *h;
  296. // First we update the hit count of each layer of the given page
  297. for (i = 0; i <= max_layer; i++)
  298. pg->hit_layer(i);
  299. // Then we update the position of these hit count records
  300. for (i = 0; i <= max_layer; i++) {
  301. h = pg->get_hit_count(i);
  302. hclist_.update(h);
  303. }
  304. #if 1
  305. hclist_.check_integrity();
  306. #endif
  307. }
  308. // Add a segment to an object, and adjust hit counts accordingly
  309. // XXX Call cache replacement algorithm if necessary
  310. int MClientPagePool::add_segment(const char* name, int layer, 
  311.  const MediaSegment& s)
  312. {
  313. MediaPage* pg = (MediaPage *)get_page(name);
  314. if (pg == NULL)
  315. return -1;
  316. if (layer >= pg->num_layer()) {
  317. if (s.datasize() == 0)
  318. return 0;
  319. else {
  320. fprintf(stderr, 
  321. "MClientPagePool: cannot add a new layer.n");
  322. abort();
  323. }
  324. }
  325. // Check space availability
  326. if (used_size_ + s.datasize() > max_size_) {
  327. // If atomic replacement is used, page size is deducted in
  328. // remove_page(). If fine-grain is used, evicted size is 
  329. // deducted in repl_finegrain().
  330. cache_replace(pg, s.datasize());
  331. //#ifdef MCACHE_DEBUG
  332. #if 0
  333. fprintf(stderr, 
  334. "Replaced for page %s segment (%d %d) layer %dn",
  335. name, s.start(), s.end(), layer);
  336. #endif
  337. // Add new page. When we are doing atomic replacement, the size that
  338. // we evicted may be larger than what we add.
  339. used_size_ += s.datasize();
  340. // If this layer was not 'in' before, add its hit count block
  341. if (pg->layer_size(layer) == 0)
  342. hclist_.add(pg->get_hit_count(layer));
  343. // Add new segment
  344. pg->add_segment(layer, s);
  345. return 0;
  346. }
  347. void MClientPagePool::fill_page(const char* pgname)
  348. {
  349. MediaPage *pg = (MediaPage*)get_page(pgname);
  350. used_size_ -= pg->realsize();
  351. // Lock this page before we do any replacement. 
  352. pg->lock();
  353. pg->create();
  354. // If we cannot hold the nominal size of the page, do replacement
  355. if (used_size_ + pg->size() > max_size_)
  356. // Size deduction has already been done in remove_page()
  357. cache_replace(pg, pg->size());
  358. used_size_ += pg->size();
  359. pg->unlock();
  360. }
  361. ClientPage* MClientPagePool::enter_page(int argc, const char*const* argv)
  362. {
  363. double mt = -1, et, age = -1, noc = 0;
  364. int size = -1, media_page = 0, layer = -1;
  365. for (int i = 3; i < argc; i+=2) {
  366. if (strcmp(argv[i], "modtime") == 0)
  367. mt = strtod(argv[i+1], NULL);
  368. else if (strcmp(argv[i], "size") == 0) 
  369. size = atoi(argv[i+1]);
  370. else if (strcmp(argv[i], "age") == 0)
  371. age = strtod(argv[i+1], NULL);
  372. else if (strcmp(argv[i], "noc") == 0)
  373. // non-cacheable flag
  374. noc = 1;
  375. else if (strcmp(argv[i], "pgtype") == 0) {
  376. if (strcmp(argv[i+1], "MEDIA") == 0)
  377. media_page = 1;
  378. } else if (strcmp(argv[i], "layer") == 0)
  379. layer = atoi(argv[i+1]);
  380. }
  381. // XXX allow mod time < 0 and age < 0!
  382. if ((size < 0) || (media_page && (layer <= 0))) {
  383. fprintf(stderr, "%s: wrong page information %sn",
  384. name_, argv[2]);
  385. return NULL;
  386. }
  387. et = Scheduler::instance().clock();
  388. ClientPage *pg;
  389. if (media_page)
  390. pg = new MediaPage(argv[2], size, mt, et, age, layer);
  391. else 
  392. pg = new ClientPage(argv[2], size, mt, et, age);
  393. if (add_page(pg) < 0) {
  394. delete pg;
  395. return NULL;
  396. }
  397. if (noc) 
  398. pg->set_uncacheable();
  399. if (media_page) 
  400. ((MediaPage *)pg)->lock();
  401. return pg;
  402. }
  403. int MClientPagePool::cache_replace(ClientPage *pg, int size)
  404. {
  405. switch (repl_style_) {
  406. case FINEGRAIN:
  407. return repl_finegrain(pg, size);
  408. case ATOMIC:
  409. #if 0
  410. char tmp[128];
  411. pg->name(tmp);
  412. fprintf(stderr, "Replaced for page %s size %dn", tmp, size);
  413. fprintf(stderr, "Used size %d, max size %dn", used_size_, 
  414. max_size_);
  415. #endif
  416. return repl_atomic(pg, size);
  417. default:
  418. fprintf(stderr, "Corrupted replacement style.n");
  419. abort();
  420. }
  421. // To make msvc happy
  422. return -1;
  423. }
  424. int MClientPagePool::repl_atomic(ClientPage*, int size)
  425. {
  426. // XXX We use standard LRU to determine the stream to be kicked out.
  427. // The major problem is that we do not keep discrete hit counts. 
  428. // We solve the problem by using hit counts of the base layer as 
  429. // a close approximate. Because whenever a stream is accessed, 
  430. // it's assumed that the client bw can always afford the base layer,
  431. // this should be a fairly good approximation. 
  432. HitCount *h, *p;
  433. int sz, totalsz = 0;
  434. // Repeatedly get rid of streams until get enough space
  435. h = (HitCount*)hclist_.tail();
  436. while (h != NULL) {
  437. if (h->layer() != 0) {
  438. // We only look for the base layer
  439. h = h->prev();
  440. continue;
  441. }
  442. MediaPage *pg = (MediaPage *)h->page();
  443. // Don't touch locked pages
  444. if (pg->is_tlocked() || pg->is_locked()) {
  445. h = h->prev();
  446. continue;
  447. }
  448. sz = pg->realsize();
  449. totalsz += sz;
  450. char tmp[HTTP_MAXURLLEN];
  451. pg->name(tmp);
  452. // Before we delete, find the previous hit count record that
  453. // does not belong to this page. 
  454. p = h->prev(); 
  455. while ((p != NULL) && (p->page() == h->page()))
  456. p = p->prev();
  457. h = p;
  458. // XXX Manually remove hit count before deleting it
  459. for (int i = 0; i < pg->num_layer(); i++) {
  460. p = pg->get_hit_count(i);
  461. hclist_.detach(p);
  462. }
  463. // Delete the page, together with its media segment list
  464. #if 0
  465. fprintf(stderr, "At time %g, atomic replacement evicted page %sn", 
  466. Scheduler::instance().clock(), tmp); 
  467. fprintf(stderr, "Hit count list: n");
  468. hclist_.print();
  469. fprintf(stderr,"----------------------------------------nn");
  470. #endif
  471. remove_page(tmp);
  472. if (sz >= size)
  473. return totalsz;
  474. // Continue to evict to meet the space requirement
  475. size -= sz;
  476. }
  477. fprintf(stderr, "Cache replacement cannot get enough space.n");
  478. abort();
  479. return 0; // Make msvc happy
  480. }
  481. int MClientPagePool::repl_finegrain(ClientPage *, int size)
  482. {
  483. // Traverse through hit count table, evict segments from the tail
  484. // of a layer with minimum hit counts
  485. HitCount *h, *p;
  486. int sz, totalsz = 0;
  487. // Repeatedly evict pages/segments until get enough space
  488. h = (HitCount*)hclist_.tail();
  489. while (h != NULL) {
  490. MediaPage *pg = (MediaPage *)h->page();
  491. // XXX Don't touch locked pages
  492. if (pg->is_tlocked() || pg->is_locked()) {
  493. h = h->prev();
  494. continue;
  495. }
  496. // Try to get "size" space by evicting other segments
  497. sz = pg->evict_tail_segment(h->layer(), size);
  498. // Decrease the cache used space
  499. used_size_ -= sz;
  500. totalsz += sz;
  501. // If we have not got enough space, we must have got rid of 
  502. // the entire layer
  503. assert((sz == size) || 
  504.        ((sz < size) && (pg->layer_size(h->layer()) == 0)));
  505. // If we don't have anything of this layer left, get rid of 
  506. // the hit count record. 
  507. // XXX Must do this BEFORE removing the page
  508. p = h;
  509. h = h->prev();
  510. if (pg->layer_size(p->layer()) == 0) {
  511. // XXX Should NEVER delete a hit count record!!
  512. // A hit count record is ONLY deleted when the page
  513. // is deleted (evicted from cache: ~MediaPage())
  514. hclist_.detach(p);
  515. p->reset();
  516. }
  517. // Furthermore, if the page has nothing left, get rid of it
  518. if (pg->realsize() == 0) {
  519. // NOTE: we do not manually remove hit counts of 
  520. // this page because if its realsize is 0, all 
  521. // hit count records must have already been 
  522. // detached from the page. 
  523. char tmp[HTTP_MAXURLLEN];
  524. pg->name(tmp);
  525. #if 0
  526. fprintf(stderr, "At time %g, fine-grain evicted page %sn",
  527. Scheduler::instance().clock(), tmp);
  528. fprintf(stderr, "Hit count list: n");
  529. hclist_.print();
  530. fprintf(stderr,
  531. "---------------------------------------nn");
  532. #endif
  533. // Then the hit count record will be deleted in here
  534. remove_page(tmp);
  535. }
  536. // If we've got enough space, return; otherwise continue
  537. if (sz >= size)
  538. return totalsz;
  539. size -= sz; // Evict to fill the rest
  540. }
  541. fprintf(stderr, "Cache replacement cannot get enough space.n");
  542. abort();
  543. return 0; // Make msvc happy
  544. }
  545. // Clean all hit count record of a page regardless of whether it's in the 
  546. // hit count list. Used when hclist_ is not used at all, e.g., by MediaClient.
  547. int MClientPagePool::force_remove(const char *name)
  548. {
  549. // XXX Bad hack. Needs to integrate this into ClientPagePool.
  550. ClientPage *pg = (ClientPage*)get_page(name);
  551. // We should not remove a non-existent page!!
  552. assert(pg != NULL);
  553. if (pg->type() == MEDIA) {
  554. HitCount *p;
  555. MediaPage *q = (MediaPage*)pg;
  556. used_size_ -= q->realsize();
  557. for (int i = 0; i < q->num_layer(); i++) {
  558. p = q->get_hit_count(i);
  559. hclist_.detach(p);
  560. }
  561. } else if (pg->type() == HTML)
  562. used_size_ -= pg->size();
  563. return ClientPagePool::remove_page(name);
  564. }
  565. int MClientPagePool::remove_page(const char *name)
  566. {
  567. // XXX Bad hack. Needs to integrate this into ClientPagePool.
  568. ClientPage *pg = (ClientPage*)get_page(name);
  569. // We should not remove a non-existent page!!
  570. assert(pg != NULL);
  571. if (pg->type() == MEDIA)
  572. used_size_ -= ((MediaPage *)pg)->realsize();
  573. else if (pg->type() == HTML)
  574. used_size_ -= pg->size();
  575. return ClientPagePool::remove_page(name);
  576. }
  577. //------------------------------------------------------------
  578. // MediaPagePool
  579. // Generate requests and pages for clients and servers 
  580. //------------------------------------------------------------
  581. static class MediaPagePoolClass : public TclClass {
  582. public:
  583.         MediaPagePoolClass() : TclClass("PagePool/Media") {}
  584.         TclObject* create(int, const char*const*) {
  585. return (new MediaPagePool());
  586. }
  587. } class_mediapagepool_agent;
  588. MediaPagePool::MediaPagePool() : PagePool()
  589. {
  590. size_ = NULL;
  591. duration_ = 0;
  592. layer_ = 1;
  593. }
  594. // For now, only one page, fixed size, fixed layer
  595. int MediaPagePool::command(int argc, const char*const* argv)
  596. {
  597. Tcl& tcl = Tcl::instance();
  598. if (argc == 2) {
  599. if (strcmp(argv[1], "get-poolsize") == 0) { 
  600. tcl.resultf("%d", num_pages_);
  601. return TCL_OK;
  602. } else if (strcmp(argv[1], "get-start-time") == 0) {
  603. tcl.resultf("%.17g", start_time_);
  604. return TCL_OK;
  605. } else if (strcmp(argv[1], "get-duration") == 0) {
  606. tcl.resultf("%d", duration_);
  607. return TCL_OK;
  608. }
  609. } else if (argc == 3) {
  610. if (strcmp(argv[1], "gen-pageid") == 0) {
  611. // Generating requested page id
  612. if (rvReq_ == NULL) {
  613. tcl.add_errorf("no page id ranvar.");
  614. return TCL_ERROR;
  615. }
  616. int p = (int)rvReq_->value();
  617. assert((p >= 0) && (p < num_pages_));
  618. tcl.resultf("%d", p);
  619. return TCL_OK;
  620. } else if (strcmp(argv[1], "is-media-page") == 0) {
  621. // XXX Currently all pages are media pages. Should
  622. // be able to allow both normal pages and media pages
  623. // in the future
  624. tcl.result("1");
  625. return TCL_OK;
  626. } else if (strcmp(argv[1], "get-layer") == 0) {
  627. // XXX Currently all pages have the same number of 
  628. // layers. Should be able to change this in future.
  629. tcl.resultf("%d", layer_); 
  630. return TCL_OK;
  631. } else if (strcmp(argv[1], "set-start-time") == 0) {
  632. double st = strtod(argv[2], NULL);
  633. start_time_ = st;
  634. end_time_ = st + duration_;
  635. return TCL_OK;
  636. } else if (strcmp(argv[1], "set-duration") == 0) {
  637. // XXX Need this info to set page mod time!!
  638. duration_ = atoi(argv[2]);
  639. end_time_ = start_time_ + duration_;
  640. return TCL_OK;
  641. } else if (strcmp(argv[1], "gen-init-modtime") == 0) {
  642. // XXX We are not interested in page consistency here,
  643. // so never change this page.
  644. tcl.resultf("%d", -1);
  645. return TCL_OK;
  646. } else if (strcmp(argv[1], "gen-size") == 0) {
  647. int pagenum = atoi(argv[2]);
  648. if (pagenum >= num_pages_) {
  649. tcl.add_errorf("Invalid page id %d", pagenum);
  650. return TCL_ERROR;
  651. }
  652. tcl.resultf("%d", size_[pagenum]);
  653. return TCL_OK;
  654. } else if (strcmp(argv[1], "set-layer") == 0) {
  655. layer_ = atoi(argv[2]);
  656. return TCL_OK;
  657. } else if (strcmp(argv[1], "set-num-pages") == 0) {
  658. if (size_ != NULL) {
  659. tcl.add_errorf("can't change number of pages");
  660. return TCL_ERROR;
  661. }
  662. num_pages_ = atoi(argv[2]);
  663. size_ = new int[num_pages_];
  664. return TCL_OK;
  665. } else if (strcmp(argv[1], "ranvar-req") == 0) {
  666. rvReq_ = (RandomVariable*)TclObject::lookup(argv[2]);
  667. return TCL_OK;
  668. }
  669. } else if (argc == 4) {
  670. if (strcmp(argv[1], "gen-modtime") == 0) {
  671. // This should never be called, because we never
  672. // deals with page modifications!!
  673. fprintf(stderr, "%s: gen-modtime called!n", name());
  674. abort();
  675. } else if (strcmp(argv[1], "set-pagesize") == 0) {
  676. // <pagepool> set-pagesize <pagenum> <size>
  677. int pagenum = atoi(argv[2]);
  678. if (pagenum >= num_pages_) {
  679. tcl.add_errorf("Invalid page id %d", pagenum);
  680. return TCL_ERROR;
  681. }
  682. size_[pagenum] = atoi(argv[3]);
  683. return TCL_OK;
  684. }
  685. }
  686. return PagePool::command(argc, argv);
  687. }
  688. //----------------------------------------------------------------------
  689. // PagePool that generates requests using the SURGE model
  690. //
  691. // Part of the code by Paul Barford (barford@cs.bu.edu).
  692. // Copyright (c) 1997 Trustees of Boston University
  693. //
  694. // Allow two options: (1) setting if all pages are media page or normal
  695. // HTTP pages; (2) average page size
  696. //----------------------------------------------------------------------
  697. //  static class SurgePagePoolClass : public TclClass {
  698. //  public:
  699. //          SurgePagePoolClass() : TclClass("PagePool/Surge") {}
  700. //          TclObject* create(int, const char*const*) {
  701. //   return (new SurgePagePool());
  702. //   }
  703. //  } class_surgepagepool_agent;
  704. //  SurgePagePool::SurgePagePool() : PagePool()
  705. //  {
  706. //  }
  707. //----------------------------------------------------------------------
  708. // Multimedia web applications: cache, etc.
  709. //----------------------------------------------------------------------
  710. static class MediaCacheClass : public TclClass {
  711. public:
  712. MediaCacheClass() : TclClass("Http/Cache/Media") {}
  713. TclObject* create(int, const char*const*) {
  714. return (new MediaCache());
  715. }
  716. } class_mediacache;
  717. // By default we use online prefetching
  718. MediaCache::MediaCache() : pref_style_(ONLINE_PREF)
  719. {
  720. cmap_ = new Tcl_HashTable;
  721. Tcl_InitHashTable(cmap_, TCL_ONE_WORD_KEYS);
  722. }
  723. MediaCache::~MediaCache()
  724. {
  725. Tcl_HashEntry *he;
  726. Tcl_HashSearch hs;
  727. if (cmap_) {
  728. for (he = Tcl_FirstHashEntry(cmap_, &hs);  he != NULL;
  729.      he = Tcl_NextHashEntry(&hs))
  730. delete (RegInfo*)Tcl_GetHashValue(he);
  731. Tcl_DeleteHashTable(cmap_);
  732. delete cmap_;
  733. }
  734. }
  735. AppData* MediaCache::get_data(int& size, AppData* req)
  736. {
  737. assert(req != NULL);
  738. if (req->type() != MEDIA_REQUEST) {
  739. return HttpApp::get_data(size, req);
  740. }
  741. MediaRequest *r = (MediaRequest *)req;
  742. // Get statistics block for the requestor
  743. Tcl_HashEntry *he = 
  744. Tcl_FindHashEntry(cmap_, (const char *)(r->app()));
  745. assert(he != NULL);
  746. RegInfo *ri = (RegInfo *)Tcl_GetHashValue(he);
  747. // Process request
  748. if (r->request() == MEDIAREQ_GETSEG) {
  749. // Get a new data segment
  750. MediaPage* pg = (MediaPage*)pool_->get_page(r->name());
  751. assert(pg != NULL);
  752. MediaSegment s1(r->st(), r->et());
  753. MediaSegment s2 = pg->next_overlap(r->layer(), s1);
  754. HttpMediaData *p;
  755. if (s2.datasize() == 0) {
  756. // No more data available for this layer, allocate
  757. // an ADU with data size 0 to signal the end
  758. // of transmission for this layer
  759. size = 0;
  760. p = new HttpMediaData(name(), r->name(),
  761.       r->layer(), 0, 0);
  762. } else {
  763. size = s2.datasize();
  764. p = new HttpMediaData(name(), r->name(),
  765.    r->layer(), s2.start(), s2.end());
  766. }
  767. // XXX If we are still receiving the stream, don't 
  768. // ever say that this is the last segment. If the 
  769. // page is not locked, it's still possible that we
  770. // return a NULL segment because the requested one
  771. // is not available. Don't set the 'LAST' flag in this 
  772. // case.
  773. if (s2.is_last()) {
  774. p->set_last();
  775. if (!pg->is_locked() && (s2.datasize() == 0) && 
  776.     (r->layer() == 0)) 
  777. p->set_finish();
  778. }
  779. //----------------------------------------
  780. // Update statistics of this connection
  781. //----------------------------------------
  782. // Update the highest layer that this client has requested
  783. if (ri->hl_ < r->layer())
  784. ri->hl_ = r->layer();
  785. if (size > 0) {
  786. // Update total delivered bytes
  787. ri->db_[r->layer()] += size;
  788. // Update prefetched bytes that've been delivered
  789. ri->eb_[r->layer()] += ri->pref_size(r->layer(), s2);
  790. }
  791. return p;
  792. } else if (r->request() == MEDIAREQ_CHECKSEG) {
  793. // If we are not doing online prefetching, return nothing
  794. if (pref_style_ != ONLINE_PREF)
  795. return NULL;
  796. // Check the availability of a new data segment
  797. // And refetch if it is not available
  798. MediaPage* pg = (MediaPage*)pool_->get_page(r->name());
  799. assert(pg != NULL);
  800. if (pg->is_locked()) 
  801. // If we are during the first retrieval, don't prefetch
  802. return NULL;
  803. MediaSegmentList ul = pg->is_available(r->layer(),
  804.       MediaSegment(r->st(),r->et()));
  805. if (ul.length() == 0)
  806. // All segments are available
  807. return NULL;
  808. // Otherwise do prefetching on these "holes"
  809. char *buf = ul.dump2buf();
  810. Tcl::instance().evalf("%s pref-segment %s %s %d %s", name(), 
  811.       r->app()->name(), r->name(), 
  812.       r->layer(), buf);
  813. //   log("E PREF p %s l %d %sn", r->name(), r->layer(), buf);
  814. delete []buf;
  815. ul.destroy();
  816. // Update the highest layer that this client has requested
  817. Tcl_HashEntry *he = 
  818. Tcl_FindHashEntry(cmap_, (const char *)(r->app()));
  819. assert(he != NULL);
  820. RegInfo *ri = (RegInfo *)Tcl_GetHashValue(he);
  821. if (ri->hl_ < r->layer())
  822. ri->hl_ = r->layer();
  823. return NULL;
  824. }
  825. fprintf(stderr, 
  826. "MediaCache %s gets an unknown MediaRequest type %dn",
  827. name(), r->request());
  828. abort();
  829. return NULL; // Make msvc happy
  830. }
  831. // Add received media segment into page pool
  832. void MediaCache::process_data(int size, AppData* data) 
  833. {
  834. switch (data->type()) {
  835. case MEDIA_DATA: {
  836. HttpMediaData* d = (HttpMediaData*)data;
  837. // Cache this segment, do replacement if necessary
  838. if (mpool()->add_segment(d->page(), d->layer(), 
  839.  MediaSegment(*d)) == -1) {
  840. fprintf(stderr, "MediaCache %s gets a segment for an "
  841. "unknown page %sn", name(), d->page());
  842. abort();
  843. }
  844. if (d->is_pref()) {
  845. // Update total prefetched bytes
  846. Tcl_HashEntry *he = Tcl_FindHashEntry(cmap_, 
  847. (const char*)(d->conid()));
  848. // Client-cache-server disconnection procedure:
  849. // (1) client disconnects from cache, then
  850. // (2) cache disconnects from server and shuts down 
  851. //     prefetching channel. 
  852. // Therefore, after client disconnects, the cache 
  853. // may still receive a few prefetched segments. 
  854. // Ignore those because we no longer keep statistics
  855. // about the torn-down connection.
  856. if (he != NULL) {
  857. RegInfo *ri = (RegInfo *)Tcl_GetHashValue(he);
  858. ri->add_pref(d->layer(), MediaSegment(*d));
  859. ri->pb_[d->layer()] += d->datasize();
  860. }
  861. }
  862. // XXX debugging only
  863. #if 1
  864.    log("E RSEG p %s l %d s %d e %d z %d f %dn", 
  865.        d->page(), d->layer(), d->st(), d->et(), d->datasize(),
  866.        d->is_pref());
  867. #endif
  868. break;
  869. default:
  870. HttpCache::process_data(size, data);
  871. }
  872. }
  873. int MediaCache::command(int argc, const char*const* argv) 
  874. {
  875. Tcl& tcl = Tcl::instance();
  876. if (argc == 2) {
  877. if (strcmp(argv[1], "get-pref-style") == 0) {
  878. switch (pref_style_) {
  879. case NOPREF:
  880. tcl.result("NOPREF");
  881. break;
  882. case ONLINE_PREF:
  883. tcl.result("ONLINE_PREF");
  884. break;
  885. case OFFLINE_PREF:
  886. tcl.result("OFFLINE_PREF");
  887. break;
  888. default:
  889. fprintf(stderr, 
  890. "Corrupted prefetching style %d", 
  891. pref_style_);
  892. return TCL_ERROR;
  893. }
  894. return TCL_OK;
  895. }
  896. } else if (argc == 3) {
  897. if (strcmp(argv[1], "offline-complete") == 0) {
  898. // Delete whatever segments in the given page, 
  899. // make it complete. Used by offline prefetching
  900. ClientPage *pg = mpool()->get_page(argv[2]);
  901. if (pg == NULL)
  902. // XXX It's possible that we've already kicked
  903. // it out of the cache. Do nothing.
  904. return TCL_OK;
  905. assert(pg->type() == MEDIA);
  906. assert(!((MediaPage*)pg)->is_locked());
  907. mpool()->fill_page(argv[2]);
  908. return TCL_OK;
  909. } else if (strcmp(argv[1], "set-pref-style") == 0) {
  910. // Set prefetching style
  911. // <obj> set-pref-style <style>
  912. //
  913. // style can be: NOPREF, ONLINE_PREF, OFFLINE_PREF
  914. if (strcmp(argv[2], "NOPREF") == 0) 
  915. pref_style_ = NOPREF;
  916. else if (strcmp(argv[2], "ONLINE_PREF") == 0) 
  917. pref_style_ = ONLINE_PREF;
  918. else if (strcmp(argv[2], "OFFLINE_PREF") == 0) 
  919. pref_style_ = OFFLINE_PREF;
  920. else {
  921. fprintf(stderr, "Wrong prefetching style %s",
  922. argv[2]);
  923. return TCL_ERROR;
  924. }
  925. return TCL_OK;
  926. } else if (strcmp(argv[1], "dump-page") == 0) {
  927. // Dump segments of a given page
  928. ClientPage *p=(ClientPage*)mpool()->get_page(argv[2]);
  929. if (p->type() != MEDIA)
  930. // Do nothing for non-media pages
  931. return TCL_OK;
  932. MediaPage *pg = (MediaPage *)p;
  933. char *buf;
  934. for (int i = 0; i < pg->num_layer(); i++) {
  935. buf = pg->print_layer(i);
  936. if (strlen(buf) > 0)
  937. log("E SEGS p %s l %d %sn", argv[2], 
  938.     i, buf);
  939. delete []buf;
  940. }
  941. return TCL_OK;
  942. } else if (strcmp(argv[1], "stream-received") == 0) {
  943. // We've got the entire page, unlock it
  944. MediaPage *pg = (MediaPage*)mpool()->get_page(argv[2]);
  945. assert(pg != NULL);
  946. pg->unlock();
  947. // XXX Should we clear all "last" flag of segments??
  948. #ifdef MCACHE_DEBUG
  949. // Printing out current buffer status of the page
  950. char *buf;
  951. for (int i = 0; i < pg->num_layer(); i++) {
  952. buf = pg->print_layer(i);
  953. log("E SEGS p %s l %d %sn", argv[2], i, buf);
  954. delete []buf;
  955. }
  956. #endif
  957. // Show cache free size
  958. log("E SIZ n %d z %d t %dn", mpool()->num_pages(),
  959.     mpool()->usedsize(), mpool()->maxsize());
  960. return TCL_OK;
  961. }
  962. } else if (argc == 5) {
  963. if (strcmp(argv[1], "register-client") == 0) {
  964. // <server> register-client <app> <client> <pageid>
  965. TclObject *a = TclObject::lookup(argv[2]);
  966. assert(a != NULL);
  967. int newEntry;
  968. Tcl_HashEntry *he = Tcl_CreateHashEntry(cmap_, 
  969. (const char *)a, &newEntry);
  970. if (he == NULL) {
  971. tcl.add_errorf("cannot create hash entry");
  972. return TCL_ERROR;
  973. }
  974. if (!newEntry) {
  975. tcl.add_errorf("duplicate connection");
  976. return TCL_ERROR;
  977. }
  978. RegInfo *p = new RegInfo;
  979. p->client_ = (HttpApp*)TclObject::lookup(argv[3]);
  980. assert(p->client_ != NULL);
  981. strcpy(p->name_, argv[4]);
  982. Tcl_SetHashValue(he, (ClientData)p);
  983. // Lock the page while transmitting it to a client
  984. MediaPage *pg = (MediaPage*)mpool()->get_page(argv[4]);
  985. assert((pg != NULL) && (pg->type() == MEDIA));
  986. pg->tlock();
  987. return TCL_OK;
  988. } else if (strcmp(argv[1], "unregister-client") == 0) {
  989. // <server> unregister-client <app> <client> <pageid>
  990. TclObject *a = TclObject::lookup(argv[2]);
  991. assert(a != NULL);
  992. Tcl_HashEntry *he = 
  993. Tcl_FindHashEntry(cmap_, (const char*)a);
  994. if (he == NULL) {
  995. tcl.add_errorf("cannot find hash entry");
  996. return TCL_ERROR;
  997. }
  998. RegInfo *ri = (RegInfo*)Tcl_GetHashValue(he);
  999. // Update hit count
  1000. mpool()->hc_update(argv[4], ri->hl_);
  1001. #ifdef MCACHE_DEBUG
  1002. printf("Cache %d hit counts: n", id_);
  1003. mpool()->dump_hclist();
  1004. #endif
  1005. // Dump per-connection statistics
  1006. for (int i = 0; i <= ri->hl_; i++)
  1007. log("E STAT p %s l %d d %d e %d p %dn",
  1008.     ri->name_, i, ri->db_[i], ri->eb_[i], 
  1009.     ri->pb_[i]);
  1010. delete ri;
  1011. Tcl_DeleteHashEntry(he);
  1012. // Lock the page while transmitting it to a client
  1013. MediaPage *pg = (MediaPage*)mpool()->get_page(argv[4]);
  1014. assert((pg != NULL) && (pg->type() == MEDIA));
  1015. pg->tunlock();
  1016. return TCL_OK;
  1017. }
  1018. }
  1019. return HttpCache::command(argc, argv);
  1020. }
  1021. //----------------------------------------------------------------------
  1022. // Media web client 
  1023. //   Use C++ interface to records quality of received stream.
  1024. // NOTE: 
  1025. //   It has OTcl inheritance, but no C++ inheritance!
  1026. //----------------------------------------------------------------------
  1027. static class HttpMediaClientClass : public TclClass {
  1028. public:
  1029. HttpMediaClientClass() : TclClass("Http/Client/Media") {}
  1030. TclObject* create(int, const char*const*) {
  1031. return (new MediaClient());
  1032. }
  1033. } class_httpmediaclient;
  1034. // Records the quality of stream received
  1035. void MediaClient::process_data(int size, AppData* data)
  1036. {
  1037. assert(data != NULL);
  1038. switch (data->type()) {
  1039. case MEDIA_DATA: {
  1040. HttpMediaData* d = (HttpMediaData*)data;
  1041. // XXX Don't pass any data to page pool!!
  1042.     if (mpool()->add_segment(d->page(), d->layer(), 
  1043.      MediaSegment(*d)) == -1) {
  1044.     fprintf(stderr, 
  1045.   "MediaCache %s gets a segment for an unknown page %sn", name(), d->page());
  1046. //      abort();
  1047.     }
  1048. // Note: we store the page only to produce some statistics
  1049. // later so that we need not do postprocessing of traces.
  1050. #if 1
  1051.      log("C RSEG p %s l %d s %d e %d z %dn", 
  1052.          d->page(), d->layer(), d->st(), d->et(), d->datasize());
  1053. #endif
  1054. break;
  1055. }
  1056. default:
  1057. HttpClient::process_data(size, data);
  1058. }
  1059. }
  1060. int MediaClient::command(int argc, const char*const* argv)
  1061. {
  1062. if (argc == 3) {
  1063. if (strcmp(argv[1], "stream-received") == 0) {
  1064. // XXX This is the place to do statistics collection
  1065. // about quality of received stream.
  1066. // 
  1067. // Dump delivered quality log
  1068. MediaPage *pg = (MediaPage*)mpool()->get_page(argv[2]);
  1069. assert(pg != NULL);
  1070. // Printing out current buffer status of the page
  1071. char *buf;
  1072. for (int i = 0; i < pg->num_layer(); i++) {
  1073. buf = pg->print_layer(i);
  1074. if (strlen(buf) > 0) 
  1075. log("C SEGS p %s l %d %sn", 
  1076.     argv[2], i, buf);
  1077. delete []buf;
  1078. }
  1079. // then delete the stream from buffer
  1080. mpool()->force_remove(argv[2]);
  1081. return TCL_OK;
  1082. }
  1083. }
  1084. return HttpClient::command(argc, argv);
  1085. }
  1086. //----------------------------------------------------------------------
  1087. // Multimedia web server
  1088. //----------------------------------------------------------------------
  1089. static class MediaServerClass : public TclClass {
  1090. public:
  1091. MediaServerClass() : TclClass("Http/Server/Media") {}
  1092. TclObject* create(int, const char*const*) {
  1093. return (new MediaServer());
  1094. }
  1095. } class_mediaserver;
  1096. MediaServer::MediaServer() : HttpServer() 
  1097. {
  1098. long keySizeInBytes = sizeof (PageID);
  1099. long keySizeInSizeOfInt;
  1100. if ((keySizeInBytes % sizeof (int)) == 0) {
  1101. keySizeInSizeOfInt = keySizeInBytes / sizeof (int);
  1102. } else {
  1103. keySizeInSizeOfInt = keySizeInBytes / sizeof (int) + 1;
  1104. }
  1105. pref_ = new Tcl_HashTable;
  1106. Tcl_InitHashTable(pref_, keySizeInSizeOfInt);
  1107. cmap_ = new Tcl_HashTable;
  1108. Tcl_InitHashTable(cmap_, TCL_ONE_WORD_KEYS);
  1109. }
  1110. MediaServer::~MediaServer() 
  1111. {
  1112. Tcl_HashEntry *he;
  1113. Tcl_HashSearch hs;
  1114. if (pref_ != NULL) {
  1115. for (he = Tcl_FirstHashEntry(pref_, &hs);  he != NULL;
  1116.      he = Tcl_NextHashEntry(&hs)) {
  1117. PrefInfo *pi = (PrefInfo*)Tcl_GetHashValue(he);
  1118. pi->sl_->destroy();
  1119. delete pi->sl_;
  1120. }
  1121. Tcl_DeleteHashTable(pref_);
  1122. delete pref_;
  1123. }
  1124. if (cmap_ != NULL) {
  1125. for (he = Tcl_FirstHashEntry(cmap_, &hs);  he != NULL;
  1126.      he = Tcl_NextHashEntry(&hs))
  1127. delete (RegInfo*)Tcl_GetHashValue(he);
  1128. Tcl_DeleteHashTable(cmap_);
  1129. delete cmap_;
  1130. }
  1131. }
  1132. // Return the next segment to be sent to a particular application
  1133. MediaSegment MediaServer::get_next_segment(MediaRequest *r, Application*& ci)
  1134. {
  1135. MediaPage* pg = (MediaPage*)pool_->get_page(r->name());
  1136. assert(pg != NULL);
  1137. // XXX Extremely hacky way to map media app names to 
  1138. // HTTP connections. Should maintain another hash table for this.
  1139. RegInfo *ri = get_reginfo(r->app());
  1140. assert(ri != NULL);
  1141. PrefInfoQ* q = get_piq(r->name(), ri->client_);
  1142. // We are not on the prefetching list, send a normal data segment
  1143. if ((q == NULL) || (q->is_empty())) {
  1144. MediaSegment s1(r->st(), r->et());
  1145. return pg->next_overlap(r->layer(), s1);
  1146. }
  1147. // Cycle through the prefetched segments that we need to send
  1148. int found = 0;
  1149. int searched = 0;
  1150. PrefInfo *pi; 
  1151. while (!found) {
  1152. PrefInfoE *pe = q->dequeue();
  1153. pi = pe->data();
  1154. q->enqueue(pe);
  1155. // If there's a pending segment in any layer, send it
  1156. for (int i = 0; i < pg->num_layer(); i++) 
  1157. if (pi->sl_[i].length() > 0) 
  1158. found = 1;
  1159. // If no pending prefetched segments, return empty
  1160. if (searched++ == q->size()) 
  1161. return MediaSegment(0, 0);
  1162. }
  1163. // Send a segment from the prefetching list. Only use the data size
  1164. // included in the request.
  1165. MediaSegmentList *p = pi->sl_;
  1166. // Set return conid
  1167. ci = pi->conid_;
  1168. // Find one available segment in prefetching list if there is none
  1169. // in the given layer
  1170. int l = r->layer(), i = 0;
  1171. MediaSegment res;
  1172. while ((res.datasize() == 0) && (i < pg->num_layer())) {
  1173. // next() doesn't work. Need a method that returns the 
  1174. // *FIRST* non-empty segment which satisfies the size 
  1175. // constraint.
  1176. res = p[l].get_nextseg(MediaSegment(0, r->datasize()));
  1177. i++;
  1178. l = (l+1) % pg->num_layer();
  1179. }
  1180. // XXX We must do boundary check of the prefetched segments to make
  1181. // sure that the start and end offsets are valid!
  1182. if (res.start() < 0) 
  1183. res.set_start(0);
  1184. if (res.end() > pg->layer_size(l))
  1185. res.set_end(pg->layer_size(l));
  1186. if (res.datasize() > 0) {
  1187. // XXX We may end up getting data from another layer!!
  1188. l = (l-1+pg->num_layer()) % pg->num_layer();
  1189. if (l != r->layer())
  1190. r->set_layer(l);
  1191. // We may not be able to get the specified data size, due 
  1192. // to arbitrary stream lengths
  1193. //assert(res.datasize() == r->datasize());
  1194. p[r->layer()].evict_head(r->datasize());
  1195. }
  1196. // Set the prefetching flag of this segment
  1197. res.set_pref();
  1198. return res;
  1199. }
  1200. // Similar to MediaCache::get_data(), but ignore segment availability checking
  1201. AppData* MediaServer::get_data(int& size, AppData *req)
  1202. {
  1203. assert((req != NULL) && (req->type() == MEDIA_REQUEST));
  1204. MediaRequest *r = (MediaRequest *)req;
  1205. Application* conid = NULL;
  1206. if (r->request() == MEDIAREQ_GETSEG) {
  1207. // Get a new data segment
  1208. MediaSegment s2 = get_next_segment(r, conid);
  1209. HttpMediaData *p;
  1210. if (s2.datasize() == 0) {
  1211. // No more data available for this layer, most likely
  1212. // it's because this layer is finished.
  1213. size = 0;
  1214. p = new HttpMediaData(name(), r->name(),
  1215.       r->layer(), 0, 0);
  1216. } else {
  1217. size = s2.datasize();
  1218. p = new HttpMediaData(name(), r->name(),
  1219.    r->layer(), s2.start(), s2.end());
  1220. }
  1221. if (s2.is_last()) {
  1222. p->set_last();
  1223. // Tear down the connection after we've sent the last
  1224. // segment of the base layer and are requested again.
  1225. if ((s2.datasize() == 0) && (r->layer() == 0))
  1226. p->set_finish();
  1227. }
  1228. if (s2.is_pref()) {
  1229. // Add connection id into returned data
  1230. p->set_conid(conid);
  1231. p->set_pref();
  1232. }
  1233. return p;
  1234. } else if (r->request() == MEDIAREQ_CHECKSEG) 
  1235. // We don't need to return anything, so just NULL
  1236. return NULL;
  1237. else {
  1238. fprintf(stderr, 
  1239.        "MediaServer %s gets an unknown MediaRequest type %dn",
  1240. name(), r->request());
  1241. abort();
  1242. }
  1243. /*NOTREACHED*/
  1244. return NULL; // Make msvc happy
  1245. }
  1246. int MediaServer::command(int argc, const char*const* argv)
  1247. {
  1248. Tcl& tcl = Tcl::instance();
  1249. if (argc == 3) {
  1250. if (strcmp(argv[1], "is-media-page") == 0) {
  1251. ClientPage *pg = pool_->get_page(argv[2]);
  1252. if (pg && (pg->type() == MEDIA))
  1253. tcl.result("1");
  1254. else 
  1255. tcl.result("0");
  1256. return TCL_OK;
  1257. }
  1258. } else if (argc == 5) { 
  1259. if (strcmp(argv[1], "stop-prefetching") == 0) {
  1260. /*
  1261.  * <server> stop-prefetching <Client> <conid> <pagenum>
  1262.  */
  1263. HttpApp *app = static_cast <HttpApp *> (TclObject::lookup(argv[2]));
  1264. assert(app != NULL);
  1265. int id = atoi (argv[4]);
  1266. PageID pageId (app, id);
  1267. Tcl_HashEntry *he = 
  1268. Tcl_FindHashEntry(pref_, (const char*)&pageId);
  1269. if (he == NULL) {
  1270. tcl.add_errorf(
  1271.   "Server %d cannot stop prefetching!n", id_);
  1272. return TCL_ERROR;
  1273. }
  1274. TclObject *conId = TclObject::lookup(argv[3]);
  1275. assert(conId != NULL);
  1276. PrefInfoQ *q = (PrefInfoQ*)Tcl_GetHashValue(he);
  1277. PrefInfoE *pe = find_prefinfo(q, (Application*)conId);
  1278. assert(pe != NULL);
  1279. PrefInfo *pi = pe->data();
  1280. MediaSegmentList *p = pi->sl_;
  1281. assert(p != NULL);
  1282. for (int i = 0; i < MAX_LAYER; i++)
  1283. p[i].destroy();
  1284. delete []p;
  1285. delete pi;
  1286. q->detach(pe);
  1287. delete pe;
  1288. // If no more prefetching streams left for this client,
  1289. // delete all the information.
  1290. // Return 0 means that we still have prefetching 
  1291. // clients left, don't tear down the channel yet. 
  1292. // Otherwise return 1. 
  1293. int res = 0;
  1294. if (q->is_empty()) {
  1295. delete q;
  1296. Tcl_DeleteHashEntry(he);
  1297. res = 1;
  1298. }
  1299. tcl.resultf("%d", res);
  1300. return (TCL_OK);
  1301. } else if (strcmp(argv[1], "register-client") == 0) {
  1302. // <cache> register-client <app> <client> <pageid>
  1303. TclObject *a = TclObject::lookup(argv[2]);
  1304. assert(a != NULL);
  1305. int newEntry;
  1306. Tcl_HashEntry *he = Tcl_CreateHashEntry(cmap_, 
  1307. (const char *)a, &newEntry);
  1308. if (he == NULL) {
  1309. tcl.add_errorf("cannot create hash entry");
  1310. return TCL_ERROR;
  1311. }
  1312. if (!newEntry) {
  1313. tcl.add_errorf("duplicate connection");
  1314. return TCL_ERROR;
  1315. }
  1316. RegInfo *p = new RegInfo;
  1317. p->client_ = (HttpApp*)TclObject::lookup(argv[3]);
  1318. assert(p->client_ != NULL);
  1319. strcpy(p->name_, argv[4]);
  1320. Tcl_SetHashValue(he, (ClientData)p);
  1321. return TCL_OK;
  1322. } else if (strcmp(argv[1], "unregister-client") == 0) {
  1323. // <cache> unregister-client <app> <client> <pageid>
  1324. TclObject *a = TclObject::lookup(argv[2]);
  1325. assert(a != NULL);
  1326. Tcl_HashEntry *he = 
  1327. Tcl_FindHashEntry(cmap_, (const char*)a);
  1328. if (he == NULL) {
  1329. tcl.add_errorf("cannot find hash entry");
  1330. return TCL_ERROR;
  1331. }
  1332. RegInfo *p = (RegInfo*)Tcl_GetHashValue(he);
  1333. delete p;
  1334. Tcl_DeleteHashEntry(he);
  1335. return TCL_OK;
  1336. }
  1337. } else {
  1338. if (strcmp(argv[1], "enter-page") == 0) {
  1339. ClientPage *pg = pool_->enter_page(argc, argv);
  1340. if (pg == NULL)
  1341. return TCL_ERROR;
  1342. if (pg->type() == MEDIA) 
  1343. ((MediaPage*)pg)->create();
  1344. // Unlock the page after creation
  1345. ((MediaPage*)pg)->unlock(); 
  1346. return TCL_OK;
  1347. } else if (strcmp(argv[1], "register-prefetch") == 0) {
  1348. /*
  1349.  * <server> register-prefetch <client> <pagenum> 
  1350.  *  <conid> <layer> {<segments>}
  1351.  * Registers a list of segments to be prefetched by 
  1352.  * <client>, where each <segment> is a pair of 
  1353.  * (start, end). <pagenum> should be pageid without 
  1354.  * preceding [server:] prefix.
  1355.  * 
  1356.  * <conid> is the OTcl name of the original client 
  1357.  * who requested the page. This is used for the cache
  1358.  * to get statistics about a particular connection.
  1359.  * 
  1360.  * <client> is the requestor of the stream.
  1361.  */
  1362. HttpApp *app = static_cast <HttpApp *> (TclObject::lookup(argv[2]));
  1363. assert(app != NULL);
  1364. int id = atoi (argv[3]);
  1365. PageID pageId (app, id);
  1366. int newEntry = 1;
  1367. Tcl_HashEntry *he = Tcl_CreateHashEntry(pref_, 
  1368. (const char*)&pageId, &newEntry);
  1369. if (he == NULL) {
  1370. fprintf(stderr, "Cannot create entry.n");
  1371. return TCL_ERROR;
  1372. }
  1373. PrefInfo *pi;
  1374. PrefInfoE *pe;
  1375. PrefInfoQ *q; 
  1376. MediaSegmentList *p;
  1377. TclObject *conId = TclObject::lookup(argv[4]);
  1378. if (newEntry) {
  1379. q = new PrefInfoQ;
  1380. Tcl_SetHashValue(he, (ClientData)q);
  1381. pe = NULL;
  1382. } else {
  1383. q = (PrefInfoQ *)Tcl_GetHashValue(he);
  1384. pe = find_prefinfo(q, (Application*)conId);
  1385. }
  1386. if (pe == NULL) {
  1387. pi = new PrefInfo;
  1388. pi->conid_ = (Application*)conId;
  1389. p = pi->sl_ = new MediaSegmentList[MAX_LAYER];
  1390. q->enqueue(new PrefInfoE(pi));
  1391. } else {
  1392. pi = pe->data();
  1393. p = pi->sl_;
  1394. }
  1395. assert((pi != NULL) && (p != NULL));
  1396. // Preempt all old requests because they 
  1397. // cannot reach the cache "in time"
  1398. int layer = atoi(argv[5]);
  1399. p[layer].destroy();
  1400. // Add segments into prefetching list
  1401. assert(argc % 2 == 0);
  1402. for (int i = 6; i < argc; i+=2)
  1403. p[layer].add(MediaSegment(atoi(argv[i]), 
  1404.   atoi(argv[i+1])));
  1405. return TCL_OK;
  1406. }
  1407. }
  1408. return HttpServer::command(argc, argv);
  1409. }