Index: third_party/libjingle/overrides/talk/base/thread.cc |
=================================================================== |
--- third_party/libjingle/overrides/talk/base/thread.cc (revision 0) |
+++ third_party/libjingle/overrides/talk/base/thread.cc (revision 0) |
@@ -0,0 +1,557 @@ |
+/* |
Sergey Ulanov
2012/02/25 00:34:40
I believe this should be the same copyright header
|
+ * libjingle |
+ * Copyright 2004 Google Inc. |
+ * |
+ * Redistribution and use in source and binary forms, with or without |
+ * modification, are permitted provided that the following conditions are met: |
+ * |
+ * 1. Redistributions of source code must retain the above copyright notice, |
+ * this list of conditions and the following disclaimer. |
+ * 2. Redistributions in binary form must reproduce the above copyright notice, |
+ * this list of conditions and the following disclaimer in the documentation |
+ * and/or other materials provided with the distribution. |
+ * 3. The name of the author may not be used to endorse or promote products |
+ * derived from this software without specific prior written permission. |
+ * |
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ */ |
+ |
+#include "talk/base/thread.h" |
+ |
+#if defined(WIN32) |
+#include <comdef.h> |
+#elif defined(POSIX) |
+#include <time.h> |
+#endif |
+ |
+#include "talk/base/common.h" |
+#include "talk/base/logging.h" |
+#include "talk/base/stringutils.h" |
+#include "talk/base/timeutils.h" |
+ |
+#ifdef USE_COCOA_THREADING |
+#if !defined(OSX) && !defined(IOS) |
+#error USE_COCOA_THREADING is defined but not OSX nor IOS |
+#endif |
+#include "talk/base/maccocoathreadhelper.h" |
+#include "talk/base/scoped_autorelease_pool.h" |
+#endif |
+ |
+namespace talk_base { |
+ |
+ThreadManager* ThreadManager::Instance() { |
+ LIBJINGLE_DEFINE_STATIC_LOCAL(ThreadManager, thread_manager, ()); |
+ return &thread_manager; |
+} |
+ |
+#ifdef POSIX |
+ThreadManager::ThreadManager() { |
+ pthread_key_create(&key_, NULL); |
+ WrapCurrentThread(); |
+#ifdef USE_COCOA_THREADING |
+ InitCocoaMultiThreading(); |
+#endif |
+} |
+ |
+ThreadManager::~ThreadManager() { |
+#ifdef USE_COCOA_THREADING |
+ // This is called during exit, at which point apparently no NSAutoreleasePools |
+ // are available; but we might still need them to do cleanup (or we get the |
+ // "no autoreleasepool in place, just leaking" warning when exiting). |
+ ScopedAutoreleasePool pool; |
+#endif |
+ UnwrapCurrentThread(); |
+ pthread_key_delete(key_); |
+} |
+ |
+Thread *ThreadManager::CurrentThread() { |
+ return static_cast<Thread *>(pthread_getspecific(key_)); |
+} |
+ |
+void ThreadManager::SetCurrentThread(Thread *thread) { |
+ pthread_setspecific(key_, thread); |
+} |
+#endif |
+ |
+#ifdef WIN32 |
+ThreadManager::ThreadManager() { |
Ronghua Wu (Left Chromium)
2012/02/24 23:39:16
Removed WrapCurrentThread().
|
+ key_ = TlsAlloc(); |
+} |
+ |
+ThreadManager::~ThreadManager() { |
+ UnwrapCurrentThread(); |
+ TlsFree(key_); |
+} |
+ |
+Thread *ThreadManager::CurrentThread() { |
+ Thread* result = static_cast<Thread *>(TlsGetValue(key_)); |
Ronghua Wu (Left Chromium)
2012/02/24 23:39:16
Changed CurrentThread to WrapCurrentWithThreadMana
|
+ if (NULL == result) { |
+ result = new Thread(); |
+ result->WrapCurrentWithThreadManager(this); |
+ } |
+ return result; |
+} |
+ |
+void ThreadManager::SetCurrentThread(Thread *thread) { |
+ TlsSetValue(key_, thread); |
+} |
+#endif |
+ |
+// static |
+Thread *ThreadManager::WrapCurrentThread() { |
+ Thread* result = CurrentThread(); |
+ if (NULL == result) { |
+ result = new Thread(); |
+ result->WrapCurrentWithThreadManager(this); |
+ } |
+ return result; |
+} |
+ |
+// static |
+void ThreadManager::UnwrapCurrentThread() { |
+ Thread* t = CurrentThread(); |
+ if (t && !(t->IsOwned())) { |
+ t->UnwrapCurrent(); |
+ delete t; |
+ } |
+} |
+ |
+struct ThreadInit { |
+ Thread* thread; |
+ Runnable* runnable; |
+}; |
+ |
+Thread::Thread(SocketServer* ss) |
+ : MessageQueue(ss), |
+ priority_(PRIORITY_NORMAL), |
+ started_(false), |
+ has_sends_(false), |
+#if defined(WIN32) |
+ thread_(NULL), |
+#endif |
+ owned_(true), |
+ delete_self_when_complete_(false) { |
+ SetName("Thread", this); // default name |
+} |
+ |
+Thread::~Thread() { |
+ Stop(); |
+ if (active_) |
+ Clear(NULL); |
+} |
+ |
+bool Thread::SleepMs(int milliseconds) { |
+#ifdef WIN32 |
+ ::Sleep(milliseconds); |
+ return true; |
+#else |
+ // POSIX has both a usleep() and a nanosleep(), but the former is deprecated, |
+ // so we use nanosleep() even though it has greater precision than necessary. |
+ struct timespec ts; |
+ ts.tv_sec = milliseconds / 1000; |
+ ts.tv_nsec = (milliseconds % 1000) * 1000000; |
+ int ret = nanosleep(&ts, NULL); |
+ if (ret != 0) { |
+ LOG_ERR(LS_WARNING) << "nanosleep() returning early"; |
+ return false; |
+ } |
+ return true; |
+#endif |
+} |
+ |
+bool Thread::SetName(const std::string& name, const void* obj) { |
+ if (started_) return false; |
+ name_ = name; |
+ if (obj) { |
+ char buf[16]; |
+ sprintfn(buf, sizeof(buf), " 0x%p", obj); |
+ name_ += buf; |
+ } |
+ return true; |
+} |
+ |
+bool Thread::SetPriority(ThreadPriority priority) { |
+#if defined(WIN32) |
+ if (started_) { |
+ BOOL ret = FALSE; |
+ if (priority == PRIORITY_NORMAL) { |
+ ret = ::SetThreadPriority(thread_, THREAD_PRIORITY_NORMAL); |
+ } else if (priority == PRIORITY_HIGH) { |
+ ret = ::SetThreadPriority(thread_, THREAD_PRIORITY_HIGHEST); |
+ } else if (priority == PRIORITY_ABOVE_NORMAL) { |
+ ret = ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL); |
+ } else if (priority == PRIORITY_IDLE) { |
+ ret = ::SetThreadPriority(thread_, THREAD_PRIORITY_IDLE); |
+ } |
+ if (!ret) { |
+ return false; |
+ } |
+ } |
+ priority_ = priority; |
+ return true; |
+#else |
+ // TODO: Implement for Linux/Mac if possible. |
+ if (started_) return false; |
+ priority_ = priority; |
+ return true; |
+#endif |
+} |
+ |
+bool Thread::Start(Runnable* runnable) { |
+ ASSERT(owned_); |
+ if (!owned_) return false; |
+ ASSERT(!started_); |
+ if (started_) return false; |
+ |
+ // Make sure that ThreadManager is created on the main thread before |
+ // we start a new thread. |
+ ThreadManager::Instance(); |
+ |
+ ThreadInit* init = new ThreadInit; |
+ init->thread = this; |
+ init->runnable = runnable; |
+#if defined(WIN32) |
+ DWORD flags = 0; |
+ if (priority_ != PRIORITY_NORMAL) { |
+ flags = CREATE_SUSPENDED; |
+ } |
+ thread_ = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PreRun, init, flags, |
+ NULL); |
+ if (thread_) { |
+ started_ = true; |
+ if (priority_ != PRIORITY_NORMAL) { |
+ SetPriority(priority_); |
+ ::ResumeThread(thread_); |
+ } |
+ } else { |
+ return false; |
+ } |
+#elif defined(POSIX) |
+ pthread_attr_t attr; |
+ pthread_attr_init(&attr); |
+ if (priority_ != PRIORITY_NORMAL) { |
+ if (priority_ == PRIORITY_IDLE) { |
+ // There is no POSIX-standard way to set a below-normal priority for an |
+ // individual thread (only whole process), so let's not support it. |
+ LOG(LS_WARNING) << "PRIORITY_IDLE not supported"; |
+ } else { |
+ // Set real-time round-robin policy. |
+ if (pthread_attr_setschedpolicy(&attr, SCHED_RR) != 0) { |
+ LOG(LS_ERROR) << "pthread_attr_setschedpolicy"; |
+ } |
+ struct sched_param param; |
+ if (pthread_attr_getschedparam(&attr, ¶m) != 0) { |
+ LOG(LS_ERROR) << "pthread_attr_getschedparam"; |
+ } else { |
+ // The numbers here are arbitrary. |
+ if (priority_ == PRIORITY_HIGH) { |
+ param.sched_priority = 6; // 6 = HIGH |
+ } else { |
+ ASSERT(priority_ == PRIORITY_ABOVE_NORMAL); |
+ param.sched_priority = 4; // 4 = ABOVE_NORMAL |
+ } |
+ if (pthread_attr_setschedparam(&attr, ¶m) != 0) { |
+ LOG(LS_ERROR) << "pthread_attr_setschedparam"; |
+ } |
+ } |
+ } |
+ } |
+ int error_code = pthread_create(&thread_, &attr, PreRun, init); |
+ if (0 != error_code) { |
+ LOG(LS_ERROR) << "Unable to create pthread, error " << error_code; |
+ return false; |
+ } |
+ started_ = true; |
+#endif |
+ return true; |
+} |
+ |
+void Thread::Join() { |
+ if (started_) { |
+ ASSERT(!IsCurrent()); |
+#if defined(WIN32) |
+ WaitForSingleObject(thread_, INFINITE); |
+ CloseHandle(thread_); |
+ thread_ = NULL; |
+#elif defined(POSIX) |
+ void *pv; |
+ pthread_join(thread_, &pv); |
+#endif |
+ started_ = false; |
+ } |
+} |
+ |
+#ifdef WIN32 |
+// As seen on MSDN. |
+// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.71).aspx |
+#define MSDEV_SET_THREAD_NAME 0x406D1388 |
+typedef struct tagTHREADNAME_INFO { |
+ DWORD dwType; |
+ LPCSTR szName; |
+ DWORD dwThreadID; |
+ DWORD dwFlags; |
+} THREADNAME_INFO; |
+ |
+void SetThreadName(DWORD dwThreadID, LPCSTR szThreadName) { |
+ THREADNAME_INFO info; |
+ info.dwType = 0x1000; |
+ info.szName = szThreadName; |
+ info.dwThreadID = dwThreadID; |
+ info.dwFlags = 0; |
+ |
+ __try { |
+ RaiseException(MSDEV_SET_THREAD_NAME, 0, sizeof(info) / sizeof(DWORD), |
+ reinterpret_cast<DWORD*>(&info)); |
+ } |
+ __except(EXCEPTION_CONTINUE_EXECUTION) { |
+ } |
+} |
+#endif // WIN32 |
+ |
+void* Thread::PreRun(void* pv) { |
+ ThreadInit* init = static_cast<ThreadInit*>(pv); |
+ ThreadManager::Instance()->SetCurrentThread(init->thread); |
+#if defined(WIN32) |
+ SetThreadName(GetCurrentThreadId(), init->thread->name_.c_str()); |
+#elif defined(POSIX) |
+ // TODO: See if naming exists for pthreads. |
+#endif |
+#ifdef USE_COCOA_THREADING |
+ // Make sure the new thread has an autoreleasepool |
+ ScopedAutoreleasePool pool; |
+#endif |
+ if (init->runnable) { |
+ init->runnable->Run(init->thread); |
+ } else { |
+ init->thread->Run(); |
+ } |
+ if (init->thread->delete_self_when_complete_) { |
+ init->thread->started_ = false; |
+ delete init->thread; |
+ } |
+ delete init; |
+ return NULL; |
+} |
+ |
+void Thread::Run() { |
+ ProcessMessages(kForever); |
+} |
+ |
+bool Thread::IsOwned() { |
+ return owned_; |
+} |
+ |
+void Thread::Stop() { |
+ MessageQueue::Quit(); |
+ Join(); |
+} |
+ |
+void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) { |
+ if (fStop_) |
+ return; |
+ |
+ // Sent messages are sent to the MessageHandler directly, in the context |
+ // of "thread", like Win32 SendMessage. If in the right context, |
+ // call the handler directly. |
+ |
+ Message msg; |
+ msg.phandler = phandler; |
+ msg.message_id = id; |
+ msg.pdata = pdata; |
+ if (IsCurrent()) { |
+ phandler->OnMessage(&msg); |
+ return; |
+ } |
+ |
+ AutoThread thread; |
+ Thread *current_thread = Thread::Current(); |
+ ASSERT(current_thread != NULL); // AutoThread ensures this |
+ |
+ bool ready = false; |
+ { |
+ CritScope cs(&crit_); |
+ EnsureActive(); |
+ _SendMessage smsg; |
+ smsg.thread = current_thread; |
+ smsg.msg = msg; |
+ smsg.ready = &ready; |
+ sendlist_.push_back(smsg); |
+ has_sends_ = true; |
+ } |
+ |
+ // Wait for a reply |
+ |
+ ss_->WakeUp(); |
+ |
+ bool waited = false; |
+ while (!ready) { |
+ current_thread->ReceiveSends(); |
+ current_thread->socketserver()->Wait(kForever, false); |
+ waited = true; |
+ } |
+ |
+ // Our Wait loop above may have consumed some WakeUp events for this |
+ // MessageQueue, that weren't relevant to this Send. Losing these WakeUps can |
+ // cause problems for some SocketServers. |
+ // |
+ // Concrete example: |
+ // Win32SocketServer on thread A calls Send on thread B. While processing the |
+ // message, thread B Posts a message to A. We consume the wakeup for that |
+ // Post while waiting for the Send to complete, which means that when we exit |
+ // this loop, we need to issue another WakeUp, or else the Posted message |
+ // won't be processed in a timely manner. |
+ |
+ if (waited) { |
+ current_thread->socketserver()->WakeUp(); |
+ } |
+} |
+ |
+void Thread::ReceiveSends() { |
+ // Before entering critical section, check boolean. |
+ |
+ if (!has_sends_) |
+ return; |
+ |
+ // Receive a sent message. Cleanup scenarios: |
+ // - thread sending exits: We don't allow this, since thread can exit |
+ // only via Join, so Send must complete. |
+ // - thread receiving exits: Wakeup/set ready in Thread::Clear() |
+ // - object target cleared: Wakeup/set ready in Thread::Clear() |
+ crit_.Enter(); |
+ while (!sendlist_.empty()) { |
+ _SendMessage smsg = sendlist_.front(); |
+ sendlist_.pop_front(); |
+ crit_.Leave(); |
+ smsg.msg.phandler->OnMessage(&smsg.msg); |
+ crit_.Enter(); |
+ *smsg.ready = true; |
+ smsg.thread->socketserver()->WakeUp(); |
+ } |
+ has_sends_ = false; |
+ crit_.Leave(); |
+} |
+ |
+void Thread::Clear(MessageHandler *phandler, uint32 id, |
+ MessageList* removed) { |
+ CritScope cs(&crit_); |
+ |
+ // Remove messages on sendlist_ with phandler |
+ // Object target cleared: remove from send list, wakeup/set ready |
+ // if sender not NULL. |
+ |
+ std::list<_SendMessage>::iterator iter = sendlist_.begin(); |
+ while (iter != sendlist_.end()) { |
+ _SendMessage smsg = *iter; |
+ if (smsg.msg.Match(phandler, id)) { |
+ if (removed) { |
+ removed->push_back(smsg.msg); |
+ } else { |
+ delete smsg.msg.pdata; |
+ } |
+ iter = sendlist_.erase(iter); |
+ *smsg.ready = true; |
+ smsg.thread->socketserver()->WakeUp(); |
+ continue; |
+ } |
+ ++iter; |
+ } |
+ |
+ MessageQueue::Clear(phandler, id, removed); |
+} |
+ |
+bool Thread::ProcessMessages(int cmsLoop) { |
+ uint32 msEnd = (kForever == cmsLoop) ? 0 : TimeAfter(cmsLoop); |
+ int cmsNext = cmsLoop; |
+ |
+ while (true) { |
+#ifdef USE_COCOA_THREADING |
+ // see: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html |
+ // Each thread is supposed to have an autorelease pool. Also for event loops |
+ // like this, autorelease pool needs to be created and drained/released |
+ // for each cycle. |
+ ScopedAutoreleasePool pool; |
+#endif |
+ Message msg; |
+ if (!Get(&msg, cmsNext)) |
+ return !IsQuitting(); |
+ Dispatch(&msg); |
+ |
+ if (cmsLoop != kForever) { |
+ cmsNext = TimeUntil(msEnd); |
+ if (cmsNext < 0) |
+ return true; |
+ } |
+ } |
+} |
+ |
+bool Thread::WrapCurrent() { |
+ return WrapCurrentWithThreadManager(ThreadManager::Instance()); |
+} |
+ |
+bool Thread::WrapCurrentWithThreadManager(ThreadManager* thread_manager) { |
+ if (started_) |
+ return false; |
+#if defined(WIN32) |
+ // We explicitly ask for no rights other than synchronization. |
+ // This gives us the best chance of succeeding. |
+ thread_ = OpenThread(SYNCHRONIZE, FALSE, GetCurrentThreadId()); |
+ if (!thread_) { |
+ LOG_GLE(LS_ERROR) << "Unable to get handle to thread."; |
+ return false; |
+ } |
+#elif defined(POSIX) |
+ thread_ = pthread_self(); |
+#endif |
+ owned_ = false; |
+ started_ = true; |
+ thread_manager->SetCurrentThread(this); |
+ return true; |
+} |
+ |
+void Thread::UnwrapCurrent() { |
+ // Clears the platform-specific thread-specific storage. |
+ ThreadManager::Instance()->SetCurrentThread(NULL); |
+#ifdef WIN32 |
+ if (!CloseHandle(thread_)) { |
+ LOG_GLE(LS_ERROR) << "When unwrapping thread, failed to close handle."; |
+ } |
+#endif |
+ started_ = false; |
+} |
+ |
+ |
+AutoThread::AutoThread(SocketServer* ss) : Thread(ss) { |
+ if (!ThreadManager::Instance()->CurrentThread()) { |
+ ThreadManager::Instance()->SetCurrentThread(this); |
+ } |
+} |
+ |
+AutoThread::~AutoThread() { |
+ if (ThreadManager::Instance()->CurrentThread() == this) { |
+ ThreadManager::Instance()->SetCurrentThread(NULL); |
+ } |
+} |
+ |
+#ifdef WIN32 |
+void ComThread::Run() { |
+ HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); |
+ ASSERT(SUCCEEDED(hr)); |
+ if (SUCCEEDED(hr)) { |
+ Thread::Run(); |
+ CoUninitialize(); |
+ } else { |
+ LOG(LS_ERROR) << "CoInitialize failed, hr=" << hr; |
+ } |
+} |
+#endif |
+ |
+} // namespace talk_base |
Property changes on: third_party\libjingle\overrides\talk\base\thread.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |