AJA NTV2 SDK  17.0.1.1246
NTV2 SDK 17.0.1.1246
threadimpl.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
9 #include "ajabase/common/timer.h"
10 #include "ajabase/system/debug.h"
11 #include <errno.h>
12 #include <sched.h>
13 #include <signal.h>
14 #include <sys/resource.h>
15 #include <sys/syscall.h>
16 #include <sys/time.h>
17 #include <mach/mach_init.h>
18 #include <mach/thread_policy.h>
19 #include <mach/thread_act.h>
20 #include <mach/mach_time.h>
21 
22 static const size_t STACK_SIZE = 1024 * 1024;
23 
24 AJAThreadImpl::AJAThreadImpl(AJAThread* pThreadContext) :
25  mpThreadContext(pThreadContext),
26  mThread(0),
27  mPriority(AJA_ThreadPriority_Normal),
28  mThreadFunc(0),
29  mpUserContext(0),
30  mTerminate(false),
31  mExiting(false)
32 {
33  int rc = pthread_mutex_init(&mExitMutex, 0);
34  if (rc)
35  {
36  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAThreadImpl(%p) mutex init reported error %d", mpThreadContext, rc);
37  }
38 
39  rc = pthread_cond_init(&mExitCond, 0);
40  if (rc)
41  {
42  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAThreadImpl(%p) cond init reported error %d", mpThreadContext, rc);
43  }
44 }
45 
46 
48 {
49  Stop();
50 
51  int rc = pthread_mutex_destroy(&mExitMutex);
52  if (rc)
53  {
54  AJA_REPORT(0, AJA_DebugSeverity_Error, "~AJAThreadImpl(%p) mutex destroy reported error %d", mpThreadContext, rc);
55  }
56 
57  rc = pthread_cond_destroy(&mExitCond);
58  if (rc)
59  {
60  AJA_REPORT(0, AJA_DebugSeverity_Error, "~AJAThreadImpl(%p) cond destroy reported error %d", mpThreadContext, rc);
61  }
62 }
63 
66 {
67  AJAAutoLock lock(&mLock);
68 
69  // return success if thread is already running
70  if (Active())
71  {
72  return AJA_STATUS_SUCCESS;
73  }
74 
75  // Windows version currently uses the default stack size
76  // The docs say this is 1MB, so give our threads the same stack size
77  pthread_attr_t attr;
78  int rc = 0;
79  rc |= pthread_attr_init(&attr);
80  rc |= pthread_attr_setstacksize(&attr, STACK_SIZE);
81  rc |= pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
82  if (rc)
83  {
84  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAThreadImpl::Start(%p) error setting thread attributes", mpThreadContext);
85  mThread = 0;
86  return AJA_STATUS_FAIL;
87  }
88 
89  // create the thread
90  mTerminate = false;
91  mExiting = false;
92  rc = pthread_create(&mThread, &attr, ThreadProcStatic, this);
93  if (rc)
94  {
95  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAThreadImpl::Start(%p) error %d creating thread", mpThreadContext, rc);
96  mThread = 0;
97  return AJA_STATUS_FAIL;
98  }
99 
100  // set the thread priority
101  return SetPriority(mPriority);
102 }
103 
104 
105 AJAStatus
106 AJAThreadImpl::Stop(uint32_t timeout)
107 {
108  AJAAutoLock lock(&mLock);
109  AJAStatus returnStatus = AJA_STATUS_SUCCESS;
110 
111  // return success if the thread is already stopped
112  if (!Active())
113  {
114  return AJA_STATUS_SUCCESS;
115  }
116 
117  // wait for thread to stop
118  int rc = pthread_mutex_lock(&mExitMutex);
119  if (rc)
120  {
121  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAThreadImpl::Stop(%p) error %d locking exit mutex", mpThreadContext, rc);
122  return AJA_STATUS_FAIL;
123  }
124 
125  // calculate how long to wait
126  struct timespec ts;
128 
129  if (timeout == 0xffffffff)
130  {
131  ts.tv_sec += 60 * 60 * 24 * 365; // A year is infinite enough
132  }
133  else
134  {
135  // Calculates the non-second portion of the timeout
136  // to nanoseconds.
137  uint32_t nsec = ((timeout % 1000) * 1000000);
138 
139  // Add the current nanosecond count to the result
140  nsec += ts.tv_nsec;
141 
142  struct timespec ts1;
143  ts1 = ts;
144 
145  // The addition of the two nanosecond values may wrap to
146  // another second, so note that; but our nsec portion will
147  // always be less than a second. Finally, create the
148  // absolute time during which the timeout will lapse.
149  ts.tv_sec += (timeout / 1000) + (nsec / 1000000000);
150  ts.tv_nsec = (nsec % 1000000000);
151  }
152 
153  // signal thread to stop
154  mTerminate = true;
155 
156 
157  // loop until signaled or timed out
158  do
159  {
160  if (mExiting == false)
161  {
162  rc = pthread_cond_timedwait(&mExitCond, &mExitMutex, &ts);
163  if (rc)
164  {
165  returnStatus = AJA_STATUS_FAIL;
166  AJA_REPORT(
167  0,
169  "AJAThread(%p)::Stop pthread_cond_timedwait returned error %d",
170  mpThreadContext, rc);
171 
172  // non-timeout errors release the mutex by themselves
173  if (rc == ETIMEDOUT)
174  {
175  rc = pthread_mutex_unlock(&mExitMutex);
176  if (rc)
177  {
178  AJA_REPORT(
179  0,
181  "AJAThread(%p)::Stop error %d unlocking timeout mutex",
182  mpThreadContext, rc);
183  }
184  }
185  break;
186  }
187  }
188 
189  // deal with spurious wakeups here
190  if (!mExiting)
191  continue; // the thread hasn't left its loop, so wait some more
192  else
193  {
194  rc = pthread_mutex_unlock(&mExitMutex);
195  if (rc)
196  {
197  returnStatus = AJA_STATUS_FAIL;
198  AJA_REPORT(
199  0,
201  "AJAThread(%p)::Stop error %d unlocking exit mutex",
202  mpThreadContext, rc);
203  }
204  break;
205  }
206  } while (rc == 0);
207 
208  // close thread handle
209  void* exitValue;
210  rc = pthread_join(mThread, &exitValue);
211  if( rc )
212  {
213  returnStatus = AJA_STATUS_FAIL;
214  AJA_REPORT(
215  0,
217  "AJAThread(%p)::Stop error %d from pthread_join",
218  mpThreadContext, rc);
219  }
220  mThread = 0;
221 
222  return returnStatus;
223 }
224 
225 
226 AJAStatus
227 AJAThreadImpl::Kill(uint32_t exitCode)
228 {
229  AJA_UNUSED(exitCode);
230 
231  AJAAutoLock lock(&mLock);
232  AJAStatus returnStatus = AJA_STATUS_SUCCESS;
233 
234  // If the thread doesn't exist, consider the Kill successful
235  // with zero, no signal sent to thread, it's only checked for validity
236  if (!pthread_kill(mThread, 0))
237  return returnStatus;
238 
239  // Try to make the thread as killable as possible
240  int rc = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
241  if( rc )
242  {
243  returnStatus = AJA_STATUS_FAIL;
244  AJA_REPORT(
245  0,
247  "AJAThread(%p)::Kill error %d from pthread_setcancelstate",
248  mpThreadContext, rc);
249  }
250  rc = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
251  if( rc )
252  {
253  returnStatus = AJA_STATUS_FAIL;
254  AJA_REPORT(
255  0,
257  "AJAThread(%p)::Kill error %d from pthread_setcanceltype",
258  mpThreadContext, rc);
259  }
260 
261  // This should kill the thread, but there are no guarantees
262  rc = pthread_cancel(mThread);
263  if( rc )
264  {
265  returnStatus = AJA_STATUS_FAIL;
266  AJA_REPORT(
267  0,
269  "AJAThread(%p)::Kill error %d from pthread_cancel",
270  mpThreadContext, rc);
271  }
272 
273  return returnStatus;
274 }
275 
276 
277 bool
279 {
280  // if no handle thread is not active
281  if (mThread == 0)
282  {
283  return false;
284  }
285 
286  // with zero, no signal sent to thread, it's only checked for validity
287  if (!pthread_kill(mThread, 0))
288  return true;
289 
290  // the thread has terminated, so clear the handle
291  mThread = 0;
292 
293  return false;
294 }
295 
296 bool
298 {
299  if(mThread == 0)
300  {
301  return false;
302  }
303 
304  if( pthread_equal(mThread, pthread_self()) )
305  {
306  return true;
307  }
308 
309  return false;
310 }
311 
312 
313 AJAStatus
315 {
316  AJAAutoLock lock(&mLock);
317  int nice = 0;
318 
319  switch (threadPriority)
320  {
322  nice = 0;
323  break;
325  nice = -10;
326  break;
328  nice = 10;
329  break;
331  nice = 19;
332  break;
334  nice = 5;
335  break;
337  default:
338  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAThread(%p)::SetPriority: bad thread priority %d", mpThreadContext, threadPriority);
339  return AJA_STATUS_RANGE;
340  }
341 
342  // save priority for starts
343  mPriority = threadPriority;
344 
345  // If thread isn't running, we're done (but we've logged the desired priority for later)
346  if (!Active())
347  return AJA_STATUS_SUCCESS;
348 
349  int policy;
350  struct sched_param param;
351  int rc = pthread_getschedparam(mThread, &policy, &param);
352  if (rc)
353  {
354  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAThread(%p)::SetPriority: error %d getting sched param", mpThreadContext, threadPriority);
355  return AJA_STATUS_FAIL;
356  }
357 
358  param.sched_priority = nice;
359  rc = pthread_setschedparam(mThread, policy, &param);
360  if (rc)
361  {
362  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAThread(%p)::SetPriority: error %d setting sched param", mpThreadContext, threadPriority);
363  return AJA_STATUS_FAIL;
364  }
365 
366  if (threadPriority == AJA_ThreadPriority_TimeCritical)
367  {
368  mach_timebase_info_data_t info;
369  IOReturn ioReturn = mach_timebase_info(&info);
370 
371  uint32_t period = 60000000;
372  uint32_t computation = 2000000;
373 
374  thread_time_constraint_policy timeConstraint;
375  timeConstraint.period = uint32_t(static_cast<long long>(period) * info.denom / info.numer);
376  timeConstraint.computation = uint32_t(static_cast<long long>(computation) * info.denom / info.numer);
377  timeConstraint.constraint = timeConstraint.computation * 2;
378  timeConstraint.preemptible = true;
379 
380  mach_port_t machThread = pthread_mach_thread_np(mThread);
381  while (MACH_PORT_NULL == machThread)
382  machThread = pthread_mach_thread_np(mThread);
383 
384  ioReturn = thread_policy_set(machThread, THREAD_TIME_CONSTRAINT_POLICY,
385  reinterpret_cast<thread_policy_t>(&timeConstraint), THREAD_TIME_CONSTRAINT_POLICY_COUNT);
386  }
387 
388  return AJA_STATUS_SUCCESS;
389 }
390 
391 
392 AJAStatus
394 {
395  if (pThreadPriority == NULL)
396  {
397  return AJA_STATUS_INITIALIZE;
398  }
399 
400  *pThreadPriority = mPriority;
401 
402  return AJA_STATUS_SUCCESS;
403 }
404 
405 
406 AJAStatus
408 {
409  AJA_UNUSED(policy);
410  AJA_UNUSED(priority);
411  return AJA_STATUS_FAIL;
412 }
413 
414 
415 AJAStatus
416 AJAThreadImpl::Attach(AJAThreadFunction* pThreadFunction, void* pUserContext)
417 {
418  // remember the users thread function
419  mThreadFunc = pThreadFunction;
420  mpUserContext = pUserContext;
421 
422  return AJA_STATUS_SUCCESS;
423 }
424 
425 
426 void*
427 AJAThreadImpl::ThreadProcStatic(void* pThreadImplContext)
428 {
429  // this function is called when the thread starts
430  AJAThreadImpl* pThreadImpl = static_cast<AJAThreadImpl*>(pThreadImplContext);
431  AJA_ASSERT(pThreadImpl != NULL);
432  if (pThreadImpl == NULL)
433  {
434  return (void*)0;
435  }
436 
437  // call the thread worker function
438  try
439  {
440  // if user specified function call it
441  if (pThreadImpl->mThreadFunc != NULL)
442  {
443  (*pThreadImpl->mThreadFunc)(pThreadImpl->mpThreadContext, pThreadImpl->mpUserContext);
444  }
445  // otherwise call the virtual function
446  else
447  {
448  pThreadImpl->mpThreadContext->ThreadRun();
449  }
450  }
451  catch(...)
452  {
453  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAThread(%p)::ThreadProcStatic exception in thread function", pThreadImpl->mpThreadContext);
454  return (void*)0;
455  }
456  // signal parent we're exiting
457  pThreadImpl->mExiting = true;
458 
459  int rc = pthread_mutex_lock(&pThreadImpl->mExitMutex);
460  if (rc)
461  {
462  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAThread(%p)::ThreadProcStatic error %d locking exit mutex", pThreadImpl->mpThreadContext, rc);
463  return (void*)0;
464  }
465  rc = pthread_cond_signal(&pThreadImpl->mExitCond);
466  if (rc)
467  {
468  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAThread(%p)::ThreadProcStatic error %d signaling cond variable", pThreadImpl->mpThreadContext, rc);
469  return (void*)0;
470  }
471  rc = pthread_mutex_unlock(&pThreadImpl->mExitMutex);
472  if (rc)
473  {
474  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAThread(%p)::ThreadProcStatic error %d unlocking exit mutex", pThreadImpl->mpThreadContext, rc);
475  return (void*)0;
476  }
477 
478  return (void*)1;
479 }
480 
481 AJAStatus AJAThreadImpl::SetThreadName(const char *name) {
482  int ret = pthread_setname_np(name);
483  if (ret != 0)
484  {
485  return AJA_STATUS_FAIL;
486  }
487  return AJA_STATUS_SUCCESS;
488 }
489 
491 {
492  uint64_t tid=0;
493  pthread_threadid_np(NULL, &tid);
494  return tid;
495 }
AJAThreadImpl::IsCurrentThread
bool IsCurrentThread()
Definition: threadimpl.cpp:406
AJA_ThreadPriority_High
@ AJA_ThreadPriority_High
Definition: thread.h:44
AJAThreadImpl::mPriority
AJAThreadPriority mPriority
Definition: threadimpl.h:49
NULL
#define NULL
Definition: ntv2caption608types.h:19
AJAThreadImpl::mpUserContext
void * mpUserContext
Definition: threadimpl.h:51
AJA_STATUS_SUCCESS
@ AJA_STATUS_SUCCESS
Definition: types.h:368
AJAThreadImpl::GetThreadId
static uint64_t GetThreadId()
Definition: threadimpl.cpp:685
AJAThreadImpl::SetThreadName
AJAStatus SetThreadName(const char *name)
Definition: threadimpl.cpp:672
AJA_DebugSeverity_Error
@ AJA_DebugSeverity_Error
Definition: debugshare.h:28
AJAThreadImpl::mThreadFunc
AJAThreadFunction * mThreadFunc
Definition: threadimpl.h:50
AJAThreadImpl::~AJAThreadImpl
virtual ~AJAThreadImpl()
Definition: threadimpl.cpp:84
AJAThreadImpl::mThread
pthread_t mThread
Definition: threadimpl.h:47
AJAThreadPriority
AJAThreadPriority
Definition: thread.h:39
AJA_UNUSED
#define AJA_UNUSED(_x_)
Definition: types.h:411
AJAThreadImpl
Definition: threadimpl.h:18
AJA_ThreadPriority_TimeCritical
@ AJA_ThreadPriority_TimeCritical
Definition: thread.h:45
AJAThread
Definition: thread.h:69
AJAThreadImpl::mLock
AJALock mLock
Definition: threadimpl.h:52
STACK_SIZE
static const size_t STACK_SIZE
Definition: threadimpl.cpp:22
AJAStatus
AJAStatus
Definition: types.h:365
AJA_ThreadPriority_Unknown
@ AJA_ThreadPriority_Unknown
Definition: thread.h:41
CLOCK_REALTIME
#define CLOCK_REALTIME
Definition: pthreadsextra.h:21
AJAThreadImpl::Kill
AJAStatus Kill(uint32_t exitCode)
Definition: threadimpl.cpp:332
AJAThreadImpl::mExitCond
pthread_cond_t mExitCond
Definition: threadimpl.h:61
AJA_STATUS_FAIL
@ AJA_STATUS_FAIL
Definition: types.h:369
AJA_ThreadPriority_Normal
@ AJA_ThreadPriority_Normal
Definition: thread.h:43
timer.h
Declares the AJATimer class.
AJA_REPORT
#define AJA_REPORT(_index_, _severity_, _format_,...)
Definition: debug.h:117
AJAThread::ThreadRun
virtual AJAStatus ThreadRun()
Definition: thread.cpp:38
AJAThreadImpl::Active
bool Active()
Definition: threadimpl.cpp:386
AJA_STATUS_RANGE
@ AJA_STATUS_RANGE
Definition: types.h:372
AJAAutoLock
Definition: lock.h:91
AJA_STATUS_INITIALIZE
@ AJA_STATUS_INITIALIZE
Definition: types.h:373
AJAThreadImpl::mTerminate
bool mTerminate
Definition: threadimpl.h:58
false
#define false
Definition: ntv2devicefeatures.h:25
AJA_ThreadPriority_AboveNormal
@ AJA_ThreadPriority_AboveNormal
Definition: thread.h:46
pthreadsextra.h
Declares extra symbols to make the Mac threads implementation look more like Unix.
AJAThreadRealTimePolicy
AJAThreadRealTimePolicy
Definition: thread.h:50
threadimpl.h
Declares the AJAThreadImpl class.
AJAThreadImpl::mExitMutex
pthread_mutex_t mExitMutex
Definition: threadimpl.h:60
AJA_ThreadPriority_Low
@ AJA_ThreadPriority_Low
Definition: thread.h:42
AJAThreadImpl::ThreadProcStatic
static void * ThreadProcStatic(void *pThreadImplContext)
Definition: threadimpl.cpp:581
AJAThreadImpl::AJAThreadImpl
AJAThreadImpl(AJAThread *pThreadContext)
Definition: threadimpl.cpp:45
AJAThreadImpl::Stop
AJAStatus Stop(uint32_t timeout=0xffffffff)
Definition: threadimpl.cpp:208
AJAThread::AJAThreadFunction
void AJAThreadFunction(AJAThread *pThread, void *pContext)
Definition: thread.h:22
AJAThreadImpl::mpThreadContext
AJAThread * mpThreadContext
Definition: threadimpl.h:46
AJA_ASSERT
#define AJA_ASSERT(_expression_)
Definition: debug.h:113
AJAThreadImpl::Start
AJAStatus Start()
Definition: threadimpl.cpp:116
clock_gettime
int clock_gettime(clockid_t clk_id, struct timespec *tp)
Definition: pthreadsextra.cpp:12
AJAThreadImpl::SetPriority
AJAStatus SetPriority(AJAThreadPriority threadPriority)
Definition: threadimpl.cpp:425
AJAThreadImpl::Attach
AJAStatus Attach(AJAThreadFunction *pThreadFunction, void *pUserContext)
Definition: threadimpl.cpp:566
AJAThreadImpl::GetPriority
AJAStatus GetPriority(AJAThreadPriority *pThreadPriority)
Definition: threadimpl.cpp:504
debug.h
Declares the AJADebug class.
AJAThreadImpl::SetRealTime
AJAStatus SetRealTime(AJAThreadRealTimePolicy policy, int priority)
Definition: threadimpl.cpp:522
AJAThreadImpl::mExiting
bool mExiting
Definition: threadimpl.h:59