Merged from linuxport rev21200:21590. Updated.
[xbmc:xbmc-antiquated.git] / XBMC / xbmc / lib / libUPnP / Platinum / ThirdParty / Neptune / Source / System / Posix / NptPosixThreads.cpp
1 /*****************************************************************
2 |
3 |      Neptune - Threads :: Posix Implementation
4 |
5 |      (c) 2001-2002 Gilles Boccon-Gibod
6 |      Author: Gilles Boccon-Gibod (bok@bok.net)
7 |
8  ****************************************************************/
9
10 /*----------------------------------------------------------------------
11 |       includes
12 +---------------------------------------------------------------------*/
13 #if defined(__SYMBIAN32__)
14 #include <stdio.h>
15 #endif
16 #include <pthread.h>
17 #include <unistd.h>
18 #include <time.h>
19 #include <sys/time.h>
20 #include <errno.h>
21
22 #include "NptConfig.h"
23 #include "NptTypes.h"
24 #include "NptThreads.h"
25 #include "NptLogging.h"
26 #include "NptTime.h"
27 #include "NptSystem.h"
28
29 /*----------------------------------------------------------------------
30 |       logging
31 +---------------------------------------------------------------------*/
32 NPT_SET_LOCAL_LOGGER("neptune.threads.posix")
33
34 /*----------------------------------------------------------------------
35 |       NPT_PosixMutex
36 +---------------------------------------------------------------------*/
37 class NPT_PosixMutex : public NPT_MutexInterface
38 {
39 public:
40     // methods
41              NPT_PosixMutex();
42     virtual ~NPT_PosixMutex();
43
44     // NPT_Mutex methods
45     virtual NPT_Result Lock();
46     virtual NPT_Result Unlock();
47
48 private:
49     // members
50     pthread_mutex_t m_Mutex;
51 };
52
53 /*----------------------------------------------------------------------
54 |       NPT_PosixMutex::NPT_PosixMutex
55 +---------------------------------------------------------------------*/
56 NPT_PosixMutex::NPT_PosixMutex()
57 {
58     pthread_mutex_init(&m_Mutex, NULL);
59 }
60
61 /*----------------------------------------------------------------------
62 |       NPT_PosixMutex::~NPT_PosixMutex
63 +---------------------------------------------------------------------*/
64 NPT_PosixMutex::~NPT_PosixMutex()
65 {
66     pthread_mutex_destroy(&m_Mutex);
67 }
68
69 /*----------------------------------------------------------------------
70 |       NPT_PosixMutex::Lock
71 +---------------------------------------------------------------------*/
72 NPT_Result
73 NPT_PosixMutex::Lock()
74 {
75     pthread_mutex_lock(&m_Mutex);
76     return NPT_SUCCESS;
77 }
78
79 /*----------------------------------------------------------------------
80 |       NPT_PosixMutex::Unlock
81 +---------------------------------------------------------------------*/
82 NPT_Result
83 NPT_PosixMutex::Unlock()
84 {
85     pthread_mutex_unlock(&m_Mutex);
86     return NPT_SUCCESS;
87 }
88
89 /*----------------------------------------------------------------------
90 |       NPT_Mutex::NPT_Mutex
91 +---------------------------------------------------------------------*/
92 NPT_Mutex::NPT_Mutex()
93 {
94     m_Delegate = new NPT_PosixMutex();
95 }
96
97 /*----------------------------------------------------------------------
98 |       NPT_PosixSharedVariable
99 +---------------------------------------------------------------------*/
100 class NPT_PosixSharedVariable : public NPT_SharedVariableInterface
101 {
102 public:
103     // methods
104                NPT_PosixSharedVariable(int value);
105               ~NPT_PosixSharedVariable();
106     void       SetValue(int value);
107     int        GetValue();
108     NPT_Result WaitUntilEquals(int value, NPT_Timeout timeout);
109     NPT_Result WaitWhileEquals(int value, NPT_Timeout timeout);
110
111  private:
112     // members
113     volatile int    m_Value;
114     pthread_mutex_t m_Mutex;
115     pthread_cond_t  m_Condition;
116 };
117
118 /*----------------------------------------------------------------------
119 |       NPT_PosixSharedVariable::NPT_PosixSharedVariable
120 +---------------------------------------------------------------------*/
121 NPT_PosixSharedVariable::NPT_PosixSharedVariable(int value) : 
122     m_Value(value)
123 {
124     pthread_mutex_init(&m_Mutex, NULL);
125     pthread_cond_init(&m_Condition, NULL);
126 }
127
128 /*----------------------------------------------------------------------
129 |       NPT_PosixSharedVariable::~NPT_PosixSharedVariable
130 +---------------------------------------------------------------------*/
131 NPT_PosixSharedVariable::~NPT_PosixSharedVariable()
132 {
133     pthread_cond_destroy(&m_Condition);
134     pthread_mutex_destroy(&m_Mutex);
135 }
136
137 /*----------------------------------------------------------------------
138 |       NPT_PosixSharedVariable::SetValue
139 +---------------------------------------------------------------------*/
140 void
141 NPT_PosixSharedVariable::SetValue(int value)
142 {
143     pthread_mutex_lock(&m_Mutex);
144     m_Value = value;
145     pthread_cond_broadcast(&m_Condition);
146     pthread_mutex_unlock(&m_Mutex);
147 }
148
149 /*----------------------------------------------------------------------
150 |       NPT_PosixSharedVariable::GetValue
151 +---------------------------------------------------------------------*/
152 int
153 NPT_PosixSharedVariable::GetValue()
154 {
155     // we assume that int read/write are atomic on the platform
156     return m_Value;
157 }
158
159 /*----------------------------------------------------------------------
160 |       NPT_PosixSharedVariable::WaitUntilEquals
161 +---------------------------------------------------------------------*/
162 NPT_Result
163 NPT_PosixSharedVariable::WaitUntilEquals(int value, NPT_Timeout timeout)
164 {
165     NPT_Result result = NPT_SUCCESS;
166     struct     timespec timed;
167
168     if (timeout != NPT_TIMEOUT_INFINITE) {
169         // get current time from system
170         struct timeval now;
171         if (gettimeofday(&now, NULL)) {
172             return NPT_FAILURE;
173         }
174
175         now.tv_usec += timeout * 1000;
176         if (now.tv_usec >= 1000000) {
177             now.tv_sec += now.tv_usec / 1000000;
178             now.tv_usec = now.tv_usec % 1000000;
179         }
180
181         // setup timeout
182         timed.tv_sec  = now.tv_sec;
183         timed.tv_nsec = now.tv_usec * 1000;
184     }
185     
186     pthread_mutex_lock(&m_Mutex);
187     while (value != m_Value) {
188         if (timeout == NPT_TIMEOUT_INFINITE) {
189             pthread_cond_wait(&m_Condition, &m_Mutex);
190         } else {
191             int wait_res = pthread_cond_timedwait(&m_Condition, &m_Mutex, &timed);
192             if (wait_res == ETIMEDOUT) {
193                 result = NPT_ERROR_TIMEOUT;
194                 break;
195             }
196         }
197     }
198     pthread_mutex_unlock(&m_Mutex);
199     
200     return result;
201 }
202
203 /*----------------------------------------------------------------------
204 |       NPT_PosixSharedVariable::WaitWhileEquals
205 +---------------------------------------------------------------------*/
206 NPT_Result
207 NPT_PosixSharedVariable::WaitWhileEquals(int value, NPT_Timeout timeout)
208 {
209     NPT_Result result = NPT_SUCCESS;
210     struct     timespec timed;
211
212     if (timeout != NPT_TIMEOUT_INFINITE) {
213         // get current time from system
214         struct timeval now;
215         if (gettimeofday(&now, NULL)) {
216             return NPT_FAILURE;
217         }
218
219         now.tv_usec += timeout * 1000;
220         if (now.tv_usec >= 1000000) {
221             now.tv_sec += now.tv_usec / 1000000;
222             now.tv_usec = now.tv_usec % 1000000;
223         }
224
225         // setup timeout
226         timed.tv_sec  = now.tv_sec;
227         timed.tv_nsec = now.tv_usec * 1000;
228     }
229     
230     pthread_mutex_lock(&m_Mutex);
231     while (value == m_Value) {
232         if (timeout == NPT_TIMEOUT_INFINITE) {
233             pthread_cond_wait(&m_Condition, &m_Mutex);
234         } else {
235             int wait_res = pthread_cond_timedwait(&m_Condition, &m_Mutex, &timed);
236             if (wait_res == ETIMEDOUT) {
237                 result = NPT_ERROR_TIMEOUT;
238                 break;
239             }
240         }
241     }
242     pthread_mutex_unlock(&m_Mutex);
243     
244     return result;
245 }
246
247 /*----------------------------------------------------------------------
248 |       NPT_SharedVariable::NPT_SharedVariable
249 +---------------------------------------------------------------------*/
250 NPT_SharedVariable::NPT_SharedVariable(int value)
251 {
252     m_Delegate = new NPT_PosixSharedVariable(value);
253 }
254
255 /*----------------------------------------------------------------------
256 |       NPT_PosixAtomicVariable
257 +---------------------------------------------------------------------*/
258 class NPT_PosixAtomicVariable : public NPT_AtomicVariableInterface
259 {
260  public:
261     // methods
262          NPT_PosixAtomicVariable(int value);
263         ~NPT_PosixAtomicVariable();
264     int  Increment(); 
265     int  Decrement();
266     int  GetValue();
267     void SetValue(int value);
268
269  private:
270     // members
271     volatile int    m_Value;
272     pthread_mutex_t m_Mutex;
273 };
274
275 /*----------------------------------------------------------------------
276 |       NPT_PosixAtomicVariable::NPT_PosixAtomicVariable
277 +---------------------------------------------------------------------*/
278 NPT_PosixAtomicVariable::NPT_PosixAtomicVariable(int value) : 
279     m_Value(value)
280 {
281     pthread_mutex_init(&m_Mutex, NULL);
282 }
283
284 /*----------------------------------------------------------------------
285 |       NPT_PosixAtomicVariable::~NPT_PosixAtomicVariable
286 +---------------------------------------------------------------------*/
287 NPT_PosixAtomicVariable::~NPT_PosixAtomicVariable()
288 {
289     pthread_mutex_destroy(&m_Mutex);
290 }
291
292 /*----------------------------------------------------------------------
293 |       NPT_PosixAtomicVariable::Increment
294 +---------------------------------------------------------------------*/
295 int
296 NPT_PosixAtomicVariable::Increment()
297 {
298     int value;
299
300     pthread_mutex_lock(&m_Mutex);
301     value = ++m_Value;
302     pthread_mutex_unlock(&m_Mutex);
303     
304     return value;
305 }
306
307 /*----------------------------------------------------------------------
308 |       NPT_PosixAtomicVariable::Decrement
309 +---------------------------------------------------------------------*/
310 int
311 NPT_PosixAtomicVariable::Decrement()
312 {
313     int value;
314
315     pthread_mutex_lock(&m_Mutex);
316     value = --m_Value;
317     pthread_mutex_unlock(&m_Mutex);
318     
319     return value;
320 }
321
322 /*----------------------------------------------------------------------
323 |       NPT_PosixAtomicVariable::GetValue
324 +---------------------------------------------------------------------*/
325 int
326 NPT_PosixAtomicVariable::GetValue()
327 {
328     // we assume that int read/write are atomic on the platform
329     return m_Value;
330 }
331
332 /*----------------------------------------------------------------------
333 |       NPT_PosixAtomicVariable::SetValue
334 +---------------------------------------------------------------------*/
335 void
336 NPT_PosixAtomicVariable::SetValue(int value)
337 {
338     pthread_mutex_lock(&m_Mutex);
339     m_Value = value;
340     pthread_mutex_unlock(&m_Mutex);
341 }
342
343 /*----------------------------------------------------------------------
344 |       NPT_AtomicVariable::NPT_AtomicVariable
345 +---------------------------------------------------------------------*/
346 NPT_AtomicVariable::NPT_AtomicVariable(int value)
347 {
348     m_Delegate = new NPT_PosixAtomicVariable(value);
349 }
350
351 /*----------------------------------------------------------------------
352 |       NPT_PosixThread
353 +---------------------------------------------------------------------*/
354 class NPT_PosixThread : public NPT_ThreadInterface
355 {
356  public:
357     // methods
358                 NPT_PosixThread(NPT_Thread*   delegator,
359                                 NPT_Runnable& target,
360                                 bool          detached);
361                ~NPT_PosixThread();
362     NPT_Result  Start(); 
363     NPT_Result  Wait(NPT_Timeout timeout = NPT_TIMEOUT_INFINITE);
364
365  private:
366     // methods
367     static void* EntryPoint(void* argument);
368
369     // NPT_Runnable methods
370     void Run();
371
372     // NPT_Interruptible methods
373     NPT_Result Interrupt() { return NPT_ERROR_NOT_IMPLEMENTED; }
374
375     // members
376     NPT_Thread*        m_Delegator;
377     NPT_Runnable&      m_Target;
378     bool               m_Detached;
379     pthread_t          m_ThreadId;
380     bool               m_Joined;
381     NPT_PosixMutex     m_JoinLock;
382     NPT_SharedVariable m_Done;
383 };
384
385 /*----------------------------------------------------------------------
386 |       NPT_PosixThread::NPT_PosixThread
387 +---------------------------------------------------------------------*/
388 NPT_PosixThread::NPT_PosixThread(NPT_Thread*   delegator,
389                                  NPT_Runnable& target,
390                                  bool          detached) : 
391     m_Delegator(delegator),
392     m_Target(target),
393     m_Detached(detached),
394     m_ThreadId(0),
395     m_Joined(false)
396 {
397     NPT_LOG_FINE("NPT_PosixThread::NPT_PosixThread");
398     m_Done.SetValue(0);
399 }
400
401 /*----------------------------------------------------------------------
402 |       NPT_PosixThread::~NPT_PosixThread
403 +---------------------------------------------------------------------*/
404 NPT_PosixThread::~NPT_PosixThread()
405 {
406     NPT_LOG_FINE_1("NPT_PosixThread::~NPT_PosixThread %d\n", m_ThreadId);
407
408     if (!m_Detached) {
409         // we're not detached, and not in the Run() method, so we need to 
410         // wait until the thread is done
411         Wait();
412     }
413 }
414
415 /*----------------------------------------------------------------------
416 |   NPT_Thread::GetCurrentThreadId
417 +---------------------------------------------------------------------*/
418 NPT_Thread::ThreadId 
419 NPT_Thread::GetCurrentThreadId()
420 {
421     pthread_t pid = pthread_self();
422     return (NPT_Thread::ThreadId)((void*)pid);
423 }
424
425 /*----------------------------------------------------------------------
426 |       NPT_PosixThread::EntryPoint
427 +---------------------------------------------------------------------*/
428 void*
429 NPT_PosixThread::EntryPoint(void* argument)
430 {
431     NPT_PosixThread* thread = reinterpret_cast<NPT_PosixThread*>(argument);
432
433     NPT_LOG_FINE("NPT_PosixThread::EntryPoint - in =======================");
434
435     // get the thread ID from this context, because m_ThreadId may not yet
436     // have been set by the parent thread in the Start() method
437     thread->m_ThreadId = pthread_self();
438     
439     // set random seed per thread
440     NPT_TimeStamp now;
441     NPT_System::GetCurrentTimeStamp(now);
442     NPT_System::SetRandomSeed((unsigned int)(now.m_NanoSeconds + (long)thread->m_ThreadId));
443
444     // run the thread 
445     thread->Run();
446     
447     // Logging here will cause a crash on exit because LogManager may already be destroyed    
448     //NPT_LOG_FINE("NPT_PosixThread::EntryPoint - out ======================");
449
450     // we're done with the thread object
451     // if we're detached, we need to delete ourselves
452     if (thread->m_Detached) {
453         delete thread->m_Delegator;
454     } else {
455         // notify we're done
456         thread->m_Done.SetValue(1);
457     }
458
459     // done
460     return NULL;
461 }
462
463 /*----------------------------------------------------------------------
464 |       NPT_PosixThread::Start
465 +---------------------------------------------------------------------*/
466 NPT_Result
467 NPT_PosixThread::Start()
468 {
469     NPT_LOG_FINE("NPT_PosixThread::Start - creating thread");
470
471     pthread_attr_t *attributes = NULL;
472
473 #if defined(NPT_CONFIG_THREAD_STACK_SIZE)
474     pthread_attr_t stack_size_attributes;
475     pthread_attr_init(&stack_size_attributes);
476     pthread_attr_setstacksize(&stack_size_attributes, NPT_CONFIG_THREAD_STACK_SIZE);
477     attributes = &stack_size_attributes;
478 #endif
479
480     // use local copies of some of the object's members, because for
481     // detached threads, the object instance may have deleted itself
482     // before the pthread_create() function returns
483     bool detached = m_Detached;
484
485     // create the native thread
486     pthread_t thread_id;
487     int result = pthread_create(&thread_id, attributes, EntryPoint, 
488                                 static_cast<NPT_PosixThread*>(this));
489     NPT_LOG_FINE_2("NPT_PosixThread::Start - id = %d, res=%d", 
490                    thread_id, result);
491     if (result != 0) {
492         // failed
493         return NPT_FAILURE;
494     } else {
495         // detach the thread if we're not joinable
496         if (detached) {
497             pthread_detach(thread_id);
498         } else {
499             // store the thread ID (NOTE: this is also done by the thread Run() method
500             // but it is necessary to do it from both contexts, because we don't know
501             // which one will need it first.)
502             m_ThreadId = thread_id;        
503         } 
504         return NPT_SUCCESS;
505     }
506 }
507
508 /*----------------------------------------------------------------------
509 |       NPT_PosixThread::Run
510 +---------------------------------------------------------------------*/
511 void
512 NPT_PosixThread::Run()
513 {
514     m_Target.Run();
515 }
516
517 /*----------------------------------------------------------------------
518 |       NPT_PosixThread::Wait
519 +---------------------------------------------------------------------*/
520 NPT_Result
521 NPT_PosixThread::Wait(NPT_Timeout timeout /* = NPT_TIMEOUT_INFINITE */)
522 {
523     void* return_value;
524     int   result;
525
526     NPT_LOG_FINE_1("NPT_PosixThread::Wait - waiting for id %d", m_ThreadId);
527
528     // check that we're not detached
529     if (m_ThreadId == 0 || m_Detached) {
530         return NPT_FAILURE;
531     }
532
533     // wait for the thread to finish
534     m_JoinLock.Lock();
535     if (m_Joined) {
536         NPT_LOG_FINE_1("NPT_PosixThread::Wait - %d already joined", m_ThreadId);
537         result = 0;
538     } else {
539         NPT_LOG_FINE_1("NPT_PosixThread::Wait - joining thread id %d", m_ThreadId);
540         if (timeout != NPT_TIMEOUT_INFINITE) {
541             result = m_Done.WaitUntilEquals(1, timeout);
542             if (NPT_FAILED(result)) {
543                 result = -1;
544                 goto timedout;
545             }
546         }
547
548         result = pthread_join(m_ThreadId, &return_value);
549         m_Joined = true;
550     }
551
552 timedout:
553     m_JoinLock.Unlock();
554     if (result != 0) {
555         return NPT_FAILURE;
556     } else {
557         return NPT_SUCCESS;
558     }
559 }
560
561 /*----------------------------------------------------------------------
562 |       NPT_Thread::NPT_Thread
563 +---------------------------------------------------------------------*/
564 NPT_Thread::NPT_Thread(bool detached)
565 {
566     m_Delegate = new NPT_PosixThread(this, *this, detached);
567 }
568
569 /*----------------------------------------------------------------------
570 |       NPT_Thread::NPT_Thread
571 +---------------------------------------------------------------------*/
572 NPT_Thread::NPT_Thread(NPT_Runnable& target, bool detached)
573 {
574     m_Delegate = new NPT_PosixThread(this, target, detached);
575 }
576