Asynchronous NetworkManager and various other fixes.
git-svn-id: http://libjingle.googlecode.com/svn/trunk@73 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/talk/base/autodetectproxy.cc b/talk/base/autodetectproxy.cc
index ffcac26..f79926d 100644
--- a/talk/base/autodetectproxy.cc
+++ b/talk/base/autodetectproxy.cc
@@ -38,6 +38,15 @@
PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN
};
+static const int kSavedStringLimit = 128;
+
+static void SaveStringToStack(char *dst,
+ const std::string &src,
+ size_t dst_size) {
+ strncpy(dst, src.c_str(), dst_size - 1);
+ dst[dst_size - 1] = '\0';
+}
+
AutoDetectProxy::AutoDetectProxy(const std::string& user_agent)
: agent_(user_agent), socket_(NULL), next_(0) {
}
@@ -75,7 +84,54 @@
if (MSG_TIMEOUT == msg->message_id) {
OnCloseEvent(socket_, ETIMEDOUT);
} else {
+ // This must be the ST_MSG_WORKER_DONE message that deletes the
+ // AutoDetectProxy object. We have observed crashes within this stack that
+ // seem to be highly reproducible for a small subset of users and thus are
+ // probably correlated with a specific proxy setting, so copy potentially
+ // relevant information onto the stack to make it available in Windows
+ // minidumps.
+
+ // Save the user agent and the number of auto-detection passes that we
+ // needed.
+ char agent[kSavedStringLimit];
+ SaveStringToStack(agent, agent_, sizeof agent);
+
+ int next = next_;
+
+ // Now the detected proxy config (minus the password field, which could be
+ // sensitive).
+ ProxyType type = proxy().type;
+
+ char address_hostname[kSavedStringLimit];
+ SaveStringToStack(address_hostname,
+ proxy().address.hostname(),
+ sizeof address_hostname);
+
+ uint32 address_ip = proxy().address.ip();
+
+ uint16 address_port = proxy().address.port();
+
+ char autoconfig_url[kSavedStringLimit];
+ SaveStringToStack(autoconfig_url,
+ proxy().autoconfig_url,
+ sizeof autoconfig_url);
+
+ bool autodetect = proxy().autodetect;
+
+ char bypass_list[kSavedStringLimit];
+ SaveStringToStack(bypass_list, proxy().bypass_list, sizeof bypass_list);
+
+ char username[kSavedStringLimit];
+ SaveStringToStack(username, proxy().username, sizeof username);
+
SignalThread::OnMessage(msg);
+
+ // Log the gathered data at a log level that will never actually be enabled
+ // so that the compiler is forced to retain the data on the stack.
+ LOG(LS_SENSITIVE) << agent << " " << next << " " << type << " "
+ << address_hostname << " " << address_ip << " "
+ << address_port << " " << autoconfig_url << " "
+ << autodetect << " " << bypass_list << " " << username;
}
}
diff --git a/talk/base/autodetectproxy.h b/talk/base/autodetectproxy.h
index 6bb2a4b..121737f 100644
--- a/talk/base/autodetectproxy.h
+++ b/talk/base/autodetectproxy.h
@@ -30,6 +30,7 @@
#include <string>
+#include "talk/base/constructormagic.h"
#include "talk/base/cryptstring.h"
#include "talk/base/proxyinfo.h"
#include "talk/base/signalthread.h"
@@ -83,6 +84,8 @@
ProxyInfo proxy_;
AsyncSocket* socket_;
int next_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AutoDetectProxy);
};
} // namespace talk_base
diff --git a/talk/base/messagehandler.h b/talk/base/messagehandler.h
index eb7db9b..bf7a4c2 100644
--- a/talk/base/messagehandler.h
+++ b/talk/base/messagehandler.h
@@ -25,8 +25,10 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef TALK_BASE_MESSAGEHANDLER_H__
-#define TALK_BASE_MESSAGEHANDLER_H__
+#ifndef TALK_BASE_MESSAGEHANDLER_H_
+#define TALK_BASE_MESSAGEHANDLER_H_
+
+#include "talk/base/constructormagic.h"
namespace talk_base {
@@ -35,12 +37,18 @@
// Messages get dispatched to a MessageHandler
class MessageHandler {
-public:
+ public:
virtual ~MessageHandler();
virtual void OnMessage(Message* msg) = 0;
+
+ protected:
+ MessageHandler() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MessageHandler);
};
} // namespace talk_base
-#endif // TALK_BASE_MESSAGEHANDLER_H__
+#endif // TALK_BASE_MESSAGEHANDLER_H_
diff --git a/talk/base/messagequeue.h b/talk/base/messagequeue.h
index 4d470df..54d4860 100644
--- a/talk/base/messagequeue.h
+++ b/talk/base/messagequeue.h
@@ -35,6 +35,7 @@
#include <vector>
#include "talk/base/basictypes.h"
+#include "talk/base/constructormagic.h"
#include "talk/base/criticalsection.h"
#include "talk/base/messagehandler.h"
#include "talk/base/scoped_ptr.h"
@@ -238,6 +239,9 @@
PriorityQueue dmsgq_;
uint32 dmsgq_next_num_;
CriticalSection crit_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MessageQueue);
};
} // namespace talk_base
diff --git a/talk/base/network.cc b/talk/base/network.cc
index e5dffc1..2feb361 100644
--- a/talk/base/network.cc
+++ b/talk/base/network.cc
@@ -30,7 +30,6 @@
#endif
#include "talk/base/network.h"
-#include "talk/base/stream.h"
#ifdef POSIX
#include <sys/socket.h>
@@ -47,162 +46,23 @@
#endif
#include <algorithm>
-#include <cassert>
-#include <cfloat>
-#include <cmath>
#include <cstdio>
-#include <cstring>
-#include <sstream>
#include "talk/base/host.h"
#include "talk/base/logging.h"
#include "talk/base/scoped_ptr.h"
#include "talk/base/socket.h" // includes something that makes windows happy
-#include "talk/base/stringencode.h"
-#include "talk/base/time.h"
-
-namespace {
-
-const double kAlpha = 0.5; // weight for data infinitely far in the past
-const double kHalfLife = 2000; // half life of exponential decay (in ms)
-const double kLog2 = 0.693147180559945309417;
-const double kLambda = kLog2 / kHalfLife;
-
-// assume so-so quality unless data says otherwise
-const double kDefaultQuality = talk_base::QUALITY_FAIR;
-
-typedef std::map<std::string, std::string> StrMap;
-
-void BuildMap(const StrMap& map, std::string& str) {
- str.append("{");
- bool first = true;
- for (StrMap::const_iterator i = map.begin(); i != map.end(); ++i) {
- if (!first) str.append(",");
- str.append(i->first);
- str.append("=");
- str.append(i->second);
- first = false;
- }
- str.append("}");
-}
-
-void ParseCheck(std::istringstream& ist, char ch) {
- if (ist.get() != ch)
- LOG(LERROR) << "Expecting '" << ch << "'";
-}
-
-std::string ParseString(std::istringstream& ist) {
- std::string str;
- int count = 0;
- while (ist) {
- char ch = ist.peek();
- if ((count == 0) && ((ch == '=') || (ch == ',') || (ch == '}'))) {
- break;
- } else if (ch == '{') {
- count += 1;
- } else if (ch == '}') {
- count -= 1;
- if (count < 0)
- LOG(LERROR) << "mismatched '{' and '}'";
- }
- str.append(1, static_cast<char>(ist.get()));
- }
- return str;
-}
-
-void ParseMap(const std::string& str, StrMap& map) {
- if (str.size() == 0)
- return;
- std::istringstream ist(str);
- ParseCheck(ist, '{');
- for (;;) {
- std::string key = ParseString(ist);
- ParseCheck(ist, '=');
- std::string val = ParseString(ist);
- map[key] = val;
- if (ist.peek() == ',')
- ist.get();
- else
- break;
- }
- ParseCheck(ist, '}');
- if (ist.rdbuf()->in_avail() != 0)
- LOG(LERROR) << "Unexpected characters at end";
-}
-
-} // namespace
+#include "talk/base/stream.h"
+#include "talk/base/thread.h"
namespace talk_base {
-NetworkManager::~NetworkManager() {
- for (NetworkMap::iterator i = networks_.begin(); i != networks_.end(); ++i)
- delete i->second;
-}
+namespace {
-bool NetworkManager::GetNetworks(std::vector<Network*>* result) {
- std::vector<Network*> list;
- if (!EnumNetworks(false, &list)) {
- return false;
- }
+const uint32 kUpdateNetworksMessage = 1;
- for (uint32 i = 0; i < list.size(); ++i) {
- NetworkMap::iterator iter = networks_.find(list[i]->name());
-
- Network* network;
- if (iter == networks_.end()) {
- network = list[i];
- } else {
- network = iter->second;
- network->set_ip(list[i]->ip());
- network->set_gateway_ip(list[i]->gateway_ip());
- delete list[i];
- }
-
- networks_[network->name()] = network;
- result->push_back(network);
- }
- return true;
-}
-
-void NetworkManager::DumpNetworks(bool include_ignored) {
- std::vector<Network*> list;
- EnumNetworks(include_ignored, &list);
- LOG(LS_INFO) << "NetworkManager detected " << list.size() << " networks:";
- for (size_t i = 0; i < list.size(); ++i) {
- const Network* network = list[i];
- if (!network->ignored() || include_ignored) {
- LOG(LS_INFO) << network->ToString() << ": " << network->description()
- << ", Gateway="
- << SocketAddress::IPToString(network->gateway_ip())
- << ((network->ignored()) ? ", Ignored" : "");
- }
- }
-}
-
-std::string NetworkManager::GetState() const {
- StrMap map;
- for (NetworkMap::const_iterator i = networks_.begin();
- i != networks_.end(); ++i)
- map[i->first] = i->second->GetState();
-
- std::string str;
- BuildMap(map, str);
- return str;
-}
-
-void NetworkManager::SetState(const std::string& str) {
- StrMap map;
- ParseMap(str, map);
-
- for (StrMap::iterator i = map.begin(); i != map.end(); ++i) {
- std::string name = i->first;
- std::string state = i->second;
-
- Network* network = new Network(name, "", 0, 0);
- network->SetState(state);
- networks_[name] = network;
- }
-}
+// Fetch list of networks every two seconds.
+const int kNetworksUpdateIntervalMs = 2000;
#ifdef POSIX
// Gets the default gateway for the specified interface.
@@ -215,10 +75,10 @@
uint32 gateway_ip = 0;
- FileStream fs;
+ talk_base::FileStream fs;
if (fs.Open("/proc/net/route", "r", NULL)) {
std::string line;
- while (fs.ReadLine(&line) == SR_SUCCESS && gateway_ip == 0) {
+ while (fs.ReadLine(&line) == talk_base::SR_SUCCESS && gateway_ip == 0) {
char iface[16];
unsigned int ip, gw;
if (sscanf(line.c_str(), "%7s %8X %8X", iface, &ip, &gw) == 3 &&
@@ -230,10 +90,91 @@
return gateway_ip;
}
+#endif // POSIX
+bool CompareNetworks(const Network* a, const Network* b) {
+ return a->name() < b->name();
+}
-bool NetworkManager::CreateNetworks(bool include_ignored,
- std::vector<Network*>* networks) {
+} // namespace
+
+NetworkManager::NetworkManager() {
+}
+
+NetworkManager::~NetworkManager() {
+}
+
+NetworkManagerBase::NetworkManagerBase() {
+}
+
+NetworkManagerBase::~NetworkManagerBase() {
+ for (NetworkMap::iterator i = networks_map_.begin();
+ i != networks_map_.end(); ++i) {
+ delete i->second;
+ }
+}
+
+void NetworkManagerBase::GetNetworks(NetworkList* result) const {
+ *result = networks_;
+}
+
+void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks,
+ bool force_notification) {
+ // Sort the list so that we can detect when it changes.
+ NetworkList list(new_networks);
+ std::sort(list.begin(), list.end(), CompareNetworks);
+
+ bool changed = false;
+
+ if (networks_.size() != list.size())
+ changed = true;
+
+ networks_.resize(list.size());
+
+ for (uint32 i = 0; i < list.size(); ++i) {
+ NetworkMap::iterator iter = networks_map_.find(list[i]->name());
+
+ Network* network;
+ if (iter == networks_map_.end()) {
+ // That's a new network, add it to the map.
+ network = list[i];
+ networks_map_[network->name()] = network;
+ } else {
+ network = iter->second;
+ if (network->ip() != list[i]->ip()) {
+ changed = true;
+ network->set_ip(list[i]->ip());
+ }
+
+ if (network->gateway_ip() != list[i]->gateway_ip()) {
+ changed = true;
+ network->set_gateway_ip(list[i]->gateway_ip());
+ }
+
+ delete list[i];
+ }
+
+ if (!changed && networks_[i]->name() != network->name())
+ changed = true;
+
+ networks_[i] = network;
+ }
+
+ if (changed || force_notification)
+ SignalNetworksChanged();
+}
+
+BasicNetworkManager::BasicNetworkManager()
+ : thread_(NULL),
+ started_(false) {
+}
+
+BasicNetworkManager::~BasicNetworkManager() {
+}
+
+#ifdef POSIX
+bool BasicNetworkManager::CreateNetworks(bool include_ignored,
+ NetworkList* networks) {
int fd;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
LOG_ERR(LERROR) << "socket";
@@ -248,7 +189,7 @@
LOG_ERR(LERROR) << "ioctl";
return false;
}
- assert(ifc.ifc_len < static_cast<int>(64 * sizeof(struct ifreq)));
+ ASSERT(ifc.ifc_len < static_cast<int>(64 * sizeof(struct ifreq)));
struct ifreq* ptr = reinterpret_cast<struct ifreq*>(ifc.ifc_buf);
struct ifreq* end =
@@ -283,8 +224,8 @@
#endif // POSIX
#ifdef WIN32
-bool NetworkManager::CreateNetworks(bool include_ignored,
- std::vector<Network*>* networks) {
+bool BasicNetworkManager::CreateNetworks(bool include_ignored,
+ NetworkList* networks) {
IP_ADAPTER_INFO info_temp;
ULONG len = 0;
@@ -332,7 +273,7 @@
}
#endif // WIN32
-bool NetworkManager::IsIgnoredNetwork(const Network& network) {
+bool BasicNetworkManager::IsIgnoredNetwork(const Network& network) {
#ifdef POSIX
// Ignore local networks (lo, lo0, etc)
// Also filter out VMware interfaces, typically named vmnet1 and vmnet8
@@ -354,57 +295,65 @@
return (network.ip() < 0x01000000);
}
-bool NetworkManager::EnumNetworks(bool include_ignored,
- std::vector<Network*>* result) {
- return CreateNetworks(include_ignored, result);
+void BasicNetworkManager::StartUpdating() {
+ if (started_) {
+ sent_first_update_ = false;
+ return;
+ }
+
+ thread_ = Thread::Current();
+ started_ = true;
+ sent_first_update_ = false;
+ thread_->Post(this, kUpdateNetworksMessage);
}
+void BasicNetworkManager::StopUpdating() {
+ ASSERT(Thread::Current() == thread_);
+ started_ = false;
+}
+
+void BasicNetworkManager::OnMessage(Message* msg) {
+ ASSERT(msg->message_id == kUpdateNetworksMessage);
+ DoUpdateNetworks();
+}
+
+void BasicNetworkManager::DoUpdateNetworks() {
+ if (!started_)
+ return;
+
+ ASSERT(Thread::Current() == thread_);
+
+ NetworkList list;
+ if (!CreateNetworks(false, &list)) {
+ SignalError();
+ } else {
+ MergeNetworkList(list, !sent_first_update_);
+ sent_first_update_ = true;
+ }
+
+ thread_->PostDelayed(kNetworksUpdateIntervalMs, this, kUpdateNetworksMessage);
+}
+
+void BasicNetworkManager::DumpNetworks(bool include_ignored) {
+ NetworkList list;
+ CreateNetworks(include_ignored, &list);
+ LOG(LS_INFO) << "NetworkManager detected " << list.size() << " networks:";
+ for (size_t i = 0; i < list.size(); ++i) {
+ const Network* network = list[i];
+ if (!network->ignored() || include_ignored) {
+ LOG(LS_INFO) << network->ToString() << ": " << network->description()
+ << ", Gateway="
+ << SocketAddress::IPToString(network->gateway_ip())
+ << ((network->ignored()) ? ", Ignored" : "");
+ }
+ }
+}
Network::Network(const std::string& name, const std::string& desc,
uint32 ip, uint32 gateway_ip)
: name_(name), description_(desc), ip_(ip), gateway_ip_(gateway_ip),
ignored_(false), uniform_numerator_(0), uniform_denominator_(0),
- exponential_numerator_(0), exponential_denominator_(0),
- quality_(kDefaultQuality) {
- last_data_time_ = Time();
-
- // TODO: seed the historical data with one data point based
- // on the link speed metric from XP (4.0 if < 50, 3.0 otherwise).
-}
-
-void Network::StartSession(NetworkSession* session) {
- assert(std::find(sessions_.begin(), sessions_.end(), session) ==
- sessions_.end());
- sessions_.push_back(session);
-}
-
-void Network::StopSession(NetworkSession* session) {
- SessionList::iterator iter =
- std::find(sessions_.begin(), sessions_.end(), session);
- if (iter != sessions_.end())
- sessions_.erase(iter);
-}
-
-void Network::EstimateQuality() {
- uint32 now = Time();
-
- // Add new data points for the current time.
- for (uint32 i = 0; i < sessions_.size(); ++i) {
- if (sessions_[i]->HasQuality())
- AddDataPoint(now, sessions_[i]->GetCurrentQuality());
- }
-
- // Construct the weighted average using both uniform and exponential weights.
-
- double exp_shift = exp(-kLambda * (now - last_data_time_));
- double numerator = uniform_numerator_ + exp_shift * exponential_numerator_;
- double denominator = uniform_denominator_ + exp_shift *
- exponential_denominator_;
-
- if (denominator < DBL_EPSILON)
- quality_ = kDefaultQuality;
- else
- quality_ = numerator / denominator;
+ exponential_numerator_(0), exponential_denominator_(0) {
}
std::string Network::ToString() const {
@@ -416,41 +365,4 @@
return ss.str();
}
-void Network::AddDataPoint(uint32 time, double quality) {
- uniform_numerator_ += kAlpha * quality;
- uniform_denominator_ += kAlpha;
-
- double exp_shift = exp(-kLambda * (time - last_data_time_));
- exponential_numerator_ = (1 - kAlpha) * quality + exp_shift *
- exponential_numerator_;
- exponential_denominator_ = (1 - kAlpha) + exp_shift *
- exponential_denominator_;
-
- last_data_time_ = time;
-}
-
-std::string Network::GetState() const {
- StrMap map;
- map["lt"] = talk_base::ToString<uint32>(last_data_time_);
- map["un"] = talk_base::ToString<double>(uniform_numerator_);
- map["ud"] = talk_base::ToString<double>(uniform_denominator_);
- map["en"] = talk_base::ToString<double>(exponential_numerator_);
- map["ed"] = talk_base::ToString<double>(exponential_denominator_);
-
- std::string str;
- BuildMap(map, str);
- return str;
-}
-
-void Network::SetState(const std::string& str) {
- StrMap map;
- ParseMap(str, map);
-
- last_data_time_ = FromString<uint32>(map["lt"]);
- uniform_numerator_ = FromString<double>(map["un"]);
- uniform_denominator_ = FromString<double>(map["ud"]);
- exponential_numerator_ = FromString<double>(map["en"]);
- exponential_denominator_ = FromString<double>(map["ed"]);
-}
-
} // namespace talk_base
diff --git a/talk/base/network.h b/talk/base/network.h
index 8153e54..4b315c3 100644
--- a/talk/base/network.h
+++ b/talk/base/network.h
@@ -34,50 +34,105 @@
#include <vector>
#include "talk/base/basictypes.h"
+#include "talk/base/messagehandler.h"
+#include "talk/base/sigslot.h"
namespace talk_base {
class Network;
class NetworkSession;
+class Thread;
-// Keeps track of the available network interfaces over time so that quality
-// information can be aggregated and recorded.
+// Generic network manager interface. It provides list of local
+// networks.
class NetworkManager {
public:
+ typedef std::vector<Network*> NetworkList;
+
+ NetworkManager();
virtual ~NetworkManager();
- // Updates and returns the current list of networks available on this machine.
- // This version will make sure that repeated calls return the same object for
- // a given network, so that quality is tracked appropriately.
- // Does not include ignored networks.
- bool GetNetworks(std::vector<Network*>* networks);
+ // Called when network list is updated.
+ sigslot::signal0<> SignalNetworksChanged;
- // Logs the available networks.
- void DumpNetworks(bool include_ignored);
+ // Indicates a failure when getting list of network interfaces.
+ sigslot::signal0<> SignalError;
- // Reads and writes the state of the quality database in a string format.
- std::string GetState() const;
- void SetState(const std::string& str);
+ // Start/Stop monitoring of network interfaces
+ // list. SignalNetworksChanged or SignalError is emitted immidiately
+ // after StartUpdating() is called. After that SignalNetworksChanged
+ // is emitted wheneven list of networks changes.
+ virtual void StartUpdating() = 0;
+ virtual void StopUpdating() = 0;
- // Creates a network object for each network available on the machine.
- static bool CreateNetworks(bool include_ignored,
- std::vector<Network*>* networks);
- // Determines if a network should be ignored.
- static bool IsIgnoredNetwork(const Network& network);
+ // Returns the current list of networks available on this machine.
+ // UpdateNetworks() must be called before this method is called.
+ // It makes sure that repeated calls return the same object for a
+ // given network, so that quality is tracked appropriately. Does not
+ // include ignored networks.
+ virtual void GetNetworks(NetworkList* networks) const = 0;
+};
+
+// Base class for NetworkManager implementations.
+class NetworkManagerBase : public NetworkManager {
+ public:
+ NetworkManagerBase();
+ virtual ~NetworkManagerBase();
+
+ virtual void GetNetworks(std::vector<Network*>* networks) const;
protected:
- // Fills the supplied list with all usable networks. Overrideable.
- virtual bool EnumNetworks(bool include_ignored,
- std::vector<Network*>* networks);
+ // Updates |networks_| with the networks listed in |list|. If
+ // |network_map_| already has a Network object for a network listed
+ // in the |list| then it is reused. Accept ownership of the Network
+ // objects in the |list|. SignalNetworkListUpdated is emitted if
+ // there is a change in network configuration or
+ // |force_notification| is set to true.
+ void MergeNetworkList(const NetworkList& list, bool force_notification);
private:
typedef std::map<std::string, Network*> NetworkMap;
- NetworkMap networks_;
+ void DoUpdateNetworks();
+
+ NetworkList networks_;
+ NetworkMap networks_map_;
+};
+
+// Basic implementation of the NetworkManager interface that gets list
+// of networks using OS APIs.
+class BasicNetworkManager : public NetworkManagerBase,
+ public MessageHandler {
+ public:
+ BasicNetworkManager();
+ virtual ~BasicNetworkManager();
+
+ virtual void StartUpdating();
+ virtual void StopUpdating();
+
+ // Logs the available networks.
+ static void DumpNetworks(bool include_ignored);
+
+ // MessageHandler interface.
+ virtual void OnMessage(Message* msg);
+
+ protected:
+ // Creates a network object for each network available on the machine.
+ static bool CreateNetworks(bool include_ignored, NetworkList* networks);
+ // Determines if a network should be ignored.
+ static bool IsIgnoredNetwork(const Network& network);
+
+ private:
+ friend class NetworkTest;
+
+ void DoUpdateNetworks();
+
+ Thread* thread_;
+ bool started_;
+ bool sent_first_update_;
};
// Represents a Unix-type network interface, with a name and single address.
-// It also includes the ability to track and estimate quality.
class Network {
public:
Network(const std::string& name, const std::string& description,
@@ -104,18 +159,6 @@
bool ignored() const { return ignored_; }
void set_ignored(bool ignored) { ignored_ = ignored; }
- // Updates the list of sessions that are ongoing.
- void StartSession(NetworkSession* session);
- void StopSession(NetworkSession* session);
-
- // Re-computes the estimate of near-future quality based on the information
- // as of this exact moment.
- void EstimateQuality();
-
- // Returns the current estimate of the near-future quality of connections
- // that use this local interface.
- double quality() { return quality_; }
-
// Debugging description of this network
std::string ToString() const;
@@ -132,39 +175,10 @@
double uniform_denominator_;
double exponential_numerator_;
double exponential_denominator_;
- uint32 last_data_time_;
- double quality_;
-
- // Updates the statistics maintained to include the given estimate.
- void AddDataPoint(uint32 time, double quality);
-
- // Converts the internal state to and from a string. This is used to record
- // quality information into a permanent store.
- void SetState(const std::string& str);
- std::string GetState() const;
friend class NetworkManager;
};
-// Represents a session that is in progress using a particular network and can
-// provide data about the quality of the network at any given moment.
-class NetworkSession {
- public:
- virtual ~NetworkSession() { }
-
- // Determines whether this session has an estimate at this moment. We will
- // only call GetCurrentQuality when this returns true.
- virtual bool HasQuality() = 0;
-
- // Returns an estimate of the quality at this exact moment. The result should
- // be a MOS (mean opinion score) value.
- virtual float GetCurrentQuality() = 0;
-};
-
-const double QUALITY_BAD = 3.0;
-const double QUALITY_FAIR = 3.35;
-const double QUALITY_GOOD = 3.7;
-
} // namespace talk_base
#endif // TALK_BASE_NETWORK_H_
diff --git a/talk/base/signalthread.h b/talk/base/signalthread.h
index d103acf..e7f2994 100644
--- a/talk/base/signalthread.h
+++ b/talk/base/signalthread.h
@@ -30,6 +30,7 @@
#include <string>
+#include "talk/base/constructormagic.h"
#include "talk/base/thread.h"
#include "talk/base/sigslot.h"
@@ -117,16 +118,17 @@
kStopping, // Work is being interrupted
};
- friend class Worker;
class Worker : public Thread {
public:
explicit Worker(SignalThread* parent) : parent_(parent) {}
virtual void Run() { parent_->Run(); }
+
private:
SignalThread* parent_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Worker);
};
- friend class EnterExit;
class EnterExit {
public:
explicit EnterExit(SignalThread* t) : t_(t) {
@@ -142,8 +144,11 @@
if (d)
delete t_;
}
+
private:
SignalThread* t_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(EnterExit);
};
void Run();
@@ -154,6 +159,8 @@
CriticalSection cs_;
State state_;
int refcount_;
+
+ DISALLOW_COPY_AND_ASSIGN(SignalThread);
};
///////////////////////////////////////////////////////////////////////////////
diff --git a/talk/base/stringencode.cc b/talk/base/stringencode.cc
index 81ab6e9..49dc42f 100644
--- a/talk/base/stringencode.cc
+++ b/talk/base/stringencode.cc
@@ -530,6 +530,44 @@
return fields->size();
}
+size_t tokenize_append(const std::string& source, char delimiter,
+ std::vector<std::string>* fields) {
+ if (!fields) return 0;
+
+ std::vector<std::string> new_fields;
+ tokenize(source, delimiter, &new_fields);
+ fields->insert(fields->end(), new_fields.begin(), new_fields.end());
+ return fields->size();
+}
+
+size_t tokenize(const std::string& source, char delimiter, char start_mark,
+ char end_mark, std::vector<std::string>* fields) {
+ if (!fields) return 0;
+ fields->clear();
+
+ std::string remain_source = source;
+ while (!remain_source.empty()) {
+ size_t start_pos = remain_source.find(start_mark);
+ if (std::string::npos == start_pos) break;
+ std::string pre_mark;
+ if (start_pos > 0) {
+ pre_mark = remain_source.substr(0, start_pos - 1);
+ }
+
+ ++start_pos;
+ size_t end_pos = remain_source.find(end_mark, start_pos);
+ if (std::string::npos == end_pos) break;
+
+ // We have found the matching marks. First tokenize the pre-mask. Then add
+ // the marked part as a single field. Finally, loop back for the post-mark.
+ tokenize_append(pre_mark, delimiter, fields);
+ fields->push_back(remain_source.substr(start_pos, end_pos - start_pos));
+ remain_source = remain_source.substr(end_pos + 1);
+ }
+
+ return tokenize_append(remain_source, delimiter, fields);
+}
+
size_t split(const std::string& source, char delimiter,
std::vector<std::string>* fields) {
ASSERT(NULL != fields);
diff --git a/talk/base/stringencode.h b/talk/base/stringencode.h
index a44b4e1..92c794b 100644
--- a/talk/base/stringencode.h
+++ b/talk/base/stringencode.h
@@ -135,6 +135,20 @@
size_t tokenize(const std::string& source, char delimiter,
std::vector<std::string>* fields);
+// Tokenize and append the tokens to fields. Return the new size of fields.
+size_t tokenize_append(const std::string& source, char delimiter,
+ std::vector<std::string>* fields);
+
+// Splits the source string into multiple fields separated by delimiter, with
+// duplicates of delimiter ignored. Trailing delimiter ignored. A substring in
+// between the start_mark and the end_mark is treated as a single field. Return
+// the size of fields. For example, if source is "filename
+// \"/Library/Application Support/media content.txt\"", delimiter is ' ', and
+// the start_mark and end_mark are '"', this method returns two fields:
+// "filename" and "/Library/Application Support/media content.txt".
+size_t tokenize(const std::string& source, char delimiter, char start_mark,
+ char end_mark, std::vector<std::string>* fields);
+
// Safe sprintf to std::string
//void sprintf(std::string& value, size_t maxlen, const char * format, ...)
// PRINTF_FORMAT(3);
diff --git a/talk/base/thread.h b/talk/base/thread.h
index 42656e9..efbdae7 100644
--- a/talk/base/thread.h
+++ b/talk/base/thread.h
@@ -37,6 +37,7 @@
#include <pthread.h>
#endif
+#include "talk/base/constructormagic.h"
#include "talk/base/messagequeue.h"
#ifdef WIN32
@@ -87,6 +88,8 @@
#ifdef WIN32
static DWORD key_;
#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadManager);
};
class Thread;
@@ -109,6 +112,12 @@
public:
virtual ~Runnable() {}
virtual void Run(Thread* thread) = 0;
+
+ protected:
+ Runnable() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Runnable);
};
class Thread : public MessageQueue {
@@ -208,6 +217,8 @@
bool owned_;
friend class ThreadManager;
+
+ DISALLOW_COPY_AND_ASSIGN(Thread);
};
// AutoThread automatically installs itself at construction
@@ -215,16 +226,25 @@
// _not already_ associated with the current OS thread.
class AutoThread : public Thread {
-public:
+ public:
AutoThread(SocketServer* ss = 0);
virtual ~AutoThread();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutoThread);
};
// Win32 extension for threads that need to use COM
#ifdef WIN32
class ComThread : public Thread {
+ public:
+ ComThread() {}
+
protected:
virtual void Run();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ComThread);
};
#endif
@@ -238,8 +258,11 @@
~SocketServerScope() {
Thread::Current()->set_socketserver(old_ss_);
}
+
private:
SocketServer* old_ss_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SocketServerScope);
};
} // namespace talk_base
diff --git a/talk/examples/call/call_main.cc b/talk/examples/call/call_main.cc
index b6ebb3a..0956836 100644
--- a/talk/examples/call/call_main.cc
+++ b/talk/examples/call/call_main.cc
@@ -46,6 +46,7 @@
#include "talk/examples/login/xmpppump.h"
#include "talk/examples/call/callclient.h"
#include "talk/examples/call/console.h"
+#include "talk/session/phone/fakemediaengine.h"
#include "talk/session/phone/filemediaengine.h"
#include "talk/session/phone/mediasessionclient.h"
#include "talk/session/phone/srtpfilter.h"
@@ -216,6 +217,7 @@
return file_media_engine;
}
+
// TODO: Move this into Console.
void Print(const char* chars) {
printf("%s", chars);
@@ -247,6 +249,7 @@
DEFINE_string(voiceinput, NULL, "RTP dump file for voice input.");
DEFINE_string(voiceoutput, NULL, "RTP dump file for voice output.");
DEFINE_string(videoinput, NULL, "RTP dump file for video input.");
+ DEFINE_string(yuvvideoinput, NULL, "YUV file for video input.");
DEFINE_string(videooutput, NULL, "RTP dump file for video output.");
DEFINE_bool(debugsrtp, false, "Enable debugging for srtp.");
DEFINE_bool(help, false, "Prints this message");
@@ -389,7 +392,6 @@
// The engine will be released by the client later.
client->SetMediaEngine(engine);
}
-
Console *console = new Console(main_thread, client);
client->SetConsole(console);
client->SetAutoAccept(auto_accept);
diff --git a/talk/examples/call/callclient.cc b/talk/examples/call/callclient.cc
index 3fbd579..943ccb5 100644
--- a/talk/examples/call/callclient.cc
+++ b/talk/examples/call/callclient.cc
@@ -329,7 +329,7 @@
// TODO: It looks like we are leaking many objects. E.g.
// |network_manager_| is never deleted.
- network_manager_ = new talk_base::NetworkManager();
+ network_manager_ = new talk_base::BasicNetworkManager();
// TODO: Decide if the relay address should be specified here.
talk_base::SocketAddress stun_addr("stun.l.google.com", 19302);
diff --git a/talk/examples/call/callclient.h b/talk/examples/call/callclient.h
index 5bfeb87..3af2b39 100644
--- a/talk/examples/call/callclient.h
+++ b/talk/examples/call/callclient.h
@@ -66,11 +66,11 @@
class PortAllocator;
class MediaEngine;
class MediaSessionClient;
-class NamedSource;
class Receiver;
class Call;
-struct CallOptions;
class SessionManagerTask;
+struct CallOptions;
+struct NamedSource;
}
struct RosterItem {
diff --git a/talk/main.scons b/talk/main.scons
index c8b0fd9..9289dcf 100644
--- a/talk/main.scons
+++ b/talk/main.scons
@@ -79,11 +79,11 @@
# List all the locales we localize to.
root_env.AppendUnique(locales = [
- 'ar', 'bg', 'bn', 'ca', 'cs', 'da', 'de', 'el', 'en-GB', 'es', 'es-419',
- 'et', 'fa', 'fi', 'fil', 'fr', 'gu', 'hi', 'hr', 'hu', 'id', 'is', 'it',
- 'iw', 'ja', 'kn', 'ko', 'lt', 'lv', 'ml', 'mr', 'ms', 'nl', 'no', 'or',
- 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk', 'sl', 'sr', 'sv', 'ta', 'te',
- 'th', 'tl', 'tr', 'uk', 'ur', 'vi', 'zh-CN', 'zh-TW'])
+ 'ar', 'bg', 'bn', 'ca', 'cs', 'da', 'de', 'el', 'en', 'en-GB', 'es',
+ 'es-419', 'et', 'fa', 'fi', 'fil', 'fr', 'gu', 'hi', 'hr', 'hu', 'id',
+ 'is', 'it', 'iw', 'ja', 'kn', 'ko', 'lt', 'lv', 'ml', 'mr', 'ms', 'nl',
+ 'no', 'or', 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk', 'sl', 'sr', 'sv',
+ 'ta', 'te', 'th', 'tl', 'tr', 'uk', 'ur', 'vi', 'zh-CN', 'zh-TW'])
#-------------------------------------------------------------------------------
# W I N D O W S
@@ -117,6 +117,7 @@
'/Zc:forScope', # handle 'for (int i = 0 ...)' right
'/EHs-c-', # disable C++ EH
'/GR-', # disable RTTI
+ '/Gy', # enable function level linking
'/wd4996', # ignore POSIX deprecated warnings
# promote certain level 4 warnings
@@ -169,6 +170,7 @@
'$PLATFORM_SDK_VISTA_6_0_DIR/Lib'
],
LINKFLAGS = [
+ '-opt:ref', # Remove unused references (functions/data).
'-manifest' # TODO: Why do we need this?
],
MIDLFLAGS = [
@@ -247,7 +249,6 @@
envs.append(win_opt_env)
-
#-------------------------------------------------------------------------------
# P O S I X
#
@@ -267,6 +268,9 @@
'-Werror',
'-Wno-switch',
'-fno-exceptions',
+ # Needed for a clean ABI and for link-time dead-code removal to work
+ # properly.
+ '-fvisibility=hidden',
],
CXXFLAGS = [
'-Wno-non-virtual-dtor',
@@ -305,6 +309,8 @@
'-ObjC',
'-arch', 'i386',
'-m32',
+ # Enable dead-code removal.
+ '-dead_strip',
],
FRAMEWORKS = [
'CoreServices',
@@ -365,13 +371,9 @@
'LINUX',
],
CCFLAGS = [
- # TODO: Some or all of this may be desirable for Mac too.
# Needed for link-time dead-code removal to work properly.
'-ffunction-sections',
'-fdata-sections',
- # Needed for a clean ABI and for link-time dead-code removal to work
- # properly.
- '-fvisibility=hidden',
# Generate debugging info in the DWARF2 format.
'-gdwarf-2',
# Generate maximal debugging information. (It is stripped from what we ship
diff --git a/talk/p2p/client/basicportallocator.cc b/talk/p2p/client/basicportallocator.cc
index 6c54c09..51d069e 100644
--- a/talk/p2p/client/basicportallocator.cc
+++ b/talk/p2p/client/basicportallocator.cc
@@ -232,7 +232,11 @@
: PortAllocatorSession(allocator->flags()), allocator_(allocator),
name_(name), session_type_(session_type), network_thread_(NULL),
socket_factory_(allocator->socket_factory()), allocation_started_(false),
+ network_manager_started_(false),
running_(false) {
+ allocator_->network_manager()->SignalNetworksChanged.connect(
+ this, &BasicPortAllocatorSession::OnNetworksChanged);
+ allocator_->network_manager()->StartUpdating();
}
BasicPortAllocatorSession::~BasicPortAllocatorSession() {
@@ -347,14 +351,21 @@
network_thread_->Post(this, MSG_ALLOCATE);
}
+void BasicPortAllocatorSession::OnAllocate() {
+ if (network_manager_started_)
+ DoAllocate();
+
+ allocation_started_ = true;
+ if (running_)
+ network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE);
+}
+
// For each network, see if we have a sequence that covers it already. If not,
// create a new sequence to create the appropriate ports.
-void BasicPortAllocatorSession::OnAllocate() {
+void BasicPortAllocatorSession::DoAllocate() {
std::vector<talk_base::Network*> networks;
-
- if (!allocator_->network_manager()->GetNetworks(&networks)) {
- LOG(LS_ERROR) << "Failed to enumerate networks";
- } else if (networks.empty()) {
+ allocator_->network_manager()->GetNetworks(&networks);
+ if (networks.empty()) {
LOG(LS_WARNING) << "Machine has no networks; no ports will be allocated";
} else {
for (uint32 i = 0; i < networks.size(); ++i) {
@@ -374,8 +385,8 @@
sequence_flags |= PORTALLOCATOR_DISABLE_RELAY;
}
- // Disable phases that would only create ports equivalent to ones that we
- // have already made.
+ // Disable phases that would only create ports equivalent to
+ // ones that we have already made.
DisableEquivalentPhases(networks[i], config, &sequence_flags);
if ((sequence_flags & DISABLE_ALL_PHASES) == DISABLE_ALL_PHASES) {
@@ -391,10 +402,12 @@
sequences_.push_back(sequence);
}
}
+}
- allocation_started_ = true;
- if (running_)
- network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE);
+void BasicPortAllocatorSession::OnNetworksChanged() {
+ network_manager_started_ = true;
+ if (allocation_started_)
+ DoAllocate();
}
void BasicPortAllocatorSession::DisableEquivalentPhases(
diff --git a/talk/p2p/client/basicportallocator.h b/talk/p2p/client/basicportallocator.h
index c9adfd8..0e1d00a 100644
--- a/talk/p2p/client/basicportallocator.h
+++ b/talk/p2p/client/basicportallocator.h
@@ -140,6 +140,8 @@
void OnConfigTimeout();
void AllocatePorts();
void OnAllocate();
+ void DoAllocate();
+ void OnNetworksChanged();
void DisableEquivalentPhases(talk_base::Network* network,
PortConfiguration* config, uint32* flags);
void AddAllocatedPort(Port* port, AllocationSequence* seq, float pref,
@@ -159,6 +161,7 @@
talk_base::PacketSocketFactory* socket_factory_;
bool configuration_done_;
bool allocation_started_;
+ bool network_manager_started_;
bool running_; // set when StartGetAllPorts is called
std::vector<PortConfiguration*> configs_;
std::vector<AllocationSequence*> sequences_;
diff --git a/talk/p2p/client/socketmonitor.cc b/talk/p2p/client/socketmonitor.cc
index bf32d84..5fe5304 100644
--- a/talk/p2p/client/socketmonitor.cc
+++ b/talk/p2p/client/socketmonitor.cc
@@ -138,7 +138,6 @@
info.recv_bytes_second = connection->recv_bytes_second();
info.local_candidate = connection->local_candidate();
info.remote_candidate = connection->remote_candidate();
- info.est_quality = connection->port()->network()->quality();
info.key = connection;
connection_infos_.push_back(info);
}
diff --git a/talk/p2p/client/socketmonitor.h b/talk/p2p/client/socketmonitor.h
index cf29d9d..9facf3f 100644
--- a/talk/p2p/client/socketmonitor.h
+++ b/talk/p2p/client/socketmonitor.h
@@ -51,7 +51,6 @@
size_t recv_bytes_second;
Candidate local_candidate;
Candidate remote_candidate;
- double est_quality;
void *key;
};
diff --git a/talk/session/phone/currentspeakermonitor.h b/talk/session/phone/currentspeakermonitor.h
index adf6b48..84207fb 100644
--- a/talk/session/phone/currentspeakermonitor.h
+++ b/talk/session/phone/currentspeakermonitor.h
@@ -38,11 +38,11 @@
namespace cricket {
-class AudioInfo;
class BaseSession;
class Call;
-class MediaSources;
class Session;
+struct AudioInfo;
+struct MediaSources;
// Note that the call's audio monitor must be started before this is started.
// It's recommended that the audio monitor be started with a 100 ms period.
diff --git a/talk/session/phone/videorendererfactory.h b/talk/session/phone/videorendererfactory.h
index 14e9a3a..058bf8c 100644
--- a/talk/session/phone/videorendererfactory.h
+++ b/talk/session/phone/videorendererfactory.h
@@ -62,4 +62,4 @@
} // namespace cricket
-#endif // TALK_SESSION_PHONE_VIDEORENDERERFACTORY_H_
\ No newline at end of file
+#endif // TALK_SESSION_PHONE_VIDEORENDERERFACTORY_H_