Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2005, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 // ---
     31 // Author: Paul Menage <opensource (at) google.com>
     32 //
     33 // Some wrappers for pthread functions so that we can be LD_PRELOADed
     34 // against non-pthreads apps.
     35 //
     36 // This module will behave very strangely if some pthreads functions
     37 // exist and others don't.
     38 
     39 #include "config.h"
     40 #include <assert.h>
     41 #include <string.h>    // for memcmp
     42 #include <stdio.h>     // for __isthreaded on FreeBSD
     43 // We don't actually need strings. But including this header seems to
     44 // stop the compiler trying to short-circuit our pthreads existence
     45 // tests and claiming that the address of a function is always
     46 // non-zero. I have no idea why ...
     47 #include <string>
     48 #include "maybe_threads.h"
     49 #include "base/basictypes.h"
     50 
     51 // __THROW is defined in glibc systems.  It means, counter-intuitively,
     52 // "This function will never throw an exception."  It's an optional
     53 // optimization tool, but we may need to use it to match glibc prototypes.
     54 #ifndef __THROW    // I guess we're not on a glibc system
     55 # define __THROW   // __THROW is just an optimization, so ok to make it ""
     56 #endif
     57 
     58 // These are the methods we're going to conditionally include.
     59 extern "C" {
     60   int pthread_key_create (pthread_key_t*, void (*)(void*))
     61       __THROW ATTRIBUTE_WEAK;
     62   void *pthread_getspecific(pthread_key_t)
     63       __THROW ATTRIBUTE_WEAK;
     64   int pthread_setspecific(pthread_key_t, const void*)
     65       __THROW ATTRIBUTE_WEAK;
     66   int pthread_once(pthread_once_t *, void (*)(void))
     67       ATTRIBUTE_WEAK;
     68 }
     69 
     70 #define MAX_PERTHREAD_VALS 16
     71 static void *perftools_pthread_specific_vals[MAX_PERTHREAD_VALS];
     72 static int next_key;
     73 
     74 int perftools_pthread_key_create(pthread_key_t *key,
     75                                  void (*destr_function) (void *)) {
     76   if (pthread_key_create) {
     77     return pthread_key_create(key, destr_function);
     78   } else {
     79     assert(next_key < MAX_PERTHREAD_VALS);
     80     *key = (pthread_key_t)(next_key++);
     81     return 0;
     82   }
     83 }
     84 
     85 void *perftools_pthread_getspecific(pthread_key_t key) {
     86   if (pthread_getspecific) {
     87     return pthread_getspecific(key);
     88   } else {
     89     return perftools_pthread_specific_vals[(int)key];
     90   }
     91 }
     92 
     93 int perftools_pthread_setspecific(pthread_key_t key, void *val) {
     94   if (pthread_setspecific) {
     95     return pthread_setspecific(key, val);
     96   } else {
     97     perftools_pthread_specific_vals[(int)key] = val;
     98     return 0;
     99   }
    100 }
    101 
    102 
    103 static pthread_once_t pthread_once_init = PTHREAD_ONCE_INIT;
    104 int perftools_pthread_once(pthread_once_t *ctl,
    105                            void  (*init_routine) (void)) {
    106 #ifdef __FreeBSD__
    107   // On __FreeBSD__, calling pthread_once on a system that is not
    108   // linked with -pthread is silently a noop. :-( Luckily, we have a
    109   // workaround: FreeBSD exposes __isthreaded in <stdio.h>, which is
    110   // set to 1 when the first thread is spawned.  So on those systems,
    111   // we can use our own separate pthreads-once mechanism, which is
    112   // used until __isthreaded is 1 (which will never be true if the app
    113   // is not linked with -pthread).
    114   static bool pthread_once_ran_before_threads = false;
    115   if (pthread_once_ran_before_threads) {
    116     return 0;
    117   }
    118   if (!__isthreaded) {
    119     init_routine();
    120     pthread_once_ran_before_threads = true;
    121     return 0;
    122   }
    123 #elif defined(__ANDROID__)
    124   // Android >= 2.3 (GB) always implement pthread_once.
    125   return pthread_once(ctl, init_routine);
    126 #else
    127   if (pthread_once) {
    128     return pthread_once(ctl, init_routine);
    129   } else {
    130     if (memcmp(ctl, &pthread_once_init, sizeof(*ctl)) == 0) {
    131       init_routine();
    132       ++*(char*)(ctl);        // make it so it's no longer equal to init
    133     }
    134     return 0;
    135   }
    136 #endif
    137 }
    138