1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/android/java/jni_helper.h"
6
7#include <map>
8
9#include "base/android/jni_android.h"
10#include "base/lazy_instance.h"
11#include "base/threading/platform_thread.h"
12
13namespace content {
14
15namespace {
16
17struct MethodIdentifier {
18  const char* class_name;
19  const char* method;
20  const char* jni_signature;
21
22  bool operator<(const MethodIdentifier& other) const {
23    int r = strcmp(class_name, other.class_name);
24    if (r < 0) {
25      return true;
26    } else if (r > 0) {
27      return false;
28    }
29
30    r = strcmp(method, other.method);
31    if (r < 0) {
32      return true;
33    } else if (r > 0) {
34      return false;
35    }
36
37    return strcmp(jni_signature, other.jni_signature) < 0;
38  }
39};
40
41typedef std::map<MethodIdentifier, jmethodID> MethodIDMap;
42
43const base::subtle::AtomicWord kUnlocked = 0;
44const base::subtle::AtomicWord kLocked = 1;
45base::subtle::AtomicWord g_method_id_map_lock = kUnlocked;
46
47base::LazyInstance<MethodIDMap> g_method_id_map = LAZY_INSTANCE_INITIALIZER;
48
49}  // namespace
50
51jmethodID GetMethodIDFromClassName(JNIEnv* env,
52                                   const char* class_name,
53                                   const char* method,
54                                   const char* jni_signature) {
55  MethodIdentifier key;
56  key.class_name = class_name;
57  key.method = method;
58  key.jni_signature = jni_signature;
59
60  MethodIDMap* map = g_method_id_map.Pointer();
61  bool found = false;
62
63  while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock,
64                                              kUnlocked,
65                                              kLocked) != kUnlocked) {
66    base::PlatformThread::YieldCurrentThread();
67  }
68  MethodIDMap::const_iterator iter = map->find(key);
69  if (iter != map->end()) {
70    found = true;
71  }
72  base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked);
73
74  // Addition to the map does not invalidate this iterator.
75  if (found) {
76    return iter->second;
77  }
78
79  base::android::ScopedJavaLocalRef<jclass> clazz(
80      env, env->FindClass(class_name));
81  jmethodID id = base::android::MethodID::Get<
82      base::android::MethodID::TYPE_INSTANCE>(
83          env, clazz.obj(), method, jni_signature);
84
85  while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock,
86                                              kUnlocked,
87                                              kLocked) != kUnlocked) {
88    base::PlatformThread::YieldCurrentThread();
89  }
90  // Another thread may have populated the map already.
91  std::pair<MethodIDMap::const_iterator, bool> result =
92      map->insert(std::make_pair(key, id));
93  DCHECK_EQ(id, result.first->second);
94  base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked);
95
96  return id;
97}
98
99}  // namespace content
100