Skip to content

Commit 3d78561

Browse files
authored
Merge pull request protocolbuffers#3634 from TeBoring/ruby-bug
Cherry pick bug fix for ruby
2 parents 8741da3 + a459b22 commit 3d78561

File tree

5 files changed

+37
-23
lines changed

5 files changed

+37
-23
lines changed

ruby/ext/google/protobuf_c/encode_decode.c

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,6 @@ rb_data_type_t MapParseFrame_type = {
280280
{ MapParseFrame_mark, MapParseFrame_free, NULL },
281281
};
282282

283-
// Array of Ruby objects wrapping map_parse_frame_t.
284-
// We don't allow multiple concurrent decodes, so we assume that this global
285-
// variable is specific to the "current" decode.
286-
VALUE map_parse_frames;
287-
288283
static map_parse_frame_t* map_push_frame(VALUE map,
289284
const map_handlerdata_t* handlerdata) {
290285
map_parse_frame_t* frame = ALLOC(map_parse_frame_t);
@@ -293,16 +288,12 @@ static map_parse_frame_t* map_push_frame(VALUE map,
293288
native_slot_init(handlerdata->key_field_type, &frame->key_storage);
294289
native_slot_init(handlerdata->value_field_type, &frame->value_storage);
295290

296-
rb_ary_push(map_parse_frames,
291+
Map_set_frame(map,
297292
TypedData_Wrap_Struct(rb_cObject, &MapParseFrame_type, frame));
298293

299294
return frame;
300295
}
301296

302-
static void map_pop_frame() {
303-
rb_ary_pop(map_parse_frames);
304-
}
305-
306297
// Handler to begin a map entry: allocates a temporary frame. This is the
307298
// 'startsubmsg' handler on the msgdef that contains the map field.
308299
static void *startmapentry_handler(void *closure, const void *hd) {
@@ -336,7 +327,7 @@ static bool endmap_handler(void *closure, const void *hd, upb_status* s) {
336327
&frame->value_storage);
337328

338329
Map_index_set(frame->map, key, value);
339-
map_pop_frame();
330+
Map_set_frame(frame->map, Qnil);
340331

341332
return true;
342333
}
@@ -775,10 +766,6 @@ VALUE Message_decode(VALUE klass, VALUE data) {
775766
msg_rb = rb_class_new_instance(0, NULL, msgklass);
776767
TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg);
777768

778-
// We generally expect this to be clear already, but clear it in case parsing
779-
// previously got interrupted somehow.
780-
rb_ary_clear(map_parse_frames);
781-
782769
{
783770
const upb_pbdecodermethod* method = msgdef_decodermethod(desc);
784771
const upb_handlers* h = upb_pbdecodermethod_desthandlers(method);
@@ -823,10 +810,6 @@ VALUE Message_decode_json(VALUE klass, VALUE data) {
823810
msg_rb = rb_class_new_instance(0, NULL, msgklass);
824811
TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg);
825812

826-
// We generally expect this to be clear already, but clear it in case parsing
827-
// previously got interrupted somehow.
828-
rb_ary_clear(map_parse_frames);
829-
830813
{
831814
const upb_json_parsermethod* method = msgdef_jsonparsermethod(desc);
832815
stackenv se;

ruby/ext/google/protobuf_c/map.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ void Map_mark(void* _self) {
146146
Map* self = _self;
147147

148148
rb_gc_mark(self->value_type_class);
149+
rb_gc_mark(self->parse_frame);
149150

150151
if (self->value_type == UPB_TYPE_STRING ||
151152
self->value_type == UPB_TYPE_BYTES ||
@@ -174,6 +175,12 @@ VALUE Map_alloc(VALUE klass) {
174175
return TypedData_Wrap_Struct(klass, &Map_type, self);
175176
}
176177

178+
VALUE Map_set_frame(VALUE map, VALUE val) {
179+
Map* self = ruby_to_Map(map);
180+
self->parse_frame = val;
181+
return val;
182+
}
183+
177184
static bool needs_typeclass(upb_fieldtype_t type) {
178185
switch (type) {
179186
case UPB_TYPE_MESSAGE:
@@ -227,6 +234,7 @@ VALUE Map_init(int argc, VALUE* argv, VALUE _self) {
227234

228235
self->key_type = ruby_to_fieldtype(argv[0]);
229236
self->value_type = ruby_to_fieldtype(argv[1]);
237+
self->parse_frame = Qnil;
230238

231239
// Check that the key type is an allowed type.
232240
switch (self->key_type) {

ruby/ext/google/protobuf_c/protobuf.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,4 @@ void Init_protobuf_c() {
112112

113113
upb_def_to_ruby_obj_map = rb_hash_new();
114114
rb_gc_register_address(&upb_def_to_ruby_obj_map);
115-
map_parse_frames = rb_ary_new();
116-
rb_gc_register_address(&map_parse_frames);
117115
}

ruby/ext/google/protobuf_c/protobuf.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,6 @@ extern VALUE cBuilder;
166166
extern VALUE cError;
167167
extern VALUE cParseError;
168168

169-
extern VALUE map_parse_frames;
170-
171169
// We forward-declare all of the Ruby method implementations here because we
172170
// sometimes call the methods directly across .c files, rather than going
173171
// through Ruby's method dispatching (e.g. during message parse). It's cleaner
@@ -397,6 +395,7 @@ typedef struct {
397395
upb_fieldtype_t key_type;
398396
upb_fieldtype_t value_type;
399397
VALUE value_type_class;
398+
VALUE parse_frame;
400399
upb_strtable table;
401400
} Map;
402401

@@ -405,6 +404,7 @@ void Map_free(void* self);
405404
VALUE Map_alloc(VALUE klass);
406405
VALUE Map_init(int argc, VALUE* argv, VALUE self);
407406
void Map_register(VALUE module);
407+
VALUE Map_set_frame(VALUE self, VALUE val);
408408

409409
extern const rb_data_type_t Map_type;
410410
extern VALUE cMap;

ruby/tests/basic.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,18 @@ module BasicTest
9696
optional :d, :enum, 4, "TestEnum"
9797
end
9898
end
99+
100+
add_message "repro.Outer" do
101+
map :items, :int32, :message, 1, "repro.Inner"
102+
end
103+
104+
add_message "repro.Inner" do
105+
end
99106
end
100107

108+
109+
Outer = pool.lookup("repro.Outer").msgclass
110+
Inner = pool.lookup("repro.Inner").msgclass
101111
Foo = pool.lookup("Foo").msgclass
102112
Bar = pool.lookup("Bar").msgclass
103113
Baz = pool.lookup("Baz").msgclass
@@ -675,6 +685,21 @@ def test_map_corruption
675685
m.map_string_int32['aaa'] = 3
676686
end
677687

688+
def test_concurrent_decoding
689+
o = Outer.new
690+
o.items[0] = Inner.new
691+
raw = Outer.encode(o)
692+
693+
thds = 2.times.map do
694+
Thread.new do
695+
100000.times do
696+
assert_equal o, Outer.decode(raw)
697+
end
698+
end
699+
end
700+
thds.map(&:join)
701+
end
702+
678703
def test_map_encode_decode
679704
m = MapMessage.new(
680705
:map_string_int32 => {"a" => 1, "b" => 2},

0 commit comments

Comments
 (0)