1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "share_memory_block.h"
17
18 #include <cstring>
19 #include <fcntl.h>
20 #include <sys/mman.h>
21 #include <sys/syscall.h>
22 #include <unistd.h>
23 #include "ashmem.h"
24 #include "logging.h"
25 #include "securec.h"
26
27 namespace {
28 const int HEAD_OFFSET_LEN = 4;
29 constexpr uint32_t TIMEOUT_SEC = 1;
30 const int WAIT_RELEASE_TIMEOUT_US = 10;
31 #ifndef PAGE_SIZE
32 constexpr uint32_t PAGE_SIZE = 4096;
33 #endif
34 } // namespace
35
36 struct PthreadLocker {
PthreadLockerPthreadLocker37 explicit PthreadLocker(pthread_mutex_t& mutex) : mutex_(mutex)
38 {
39 pthread_mutex_lock(&mutex_);
40 }
41
~PthreadLockerPthreadLocker42 ~PthreadLocker()
43 {
44 pthread_mutex_unlock(&mutex_);
45 }
46
47 private:
48 pthread_mutex_t& mutex_;
49 };
50
ShareMemoryBlock()51 ShareMemoryBlock::ShareMemoryBlock()
52 : fileDescriptor_(-1),
53 memoryPoint_(nullptr),
54 memorySize_(0),
55 memoryName_(),
56 header_(nullptr),
57 reusePloicy_(ReusePolicy::DROP_NONE)
58 {
59 }
60
CreateBlockWithFd(std::string name,uint32_t size,int fd)61 bool ShareMemoryBlock::CreateBlockWithFd(std::string name, uint32_t size, int fd)
62 {
63 CHECK_TRUE(fd >= 0, false, "CreateBlock FAIL SYS_memfd_create");
64
65 auto ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
66 if (ptr == MAP_FAILED) {
67 const int bufSize = 256;
68 char buf[bufSize] = {0};
69 strerror_r(errno, buf, bufSize);
70 HILOG_ERROR(LOG_CORE, "CreateBlockWithFd mmap ERR : %s", buf);
71 return false;
72 }
73
74 fileDescriptor_ = fd;
75 memoryPoint_ = ptr;
76 memorySize_ = size;
77
78 memoryName_ = name;
79 header_ = reinterpret_cast<BlockHeader*>(ptr);
80 return true;
81 }
82
CreateBlock(std::string name,uint32_t size)83 bool ShareMemoryBlock::CreateBlock(std::string name, uint32_t size)
84 {
85 HILOG_INFO(LOG_CORE, "CreateBlock %s %d", name.c_str(), size);
86 CHECK_TRUE(size > sizeof(BlockHeader), false, "size %u too less!", size);
87 CHECK_TRUE(size % PAGE_SIZE == 0, false, "size %u not times of %d!", size, PAGE_SIZE);
88
89 int fd = OHOS::AshmemCreate(name.c_str(), size);
90 CHECK_TRUE(fd >= 0, false, "OHOS::AshmemCreate fail.");
91
92 int check = OHOS::AshmemSetProt(fd, PROT_READ | PROT_WRITE);
93 if (check < 0) {
94 close(fd);
95 const int bufSize = 256;
96 char buf[bufSize] = {0};
97 strerror_r(errno, buf, bufSize);
98 HILOG_ERROR(LOG_CORE, "OHOS::AshmemSetProt ERR : %s", buf);
99 return false;
100 }
101
102 auto ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
103 if (ptr == MAP_FAILED) {
104 close(fd);
105 const int bufSize = 256;
106 char buf[bufSize] = {0};
107 strerror_r(errno, buf, bufSize);
108 HILOG_ERROR(LOG_CORE, "CreateBlock mmap ERR : %s", buf);
109 return false;
110 }
111
112 fileDescriptor_ = fd;
113 memoryPoint_ = ptr;
114 memorySize_ = size;
115
116 memoryName_ = name;
117 header_ = reinterpret_cast<BlockHeader*>(ptr);
118
119 // initialize header infos
120 header_->info.readOffset_ = 0;
121 header_->info.writeOffset_ = 0;
122 header_->info.memorySize_ = size - sizeof(BlockHeader);
123 header_->info.bytesCount_ = 0;
124 header_->info.chunkCount_ = 0;
125
126 pthread_mutexattr_t muAttr;
127 pthread_mutexattr_init(&muAttr);
128 pthread_mutexattr_setpshared(&muAttr, PTHREAD_PROCESS_SHARED);
129 pthread_mutexattr_settype(&muAttr, PTHREAD_MUTEX_RECURSIVE);
130 pthread_mutex_init(&header_->info.mutex_, &muAttr);
131 return true;
132 }
133
Valid() const134 bool ShareMemoryBlock::Valid() const
135 {
136 return header_ != nullptr;
137 }
138
ShareMemoryBlock(const std::string & name,uint32_t size)139 ShareMemoryBlock::ShareMemoryBlock(const std::string& name, uint32_t size) : ShareMemoryBlock()
140 {
141 CreateBlock(name, size);
142 }
143
ShareMemoryBlock(const std::string & name,uint32_t size,int fd)144 ShareMemoryBlock::ShareMemoryBlock(const std::string& name, uint32_t size, int fd) : ShareMemoryBlock()
145 {
146 CreateBlockWithFd(name, size, fd);
147 }
148
~ShareMemoryBlock()149 ShareMemoryBlock::~ShareMemoryBlock()
150 {
151 ReleaseBlock();
152 }
153
ReleaseBlock()154 bool ShareMemoryBlock::ReleaseBlock()
155 {
156 if (memorySize_ > 0) {
157 munmap(memoryPoint_, memorySize_);
158 memoryPoint_ = nullptr;
159 memorySize_ = 0;
160 }
161
162 if (fileDescriptor_ >= 0) {
163 close(fileDescriptor_);
164 fileDescriptor_ = -1;
165 }
166 return true;
167 }
168
GetCurrentFreeMemory(uint32_t size)169 int8_t* ShareMemoryBlock::GetCurrentFreeMemory(uint32_t size)
170 {
171 CHECK_NOTNULL(header_, nullptr, "header not ready!");
172 uint32_t realSize = size + sizeof(uint32_t) + HEAD_OFFSET_LEN;
173
174 uint32_t wp = header_->info.writeOffset_;
175 if (wp + realSize > header_->info.memorySize_) { // 后面部分放不下,从头开始放
176 if (header_->info.readOffset_ == 0) {
177 return nullptr;
178 }
179 *((uint32_t*)(&header_->data[wp])) = 0xffffffff;
180 wp = 0;
181 }
182 if (wp < header_->info.readOffset_ && header_->info.readOffset_ < wp + realSize) { //
183 return nullptr;
184 }
185
186 return &header_->data[wp + sizeof(uint32_t)];
187 }
188
GetFreeMemory(uint32_t size)189 int8_t* ShareMemoryBlock::GetFreeMemory(uint32_t size)
190 {
191 if (reusePloicy_ == ReusePolicy::DROP_NONE) {
192 return GetCurrentFreeMemory(size);
193 }
194 int8_t* ret = nullptr;
195 while (true) {
196 ret = GetCurrentFreeMemory(size);
197 if (ret != nullptr) {
198 break;
199 }
200 if (!Next()) {
201 return nullptr;
202 }
203 }
204 return ret;
205 }
206
UseFreeMemory(int8_t * pmem,uint32_t size)207 bool ShareMemoryBlock::UseFreeMemory(int8_t* pmem, uint32_t size)
208 {
209 uint32_t wp = pmem - sizeof(uint32_t) - header_->data;
210 *((int*)(&header_->data[wp])) = size;
211
212 header_->info.writeOffset_ = wp + sizeof(uint32_t) + size;
213 return true;
214 }
215
PutRaw(const int8_t * data,uint32_t size)216 bool ShareMemoryBlock::PutRaw(const int8_t* data, uint32_t size)
217 {
218 CHECK_NOTNULL(header_, false, "header not ready!");
219 PthreadLocker locker(header_->info.mutex_);
220 int8_t* rawMemory = GetFreeMemory(size);
221 if (rawMemory == nullptr) {
222 HILOG_ERROR(LOG_CORE, "PutRaw not enough space [%d]", size);
223 return false;
224 }
225 if (memcpy_s(rawMemory, size, data, size) != EOK) {
226 HILOG_ERROR(LOG_CORE, "memcpy_s error");
227 return false;
228 }
229
230 UseFreeMemory(rawMemory, size);
231 ++header_->info.bytesCount_;
232 ++header_->info.chunkCount_;
233 return true;
234 }
235
PutRawTimeout(const int8_t * data,uint32_t size)236 bool ShareMemoryBlock::PutRawTimeout(const int8_t* data, uint32_t size)
237 {
238 CHECK_NOTNULL(header_, false, "header not ready!");
239
240 struct timespec time_out;
241 clock_gettime(CLOCK_REALTIME, &time_out);
242 time_out.tv_sec += TIMEOUT_SEC;
243 if (pthread_mutex_timedlock(&header_->info.mutex_, &time_out) != 0) {
244 HILOG_ERROR(LOG_CORE, "PutRawTimeout failed %d", errno);
245 return false;
246 }
247
248 int8_t* rawMemory = GetFreeMemory(size);
249 if (rawMemory == nullptr) {
250 HILOG_ERROR(LOG_CORE, "PutRaw not enough space [%d]", size);
251 pthread_mutex_unlock(&header_->info.mutex_);
252 return false;
253 }
254 if (memcpy_s(rawMemory, size, data, size) != EOK) {
255 HILOG_ERROR(LOG_CORE, "memcpy_s error");
256 pthread_mutex_unlock(&header_->info.mutex_);
257 return false;
258 }
259
260 UseFreeMemory(rawMemory, size);
261 ++header_->info.bytesCount_;
262 ++header_->info.chunkCount_;
263
264 pthread_mutex_unlock(&header_->info.mutex_);
265 return true;
266 }
267
PutWithPayloadTimeout(const int8_t * header,uint32_t headerSize,const int8_t * payload,uint32_t payloadSize)268 bool ShareMemoryBlock::PutWithPayloadTimeout(const int8_t* header, uint32_t headerSize,
269 const int8_t* payload, uint32_t payloadSize)
270 {
271 CHECK_NOTNULL(header_, false, "header not ready!");
272 struct timespec time_out;
273 clock_gettime(CLOCK_REALTIME, &time_out);
274 time_out.tv_sec += TIMEOUT_SEC;
275 if (pthread_mutex_timedlock(&header_->info.mutex_, &time_out) != 0) {
276 return false;
277 }
278
279 int8_t* rawMemory = GetFreeMemory(headerSize + payloadSize);
280 if (rawMemory == nullptr) {
281 pthread_mutex_unlock(&header_->info.mutex_);
282 return false;
283 }
284 if (memcpy_s(rawMemory, headerSize, header, headerSize) != EOK) {
285 pthread_mutex_unlock(&header_->info.mutex_);
286 return false;
287 }
288 if (payloadSize > 0) {
289 if (memcpy_s(rawMemory + headerSize, payloadSize, payload, payloadSize) != EOK) {
290 pthread_mutex_unlock(&header_->info.mutex_);
291 return false;
292 }
293 }
294 UseFreeMemory(rawMemory, headerSize + payloadSize);
295 ++header_->info.bytesCount_;
296 ++header_->info.chunkCount_;
297
298 pthread_mutex_unlock(&header_->info.mutex_);
299 return true;
300 }
301
302 #ifndef NO_PROTOBUF
PutMessage(const google::protobuf::Message & pmsg,const std::string & pluginName)303 bool ShareMemoryBlock::PutMessage(const google::protobuf::Message& pmsg, const std::string& pluginName)
304 {
305 size_t size = pmsg.ByteSizeLong();
306
307 CHECK_NOTNULL(header_, false, "header not ready!");
308 PthreadLocker locker(header_->info.mutex_);
309 int8_t* rawMemory = GetFreeMemory(size);
310 if (rawMemory == nullptr) {
311 HILOG_ERROR(LOG_CORE, "%s: PutMessage not enough space [%zu]", pluginName.c_str(), size);
312 return false;
313 }
314
315 int ret = pmsg.SerializeToArray(rawMemory, size);
316 if (ret <= 0) {
317 HILOG_ERROR(LOG_CORE, "%s: SerializeToArray failed with %d, size: %zu", __func__, ret, size);
318 return false;
319 }
320 UseFreeMemory(rawMemory, size);
321 ++header_->info.bytesCount_;
322 ++header_->info.chunkCount_;
323 return true;
324 }
325 #endif
326
TakeData(const DataHandler & func)327 bool ShareMemoryBlock::TakeData(const DataHandler& func)
328 {
329 CHECK_NOTNULL(header_, false, "header not ready!");
330 CHECK_TRUE(static_cast<bool>(func), false, "func invalid!");
331
332 PthreadLocker locker(header_->info.mutex_);
333 auto size = GetDataSize();
334 if (size == 0) {
335 return false;
336 }
337 auto ptr = GetDataPoint();
338 CHECK_TRUE(func(ptr, size), false, "call func FAILED!");
339 CHECK_TRUE(Next(), false, "move read pointer FAILED!");
340 --header_->info.chunkCount_;
341 return true;
342 }
343
GetDataSize()344 uint32_t ShareMemoryBlock::GetDataSize()
345 {
346 if (header_->info.readOffset_ == header_->info.writeOffset_) {
347 return 0;
348 }
349 uint32_t ret = *((uint32_t*)(&header_->data[header_->info.readOffset_]));
350 if (ret == 0xffffffff) {
351 ret = *((uint32_t*)(&header_->data[0]));
352 }
353 return ret;
354 }
355
GetDataPoint()356 const int8_t* ShareMemoryBlock::GetDataPoint()
357 {
358 if (*((uint32_t*)(&header_->data[header_->info.readOffset_])) == 0xffffffff) {
359 return &header_->data[HEAD_OFFSET_LEN];
360 }
361 return &header_->data[header_->info.readOffset_ + HEAD_OFFSET_LEN];
362 }
363
Next()364 bool ShareMemoryBlock::Next()
365 {
366 if (header_->info.readOffset_ == header_->info.writeOffset_) {
367 return false;
368 }
369 uint32_t size = *((uint32_t*)(&header_->data[header_->info.readOffset_]));
370 if (size == 0xffffffff) {
371 size = *((uint32_t*)(&header_->data[0]));
372 header_->info.readOffset_ = size + sizeof(uint32_t);
373 } else {
374 header_->info.readOffset_ += size + sizeof(uint32_t);
375 }
376 return true;
377 }
378
GetName()379 std::string ShareMemoryBlock::GetName()
380 {
381 return memoryName_;
382 }
383
GetSize()384 uint32_t ShareMemoryBlock::GetSize()
385 {
386 return memorySize_;
387 }
388
GetfileDescriptor()389 int ShareMemoryBlock::GetfileDescriptor()
390 {
391 return fileDescriptor_;
392 }
393
PutWithPayloadSync(const int8_t * header,uint32_t headerSize,const int8_t * payload,uint32_t payloadSize)394 bool ShareMemoryBlock::PutWithPayloadSync(const int8_t* header, uint32_t headerSize,
395 const int8_t* payload, uint32_t payloadSize)
396 {
397 CHECK_NOTNULL(header_, false, "header not ready!");
398 pthread_mutex_lock(&header_->info.mutex_);
399 int8_t* rawMemory = GetFreeMemory(headerSize + payloadSize);
400 if (rawMemory == nullptr) {
401 while (true) {
402 if (rawMemory == nullptr) {
403 pthread_mutex_unlock(&header_->info.mutex_);
404 usleep(WAIT_RELEASE_TIMEOUT_US);
405 pthread_mutex_lock(&header_->info.mutex_);
406 rawMemory = GetFreeMemory(headerSize + payloadSize);
407 continue;
408 }
409 break;
410 }
411 }
412 if (memcpy_s(rawMemory, headerSize + payloadSize, header, headerSize) != EOK) {
413 pthread_mutex_unlock(&header_->info.mutex_);
414 return false;
415 }
416 if (payloadSize > 0) {
417 if (memcpy_s(rawMemory + headerSize, payloadSize, payload, payloadSize) != EOK) {
418 pthread_mutex_unlock(&header_->info.mutex_);
419 return false;
420 }
421 }
422 UseFreeMemory(rawMemory, headerSize + payloadSize);
423 ++header_->info.bytesCount_;
424 ++header_->info.chunkCount_;
425 pthread_mutex_unlock(&header_->info.mutex_);
426 return true;
427 }
ClearShareMemoryBlock()428 void ShareMemoryBlock::ClearShareMemoryBlock()
429 {
430 // clear header infos
431 PthreadLocker locker(header_->info.mutex_);
432 header_->info.readOffset_ = 0;
433 header_->info.writeOffset_ = 0;
434 header_->info.bytesCount_ = 0;
435 header_->info.chunkCount_ = 0;
436 }
437