OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/client/plugin/chromoting_instance.h" | 5 #include "remoting/client/plugin/chromoting_instance.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 return "INCOMPATIBLE_PROTOCOL"; | 84 return "INCOMPATIBLE_PROTOCOL"; |
85 case ChromotingInstance::ERROR_NETWORK_FAILURE: | 85 case ChromotingInstance::ERROR_NETWORK_FAILURE: |
86 return "NETWORK_FAILURE"; | 86 return "NETWORK_FAILURE"; |
87 case ChromotingInstance::ERROR_HOST_OVERLOAD: | 87 case ChromotingInstance::ERROR_HOST_OVERLOAD: |
88 return "HOST_OVERLOAD"; | 88 return "HOST_OVERLOAD"; |
89 } | 89 } |
90 NOTREACHED(); | 90 NOTREACHED(); |
91 return ""; | 91 return ""; |
92 } | 92 } |
93 | 93 |
94 } // namespace | |
95 | |
96 // This flag blocks LOGs to the UI if we're already in the middle of logging | 94 // This flag blocks LOGs to the UI if we're already in the middle of logging |
97 // to the UI. This prevents a potential infinite loop if we encounter an error | 95 // to the UI. This prevents a potential infinite loop if we encounter an error |
98 // while sending the log message to the UI. | 96 // while sending the log message to the UI. |
99 static bool g_logging_to_plugin = false; | 97 bool g_logging_to_plugin = false; |
100 static bool g_has_logging_instance = false; | 98 bool g_has_logging_instance = false; |
101 static ChromotingInstance* g_logging_instance = NULL; | 99 base::LazyInstance<scoped_refptr<base::SingleThreadTaskRunner> >::Leaky |
102 static logging::LogMessageHandlerFunction g_logging_old_handler = NULL; | 100 g_logging_task_runner = LAZY_INSTANCE_INITIALIZER; |
| 101 base::LazyInstance<base::WeakPtr<ChromotingInstance> >::Leaky |
| 102 g_logging_instance = LAZY_INSTANCE_INITIALIZER; |
| 103 base::LazyInstance<base::Lock>::Leaky |
| 104 g_logging_lock = LAZY_INSTANCE_INITIALIZER; |
| 105 logging::LogMessageHandlerFunction g_logging_old_handler = NULL; |
103 | 106 |
104 static base::LazyInstance<base::Lock>::Leaky | 107 } // namespace |
105 g_logging_lock = LAZY_INSTANCE_INITIALIZER; | |
106 | 108 |
107 // String sent in the "hello" message to the plugin to describe features. | 109 // String sent in the "hello" message to the plugin to describe features. |
108 const char ChromotingInstance::kApiFeatures[] = | 110 const char ChromotingInstance::kApiFeatures[] = |
109 "highQualityScaling injectKeyEvent sendClipboardItem remapKey trapKey " | 111 "highQualityScaling injectKeyEvent sendClipboardItem remapKey trapKey " |
110 "notifyClientDimensions pauseVideo"; | 112 "notifyClientDimensions pauseVideo"; |
111 | 113 |
112 bool ChromotingInstance::ParseAuthMethods(const std::string& auth_methods_str, | 114 bool ChromotingInstance::ParseAuthMethods(const std::string& auth_methods_str, |
113 ClientConfig* config) { | 115 ClientConfig* config) { |
114 std::vector<std::string> auth_methods; | 116 std::vector<std::string> auth_methods; |
115 base::SplitString(auth_methods_str, ',', &auth_methods); | 117 base::SplitString(auth_methods_str, ',', &auth_methods); |
(...skipping 11 matching lines...) Expand all Loading... |
127 | 129 |
128 return true; | 130 return true; |
129 } | 131 } |
130 | 132 |
131 ChromotingInstance::ChromotingInstance(PP_Instance pp_instance) | 133 ChromotingInstance::ChromotingInstance(PP_Instance pp_instance) |
132 : pp::Instance(pp_instance), | 134 : pp::Instance(pp_instance), |
133 initialized_(false), | 135 initialized_(false), |
134 plugin_message_loop_( | 136 plugin_message_loop_( |
135 new PluginMessageLoopProxy(&plugin_thread_delegate_)), | 137 new PluginMessageLoopProxy(&plugin_thread_delegate_)), |
136 context_(plugin_message_loop_), | 138 context_(plugin_message_loop_), |
137 thread_proxy_(new ScopedThreadProxy(plugin_message_loop_)) { | 139 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
138 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL); | 140 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL); |
139 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); | 141 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); |
140 | 142 |
141 // Resister this instance to handle debug log messsages. | 143 // Resister this instance to handle debug log messsages. |
142 RegisterLoggingInstance(); | 144 RegisterLoggingInstance(); |
143 | 145 |
144 // Send hello message. | 146 // Send hello message. |
145 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); | 147 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); |
146 data->SetInteger("apiVersion", kApiVersion); | 148 data->SetInteger("apiVersion", kApiVersion); |
147 data->SetString("apiFeatures", kApiFeatures); | 149 data->SetString("apiFeatures", kApiFeatures); |
148 data->SetInteger("apiMinVersion", kApiMinMessagingVersion); | 150 data->SetInteger("apiMinVersion", kApiMinMessagingVersion); |
149 PostChromotingMessage("hello", data.Pass()); | 151 PostChromotingMessage("hello", data.Pass()); |
150 } | 152 } |
151 | 153 |
152 ChromotingInstance::~ChromotingInstance() { | 154 ChromotingInstance::~ChromotingInstance() { |
153 DCHECK(plugin_message_loop_->BelongsToCurrentThread()); | 155 DCHECK(plugin_message_loop_->BelongsToCurrentThread()); |
154 | 156 |
155 // Detach the log proxy so we don't log anything else to the UI. | |
156 // This needs to be done before the instance is unregistered for logging. | |
157 thread_proxy_->Detach(); | |
158 | |
159 // Unregister this instance so that debug log messages will no longer be sent | 157 // Unregister this instance so that debug log messages will no longer be sent |
160 // to it. This will stop all logging in all Chromoting instances. | 158 // to it. This will stop all logging in all Chromoting instances. |
161 UnregisterLoggingInstance(); | 159 UnregisterLoggingInstance(); |
162 | 160 |
163 if (client_.get()) { | 161 if (client_.get()) { |
164 base::WaitableEvent done_event(true, false); | 162 base::WaitableEvent done_event(true, false); |
165 client_->Stop(base::Bind(&base::WaitableEvent::Signal, | 163 client_->Stop(base::Bind(&base::WaitableEvent::Signal, |
166 base::Unretained(&done_event))); | 164 base::Unretained(&done_event))); |
167 done_event.Wait(); | 165 done_event.Wait(); |
168 } | 166 } |
169 | 167 |
170 // Stopping the context shuts down all chromoting threads. | 168 // Stopping the context shuts down all chromoting threads. |
171 context_.Stop(); | 169 context_.Stop(); |
172 | 170 |
173 // Delete |thread_proxy_| before we detach |plugin_message_loop_|, | 171 // Ensure that nothing touches the plugin thread delegate after this point. |
174 // otherwise ScopedThreadProxy may DCHECK when being destroyed. | |
175 thread_proxy_.reset(); | |
176 | |
177 plugin_message_loop_->Detach(); | 172 plugin_message_loop_->Detach(); |
178 } | 173 } |
179 | 174 |
180 bool ChromotingInstance::Init(uint32_t argc, | 175 bool ChromotingInstance::Init(uint32_t argc, |
181 const char* argn[], | 176 const char* argn[], |
182 const char* argv[]) { | 177 const char* argv[]) { |
183 CHECK(!initialized_); | 178 CHECK(!initialized_); |
184 initialized_ = true; | 179 initialized_ = true; |
185 | 180 |
186 VLOG(1) << "Started ChromotingInstance::Init"; | 181 VLOG(1) << "Started ChromotingInstance::Init"; |
(...skipping 431 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
618 logging::SetLogMessageHandler(&LogToUI); | 613 logging::SetLogMessageHandler(&LogToUI); |
619 } | 614 } |
620 | 615 |
621 void ChromotingInstance::RegisterLoggingInstance() { | 616 void ChromotingInstance::RegisterLoggingInstance() { |
622 base::AutoLock lock(g_logging_lock.Get()); | 617 base::AutoLock lock(g_logging_lock.Get()); |
623 | 618 |
624 // Register this instance as the one that will handle all logging calls | 619 // Register this instance as the one that will handle all logging calls |
625 // and display them to the user. | 620 // and display them to the user. |
626 // If multiple plugins are run, then the last one registered will handle all | 621 // If multiple plugins are run, then the last one registered will handle all |
627 // logging for all instances. | 622 // logging for all instances. |
628 g_logging_instance = this; | 623 g_logging_instance.Get() = weak_factory_.GetWeakPtr(); |
| 624 g_logging_task_runner.Get() = plugin_message_loop_; |
629 g_has_logging_instance = true; | 625 g_has_logging_instance = true; |
630 } | 626 } |
631 | 627 |
632 void ChromotingInstance::UnregisterLoggingInstance() { | 628 void ChromotingInstance::UnregisterLoggingInstance() { |
633 base::AutoLock lock(g_logging_lock.Get()); | 629 base::AutoLock lock(g_logging_lock.Get()); |
634 | 630 |
635 // Don't unregister unless we're the currently registered instance. | 631 // Don't unregister unless we're the currently registered instance. |
636 if (this != g_logging_instance) | 632 if (this != g_logging_instance.Get().get()) |
637 return; | 633 return; |
638 | 634 |
639 // Unregister this instance for logging. | 635 // Unregister this instance for logging. |
640 g_has_logging_instance = false; | 636 g_has_logging_instance = false; |
641 g_logging_instance = NULL; | 637 g_logging_instance.Get().reset(); |
| 638 g_logging_task_runner.Get() = NULL; |
642 | 639 |
643 VLOG(1) << "Unregistering global log handler"; | 640 VLOG(1) << "Unregistering global log handler"; |
644 } | 641 } |
645 | 642 |
646 // static | 643 // static |
647 bool ChromotingInstance::LogToUI(int severity, const char* file, int line, | 644 bool ChromotingInstance::LogToUI(int severity, const char* file, int line, |
648 size_t message_start, | 645 size_t message_start, |
649 const std::string& str) { | 646 const std::string& str) { |
650 // Note that we're reading |g_has_logging_instance| outside of a lock. | 647 // Note that we're reading |g_has_logging_instance| outside of a lock. |
651 // This lockless read is done so that we don't needlessly slow down global | 648 // This lockless read is done so that we don't needlessly slow down global |
652 // logging with a lock for each log message. | 649 // logging with a lock for each log message. |
653 // | 650 // |
654 // This lockless read is safe because: | 651 // This lockless read is safe because: |
655 // | 652 // |
656 // Misreading a false value (when it should be true) means that we'll simply | 653 // Misreading a false value (when it should be true) means that we'll simply |
657 // skip processing a few log messages. | 654 // skip processing a few log messages. |
658 // | 655 // |
659 // Misreading a true value (when it should be false) means that we'll take | 656 // Misreading a true value (when it should be false) means that we'll take |
660 // the lock and check |g_logging_instance| unnecessarily. This is not | 657 // the lock and check |g_logging_instance| unnecessarily. This is not |
661 // problematic because we always set |g_logging_instance| inside a lock. | 658 // problematic because we always set |g_logging_instance| inside a lock. |
662 if (g_has_logging_instance) { | 659 if (g_has_logging_instance) { |
663 // Do not LOG anything while holding this lock or else the code will | 660 scoped_refptr<base::SingleThreadTaskRunner> logging_task_runner; |
664 // deadlock while trying to re-get the lock we're already in. | 661 base::WeakPtr<ChromotingInstance> logging_instance; |
665 base::AutoLock lock(g_logging_lock.Get()); | 662 |
666 if (g_logging_instance && | 663 { |
667 // If |g_logging_to_plugin| is set and we're on the logging thread, then | 664 base::AutoLock lock(g_logging_lock.Get()); |
668 // this LOG message came from handling a previous LOG message and we | 665 // If we're on the logging thread and |g_logging_to_plugin| is set then |
669 // should skip it to avoid an infinite loop of LOG messages. | 666 // this LOG message came from handling a previous LOG message and we |
670 // We don't have a lock around |g_in_processtoui|, but that's OK since | 667 // should skip it to avoid an infinite loop of LOG messages. |
671 // the value is only read/written on the logging thread. | 668 if (!g_logging_task_runner.Get()->BelongsToCurrentThread() || |
672 (!g_logging_instance->plugin_message_loop_->BelongsToCurrentThread() || | 669 !g_logging_to_plugin) { |
673 !g_logging_to_plugin)) { | 670 logging_task_runner = g_logging_task_runner.Get(); |
| 671 logging_instance = g_logging_instance.Get(); |
| 672 } |
| 673 } |
| 674 |
| 675 if (logging_task_runner.get()) { |
674 std::string message = remoting::GetTimestampString(); | 676 std::string message = remoting::GetTimestampString(); |
675 message += (str.c_str() + message_start); | 677 message += (str.c_str() + message_start); |
676 // |thread_proxy_| is safe to use here because we detach it before | 678 |
677 // tearing down the |g_logging_instance|. | 679 logging_task_runner->PostTask( |
678 g_logging_instance->thread_proxy_->PostTask( | |
679 FROM_HERE, base::Bind(&ChromotingInstance::ProcessLogToUI, | 680 FROM_HERE, base::Bind(&ChromotingInstance::ProcessLogToUI, |
680 base::Unretained(g_logging_instance), message)); | 681 logging_instance, message)); |
681 } | 682 } |
682 } | 683 } |
683 | 684 |
684 if (g_logging_old_handler) | 685 if (g_logging_old_handler) |
685 return (g_logging_old_handler)(severity, file, line, message_start, str); | 686 return (g_logging_old_handler)(severity, file, line, message_start, str); |
686 return false; | 687 return false; |
687 } | 688 } |
688 | 689 |
689 void ChromotingInstance::ProcessLogToUI(const std::string& message) { | 690 void ChromotingInstance::ProcessLogToUI(const std::string& message) { |
690 DCHECK(plugin_message_loop_->BelongsToCurrentThread()); | 691 DCHECK(plugin_message_loop_->BelongsToCurrentThread()); |
691 | 692 |
692 // This flag (which is set only here) is used to prevent LogToUI from posting | 693 // This flag (which is set only here) is used to prevent LogToUI from posting |
693 // new tasks while we're in the middle of servicing a LOG call. This can | 694 // new tasks while we're in the middle of servicing a LOG call. This can |
694 // happen if the call to LogDebugInfo tries to LOG anything. | 695 // happen if the call to LogDebugInfo tries to LOG anything. |
| 696 // Since it is read on the plugin thread, we don't need to lock to set it. |
695 g_logging_to_plugin = true; | 697 g_logging_to_plugin = true; |
696 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); | 698 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); |
697 data->SetString("message", message); | 699 data->SetString("message", message); |
698 PostChromotingMessage("logDebugMessage", data.Pass()); | 700 PostChromotingMessage("logDebugMessage", data.Pass()); |
699 g_logging_to_plugin = false; | 701 g_logging_to_plugin = false; |
700 } | 702 } |
701 | 703 |
702 bool ChromotingInstance::IsConnected() { | 704 bool ChromotingInstance::IsConnected() { |
703 return host_connection_.get() && | 705 return host_connection_.get() && |
704 (host_connection_->state() == protocol::ConnectionToHost::CONNECTED); | 706 (host_connection_->state() == protocol::ConnectionToHost::CONNECTED); |
705 } | 707 } |
706 | 708 |
707 } // namespace remoting | 709 } // namespace remoting |
OLD | NEW |