1/*
2 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "thread_win.h"
12
13#include <assert.h>
14#include <process.h>
15#include <stdio.h>
16#include <windows.h>
17
18#include "set_thread_name_win.h"
19#include "trace.h"
20
21namespace webrtc {
22ThreadWindows::ThreadWindows(ThreadRunFunction func, ThreadObj obj,
23                             ThreadPriority prio, const char* threadName)
24    : ThreadWrapper(),
25      _runFunction(func),
26      _obj(obj),
27      _alive(false),
28      _dead(true),
29      _doNotCloseHandle(false),
30      _prio(prio),
31      _event(NULL),
32      _thread(NULL),
33      _id(0),
34      _name(),
35      _setThreadName(false)
36{
37    _event = EventWrapper::Create();
38    _critsectStop = CriticalSectionWrapper::CreateCriticalSection();
39    if (threadName != NULL)
40    {
41        // Set the thread name to appear in the VS debugger.
42        _setThreadName = true;
43        strncpy(_name, threadName, kThreadMaxNameLength);
44    }
45}
46
47ThreadWindows::~ThreadWindows()
48{
49#ifdef _DEBUG
50    assert(!_alive);
51#endif
52    if (_thread)
53    {
54        CloseHandle(_thread);
55    }
56    if(_event)
57    {
58        delete _event;
59    }
60    if(_critsectStop)
61    {
62        delete _critsectStop;
63    }
64}
65
66uint32_t ThreadWrapper::GetThreadId() {
67  return GetCurrentThreadId();
68}
69
70unsigned int WINAPI ThreadWindows::StartThread(LPVOID lpParameter)
71{
72    static_cast<ThreadWindows*>(lpParameter)->Run();
73    return 0;
74}
75
76bool ThreadWindows::Start(unsigned int& threadID)
77{
78    _doNotCloseHandle = false;
79
80    // Set stack size to 1M
81    _thread=(HANDLE)_beginthreadex(NULL, 1024*1024, StartThread, (void*)this, 0,
82                                   &threadID);
83    if(_thread == NULL)
84    {
85        return false;
86    }
87    _id = threadID;
88    _event->Wait(INFINITE);
89
90    switch(_prio)
91    {
92    case kLowPriority:
93        SetThreadPriority(_thread, THREAD_PRIORITY_BELOW_NORMAL);
94        break;
95    case kNormalPriority:
96        SetThreadPriority(_thread, THREAD_PRIORITY_NORMAL);
97        break;
98    case kHighPriority:
99        SetThreadPriority(_thread, THREAD_PRIORITY_ABOVE_NORMAL);
100        break;
101    case kHighestPriority:
102        SetThreadPriority(_thread, THREAD_PRIORITY_HIGHEST);
103        break;
104    case kRealtimePriority:
105        SetThreadPriority(_thread, THREAD_PRIORITY_TIME_CRITICAL);
106        break;
107    };
108    return true;
109}
110
111bool ThreadWindows::SetAffinity(const int* processorNumbers,
112                                const unsigned int amountOfProcessors)
113{
114    DWORD_PTR processorBitMask = 0;
115    for(unsigned int processorIndex = 0;
116        processorIndex < amountOfProcessors;
117        processorIndex++)
118    {
119        // Convert from an array with processor numbers to a bitmask
120        // Processor numbers start at zero.
121        // TODO (hellner): this looks like a bug. Shouldn't the '=' be a '+='?
122        // Or even better |=
123        processorBitMask = 1 << processorNumbers[processorIndex];
124    }
125    return SetThreadAffinityMask(_thread,processorBitMask) != 0;
126}
127
128void ThreadWindows::SetNotAlive()
129{
130    _alive = false;
131}
132
133bool ThreadWindows::Shutdown()
134{
135    DWORD exitCode = 0;
136    BOOL ret = TRUE;
137    if (_thread)
138    {
139        ret = TerminateThread(_thread, exitCode);
140        _alive = false;
141        _dead = true;
142        _thread = NULL;
143    }
144    return ret == TRUE;
145}
146
147bool ThreadWindows::Stop()
148{
149    _critsectStop->Enter();
150    // Prevents the handle from being closed in ThreadWindows::Run()
151    _doNotCloseHandle = true;
152    _alive = false;
153    bool signaled = false;
154    if (_thread && !_dead)
155    {
156        _critsectStop->Leave();
157        // Wait up to 2 seconds for the thread to complete.
158        if( WAIT_OBJECT_0 == WaitForSingleObject(_thread, 2000))
159        {
160            signaled = true;
161        }
162        _critsectStop->Enter();
163    }
164    if (_thread)
165    {
166        CloseHandle(_thread);
167        _thread = NULL;
168    }
169    _critsectStop->Leave();
170
171    if (_dead || signaled)
172    {
173        return true;
174    }
175    else
176    {
177        return false;
178    }
179}
180
181void ThreadWindows::Run()
182{
183    _alive = true;
184    _dead = false;
185    _event->Set();
186
187    // All tracing must be after _event->Set to avoid deadlock in Trace.
188    if (_setThreadName)
189    {
190        WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, _id,
191                     "Thread with name:%s started ", _name);
192        SetThreadName(-1, _name); // -1, set thread name for the calling thread.
193    }else
194    {
195        WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, _id,
196                     "Thread without name started");
197    }
198
199    do
200    {
201        if (_runFunction)
202        {
203            if (!_runFunction(_obj))
204            {
205                _alive = false;
206            }
207        } else {
208            _alive = false;
209        }
210    } while(_alive);
211
212    if (_setThreadName)
213    {
214        WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, _id,
215                     "Thread with name:%s stopped", _name);
216    } else {
217        WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,_id,
218                     "Thread without name stopped");
219    }
220
221    _critsectStop->Enter();
222
223    if (_thread && !_doNotCloseHandle)
224    {
225        HANDLE thread = _thread;
226        _thread = NULL;
227        CloseHandle(thread);
228    }
229    _dead = true;
230
231    _critsectStop->Leave();
232};
233} // namespace webrtc
234