Home | History | Annotate | Download | only in source
      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 "webrtc/system_wrappers/source/event_timer_posix.h"
     12 
     13 #include <errno.h>
     14 #include <pthread.h>
     15 #include <signal.h>
     16 #include <stdio.h>
     17 #include <string.h>
     18 #include <sys/time.h>
     19 #include <unistd.h>
     20 
     21 #include "webrtc/base/checks.h"
     22 
     23 namespace webrtc {
     24 
     25 // static
     26 EventTimerWrapper* EventTimerWrapper::Create() {
     27   return new EventTimerPosix();
     28 }
     29 
     30 const long int E6 = 1000000;
     31 const long int E9 = 1000 * E6;
     32 
     33 EventTimerPosix::EventTimerPosix()
     34     : event_set_(false),
     35       timer_thread_(nullptr),
     36       created_at_(),
     37       periodic_(false),
     38       time_(0),
     39       count_(0) {
     40   pthread_mutexattr_t attr;
     41   pthread_mutexattr_init(&attr);
     42   pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
     43   pthread_mutex_init(&mutex_, &attr);
     44 #ifdef WEBRTC_CLOCK_TYPE_REALTIME
     45   pthread_cond_init(&cond_, 0);
     46 #else
     47   pthread_condattr_t cond_attr;
     48   pthread_condattr_init(&cond_attr);
     49   pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
     50   pthread_cond_init(&cond_, &cond_attr);
     51   pthread_condattr_destroy(&cond_attr);
     52 #endif
     53 }
     54 
     55 EventTimerPosix::~EventTimerPosix() {
     56   StopTimer();
     57   pthread_cond_destroy(&cond_);
     58   pthread_mutex_destroy(&mutex_);
     59 }
     60 
     61 // TODO(pbos): Make this void.
     62 bool EventTimerPosix::Set() {
     63   RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
     64   event_set_ = true;
     65   pthread_cond_signal(&cond_);
     66   pthread_mutex_unlock(&mutex_);
     67   return true;
     68 }
     69 
     70 EventTypeWrapper EventTimerPosix::Wait(unsigned long timeout) {
     71   int ret_val = 0;
     72   RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
     73 
     74   if (!event_set_) {
     75     if (WEBRTC_EVENT_INFINITE != timeout) {
     76       timespec end_at;
     77 #ifndef WEBRTC_MAC
     78 #ifdef WEBRTC_CLOCK_TYPE_REALTIME
     79       clock_gettime(CLOCK_REALTIME, &end_at);
     80 #else
     81       clock_gettime(CLOCK_MONOTONIC, &end_at);
     82 #endif
     83 #else
     84       timeval value;
     85       struct timezone time_zone;
     86       time_zone.tz_minuteswest = 0;
     87       time_zone.tz_dsttime = 0;
     88       gettimeofday(&value, &time_zone);
     89       TIMEVAL_TO_TIMESPEC(&value, &end_at);
     90 #endif
     91       end_at.tv_sec  += timeout / 1000;
     92       end_at.tv_nsec += (timeout - (timeout / 1000) * 1000) * E6;
     93 
     94       if (end_at.tv_nsec >= E9) {
     95         end_at.tv_sec++;
     96         end_at.tv_nsec -= E9;
     97       }
     98       while (ret_val == 0 && !event_set_)
     99         ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at);
    100     } else {
    101       while (ret_val == 0 && !event_set_)
    102         ret_val = pthread_cond_wait(&cond_, &mutex_);
    103     }
    104   }
    105 
    106   RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
    107 
    108   // Reset and signal if set, regardless of why the thread woke up.
    109   if (event_set_) {
    110     ret_val = 0;
    111     event_set_ = false;
    112   }
    113   pthread_mutex_unlock(&mutex_);
    114 
    115   return ret_val == 0 ? kEventSignaled : kEventTimeout;
    116 }
    117 
    118 EventTypeWrapper EventTimerPosix::Wait(timespec* end_at) {
    119   int ret_val = 0;
    120   RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
    121 
    122   while (ret_val == 0 && !event_set_)
    123     ret_val = pthread_cond_timedwait(&cond_, &mutex_, end_at);
    124 
    125   RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
    126 
    127   // Reset and signal if set, regardless of why the thread woke up.
    128   if (event_set_) {
    129     ret_val = 0;
    130     event_set_ = false;
    131   }
    132   pthread_mutex_unlock(&mutex_);
    133 
    134   return ret_val == 0 ? kEventSignaled : kEventTimeout;
    135 }
    136 
    137 bool EventTimerPosix::StartTimer(bool periodic, unsigned long time) {
    138   pthread_mutex_lock(&mutex_);
    139   if (timer_thread_) {
    140     if (periodic_) {
    141       // Timer already started.
    142       pthread_mutex_unlock(&mutex_);
    143       return false;
    144     } else  {
    145       // New one shot timer
    146       time_ = time;
    147       created_at_.tv_sec = 0;
    148       timer_event_->Set();
    149       pthread_mutex_unlock(&mutex_);
    150       return true;
    151     }
    152   }
    153 
    154   // Start the timer thread
    155   timer_event_.reset(new EventTimerPosix());
    156   const char* thread_name = "WebRtc_event_timer_thread";
    157   timer_thread_.reset(new rtc::PlatformThread(Run, this, thread_name));
    158   periodic_ = periodic;
    159   time_ = time;
    160   timer_thread_->Start();
    161   timer_thread_->SetPriority(rtc::kRealtimePriority);
    162   pthread_mutex_unlock(&mutex_);
    163 
    164   return true;
    165 }
    166 
    167 bool EventTimerPosix::Run(void* obj) {
    168   return static_cast<EventTimerPosix*>(obj)->Process();
    169 }
    170 
    171 bool EventTimerPosix::Process() {
    172   pthread_mutex_lock(&mutex_);
    173   if (created_at_.tv_sec == 0) {
    174 #ifndef WEBRTC_MAC
    175 #ifdef WEBRTC_CLOCK_TYPE_REALTIME
    176     clock_gettime(CLOCK_REALTIME, &created_at_);
    177 #else
    178     clock_gettime(CLOCK_MONOTONIC, &created_at_);
    179 #endif
    180 #else
    181     timeval value;
    182     struct timezone time_zone;
    183     time_zone.tz_minuteswest = 0;
    184     time_zone.tz_dsttime = 0;
    185     gettimeofday(&value, &time_zone);
    186     TIMEVAL_TO_TIMESPEC(&value, &created_at_);
    187 #endif
    188     count_ = 0;
    189   }
    190 
    191   timespec end_at;
    192   unsigned long long time = time_ * ++count_;
    193   end_at.tv_sec  = created_at_.tv_sec + time / 1000;
    194   end_at.tv_nsec = created_at_.tv_nsec + (time - (time / 1000) * 1000) * E6;
    195 
    196   if (end_at.tv_nsec >= E9) {
    197     end_at.tv_sec++;
    198     end_at.tv_nsec -= E9;
    199   }
    200 
    201   pthread_mutex_unlock(&mutex_);
    202   if (timer_event_->Wait(&end_at) == kEventSignaled)
    203     return true;
    204 
    205   pthread_mutex_lock(&mutex_);
    206   if (periodic_ || count_ == 1)
    207     Set();
    208   pthread_mutex_unlock(&mutex_);
    209 
    210   return true;
    211 }
    212 
    213 bool EventTimerPosix::StopTimer() {
    214   if (timer_event_) {
    215     timer_event_->Set();
    216   }
    217   if (timer_thread_) {
    218     timer_thread_->Stop();
    219     timer_thread_.reset();
    220   }
    221   timer_event_.reset();
    222 
    223   // Set time to zero to force new reference time for the timer.
    224   memset(&created_at_, 0, sizeof(created_at_));
    225   count_ = 0;
    226   return true;
    227 }
    228 
    229 }  // namespace webrtc
    230