Skip to content

Commit 9087337

Browse files
habermanzhangskz
authored andcommitted
Moved ObjectCache into an internal module.
This type has always been internal-only, but moving it to a module named `Internal` will make this clearer. PiperOrigin-RevId: 606649281
1 parent 62e7a56 commit 9087337

6 files changed

Lines changed: 117 additions & 115 deletions

File tree

ruby/ext/google/protobuf_c/protobuf.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -241,16 +241,17 @@ static void ObjectCache_Init(VALUE protobuf) {
241241
item_try_add = rb_intern("try_add");
242242

243243
rb_gc_register_address(&weak_obj_cache);
244+
VALUE internal = rb_const_get(protobuf, rb_intern("Internal"));
244245
#if SIZEOF_LONG >= SIZEOF_VALUE
245-
VALUE cache_class = rb_const_get(protobuf, rb_intern("ObjectCache"));
246+
VALUE cache_class = rb_const_get(internal, rb_intern("ObjectCache"));
246247
#else
247-
VALUE cache_class = rb_const_get(protobuf, rb_intern("LegacyObjectCache"));
248+
VALUE cache_class = rb_const_get(internal, rb_intern("LegacyObjectCache"));
248249
#endif
249250

250251
weak_obj_cache = rb_class_new_instance(0, NULL, cache_class);
251-
rb_const_set(protobuf, rb_intern("OBJECT_CACHE"), weak_obj_cache);
252-
rb_const_set(protobuf, rb_intern("SIZEOF_LONG"), INT2NUM(SIZEOF_LONG));
253-
rb_const_set(protobuf, rb_intern("SIZEOF_VALUE"), INT2NUM(SIZEOF_VALUE));
252+
rb_const_set(internal, rb_intern("OBJECT_CACHE"), weak_obj_cache);
253+
rb_const_set(internal, rb_intern("SIZEOF_LONG"), INT2NUM(SIZEOF_LONG));
254+
rb_const_set(internal, rb_intern("SIZEOF_VALUE"), INT2NUM(SIZEOF_VALUE));
254255
}
255256

256257
static VALUE ObjectCache_GetKey(const void *key) {

ruby/lib/google/protobuf.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
# require mixins before we hook them into the java & c code
99
require 'google/protobuf/message_exts'
10-
require 'google/protobuf/object_cache'
10+
require 'google/protobuf/internal/object_cache'
1111

1212
# We define these before requiring the platform-specific modules.
1313
# That way the module init can grab references to these.

ruby/lib/google/protobuf/ffi/object_cache.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ def self.interpreter_supports_non_finalized_keys_in_weak_map?
1818

1919
def self.cache_implementation
2020
if interpreter_supports_non_finalized_keys_in_weak_map? and SIZEOF_LONG >= SIZEOF_VALUE
21-
Google::Protobuf::ObjectCache
21+
Google::Protobuf::Internal::ObjectCache
2222
else
23-
Google::Protobuf::LegacyObjectCache
23+
Google::Protobuf::Internal::LegacyObjectCache
2424
end
2525
end
2626

2727
public
2828
OBJECT_CACHE = cache_implementation.new
2929
end
30-
end
30+
end
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Protocol Buffers - Google's data interchange format
2+
# Copyright 2023 Google Inc. All rights reserved.
3+
#
4+
# Use of this source code is governed by a BSD-style
5+
# license that can be found in the LICENSE file or at
6+
# https://developers.google.com/open-source/licenses/bsd
7+
8+
module Google
9+
module Protobuf
10+
module Internal
11+
# A pointer -> Ruby Object cache that keeps references to Ruby wrapper
12+
# objects. This allows us to look up any Ruby wrapper object by the address
13+
# of the object it is wrapping. That way we can avoid ever creating two
14+
# different wrapper objects for the same C object, which saves memory and
15+
# preserves object identity.
16+
#
17+
# We use WeakMap for the cache. If sizeof(long) > sizeof(VALUE), we also
18+
# need a secondary Hash to store WeakMap keys, because our pointer keys may
19+
# need to be stored as Bignum instead of Fixnum. Since WeakMap is weak for
20+
# both keys and values, a Bignum key will cause the WeakMap entry to be
21+
# collected immediately unless there is another reference to the Bignum.
22+
# This happens on 64-bit Windows, on which pointers are 64 bits but longs
23+
# are 32 bits. In this case, we enable the secondary Hash to hold the keys
24+
# and prevent them from being collected.
25+
class ObjectCache
26+
def initialize
27+
@map = ObjectSpace::WeakMap.new
28+
@mutex = Mutex.new
29+
end
30+
31+
def get(key)
32+
@map[key]
33+
end
34+
35+
def try_add(key, value)
36+
@map[key] || @mutex.synchronize do
37+
@map[key] ||= value
38+
end
39+
end
40+
end
41+
42+
class LegacyObjectCache
43+
def initialize
44+
@secondary_map = {}
45+
@map = ObjectSpace::WeakMap.new
46+
@mutex = Mutex.new
47+
end
48+
49+
def get(key)
50+
value = if secondary_key = @secondary_map[key]
51+
@map[secondary_key]
52+
else
53+
@mutex.synchronize do
54+
@map[(@secondary_map[key] ||= Object.new)]
55+
end
56+
end
57+
58+
# GC if we could remove at least 2000 entries or 20% of the table size
59+
# (whichever is greater). Since the cost of the GC pass is O(N), we
60+
# want to make sure that we condition this on overall table size, to
61+
# avoid O(N^2) CPU costs.
62+
cutoff = (@secondary_map.size * 0.2).ceil
63+
cutoff = 2_000 if cutoff < 2_000
64+
if (@secondary_map.size - @map.size) > cutoff
65+
purge
66+
end
67+
68+
value
69+
end
70+
71+
def try_add(key, value)
72+
if secondary_key = @secondary_map[key]
73+
if old_value = @map[secondary_key]
74+
return old_value
75+
end
76+
end
77+
78+
@mutex.synchronize do
79+
secondary_key ||= (@secondary_map[key] ||= Object.new)
80+
@map[secondary_key] ||= value
81+
end
82+
end
83+
84+
private
85+
86+
def purge
87+
@mutex.synchronize do
88+
@secondary_map.each do |key, secondary_key|
89+
unless @map.key?(secondary_key)
90+
@secondary_map.delete(key)
91+
end
92+
end
93+
end
94+
nil
95+
end
96+
end
97+
end
98+
end
99+
end

ruby/lib/google/protobuf/object_cache.rb

Lines changed: 0 additions & 97 deletions
This file was deleted.

ruby/tests/object_cache_test.rb

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33

44
class PlatformTest < Test::Unit::TestCase
55
def test_correct_implementation_for_platform
6-
omit('OBJECT_CACHE not defined') unless defined? Google::Protobuf::OBJECT_CACHE
7-
if Google::Protobuf::SIZEOF_LONG >= Google::Protobuf::SIZEOF_VALUE and not defined? JRUBY_VERSION
8-
assert_instance_of Google::Protobuf::ObjectCache, Google::Protobuf::OBJECT_CACHE
6+
omit('OBJECT_CACHE not defined') unless defined? Google::Protobuf::Internal::OBJECT_CACHE
7+
if Google::Protobuf::Internal::SIZEOF_LONG >= Google::Protobuf::Internal::SIZEOF_VALUE and not defined? JRUBY_VERSION
8+
assert_instance_of Google::Protobuf::Internal::ObjectCache, Google::Protobuf::Internal::OBJECT_CACHE
99
else
10-
assert_instance_of Google::Protobuf::LegacyObjectCache, Google::Protobuf::OBJECT_CACHE
10+
assert_instance_of Google::Protobuf::Internal::LegacyObjectCache, Google::Protobuf::Internal::OBJECT_CACHE
1111
end
1212
end
1313
end
@@ -47,19 +47,18 @@ def test_multithreaded_access
4747

4848
class ObjectCacheTest < Test::Unit::TestCase
4949
def create
50-
omit('OBJECT_CACHE not defined') unless defined? Google::Protobuf::OBJECT_CACHE
51-
Google::Protobuf::ObjectCache.new
50+
omit('OBJECT_CACHE not defined') unless defined? Google::Protobuf::Internal::OBJECT_CACHE
51+
Google::Protobuf::Internal::ObjectCache.new
5252
end
5353

5454
include ObjectCacheTestModule
5555
end
5656

5757
class LegacyObjectCacheTest < Test::Unit::TestCase
5858
def create
59-
omit('OBJECT_CACHE not defined') unless defined? Google::Protobuf::OBJECT_CACHE
60-
Google::Protobuf::LegacyObjectCache.new
59+
omit('OBJECT_CACHE not defined') unless defined? Google::Protobuf::Internal::OBJECT_CACHE
60+
Google::Protobuf::Internal::LegacyObjectCache.new
6161
end
6262

6363
include ObjectCacheTestModule
6464
end
65-

0 commit comments

Comments
 (0)