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_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 namespace webrtc {
     22 
     23 const long int E6 = 1000000;
     24 const long int E9 = 1000 * E6;
     25 
     26 EventWrapper* EventPosix::Create() {
     27   EventPosix* ptr = new EventPosix;
     28   if (!ptr) {
     29     return NULL;
     30   }
     31 
     32   const int error = ptr->Construct();
     33   if (error) {
     34     delete ptr;
     35     return NULL;
     36   }
     37   return ptr;
     38 }
     39 
     40 EventPosix::EventPosix()
     41     : timer_thread_(0),
     42       timer_event_(0),
     43       periodic_(false),
     44       time_(0),
     45       count_(0),
     46       state_(kDown) {
     47 }
     48 
     49 int EventPosix::Construct() {
     50   // Set start time to zero
     51   memset(&created_at_, 0, sizeof(created_at_));
     52 
     53   pthread_mutexattr_t attr;
     54   pthread_mutexattr_init(&attr);
     55   pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
     56   int result = pthread_mutex_init(&mutex_, &attr);
     57   if (result != 0) {
     58     return -1;
     59   }
     60 #ifdef WEBRTC_CLOCK_TYPE_REALTIME
     61   result = pthread_cond_init(&cond_, 0);
     62   if (result != 0) {
     63     return -1;
     64   }
     65 #else
     66   pthread_condattr_t cond_attr;
     67   result = pthread_condattr_init(&cond_attr);
     68   if (result != 0) {
     69     return -1;
     70   }
     71   result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
     72   if (result != 0) {
     73     return -1;
     74   }
     75   result = pthread_cond_init(&cond_, &cond_attr);
     76   if (result != 0) {
     77     return -1;
     78   }
     79   result = pthread_condattr_destroy(&cond_attr);
     80   if (result != 0) {
     81     return -1;
     82   }
     83 #endif
     84   return 0;
     85 }
     86 
     87 EventPosix::~EventPosix() {
     88   StopTimer();
     89   pthread_cond_destroy(&cond_);
     90   pthread_mutex_destroy(&mutex_);
     91 }
     92 
     93 bool EventPosix::Reset() {
     94   if (0 != pthread_mutex_lock(&mutex_)) {
     95     return false;
     96   }
     97   state_ = kDown;
     98   pthread_mutex_unlock(&mutex_);
     99   return true;
    100 }
    101 
    102 bool EventPosix::Set() {
    103   if (0 != pthread_mutex_lock(&mutex_)) {
    104     return false;
    105   }
    106   state_ = kUp;
    107   // Release all waiting threads
    108   pthread_cond_broadcast(&cond_);
    109   pthread_mutex_unlock(&mutex_);
    110   return true;
    111 }
    112 
    113 EventTypeWrapper EventPosix::Wait(unsigned long timeout) {
    114   int ret_val = 0;
    115   if (0 != pthread_mutex_lock(&mutex_)) {
    116     return kEventError;
    117   }
    118 
    119   if (kDown == state_) {
    120     if (WEBRTC_EVENT_INFINITE != timeout) {
    121       timespec end_at;
    122 #ifndef WEBRTC_MAC
    123 #ifdef WEBRTC_CLOCK_TYPE_REALTIME
    124       clock_gettime(CLOCK_REALTIME, &end_at);
    125 #else
    126       clock_gettime(CLOCK_MONOTONIC, &end_at);
    127 #endif
    128 #else
    129       timeval value;
    130       struct timezone time_zone;
    131       time_zone.tz_minuteswest = 0;
    132       time_zone.tz_dsttime = 0;
    133       gettimeofday(&value, &time_zone);
    134       TIMEVAL_TO_TIMESPEC(&value, &end_at);
    135 #endif
    136       end_at.tv_sec  += timeout / 1000;
    137       end_at.tv_nsec += (timeout - (timeout / 1000) * 1000) * E6;
    138 
    139       if (end_at.tv_nsec >= E9) {
    140         end_at.tv_sec++;
    141         end_at.tv_nsec -= E9;
    142       }
    143       ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at);
    144     } else {
    145       ret_val = pthread_cond_wait(&cond_, &mutex_);
    146     }
    147   }
    148 
    149   state_ = kDown;
    150   pthread_mutex_unlock(&mutex_);
    151 
    152   switch (ret_val) {
    153     case 0:
    154       return kEventSignaled;
    155     case ETIMEDOUT:
    156       return kEventTimeout;
    157     default:
    158       return kEventError;
    159   }
    160 }
    161 
    162 EventTypeWrapper EventPosix::Wait(timespec& wake_at) {
    163   int ret_val = 0;
    164   if (0 != pthread_mutex_lock(&mutex_)) {
    165     return kEventError;
    166   }
    167 
    168   if (kUp != state_) {
    169     ret_val = pthread_cond_timedwait(&cond_, &mutex_, &wake_at);
    170   }
    171   state_ = kDown;
    172 
    173   pthread_mutex_unlock(&mutex_);
    174 
    175   switch (ret_val) {
    176     case 0:
    177       return kEventSignaled;
    178     case ETIMEDOUT:
    179       return kEventTimeout;
    180     default:
    181       return kEventError;
    182   }
    183 }
    184 
    185 bool EventPosix::StartTimer(bool periodic, unsigned long time) {
    186   pthread_mutex_lock(&mutex_);
    187   if (timer_thread_) {
    188     if (periodic_) {
    189       // Timer already started.
    190       pthread_mutex_unlock(&mutex_);
    191       return false;
    192     } else  {
    193       // New one shot timer
    194       time_ = time;
    195       created_at_.tv_sec = 0;
    196       timer_event_->Set();
    197       pthread_mutex_unlock(&mutex_);
    198       return true;
    199     }
    200   }
    201 
    202   // Start the timer thread
    203   timer_event_ = static_cast<EventPosix*>(EventWrapper::Create());
    204   const char* thread_name = "WebRtc_event_timer_thread";
    205   timer_thread_ = ThreadWrapper::CreateThread(Run, this, kRealtimePriority,
    206                                               thread_name);
    207   periodic_ = periodic;
    208   time_ = time;
    209   unsigned int id = 0;
    210   bool started = timer_thread_->Start(id);
    211   pthread_mutex_unlock(&mutex_);
    212 
    213   return started;
    214 }
    215 
    216 bool EventPosix::Run(ThreadObj obj) {
    217   return static_cast<EventPosix*>(obj)->Process();
    218 }
    219 
    220 bool EventPosix::Process() {
    221   pthread_mutex_lock(&mutex_);
    222   if (created_at_.tv_sec == 0) {
    223 #ifndef WEBRTC_MAC
    224 #ifdef WEBRTC_CLOCK_TYPE_REALTIME
    225     clock_gettime(CLOCK_REALTIME, &created_at_);
    226 #else
    227     clock_gettime(CLOCK_MONOTONIC, &created_at_);
    228 #endif
    229 #else
    230     timeval value;
    231     struct timezone time_zone;
    232     time_zone.tz_minuteswest = 0;
    233     time_zone.tz_dsttime = 0;
    234     gettimeofday(&value, &time_zone);
    235     TIMEVAL_TO_TIMESPEC(&value, &created_at_);
    236 #endif
    237     count_ = 0;
    238   }
    239 
    240   timespec end_at;
    241   unsigned long long time = time_ * ++count_;
    242   end_at.tv_sec  = created_at_.tv_sec + time / 1000;
    243   end_at.tv_nsec = created_at_.tv_nsec + (time - (time / 1000) * 1000) * E6;
    244 
    245   if (end_at.tv_nsec >= E9) {
    246     end_at.tv_sec++;
    247     end_at.tv_nsec -= E9;
    248   }
    249 
    250   pthread_mutex_unlock(&mutex_);
    251   switch (timer_event_->Wait(end_at)) {
    252     case kEventSignaled:
    253       return true;
    254     case kEventError:
    255       return false;
    256     case kEventTimeout:
    257       break;
    258   }
    259 
    260   pthread_mutex_lock(&mutex_);
    261   if (periodic_ || count_ == 1)
    262     Set();
    263   pthread_mutex_unlock(&mutex_);
    264 
    265   return true;
    266 }
    267 
    268 bool EventPosix::StopTimer() {
    269   if (timer_thread_) {
    270     timer_thread_->SetNotAlive();
    271   }
    272   if (timer_event_) {
    273     timer_event_->Set();
    274   }
    275   if (timer_thread_) {
    276     if (!timer_thread_->Stop()) {
    277       return false;
    278     }
    279 
    280     delete timer_thread_;
    281     timer_thread_ = 0;
    282   }
    283   if (timer_event_) {
    284     delete timer_event_;
    285     timer_event_ = 0;
    286   }
    287 
    288   // Set time to zero to force new reference time for the timer.
    289   memset(&created_at_, 0, sizeof(created_at_));
    290   count_ = 0;
    291   return true;
    292 }
    293 
    294 }  // namespace webrtc
    295