Home | History | Annotate | Download | only in src
      1 /******************************************************************************
      2  *
      3  *  Copyright (C) 2014 Google, Inc.
      4  *
      5  *  Licensed under the Apache License, Version 2.0 (the "License");
      6  *  you may not use this file except in compliance with the License.
      7  *  You may obtain a copy of the License at:
      8  *
      9  *  http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  *
     17  ******************************************************************************/
     18 
     19 #define LOG_TAG "bt_osi_alarm"
     20 
     21 #include <assert.h>
     22 #include <errno.h>
     23 #include <hardware/bluetooth.h>
     24 #include <inttypes.h>
     25 #include <time.h>
     26 #include <utils/Log.h>
     27 
     28 #include "alarm.h"
     29 #include "list.h"
     30 #include "osi.h"
     31 
     32 struct alarm_t {
     33   // The lock is held while the callback for this alarm is being executed.
     34   // It allows us to release the coarse-grained monitor lock while a potentially
     35   // long-running callback is executing. |alarm_cancel| uses this lock to provide
     36   // a guarantee to its caller that the callback will not be in progress when it
     37   // returns.
     38   pthread_mutex_t callback_lock;
     39   period_ms_t deadline;
     40   alarm_callback_t callback;
     41   void *data;
     42 };
     43 
     44 extern bt_os_callouts_t *bt_os_callouts;
     45 
     46 // If the next wakeup time is less than this threshold, we should acquire
     47 // a wakelock instead of setting a wake alarm so we're not bouncing in
     48 // and out of suspend frequently. This value is externally visible to allow
     49 // unit tests to run faster. It should not be modified by production code.
     50 int64_t TIMER_INTERVAL_FOR_WAKELOCK_IN_MS = 3000;
     51 static const clockid_t CLOCK_ID = CLOCK_BOOTTIME;
     52 static const char *WAKE_LOCK_ID = "bluedroid_timer";
     53 
     54 // This mutex ensures that the |alarm_set|, |alarm_cancel|, and alarm callback
     55 // functions execute serially and not concurrently. As a result, this mutex also
     56 // protects the |alarms| list.
     57 static pthread_mutex_t monitor;
     58 static list_t *alarms;
     59 static timer_t timer;
     60 static bool timer_set;
     61 
     62 static bool lazy_initialize(void);
     63 static period_ms_t now(void);
     64 static void timer_callback(void *data);
     65 static void reschedule(void);
     66 
     67 alarm_t *alarm_new(void) {
     68   // Make sure we have a list we can insert alarms into.
     69   if (!alarms && !lazy_initialize())
     70     return NULL;
     71 
     72   pthread_mutexattr_t attr;
     73   pthread_mutexattr_init(&attr);
     74 
     75   alarm_t *ret = calloc(1, sizeof(alarm_t));
     76   if (!ret) {
     77     ALOGE("%s unable to allocate memory for alarm.", __func__);
     78     goto error;
     79   }
     80 
     81   // Make this a recursive mutex to make it safe to call |alarm_cancel| from
     82   // within the callback function of the alarm.
     83   int error = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
     84   if (error) {
     85     ALOGE("%s unable to create a recursive mutex: %s", __func__, strerror(error));
     86     goto error;
     87   }
     88 
     89   error = pthread_mutex_init(&ret->callback_lock, &attr);
     90   if (error) {
     91     ALOGE("%s unable to initialize mutex: %s", __func__, strerror(error));
     92     goto error;
     93   }
     94 
     95   pthread_mutexattr_destroy(&attr);
     96   return ret;
     97 
     98 error:;
     99   pthread_mutexattr_destroy(&attr);
    100   free(ret);
    101   return NULL;
    102 }
    103 
    104 void alarm_free(alarm_t *alarm) {
    105   if (!alarm)
    106     return;
    107 
    108   alarm_cancel(alarm);
    109   pthread_mutex_destroy(&alarm->callback_lock);
    110   free(alarm);
    111 }
    112 
    113 // Runs in exclusion with alarm_cancel and timer_callback.
    114 void alarm_set(alarm_t *alarm, period_ms_t deadline, alarm_callback_t cb, void *data) {
    115   assert(alarms != NULL);
    116   assert(alarm != NULL);
    117   assert(cb != NULL);
    118 
    119   pthread_mutex_lock(&monitor);
    120 
    121   // If the alarm is currently set and it's at the start of the list,
    122   // we'll need to re-schedule since we've adjusted the earliest deadline.
    123   bool needs_reschedule = (!list_is_empty(alarms) && list_front(alarms) == alarm);
    124   if (alarm->callback)
    125     list_remove(alarms, alarm);
    126 
    127   alarm->deadline = now() + deadline;
    128   alarm->callback = cb;
    129   alarm->data = data;
    130 
    131   // Add it into the timer list sorted by deadline (earliest deadline first).
    132   if (list_is_empty(alarms))
    133     list_prepend(alarms, alarm);
    134   else
    135     for (list_node_t *node = list_begin(alarms); node != list_end(alarms); node = list_next(node)) {
    136       list_node_t *next = list_next(node);
    137       if (next == list_end(alarms) || ((alarm_t *)list_node(next))->deadline >= alarm->deadline) {
    138         list_insert_after(alarms, node, alarm);
    139         break;
    140       }
    141     }
    142 
    143   // If the new alarm has the earliest deadline, we need to re-evaluate our schedule.
    144   if (needs_reschedule || (!list_is_empty(alarms) && list_front(alarms) == alarm))
    145     reschedule();
    146 
    147   pthread_mutex_unlock(&monitor);
    148 }
    149 
    150 void alarm_cancel(alarm_t *alarm) {
    151   assert(alarms != NULL);
    152   assert(alarm != NULL);
    153 
    154   pthread_mutex_lock(&monitor);
    155 
    156   bool needs_reschedule = (!list_is_empty(alarms) && list_front(alarms) == alarm);
    157 
    158   list_remove(alarms, alarm);
    159   alarm->deadline = 0;
    160   alarm->callback = NULL;
    161   alarm->data = NULL;
    162 
    163   if (needs_reschedule)
    164     reschedule();
    165 
    166   pthread_mutex_unlock(&monitor);
    167 
    168   // If the callback for |alarm| is in progress, wait here until it completes.
    169   pthread_mutex_lock(&alarm->callback_lock);
    170   pthread_mutex_unlock(&alarm->callback_lock);
    171 }
    172 
    173 static bool lazy_initialize(void) {
    174   assert(alarms == NULL);
    175 
    176   pthread_mutex_init(&monitor, NULL);
    177 
    178   alarms = list_new(NULL);
    179   if (!alarms) {
    180     ALOGE("%s unable to allocate alarm list.", __func__);
    181     return false;
    182   }
    183 
    184   return true;
    185 }
    186 
    187 static period_ms_t now(void) {
    188   assert(alarms != NULL);
    189 
    190   struct timespec ts;
    191   if (clock_gettime(CLOCK_ID, &ts) == -1) {
    192     ALOGE("%s unable to get current time: %s", __func__, strerror(errno));
    193     return 0;
    194   }
    195 
    196   return (ts.tv_sec * 1000LL) + (ts.tv_nsec / 1000000LL);
    197 }
    198 
    199 // Warning: this function is called in the context of an unknown thread.
    200 // As a result, it must be thread-safe relative to other operations on
    201 // the alarm list.
    202 static void timer_callback(void *ptr) {
    203   alarm_t *alarm = (alarm_t *)ptr;
    204   assert(alarm != NULL);
    205 
    206   pthread_mutex_lock(&monitor);
    207 
    208   bool alarm_valid = list_remove(alarms, alarm);
    209   alarm_callback_t callback = alarm->callback;
    210   void *data = alarm->data;
    211 
    212   alarm->deadline = 0;
    213   alarm->callback = NULL;
    214   alarm->data = NULL;
    215 
    216   reschedule();
    217 
    218   // The alarm was cancelled before we got to it. Release the monitor
    219   // lock and exit right away since there's nothing left to do.
    220   if (!alarm_valid) {
    221     pthread_mutex_unlock(&monitor);
    222     return;
    223   }
    224 
    225   // Downgrade lock.
    226   pthread_mutex_lock(&alarm->callback_lock);
    227   pthread_mutex_unlock(&monitor);
    228 
    229   callback(data);
    230 
    231   pthread_mutex_unlock(&alarm->callback_lock);
    232 }
    233 
    234 // NOTE: must be called with monitor lock.
    235 static void reschedule(void) {
    236   assert(alarms != NULL);
    237 
    238   if (timer_set) {
    239     timer_delete(timer);
    240     timer_set = false;
    241   }
    242 
    243   if (list_is_empty(alarms)) {
    244     bt_os_callouts->release_wake_lock(WAKE_LOCK_ID);
    245     return;
    246   }
    247 
    248   alarm_t *next = list_front(alarms);
    249   int64_t next_exp = next->deadline - now();
    250   if (next_exp < TIMER_INTERVAL_FOR_WAKELOCK_IN_MS) {
    251     int status = bt_os_callouts->acquire_wake_lock(WAKE_LOCK_ID);
    252     if (status != BT_STATUS_SUCCESS) {
    253       ALOGE("%s unable to acquire wake lock: %d", __func__, status);
    254       return;
    255     }
    256 
    257     struct sigevent sigevent;
    258     memset(&sigevent, 0, sizeof(sigevent));
    259     sigevent.sigev_notify = SIGEV_THREAD;
    260     sigevent.sigev_notify_function = (void (*)(union sigval))timer_callback;
    261     sigevent.sigev_value.sival_ptr = next;
    262     if (timer_create(CLOCK_ID, &sigevent, &timer) == -1) {
    263       ALOGE("%s unable to create timer: %s", __func__, strerror(errno));
    264       return;
    265     }
    266 
    267     struct itimerspec wakeup_time;
    268     memset(&wakeup_time, 0, sizeof(wakeup_time));
    269     wakeup_time.it_value.tv_sec = (next->deadline / 1000);
    270     wakeup_time.it_value.tv_nsec = (next->deadline % 1000) * 1000000LL;
    271     if (timer_settime(timer, TIMER_ABSTIME, &wakeup_time, NULL) == -1) {
    272       ALOGE("%s unable to set timer: %s", __func__, strerror(errno));
    273       timer_delete(timer);
    274       return;
    275     }
    276     timer_set = true;
    277   } else {
    278     if (!bt_os_callouts->set_wake_alarm(next_exp, true, timer_callback, next))
    279       ALOGE("%s unable to set wake alarm for %" PRId64 "ms.", __func__, next_exp);
    280 
    281     bt_os_callouts->release_wake_lock(WAKE_LOCK_ID);
    282   }
    283 }
    284