tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

UserData.h (5612B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef MOZILLA_GFX_USERDATA_H_
      8 #define MOZILLA_GFX_USERDATA_H_
      9 
     10 #include <stdlib.h>
     11 #include "Types.h"
     12 #include "mozilla/Assertions.h"
     13 #include "mozilla/Atomics.h"
     14 #include "mozilla/Mutex.h"
     15 
     16 namespace mozilla {
     17 namespace gfx {
     18 
     19 struct UserDataKey {
     20  int unused;
     21 };
     22 
     23 /* this class is basically a clone of the user data concept from cairo */
     24 class UserData {
     25 public:
     26  typedef void (*DestroyFunc)(void* data);
     27 
     28  UserData() : count(0), entries(nullptr) {}
     29 
     30  /* Attaches untyped userData associated with key. destroy is called on
     31   * destruction */
     32  void Add(UserDataKey* key, void* userData, DestroyFunc destroy) {
     33    for (int i = 0; i < count; i++) {
     34      if (key == entries[i].key) {
     35        if (entries[i].destroy) {
     36          entries[i].destroy(entries[i].userData);
     37        }
     38        entries[i].userData = userData;
     39        entries[i].destroy = destroy;
     40        return;
     41      }
     42    }
     43 
     44    // We could keep entries in a std::vector instead of managing it by hand
     45    // but that would propagate an stl dependency out which we'd rather not
     46    // do (see bug 666609). Plus, the entries array is expect to stay small
     47    // so doing a realloc everytime we add a new entry shouldn't be too costly
     48    entries =
     49        static_cast<Entry*>(realloc(entries, sizeof(Entry) * (count + 1)));
     50 
     51    if (!entries) {
     52      MOZ_CRASH("GFX: UserData::Add");
     53    }
     54 
     55    entries[count].key = key;
     56    entries[count].userData = userData;
     57    entries[count].destroy = destroy;
     58 
     59    count++;
     60  }
     61 
     62  /* Remove and return user data associated with key, without destroying it */
     63  void* Remove(UserDataKey* key) {
     64    for (int i = 0; i < count; i++) {
     65      if (key == entries[i].key) {
     66        void* userData = entries[i].userData;
     67        // decrement before looping so entries[i+1] doesn't read past the end:
     68        --count;
     69        for (; i < count; i++) {
     70          entries[i] = entries[i + 1];
     71        }
     72        return userData;
     73      }
     74    }
     75    return nullptr;
     76  }
     77 
     78  /* Remove and destroy a given key */
     79  void RemoveAndDestroy(UserDataKey* key) {
     80    for (int i = 0; i < count; i++) {
     81      if (key == entries[i].key) {
     82        if (entries[i].destroy) {
     83          entries[i].destroy(entries[i].userData);
     84        }
     85        // decrement before looping so entries[i+1] doesn't read past the end:
     86        --count;
     87        for (; i < count; i++) {
     88          entries[i] = entries[i + 1];
     89        }
     90      }
     91    }
     92  }
     93 
     94  /* Retrives the userData for the associated key */
     95  void* Get(UserDataKey* key) const {
     96    for (int i = 0; i < count; i++) {
     97      if (key == entries[i].key) {
     98        return entries[i].userData;
     99      }
    100    }
    101    return nullptr;
    102  }
    103 
    104  bool Has(UserDataKey* key) {
    105    for (int i = 0; i < count; i++) {
    106      if (key == entries[i].key) {
    107        return true;
    108      }
    109    }
    110    return false;
    111  }
    112 
    113  void Destroy() {
    114    if (!entries) {
    115      return;
    116    }
    117    for (int i = 0; i < count; i++) {
    118      if (entries[i].destroy) {
    119        entries[i].destroy(entries[i].userData);
    120      }
    121    }
    122    free(entries);
    123    entries = nullptr;
    124    count = 0;
    125  }
    126 
    127  ~UserData() { Destroy(); }
    128 
    129 private:
    130  struct Entry {
    131    const UserDataKey* key;
    132    void* userData;
    133    DestroyFunc destroy;
    134  };
    135 
    136  int count;
    137  Entry* entries;
    138 };
    139 
    140 class ThreadSafeUserData {
    141 protected:
    142  struct LockedUserData : public UserData {
    143    Mutex mLock;
    144 
    145    LockedUserData() : mLock("LockedUserData::mLock") {}
    146  };
    147 
    148 public:
    149  ~ThreadSafeUserData() {
    150    if (LockedUserData* userData = mUserData.exchange(nullptr)) {
    151      {
    152        MutexAutoLock lock(userData->mLock);
    153        userData->Destroy();
    154      }
    155      delete userData;
    156    }
    157  }
    158 
    159  void Add(UserDataKey* key, void* value, UserData::DestroyFunc destroy) {
    160    LockedUserData* userData = GetUserData();
    161    MutexAutoLock lock(userData->mLock);
    162    userData->Add(key, value, destroy);
    163  }
    164 
    165  void* Remove(UserDataKey* key) {
    166    LockedUserData* userData = GetUserData();
    167    MutexAutoLock lock(userData->mLock);
    168    return userData->Remove(key);
    169  }
    170 
    171  void RemoveAndDestroy(UserDataKey* key) {
    172    LockedUserData* userData = GetUserData();
    173    MutexAutoLock lock(userData->mLock);
    174    userData->RemoveAndDestroy(key);
    175  }
    176 
    177  void* Get(UserDataKey* key) const {
    178    LockedUserData* userData = GetUserData();
    179    MutexAutoLock lock(userData->mLock);
    180    return userData->Get(key);
    181  }
    182 
    183  bool Has(UserDataKey* key) {
    184    LockedUserData* userData = GetUserData();
    185    MutexAutoLock lock(userData->mLock);
    186    return userData->Has(key);
    187  }
    188 
    189 private:
    190  LockedUserData* GetUserData() const {
    191    LockedUserData* userData = mUserData;
    192    if (!userData) {
    193      userData = new LockedUserData;
    194      if (!mUserData.compareExchange(nullptr, userData)) {
    195        delete userData;
    196        userData = mUserData;
    197        MOZ_ASSERT(userData);
    198      }
    199    }
    200    return userData;
    201  }
    202 
    203  // The Mutex class is quite large. For small, frequent classes (ScaledFont,
    204  // SourceSurface, etc.) this can add a lot of memory overhead, especially if
    205  // UserData is only infrequently used. To avoid this, we only allocate the
    206  // LockedUserData if it is actually used. If unused, it only adds a single
    207  // pointer as overhead.
    208  mutable Atomic<LockedUserData*> mUserData;
    209 };
    210 
    211 }  // namespace gfx
    212 }  // namespace mozilla
    213 
    214 #endif /* MOZILLA_GFX_USERDATA_H_ */