Apollo 9.0
自动驾驶开放平台
protobuf_arena_manager.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2024 The Apollo Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
17
18#include <sys/ipc.h>
19#include <sys/shm.h>
20
21#include <cstring>
22#include <string>
23
24#include <google/protobuf/arena.h>
25#include <google/protobuf/message.h>
26
28
29namespace apollo {
30namespace cyber {
31namespace transport {
32
33const int32_t ArenaSegmentBlock::kRWLockFree = 0;
34const int32_t ArenaSegmentBlock::kWriteExclusive = -1;
36
38 : channel_id_(0), key_id_(0), base_address_(nullptr) {}
39
40ArenaSegment::ArenaSegment(uint64_t channel_id)
41 : channel_id_(channel_id),
42 key_id_(std::hash<std::string>{}("/apollo/__arena__/" +
43 std::to_string(channel_id))) {}
44
45ArenaSegment::ArenaSegment(uint64_t channel_id, void* base_address)
46 : channel_id_(channel_id),
47 key_id_(std::hash<std::string>{}("/apollo/__arena__/" +
48 std::to_string(channel_id))),
49 base_address_(base_address) {}
50
51ArenaSegment::ArenaSegment(uint64_t channel_id, uint64_t message_size,
52 uint64_t block_num, void* base_address)
53 : channel_id_(channel_id),
54 key_id_(std::hash<std::string>{}("/apollo/__arena__/" +
55 std::to_string(channel_id))),
56 base_address_(base_address) {
57 Init(message_size, block_num);
58}
59
61
62bool ArenaSegment::Init(uint64_t message_size, uint64_t block_num) {
63 uint64_t key_id = std::hash<std::string>{}("/apollo/__arena__/" +
64 std::to_string(channel_id_));
65 // fprintf(stderr, "channel_id: %lx, key_id: %lx\n", channel_id_, key_id);
66
67 for (uint32_t retry = 0; retry < 2 && !OpenOrCreate(message_size, block_num);
68 ++retry) {
69 }
70
71 return true;
72}
73
74bool ArenaSegment::OpenOrCreate(uint64_t message_size, uint64_t block_num) {
75 auto size = sizeof(ArenaSegmentState) +
76 sizeof(ArenaSegmentBlockDescriptor) * block_num +
77 message_size * block_num;
78 auto shmid =
79 shmget(static_cast<key_t>(key_id_), size, 0644 | IPC_CREAT | IPC_EXCL);
80 if (shmid == -1) {
81 if (errno == EINVAL) {
82 // TODO(all): need larger space, recreate
83 } else if (errno == EEXIST) {
84 // TODO(all): shm already exist, open only
85 return Open(message_size, block_num);
86 } else {
87 // create or open shm failed
88 return false;
89 }
90 }
92 shm_address_ = shmat(shmid, base_address_, 0);
93 if (shm_address_ == reinterpret_cast<void*>(-1)) {
94 // shmat failed
95 return false;
96 }
97
98 arenas_.resize(block_num, nullptr);
99 for (size_t i = 0; i < block_num; i++) {
100 arena_block_address_.push_back(
101 reinterpret_cast<uint64_t>(shm_address_) + sizeof(ArenaSegmentState) +
102 block_num * sizeof(ArenaSegmentBlock) + i * message_size);
103 }
104
105 state_ = reinterpret_cast<ArenaSegmentState*>(shm_address_);
106 state_->struct_.ref_count_.store(1);
107 state_->struct_.auto_extended_.store(false);
109 state_->struct_.block_num_.store(block_num);
110 state_->struct_.message_seq_.store(0);
111 blocks_ = reinterpret_cast<ArenaSegmentBlock*>(
112 reinterpret_cast<uint64_t>(shm_address_) + sizeof(ArenaSegmentState));
113 for (uint64_t i = 0; i < block_num; ++i) {
115 // blocks_[i].writing_ref_count_.store(0);
116 // blocks_[i].reading_ref_count_.store(0);
117 blocks_[i].lock_num_.store(0);
118 }
119 return true;
120}
121
122bool ArenaSegment::Open(uint64_t message_size, uint64_t block_num) {
123 auto shmid = shmget(static_cast<key_t>(key_id_), 0, 0644);
124 if (shmid == -1) {
125 // shm not exist
126 return false;
127 }
128 shm_address_ = shmat(shmid, base_address_, 0);
129 if (shm_address_ == reinterpret_cast<void*>(-1)) {
130 // shmat failed
131 return false;
132 }
134 state_ = reinterpret_cast<ArenaSegmentState*>(shm_address_);
135 state_->struct_.ref_count_.fetch_add(1);
136 blocks_ = reinterpret_cast<ArenaSegmentBlock*>(
137 reinterpret_cast<uint64_t>(shm_address_) + sizeof(ArenaSegmentState));
138
139 arenas_.resize(block_num, nullptr);
140 for (size_t i = 0; i < block_num; i++) {
141 arena_block_address_.push_back(
142 reinterpret_cast<uint64_t>(shm_address_) + sizeof(ArenaSegmentState) +
143 block_num * sizeof(ArenaSegmentBlock) + i * message_size);
144 }
145 return true;
146}
147
149
151 const auto block_num = state_->struct_.block_num_.load();
152 while (1) {
153 uint64_t next_idx = state_->struct_.message_seq_.fetch_add(1) % block_num;
154 if (AddBlockWriteLock(next_idx)) {
155 return next_idx;
156 }
157 }
158 return 0;
159}
160
161bool ArenaSegment::AddBlockWriteLock(uint64_t block_index) {
162 // base::WriteLockGuard<base::PthreadRWLock> lock(
163 // blocks_[block_index].read_write_mutex_);
164 // if (blocks_[block_index].writing_ref_count_.load() > 0) {
165 // return false;
166 // }
167 // if (blocks_[block_index].reading_ref_count_.load() > 0) {
168 // return false;
169 // }
170 // blocks_[block_index].writing_ref_count_.fetch_add(1);
171 auto& block = blocks_[block_index];
172 int32_t rw_lock_free = ArenaSegmentBlock::kRWLockFree;
173 if (!block.lock_num_.compare_exchange_weak(rw_lock_free,
175 std::memory_order_acq_rel,
176 std::memory_order_relaxed)) {
177 ADEBUG << "lock num: " << block.lock_num_.load();
178 return false;
179 }
180 return true;
181}
182
183void ArenaSegment::RemoveBlockWriteLock(uint64_t block_index) {
184 blocks_[block_index].lock_num_.fetch_add(1);
185}
186
187bool ArenaSegment::AddBlockReadLock(uint64_t block_index) {
188 // when multiple readers are reading an arena channel
189 // at the same time, using write locks can cause one
190 // or more readers to hang at the lock, whereas
191 // read locks do not have this problem
192 // base::ReadLockGuard<base::PthreadRWLock> lock(
193 // blocks_[block_index].read_write_mutex_);
194 // if (blocks_[block_index].writing_ref_count_.load() > 0) {
195 // return false;
196 // }
197 // blocks_[block_index].reading_ref_count_.fetch_add(1);
198 auto& block = blocks_[block_index];
199 int32_t lock_num = block.lock_num_.load();
200 if (lock_num < ArenaSegmentBlock::kRWLockFree) {
201 AINFO << "block is being written.";
202 return false;
203 }
204
205 int32_t try_times = 0;
206 while (!block.lock_num_.compare_exchange_weak(lock_num, lock_num + 1,
207 std::memory_order_acq_rel,
208 std::memory_order_relaxed)) {
209 ++try_times;
210 if (try_times == ArenaSegmentBlock::kMaxTryLockTimes) {
211 AINFO << "fail to add read lock num, curr num: " << lock_num;
212 return false;
213 }
214
215 lock_num = block.lock_num_.load();
216 if (lock_num < ArenaSegmentBlock::kRWLockFree) {
217 AINFO << "block is being written.";
218 return false;
219 }
220 }
221 return true;
222}
223
224void ArenaSegment::RemoveBlockReadLock(uint64_t block_index) {
225 blocks_[block_index].lock_num_.fetch_sub(1);
226}
227
229 ArenaSegmentBlockInfo* block_info) {
230 if (!block_info) {
231 return false;
232 }
233 if (!state_ || !blocks_) {
234 return false;
235 }
236
237 // TODO(all): support dynamic block size
238
239 uint64_t block_num = state_->struct_.block_num_.load();
240 uint64_t block_size = state_->struct_.message_size_.load();
241 uint64_t block_index = GetNextWritableBlockIndex();
242 block_info->block_index_ = block_index;
243 block_info->block_ = &blocks_[block_index];
244 block_info->block_buffer_address_ = reinterpret_cast<void*>(
245 reinterpret_cast<uint64_t>(shm_address_) + sizeof(ArenaSegmentState) +
246 block_num * sizeof(ArenaSegmentBlock) + block_index * block_size);
247 return true;
248}
249
251 const ArenaSegmentBlockInfo& block_info) {
252 if (!state_ || !blocks_) {
253 return;
254 }
255 if (block_info.block_index_ >= state_->struct_.block_num_.load()) {
256 return;
257 }
259}
260
262 if (!block_info) {
263 return false;
264 }
265 if (!state_ || !blocks_) {
266 return false;
267 }
268
269 if (block_info->block_index_ >= state_->struct_.block_num_.load()) {
270 return false;
271 }
272
273 // TODO(all): support dynamic block size
274
275 if (!AddBlockReadLock(block_info->block_index_)) {
276 return false;
277 }
278 uint64_t block_num = state_->struct_.block_num_.load();
279 uint64_t block_size = state_->struct_.message_size_.load();
280
281 block_info->block_ = &blocks_[block_info->block_index_];
282 block_info->block_buffer_address_ = reinterpret_cast<void*>(
283 reinterpret_cast<uint64_t>(shm_address_) + sizeof(ArenaSegmentState) +
284 block_num * sizeof(ArenaSegmentBlock) +
285 block_info->block_index_ * block_size);
286 return true;
287}
288
290 if (!state_ || !blocks_) {
291 return;
292 }
293 if (block_info.block_index_ >= state_->struct_.block_num_.load()) {
294 return;
295 }
297}
298
299ProtobufArenaManager::ProtobufArenaManager() {
300 address_allocator_ = std::make_shared<ArenaAddressAllocator>();
301}
302
304
306 const message::ArenaMessageWrapper* wrapper) {
307 return 0;
308}
309
310std::shared_ptr<ArenaSegment> ProtobufArenaManager::GetSegment(
311 uint64_t channel_id) {
312 std::lock_guard<std::mutex> lock(segments_mutex_);
313 if (segments_.find(channel_id) == segments_.end()) {
314 return nullptr;
315 }
316 return segments_[channel_id];
317}
318
320 const void* message) {
321 auto input_msg = reinterpret_cast<const google::protobuf::Message*>(message);
322 auto channel_id = GetMessageChannelId(wrapper);
323 auto segment = GetSegment(channel_id);
324 auto arena_ptr = input_msg->GetArena();
325 google::protobuf::ArenaOptions options;
326
327 if (!segment) {
328 return nullptr;
329 }
330
331 void* msg_output = nullptr;
332 if (arena_ptr == nullptr) {
333 auto arena_conf =
334 cyber::common::GlobalData::Instance()->GetChannelArenaConf(channel_id);
335 google::protobuf::ArenaOptions options;
336 options.start_block_size = arena_conf.max_msg_size();
337 options.max_block_size = arena_conf.max_msg_size();
338
339 if (!segment) {
340 return nullptr;
341 }
342
344
346 // TODO(all): AcquireBlockToWrite for dynamic adjust block
347 // auto size = input_msg->ByteSizeLong();
348 uint64_t size = 0;
349 if (!segment->AcquireBlockToWrite(size, &wb)) {
350 return nullptr;
351 }
352 this->AddMessageRelatedBlock(wrapper, wb.block_index_);
353 options.initial_block =
354 reinterpret_cast<char*>(segment->arena_block_address_[wb.block_index_]);
355 options.initial_block_size = segment->message_capacity_;
356 if (segment->arenas_[wb.block_index_] != nullptr) {
357 segment->arenas_[wb.block_index_] = nullptr;
358 }
359 segment->arenas_[wb.block_index_] =
360 std::make_shared<google::protobuf::Arena>(options);
361 auto msg = input_msg->New(segment->arenas_[wb.block_index_].get());
362 msg->CopyFrom(*input_msg);
364 wrapper, reinterpret_cast<uint64_t>(msg) -
365 reinterpret_cast<uint64_t>(segment->GetShmAddress()));
366 msg_output = reinterpret_cast<void*>(msg);
367 segment->ReleaseWrittenBlock(wb);
368 } else {
370 int block_index = -1;
371 for (size_t i = 0; i < segment->message_capacity_; i++) {
372 if (segment->arenas_[i].get() == arena_ptr) {
373 block_index = i;
374 break;
375 }
376 }
377 if (block_index == -1) {
378 return nullptr;
379 }
380 wb.block_index_ = block_index;
382 this->AddMessageRelatedBlock(wrapper, block_index);
384 wrapper, reinterpret_cast<uint64_t>(input_msg) -
385 reinterpret_cast<uint64_t>(segment->GetShmAddress()));
386 msg_output = reinterpret_cast<void*>(
387 const_cast<google::protobuf::Message*>(input_msg));
388 segment->ReleaseWrittenBlock(wb);
389 }
390
391 return msg_output;
392}
393
395 auto segment = GetSegment(GetMessageChannelId(wrapper));
396 if (!segment) {
397 return nullptr;
398 }
399
400 auto address = reinterpret_cast<uint64_t>(segment->GetShmAddress()) +
402
403 return reinterpret_cast<void*>(address);
404}
405
407 if (init_) {
408 return true;
409 }
410
411 // do something
412
413 init_ = true;
414 return true;
415}
416
417bool ProtobufArenaManager::EnableSegment(uint64_t channel_id) {
418 if (segments_.find(channel_id) != segments_.end()) {
419 return true;
420 }
421
422 // uint64_t asociated_channel_id = channel_id + 1;
423 // auto segment = SegmentFactory::CreateSegment(asociated_channel_id);
424 // segment->InitOnly(10 * 1024);
425 auto cyber_config = apollo::cyber::common::GlobalData::Instance()->Config();
426 if (!cyber_config.has_transport_conf()) {
427 return false;
428 }
429 if (!cyber_config.transport_conf().has_shm_conf()) {
430 return false;
431 }
432 if (!cyber_config.transport_conf().shm_conf().has_arena_shm_conf()) {
433 return false;
434 }
435 if (!cyber::common::GlobalData::Instance()->IsChannelEnableArenaShm(
436 channel_id)) {
437 return false;
438 }
439 auto arena_conf =
440 cyber::common::GlobalData::Instance()->GetChannelArenaConf(channel_id);
441 auto segment_shm_address = address_allocator_->Allocate(channel_id);
442 auto segment = std::make_shared<ArenaSegment>(
443 channel_id, arena_conf.max_msg_size(), arena_conf.max_pool_size(),
444 reinterpret_cast<void*>(segment_shm_address));
445 segments_[channel_id] = segment;
446 return true;
447}
448
450 if (!init_) {
451 return true;
452 }
453
454 for (auto& segment : segments_) {
455 address_allocator_->Deallocate(segment.first);
456 }
457 segments_.clear();
458
459 init_ = false;
460 return true;
461}
462
464 message::ArenaMessageWrapper* wrapper, uint64_t channel_id) {
465 wrapper->GetExtended<ExtendedStruct>()->meta_.channel_id_ = channel_id;
466}
467
472
474 message::ArenaMessageWrapper* wrapper, uint64_t address_offset) {
475 wrapper->GetExtended<ExtendedStruct>()->meta_.address_offset_ =
476 address_offset;
477}
478
483
486 std::vector<uint64_t> related_blocks;
487 auto extended = wrapper->GetExtended<ExtendedStruct>();
488 for (uint64_t i = 0; i < extended->meta_.related_blocks_size_; ++i) {
489 related_blocks.push_back(extended->meta_.related_blocks_[i]);
490 }
491 return related_blocks;
492}
493
496 auto extended = wrapper->GetExtended<ExtendedStruct>();
497 extended->meta_.related_blocks_size_ = 0;
498 // memset(extended->meta_.related_blocks_, 0,
499 // sizeof(extended->meta_.related_blocks_));
500}
501
503 message::ArenaMessageWrapper* wrapper, uint64_t block_index) {
504 auto extended = wrapper->GetExtended<ExtendedStruct>();
505 if (extended->meta_.related_blocks_size_ >=
506 sizeof(extended->meta_.related_blocks_) / sizeof(uint64_t)) {
507 return;
508 }
509 extended->meta_.related_blocks_[extended->meta_.related_blocks_size_++] =
510 block_index;
511}
512
513ProtobufArenaManager::ArenaAllocCallback ProtobufArenaManager::arena_alloc_cb_ =
514 nullptr;
515
516void* ProtobufArenaManager::ArenaAlloc(uint64_t size) {
517 return arena_alloc_cb_ ? arena_alloc_cb_(size) : nullptr;
518}
519
520void ProtobufArenaManager::ArenaDealloc(void* addr, uint64_t size) {}
521
522} // namespace transport
523} // namespace cyber
524} // namespace apollo
bool Init(uint64_t message_size, uint64_t block_num)
bool Open(uint64_t message_size, uint64_t block_num)
std::vector< std::shared_ptr< google::protobuf::Arena > > arenas_
bool OpenOrCreate(uint64_t message_size, uint64_t block_num)
bool AcquireBlockToRead(ArenaSegmentBlockInfo *block_info)
void ReleaseReadBlock(const ArenaSegmentBlockInfo &block_info)
void ReleaseWrittenBlock(const ArenaSegmentBlockInfo &block_info)
bool AcquireBlockToWrite(uint64_t size, ArenaSegmentBlockInfo *block_info)
uint64_t GetBaseAddress(const message::ArenaMessageWrapper *wrapper) override
std::shared_ptr< ArenaSegment > GetSegment(uint64_t channel_id)
void ResetMessageRelatedBlocks(message::ArenaMessageWrapper *wrapper)
void * GetMessage(message::ArenaMessageWrapper *wrapper) override
void SetMessageAddressOffset(message::ArenaMessageWrapper *wrapper, uint64_t offset)
uint64_t GetMessageAddressOffset(message::ArenaMessageWrapper *wrapper)
void SetMessageChannelId(message::ArenaMessageWrapper *wrapper, uint64_t channel_id)
uint64_t GetMessageChannelId(message::ArenaMessageWrapper *wrapper)
void * SetMessage(message::ArenaMessageWrapper *wrapper, const void *message) override
void AddMessageRelatedBlock(message::ArenaMessageWrapper *wrapper, uint64_t block_index)
std::vector< uint64_t > GetMessageRelatedBlocks(message::ArenaMessageWrapper *wrapper)
int message_size
#define ADEBUG
Definition log.h:41
#define AINFO
Definition log.h:42
bool Init(const char *binary_name, const std::string &dag_info)
Definition init.cc:99
class register implement
Definition future.h:29
struct apollo::cyber::transport::ArenaSegmentState::@3 struct_
struct apollo::cyber::transport::ExtendedStruct::@5 meta_