thread_local.h 3.5 KB
Newer Older
1 2 3 4 5 6 7 8 9
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_FML_THREAD_LOCAL_H_
#define FLUTTER_FML_THREAD_LOCAL_H_

#include <functional>

10
#include "flutter/fml/build_config.h"
11
#include "flutter/fml/logging.h"
12
#include "flutter/fml/macros.h"
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57

#define FML_THREAD_LOCAL_PTHREADS OS_MACOSX || OS_LINUX || OS_ANDROID

#if FML_THREAD_LOCAL_PTHREADS
#include <pthread.h>
#endif

namespace fml {

using ThreadLocalDestroyCallback = std::function<void(intptr_t)>;

#if FML_THREAD_LOCAL_PTHREADS

// thread_local is unavailable and we have to resort to pthreads.

#define FML_THREAD_LOCAL static

class ThreadLocal {
 private:
  class Box {
   public:
    Box(ThreadLocalDestroyCallback destroy, intptr_t value)
        : destroy_(destroy), value_(value) {}

    intptr_t Value() const { return value_; }

    void SetValue(intptr_t value) {
      if (value == value_) {
        return;
      }

      DestroyValue();
      value_ = value;
    }

    void DestroyValue() {
      if (destroy_) {
        destroy_(value_);
      }
    }

   private:
    ThreadLocalDestroyCallback destroy_;
    intptr_t value_;

58
    FML_DISALLOW_COPY_AND_ASSIGN(Box);
59 60 61
  };

  static inline void ThreadLocalDestroy(void* value) {
62
    FML_CHECK(value != nullptr);
63 64 65 66 67 68 69 70 71 72 73
    auto box = reinterpret_cast<Box*>(value);
    box->DestroyValue();
    delete box;
  }

 public:
  ThreadLocal() : ThreadLocal(nullptr) {}

  ThreadLocal(ThreadLocalDestroyCallback destroy) : destroy_(destroy) {
    auto callback =
        reinterpret_cast<void (*)(void*)>(&ThreadLocal::ThreadLocalDestroy);
74
    FML_CHECK(pthread_key_create(&_key, callback) == 0);
75 76 77 78 79 80
  }

  void Set(intptr_t value) {
    auto box = reinterpret_cast<Box*>(pthread_getspecific(_key));
    if (box == nullptr) {
      box = new Box(destroy_, value);
81
      FML_CHECK(pthread_setspecific(_key, box) == 0);
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
    } else {
      box->SetValue(value);
    }
  }

  intptr_t Get() {
    auto box = reinterpret_cast<Box*>(pthread_getspecific(_key));
    return box != nullptr ? box->Value() : 0;
  }

  ~ThreadLocal() {
    // This will NOT call the destroy callbacks on thread local values still
    // active in other threads. Those must be cleared manually. The usage
    // of this class should be similar to the thread_local keyword but with
    // with a static storage specifier

    // Collect the container
    delete reinterpret_cast<Box*>(pthread_getspecific(_key));

    // Finally, collect the key
102
    FML_CHECK(pthread_key_delete(_key) == 0);
103 104 105 106 107 108
  }

 private:
  pthread_key_t _key;
  ThreadLocalDestroyCallback destroy_;

109
  FML_DISALLOW_COPY_AND_ASSIGN(ThreadLocal);
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
};

#else  // FML_THREAD_LOCAL_PTHREADS

#define FML_THREAD_LOCAL thread_local

class ThreadLocal {
 public:
  ThreadLocal() : ThreadLocal(nullptr) {}

  ThreadLocal(ThreadLocalDestroyCallback destroy)
      : destroy_(destroy), value_(0) {}

  void Set(intptr_t value) {
    if (value_ == value) {
      return;
    }

    if (value_ != 0 && destroy_) {
      destroy_(value_);
    }

    value_ = value;
  }

  intptr_t Get() { return value_; }

  ~ThreadLocal() {
    if (value_ != 0 && destroy_) {
      destroy_(value_);
    }
  }

 private:
  ThreadLocalDestroyCallback destroy_;
  intptr_t value_;

147
  FML_DISALLOW_COPY_AND_ASSIGN(ThreadLocal);
148 149 150 151 152 153 154 155 156 157 158 159 160
};

#endif  // FML_THREAD_LOCAL_PTHREADS

#ifndef FML_THREAD_LOCAL

#error Thread local storage unavailable on the platform.

#endif  // FML_THREAD_LOCAL

}  // namespace fml

#endif  // FLUTTER_FML_THREAD_LOCAL_H_