|  | // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/tools/flip_server/output_ordering.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "net/tools/flip_server/flip_config.h" | 
|  | #include "net/tools/flip_server/sm_connection.h" | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | OutputOrdering::PriorityMapPointer::PriorityMapPointer() | 
|  | : ring(NULL), alarm_enabled(false) {} | 
|  |  | 
|  | OutputOrdering::PriorityMapPointer::~PriorityMapPointer() {} | 
|  |  | 
|  | // static | 
|  | double OutputOrdering::server_think_time_in_s_ = 0.0; | 
|  |  | 
|  | OutputOrdering::OutputOrdering(SMConnectionInterface* connection) | 
|  | : first_data_senders_threshold_(kInitialDataSendersThreshold), | 
|  | connection_(connection) { | 
|  | if (connection) | 
|  | epoll_server_ = connection->epoll_server(); | 
|  | } | 
|  |  | 
|  | OutputOrdering::~OutputOrdering() { Reset(); } | 
|  |  | 
|  | void OutputOrdering::Reset() { | 
|  | while (!stream_ids_.empty()) { | 
|  | StreamIdToPriorityMap::iterator sitpmi = stream_ids_.begin(); | 
|  | PriorityMapPointer& pmp = sitpmi->second; | 
|  | if (pmp.alarm_enabled) { | 
|  | epoll_server_->UnregisterAlarm(pmp.alarm_token); | 
|  | } | 
|  | stream_ids_.erase(sitpmi); | 
|  | } | 
|  | priority_map_.clear(); | 
|  | first_data_senders_.clear(); | 
|  | } | 
|  |  | 
|  | bool OutputOrdering::ExistsInPriorityMaps(uint32 stream_id) const { | 
|  | StreamIdToPriorityMap::const_iterator sitpmi = stream_ids_.find(stream_id); | 
|  | return sitpmi != stream_ids_.end(); | 
|  | } | 
|  |  | 
|  | OutputOrdering::BeginOutputtingAlarm::BeginOutputtingAlarm( | 
|  | OutputOrdering* oo, | 
|  | OutputOrdering::PriorityMapPointer* pmp, | 
|  | const MemCacheIter& mci) | 
|  | : output_ordering_(oo), pmp_(pmp), mci_(mci), epoll_server_(NULL) {} | 
|  |  | 
|  | OutputOrdering::BeginOutputtingAlarm::~BeginOutputtingAlarm() { | 
|  | if (epoll_server_ && pmp_->alarm_enabled) | 
|  | epoll_server_->UnregisterAlarm(pmp_->alarm_token); | 
|  | } | 
|  |  | 
|  | int64 OutputOrdering::BeginOutputtingAlarm::OnAlarm() { | 
|  | OnUnregistration(); | 
|  | output_ordering_->MoveToActive(pmp_, mci_); | 
|  | VLOG(2) << "ON ALARM! Should now start to output..."; | 
|  | delete this; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void OutputOrdering::BeginOutputtingAlarm::OnRegistration( | 
|  | const EpollServer::AlarmRegToken& tok, | 
|  | EpollServer* eps) { | 
|  | epoll_server_ = eps; | 
|  | pmp_->alarm_token = tok; | 
|  | pmp_->alarm_enabled = true; | 
|  | } | 
|  |  | 
|  | void OutputOrdering::BeginOutputtingAlarm::OnUnregistration() { | 
|  | pmp_->alarm_enabled = false; | 
|  | delete this; | 
|  | } | 
|  |  | 
|  | void OutputOrdering::BeginOutputtingAlarm::OnShutdown(EpollServer* eps) { | 
|  | OnUnregistration(); | 
|  | } | 
|  |  | 
|  | void OutputOrdering::MoveToActive(PriorityMapPointer* pmp, MemCacheIter mci) { | 
|  | VLOG(2) << "Moving to active!"; | 
|  | first_data_senders_.push_back(mci); | 
|  | pmp->ring = &first_data_senders_; | 
|  | pmp->it = first_data_senders_.end(); | 
|  | --pmp->it; | 
|  | connection_->ReadyToSend(); | 
|  | } | 
|  |  | 
|  | void OutputOrdering::AddToOutputOrder(const MemCacheIter& mci) { | 
|  | if (ExistsInPriorityMaps(mci.stream_id)) | 
|  | LOG(ERROR) << "OOps, already was inserted here?!"; | 
|  |  | 
|  | double think_time_in_s = server_think_time_in_s_; | 
|  | std::string x_server_latency = | 
|  | mci.file_data->headers()->GetHeader("X-Server-Latency").as_string(); | 
|  | if (!x_server_latency.empty()) { | 
|  | char* endp; | 
|  | double tmp_think_time_in_s = strtod(x_server_latency.c_str(), &endp); | 
|  | if (endp != x_server_latency.c_str() + x_server_latency.size()) { | 
|  | LOG(ERROR) << "Unable to understand X-Server-Latency of: " | 
|  | << x_server_latency | 
|  | << " for resource: " << mci.file_data->filename().c_str(); | 
|  | } else { | 
|  | think_time_in_s = tmp_think_time_in_s; | 
|  | } | 
|  | } | 
|  | StreamIdToPriorityMap::iterator sitpmi; | 
|  | sitpmi = stream_ids_.insert(std::pair<uint32, PriorityMapPointer>( | 
|  | mci.stream_id, PriorityMapPointer())).first; | 
|  | PriorityMapPointer& pmp = sitpmi->second; | 
|  |  | 
|  | BeginOutputtingAlarm* boa = new BeginOutputtingAlarm(this, &pmp, mci); | 
|  | VLOG(1) << "Server think time: " << think_time_in_s; | 
|  | epoll_server_->RegisterAlarmApproximateDelta(think_time_in_s * 1000000, boa); | 
|  | } | 
|  |  | 
|  | void OutputOrdering::SpliceToPriorityRing(PriorityRing::iterator pri) { | 
|  | MemCacheIter& mci = *pri; | 
|  | PriorityMap::iterator pmi = priority_map_.find(mci.priority); | 
|  | if (pmi == priority_map_.end()) { | 
|  | pmi = priority_map_.insert(std::pair<uint32, PriorityRing>( | 
|  | mci.priority, PriorityRing())).first; | 
|  | } | 
|  |  | 
|  | pmi->second.splice(pmi->second.end(), first_data_senders_, pri); | 
|  | StreamIdToPriorityMap::iterator sitpmi = stream_ids_.find(mci.stream_id); | 
|  | sitpmi->second.ring = &(pmi->second); | 
|  | } | 
|  |  | 
|  | MemCacheIter* OutputOrdering::GetIter() { | 
|  | while (!first_data_senders_.empty()) { | 
|  | MemCacheIter& mci = first_data_senders_.front(); | 
|  | if (mci.bytes_sent >= first_data_senders_threshold_) { | 
|  | SpliceToPriorityRing(first_data_senders_.begin()); | 
|  | } else { | 
|  | first_data_senders_.splice(first_data_senders_.end(), | 
|  | first_data_senders_, | 
|  | first_data_senders_.begin()); | 
|  | mci.max_segment_size = kInitialDataSendersThreshold; | 
|  | return &mci; | 
|  | } | 
|  | } | 
|  | while (!priority_map_.empty()) { | 
|  | PriorityRing& first_ring = priority_map_.begin()->second; | 
|  | if (first_ring.empty()) { | 
|  | priority_map_.erase(priority_map_.begin()); | 
|  | continue; | 
|  | } | 
|  | MemCacheIter& mci = first_ring.front(); | 
|  | first_ring.splice(first_ring.end(), first_ring, first_ring.begin()); | 
|  | mci.max_segment_size = kSpdySegmentSize; | 
|  | return &mci; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | void OutputOrdering::RemoveStreamId(uint32 stream_id) { | 
|  | StreamIdToPriorityMap::iterator sitpmi = stream_ids_.find(stream_id); | 
|  | if (sitpmi == stream_ids_.end()) | 
|  | return; | 
|  |  | 
|  | PriorityMapPointer& pmp = sitpmi->second; | 
|  | if (pmp.alarm_enabled) | 
|  | epoll_server_->UnregisterAlarm(pmp.alarm_token); | 
|  | else | 
|  | pmp.ring->erase(pmp.it); | 
|  | stream_ids_.erase(sitpmi); | 
|  | } | 
|  |  | 
|  | }  // namespace net |