LCOV - code coverage report
Current view: top level - src/zerocopy - fast_buffer_manager.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 446 503 88.7 %
Date: 2026-09-30 18:30:16 Functions: 69 76 90.8 %
Branches: 279 442 63.1 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (C) 2026 by Thun Lu. All rights reserved.
       3                 :            :  * Author: Thun Lu <thun.lu@zohomail.cn>
       4                 :            :  * Repo:   https://github.com/thun-res/vlink
       5                 :            :  *  _    __   __      _           __
       6                 :            :  * | |  / /  / /     (_) ____    / /__
       7                 :            :  * | | / /  / /     / / / __ \  / //_/
       8                 :            :  * | |/ /  / /___  / / / / / / / ,<
       9                 :            :  * |___/  /_____/ /_/ /_/ /_/ /_/|_|
      10                 :            :  *
      11                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
      12                 :            :  * you may not use this file except in compliance with the License.
      13                 :            :  * You may obtain a copy of the License at
      14                 :            :  *
      15                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
      16                 :            :  *
      17                 :            :  * Unless required by applicable law or agreed to in writing, software
      18                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      19                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      20                 :            :  * See the License for the specific language governing permissions and
      21                 :            :  * limitations under the License.
      22                 :            :  */
      23                 :            : 
      24                 :            : #include "./zerocopy/fast_buffer_manager.h"
      25                 :            : 
      26                 :            : #include <array>
      27                 :            : #include <atomic>
      28                 :            : #include <cstdlib>
      29                 :            : #include <cstring>
      30                 :            : #include <filesystem>
      31                 :            : #include <functional>
      32                 :            : #include <limits>
      33                 :            : #include <memory>
      34                 :            : #include <mutex>
      35                 :            : #include <new>
      36                 :            : #include <string>
      37                 :            : #include <string_view>
      38                 :            : #include <unordered_map>
      39                 :            : #include <utility>
      40                 :            : 
      41                 :            : #include "./base/memory_pool.h"
      42                 :            : #include "./base/sys_sharemem.h"
      43                 :            : #include "./base/utils.h"
      44                 :            : #include "./base/uuid.h"
      45                 :            : 
      46                 :            : #if defined(__linux__)
      47                 :            : #include <unistd.h>
      48                 :            : #endif
      49                 :            : 
      50                 :            : namespace vlink {
      51                 :            : 
      52                 :            : static constexpr size_t kFastBufferNameSize = 32;
      53                 :            : static constexpr size_t kFastBufferMaxReaders = 32;
      54                 :            : static constexpr std::string_view kFastBufferControlPrefix = "/vfb_";
      55                 :            : static constexpr std::string_view kFastBufferMemoryPrefix = "/vfbm_";
      56                 :            : 
      57                 :            : template <typename T, typename... ArgsT>
      58                 :         90 : static T* pool_new(ArgsT&&... args) noexcept {
      59                 :         90 :   void* memory = MemoryPool::global_instance().allocate(sizeof(T), alignof(T));
      60         [ +  - ]:        180 :   return memory ? new (memory) T(std::forward<ArgsT>(args)...) : nullptr;
      61                 :            : }
      62                 :            : 
      63                 :            : template <typename T>
      64                 :        120 : static void pool_delete(T* object) noexcept {
      65         [ +  + ]:        120 :   if (object) {
      66                 :         90 :     object->~T();
      67                 :         90 :     MemoryPool::global_instance().deallocate(object, sizeof(T), alignof(T));
      68                 :            :   }
      69                 :        120 : }
      70                 :            : 
      71                 :            : // PoolDeleter
      72                 :            : struct PoolDeleter final {
      73                 :            :   template <typename T>
      74                 :         12 :   void operator()(T* object) const noexcept {
      75                 :         12 :     pool_delete(object);
      76                 :         12 :   }
      77                 :            : };
      78                 :            : 
      79                 :            : template <typename T>
      80                 :            : using PoolPtr = std::unique_ptr<T, PoolDeleter>;
      81                 :            : 
      82                 :            : // PoolAllocator
      83                 :            : template <typename T>
      84                 :            : struct PoolAllocator final {
      85                 :            :   using value_type = T;
      86                 :            : 
      87                 :            :   PoolAllocator() noexcept = default;
      88                 :            : 
      89                 :            :   template <typename U>
      90                 :          8 :   PoolAllocator(const PoolAllocator<U>&) noexcept {}  // NOLINT(google-explicit-constructor)
      91                 :            : 
      92                 :         16 :   [[nodiscard]] T* allocate(size_t count) {
      93                 :         16 :     void* memory = MemoryPool::global_instance().allocate(count * sizeof(T), alignof(T));
      94                 :            : 
      95         [ -  + ]:         16 :     if VUNLIKELY (!memory) {
      96                 :          0 :       throw std::bad_alloc();
      97                 :            :     }
      98                 :            : 
      99                 :         16 :     return static_cast<T*>(memory);
     100                 :            :   }
     101                 :            : 
     102                 :         16 :   void deallocate(T* memory, size_t count) noexcept {
     103                 :         16 :     MemoryPool::global_instance().deallocate(static_cast<void*>(memory), count * sizeof(T), alignof(T));
     104                 :         16 :   }
     105                 :            : 
     106                 :            :   template <typename U>
     107                 :            :   bool operator==(const PoolAllocator<U>&) const noexcept {
     108                 :            :     return true;
     109                 :            :   }
     110                 :            : 
     111                 :            :   template <typename U>
     112                 :            :   bool operator!=(const PoolAllocator<U>&) const noexcept {
     113                 :            :     return false;
     114                 :            :   }
     115                 :            : };
     116                 :            : 
     117                 :         15 : static uint64_t generate_id() noexcept {
     118                 :         15 :   const Uuid uuid = Uuid::generate_random();
     119                 :         15 :   uint64_t id = 0;
     120                 :         15 :   std::memcpy(&id, uuid.bytes().data(), sizeof(id));
     121                 :         15 :   return id;
     122                 :            : }
     123                 :            : 
     124                 :         26 : static void format_name(std::string_view prefix, uint64_t id, char (&name)[kFastBufferNameSize]) noexcept {
     125                 :            :   static constexpr char kHexDigits[] = "0123456789abcdef";
     126                 :         26 :   char* cursor = name;
     127                 :            : 
     128                 :         26 :   std::memcpy(cursor, prefix.data(), prefix.size());
     129                 :         26 :   cursor += prefix.size();
     130                 :            : 
     131         [ +  + ]:        442 :   for (int shift = 60; shift >= 0; shift -= 4) {
     132                 :        416 :     *cursor++ = kHexDigits[(id >> shift) & 0x0F];
     133                 :            :   }
     134                 :            : 
     135                 :         26 :   *cursor = '\0';
     136                 :         26 : }
     137                 :            : 
     138                 :         61 : static bool parse_name(const char* name, std::string_view prefix, uint64_t& id) noexcept {
     139   [ +  -  +  +  :         89 :   if (std::memchr(name, '\0', kFastBufferNameSize) == nullptr || std::strlen(name) != prefix.size() + 16 ||
                   +  + ]
     140         [ -  + ]:         28 :       std::strncmp(name, prefix.data(), prefix.size()) != 0) {
     141                 :         33 :     return false;
     142                 :            :   }
     143                 :            : 
     144                 :         28 :   uint64_t parsed = 0;
     145                 :            : 
     146         [ +  + ]:        476 :   for (const char* cursor = name + prefix.size(); *cursor != '\0'; ++cursor) {
     147                 :        448 :     const char digit = *cursor;
     148                 :        448 :     uint64_t value = 0;
     149                 :            : 
     150   [ +  -  +  + ]:        448 :     if (digit >= '0' && digit <= '9') {
     151                 :        290 :       value = static_cast<uint64_t>(digit - '0');
     152   [ +  -  +  - ]:        158 :     } else if (digit >= 'a' && digit <= 'f') {
     153                 :        158 :       value = static_cast<uint64_t>(digit - 'a') + 10;
     154                 :            :     } else {
     155                 :          0 :       return false;
     156                 :            :     }
     157                 :            : 
     158                 :        448 :     parsed = (parsed << 4) | value;
     159                 :            :   }
     160                 :            : 
     161                 :         28 :   id = parsed;
     162                 :         28 :   return true;
     163                 :            : }
     164                 :            : 
     165                 :         28 : static uint32_t fold_start_time(uint64_t start_time) noexcept {
     166                 :         28 :   return static_cast<uint32_t>(start_time ^ (start_time >> 32));
     167                 :            : }
     168                 :            : 
     169                 :          9 : static uint64_t process_identity(int32_t pid) noexcept {
     170                 :          9 :   const uint64_t start_time = Utils::get_process_start_time(pid);
     171                 :            : 
     172   [ +  -  -  + ]:          9 :   if (start_time == 0 || start_time == Utils::kUnknownProcessStartTime) {
     173                 :          0 :     return 0;
     174                 :            :   }
     175                 :            : 
     176                 :          9 :   return (static_cast<uint64_t>(static_cast<uint32_t>(pid)) << 32) | fold_start_time(start_time);
     177                 :            : }
     178                 :            : 
     179                 :         18 : static uint64_t namespace_inode(const char* path) noexcept {
     180                 :            : #if defined(__linux__)
     181                 :            :   char link[64];
     182                 :         18 :   const ssize_t length = ::readlink(path, link, sizeof(link) - 1);
     183                 :            : 
     184         [ -  + ]:         18 :   if (length <= 0) {
     185                 :          0 :     return 0;
     186                 :            :   }
     187                 :            : 
     188                 :         18 :   link[length] = '\0';
     189                 :         18 :   const char* open = std::strchr(link, '[');
     190         [ +  - ]:         18 :   return open ? std::strtoull(open + 1, nullptr, 10) : 0;
     191                 :            : #else
     192                 :            :   (void)path;
     193                 :            :   return 0;
     194                 :            : #endif
     195                 :            : }
     196                 :            : 
     197                 :         23 : static bool is_identity_alive(uint64_t identity) noexcept {
     198                 :         23 :   const uint64_t start_time = Utils::get_process_start_time(static_cast<int32_t>(identity >> 32));
     199                 :            : 
     200         [ +  + ]:         23 :   if (start_time == 0) {
     201                 :          4 :     return false;
     202                 :            :   }
     203                 :            : 
     204         [ +  - ]:         38 :   return start_time == Utils::kUnknownProcessStartTime ||
     205         [ +  - ]:         38 :          fold_start_time(start_time) == static_cast<uint32_t>(identity);
     206                 :            : }
     207                 :            : 
     208                 :            : template <typename FnT>
     209                 :         14 : static void sweep_segments(std::string_view prefix, FnT&& fn) noexcept {
     210                 :            : #if defined(__linux__) || defined(__QNX__)
     211                 :            : #if defined(__QNX__)
     212                 :            :   static constexpr const char* kDirectory = "/dev/shmem";
     213                 :            : #else
     214                 :            :   static constexpr const char* kDirectory = "/dev/shm";
     215                 :            : #endif
     216                 :         14 :   std::error_code ec;
     217                 :         14 :   std::filesystem::directory_iterator iter(kDirectory, ec);
     218                 :            : 
     219   [ +  +  +  -  :        148 :   for (; !ec && iter != std::filesystem::directory_iterator(); iter.increment(ec)) {
          +  +  +  -  +  
                      + ]
     220                 :         67 :     const std::string filename = iter->path().filename().string();
     221                 :            :     char name[kFastBufferNameSize];
     222                 :         67 :     uint64_t id = 0;
     223                 :            : 
     224         [ +  + ]:         67 :     if (filename.size() >= kFastBufferNameSize - 1) {
     225                 :         28 :       continue;
     226                 :            :     }
     227                 :            : 
     228                 :         39 :     name[0] = '/';
     229                 :         39 :     std::memcpy(name + 1, filename.c_str(), filename.size() + 1);
     230                 :            : 
     231         [ +  + ]:         39 :     if (!parse_name(name, prefix, id)) {
     232                 :         33 :       continue;
     233                 :            :     }
     234                 :            : 
     235                 :          6 :     SysSharemem shared;
     236                 :            : 
     237         [ +  - ]:          6 :     if (shared.attach(name)) {
     238                 :          6 :       fn(shared);
     239                 :            :     }
     240                 :            :   }
     241                 :            : #else
     242                 :            :   (void)prefix;
     243                 :            :   (void)fn;
     244                 :            : #endif
     245                 :         14 : }
     246                 :            : 
     247                 :            : // HostBufferPlugin
     248                 :            : class HostBufferPlugin : public FastBufferPluginInterface {
     249                 :            :  public:
     250                 :          1 :   uint64_t get_protocol_id() const noexcept override { return 0x564C484F53540001ULL; }
     251                 :            : 
     252                 :         25 :   bool create(size_t size, const zerocopy::FastBuffer::Config& config,
     253                 :            :               zerocopy::FastBuffer::Buffer& buffer) noexcept override {
     254   [ +  -  +  +  :         25 :     if VUNLIKELY (size == 0 || config.device != -1 ||
          +  +  +  +  +  
             +  +  +  +  
                      + ]
     255                 :            :                   (config.memory_type != zerocopy::FastBuffer::kMemoryDefault &&
     256                 :            :                    config.memory_type != zerocopy::FastBuffer::kMemoryHost)) {
     257                 :          5 :       return false;
     258                 :            :     }
     259                 :            : 
     260                 :         20 :     auto* resource = pool_new<Bytes>();
     261                 :            : 
     262         [ -  + ]:         20 :     if VUNLIKELY (!resource) {
     263                 :          0 :       return false;
     264                 :            :     }
     265                 :            : 
     266                 :         20 :     *resource = Bytes::create(size);
     267                 :            : 
     268         [ -  + ]:         20 :     if VUNLIKELY (resource->empty()) {
     269                 :          0 :       pool_delete(resource);
     270                 :          0 :       return false;
     271                 :            :     }
     272                 :            : 
     273                 :         20 :     buffer = {resource->data(), size, resource, -1, zerocopy::FastBuffer::kMemoryHost};
     274                 :         20 :     return true;
     275                 :            :   }
     276                 :            : 
     277                 :         20 :   bool release(zerocopy::FastBuffer::Buffer& buffer) noexcept override {
     278                 :         20 :     pool_delete(static_cast<Bytes*>(buffer.handle));
     279                 :         20 :     buffer = {};
     280                 :         20 :     return true;
     281                 :            :   }
     282                 :            : 
     283                 :         59 :   bool synchronize(const zerocopy::FastBuffer::Buffer&) noexcept override { return true; }
     284                 :            : 
     285                 :          2 :   bool copy(const zerocopy::FastBuffer::Buffer& source,
     286                 :            :             const zerocopy::FastBuffer::Buffer& destination) noexcept override {
     287         [ -  + ]:          2 :     if VUNLIKELY (source.size != destination.size) {
     288                 :          0 :       return false;
     289                 :            :     }
     290                 :            : 
     291                 :          2 :     std::memmove(destination.data, source.data, source.size);
     292                 :          2 :     return true;
     293                 :            :   }
     294                 :            : 
     295                 :         12 :   bool copy_to_host(const zerocopy::FastBuffer::Buffer& source, Bytes& destination) noexcept override {
     296         [ -  + ]:         12 :     if VUNLIKELY (source.size != destination.size()) {
     297                 :          0 :       return false;
     298                 :            :     }
     299                 :            : 
     300                 :         12 :     std::memmove(destination.data(), source.data, source.size);
     301                 :         12 :     return true;
     302                 :            :   }
     303                 :            : 
     304                 :          6 :   bool copy_from_host(const Bytes& source, const zerocopy::FastBuffer::Buffer& destination) noexcept override {
     305         [ -  + ]:          6 :     if VUNLIKELY (source.size() != destination.size) {
     306                 :          0 :       return false;
     307                 :            :     }
     308                 :            : 
     309                 :          6 :     std::memmove(destination.data, source.data(), source.size());
     310                 :          6 :     return true;
     311                 :            :   }
     312                 :            : 
     313                 :         24 :   uint8_t* map(const zerocopy::FastBuffer::Buffer& buffer, zerocopy::FastBuffer::Access access) noexcept override {
     314   [ +  +  +  + ]:         13 :     return access == zerocopy::FastBuffer::kRead || access == zerocopy::FastBuffer::kWrite ||
     315                 :            :                    access == zerocopy::FastBuffer::kReadWrite
     316         [ +  + ]:         37 :                ? buffer.data
     317                 :         24 :                : nullptr;
     318                 :            :   }
     319                 :            : 
     320                 :         24 :   bool unmap(const zerocopy::FastBuffer::Buffer&, zerocopy::FastBuffer::Access access) noexcept override {
     321   [ +  +  +  +  :         24 :     return access == zerocopy::FastBuffer::kRead || access == zerocopy::FastBuffer::kWrite ||
                   +  + ]
     322                 :         24 :            access == zerocopy::FastBuffer::kReadWrite;
     323                 :            :   }
     324                 :            : 
     325                 :          5 :   const void* native_handle(const zerocopy::FastBuffer::Buffer& buffer) const noexcept override {
     326                 :          5 :     return buffer.handle;
     327                 :            :   }
     328                 :            : };
     329                 :            : 
     330                 :            : // SharedHostBufferPlugin
     331                 :            : class SharedHostBufferPlugin final : public HostBufferPlugin {
     332                 :            :  public:
     333                 :          6 :   SharedHostBufferPlugin(uint64_t identity, uint64_t pid_namespace, uint64_t time_namespace) noexcept
     334                 :          6 :       : identity_(identity), pid_namespace_(pid_namespace), time_namespace_(time_namespace) {
     335                 :          6 :     sweep_segments(kFastBufferMemoryPrefix, [this](SysSharemem& shared) {
     336         [ -  + ]:          3 :       if (shared.size() < kSegmentHeaderSize) {
     337                 :          0 :         return;
     338                 :            :       }
     339                 :            : 
     340                 :          3 :       const auto* header = static_cast<const std::atomic<uint64_t>*>(shared.data());
     341                 :          3 :       const uint64_t owner = header[0].load(std::memory_order_acquire);
     342                 :            : 
     343         [ +  - ]:          6 :       if (owner != 0 && header[1].load(std::memory_order_relaxed) == pid_namespace_ &&
     344   [ +  -  +  -  :          9 :           header[2].load(std::memory_order_relaxed) == time_namespace_ && !is_identity_alive(owner)) {
             +  +  +  + ]
     345                 :          1 :         (void)shared.detach();
     346                 :            :       }
     347                 :            :     });
     348                 :          6 :   }
     349                 :            : 
     350                 :         43 :   uint64_t get_protocol_id() const noexcept override { return 0x564C5348534D0001ULL; }
     351                 :            : 
     352                 :          8 :   bool create(size_t size, const zerocopy::FastBuffer::Config& config,
     353                 :            :               zerocopy::FastBuffer::Buffer& buffer) noexcept override {
     354   [ +  -  -  +  :          8 :     if VUNLIKELY (size == 0 || size > std::numeric_limits<size_t>::max() - kSegmentHeaderSize || config.device != -1 ||
          +  -  -  +  +  
          -  -  +  -  -  
             -  +  -  + ]
     355                 :            :                   (config.memory_type != zerocopy::FastBuffer::kMemoryDefault &&
     356                 :            :                    config.memory_type != zerocopy::FastBuffer::kMemoryShared)) {
     357                 :          0 :       return false;
     358                 :            :     }
     359                 :            : 
     360                 :          8 :     auto* segment = pool_new<Segment>();
     361                 :            : 
     362         [ -  + ]:          8 :     if VUNLIKELY (!segment) {
     363                 :          0 :       return false;
     364                 :            :     }
     365                 :            : 
     366                 :          8 :     format_name(kFastBufferMemoryPrefix, generate_id(), segment->name);
     367                 :            : 
     368         [ -  + ]:          8 :     if VUNLIKELY (!segment->shared.create(segment->name, kSegmentHeaderSize + size)) {
     369                 :          0 :       pool_delete(segment);
     370                 :          0 :       return false;
     371                 :            :     }
     372                 :            : 
     373                 :          8 :     segment->owner = true;
     374                 :          8 :     auto* base = static_cast<uint8_t*>(segment->shared.data());
     375         [ +  + ]:         32 :     auto* header = new (base) std::atomic<uint64_t>[3]{};
     376                 :          8 :     header[1].store(pid_namespace_, std::memory_order_relaxed);
     377                 :          8 :     header[2].store(time_namespace_, std::memory_order_relaxed);
     378                 :          8 :     header[0].store(identity_, std::memory_order_release);
     379                 :          8 :     buffer = {base + kSegmentHeaderSize, size, segment, -1, zerocopy::FastBuffer::kMemoryShared};
     380                 :          8 :     return true;
     381                 :            :   }
     382                 :            : 
     383                 :         13 :   bool release(zerocopy::FastBuffer::Buffer& buffer) noexcept override {
     384                 :         13 :     auto* segment = static_cast<Segment*>(buffer.handle);
     385                 :         13 :     (void)segment->shared.detach(segment->owner);
     386                 :         13 :     pool_delete(segment);
     387                 :         13 :     buffer = {};
     388                 :         13 :     return true;
     389                 :            :   }
     390                 :            : 
     391                 :         19 :   size_t get_handle_size(const zerocopy::FastBuffer::Buffer&) const noexcept override { return sizeof(Descriptor); }
     392                 :            : 
     393                 :          7 :   bool export_handle(const zerocopy::FastBuffer::Buffer& buffer, Bytes& descriptor) noexcept override {
     394         [ -  + ]:          7 :     if VUNLIKELY (descriptor.size() != sizeof(Descriptor)) {
     395                 :          0 :       return false;
     396                 :            :     }
     397                 :            : 
     398                 :          7 :     Descriptor exported;
     399                 :          7 :     std::memcpy(exported.name, static_cast<const Segment*>(buffer.handle)->name, sizeof(exported.name));
     400                 :          7 :     exported.size = buffer.size;
     401                 :          7 :     std::memcpy(descriptor.data(), &exported, sizeof(exported));
     402                 :          7 :     return true;
     403                 :            :   }
     404                 :            : 
     405                 :          5 :   bool import_handle(const Bytes& descriptor, zerocopy::FastBuffer::Buffer& buffer) noexcept override {
     406                 :          5 :     Descriptor decoded;
     407                 :            : 
     408   [ +  -  -  +  :          5 :     if VUNLIKELY (!decode(descriptor, decoded) || decoded.size == 0) {
                   -  + ]
     409                 :          0 :       return false;
     410                 :            :     }
     411                 :            : 
     412                 :          5 :     auto* segment = pool_new<Segment>();
     413                 :            : 
     414         [ -  + ]:          5 :     if VUNLIKELY (!segment) {
     415                 :          0 :       return false;
     416                 :            :     }
     417                 :            : 
     418   [ +  -  -  +  :          5 :     if VUNLIKELY (!segment->shared.attach(decoded.name) || segment->shared.size() < kSegmentHeaderSize ||
          +  -  -  +  +  
             -  +  -  -  
                      + ]
     419                 :            :                   segment->shared.size() - kSegmentHeaderSize < decoded.size) {
     420                 :          0 :       pool_delete(segment);
     421                 :          0 :       return false;
     422                 :            :     }
     423                 :            : 
     424                 :          5 :     std::memcpy(segment->name, decoded.name, sizeof(segment->name));
     425                 :          5 :     buffer = {static_cast<uint8_t*>(segment->shared.data()) + kSegmentHeaderSize, static_cast<size_t>(decoded.size),
     426                 :            :               segment, -1, zerocopy::FastBuffer::kMemoryShared};
     427                 :          5 :     return true;
     428                 :            :   }
     429                 :            : 
     430                 :          2 :   bool destroy_handle(const Bytes& descriptor) noexcept override {
     431                 :          2 :     Descriptor decoded;
     432                 :            : 
     433         [ -  + ]:          2 :     if VUNLIKELY (!decode(descriptor, decoded)) {
     434                 :          0 :       return false;
     435                 :            :     }
     436                 :            : 
     437                 :          2 :     SysSharemem shared;
     438   [ +  +  +  -  :          2 :     return shared.attach(decoded.name) && shared.detach();
             +  -  +  - ]
     439                 :          2 :   }
     440                 :            : 
     441                 :            :  private:
     442                 :            :   static constexpr size_t kSegmentHeaderSize = 64;
     443                 :            : 
     444                 :            :   struct Segment final {
     445                 :            :     char name[kFastBufferNameSize]{};
     446                 :            :     SysSharemem shared;
     447                 :            :     bool owner{false};
     448                 :            :   };
     449                 :            : 
     450                 :            :   struct Descriptor final {
     451                 :            :     char name[kFastBufferNameSize]{};
     452                 :            :     uint64_t size{0};
     453                 :            :   };
     454                 :            : 
     455                 :          7 :   static bool decode(const Bytes& descriptor, Descriptor& decoded) noexcept {
     456         [ -  + ]:          7 :     if VUNLIKELY (descriptor.size() != sizeof(Descriptor)) {
     457                 :          0 :       return false;
     458                 :            :     }
     459                 :            : 
     460                 :          7 :     std::memcpy(&decoded, descriptor.data(), sizeof(decoded));
     461                 :          7 :     uint64_t id = 0;
     462                 :          7 :     return parse_name(decoded.name, kFastBufferMemoryPrefix, id);
     463                 :            :   }
     464                 :            : 
     465                 :            :   uint64_t identity_{0};
     466                 :            :   uint64_t pid_namespace_{0};
     467                 :            :   uint64_t time_namespace_{0};
     468                 :            : };
     469                 :            : 
     470                 :            : // FastBufferControl
     471                 :            : struct alignas(8) FastBufferControl final {
     472                 :            :   std::atomic<uint64_t> protocol{0};
     473                 :            :   uint64_t size{0};
     474                 :            :   uint64_t descriptor_size{0};
     475                 :            :   std::atomic<uint64_t> owner{0};
     476                 :            :   std::atomic<uint64_t> pid_namespace{0};
     477                 :            :   std::atomic<uint64_t> time_namespace{0};
     478                 :            :   std::atomic<uint64_t> generation{0};
     479                 :            :   std::atomic<uint64_t> readers[kFastBufferMaxReaders]{};
     480                 :            : };
     481                 :            : 
     482                 :            : // FastBufferSystemDescriptor
     483                 :            : struct FastBufferSystemDescriptor final {
     484                 :            :   char name[kFastBufferNameSize]{};
     485                 :            :   uint64_t generation{0};
     486                 :            : };
     487                 :            : 
     488                 :            : static constexpr size_t kFastBufferHandleSize = sizeof(FastBufferSystemDescriptor);
     489                 :            : 
     490                 :            : static_assert(std::atomic<uint64_t>::is_always_lock_free, "Shared control blocks require lock-free atomics.");
     491                 :            : 
     492                 :            : // FastBufferShare
     493                 :            : struct FastBufferShare final {
     494                 :         15 :   ~FastBufferShare() {
     495   [ +  +  +  -  :         15 :     if (owner && shared.is_attached()) {
                   +  + ]
     496                 :          7 :       (void)shared.detach();
     497                 :            :     }
     498                 :         15 :   }
     499                 :            : 
     500                 :            :   uint64_t id{0};
     501                 :            :   uint64_t generation{0};
     502                 :            :   SysSharemem shared;
     503                 :            :   FastBufferControl* control{nullptr};
     504                 :            :   size_t reader{0};
     505                 :            :   bool owner{false};
     506                 :            : };
     507                 :            : 
     508                 :            : // FastBufferResource
     509                 :            : struct FastBufferResource final {
     510                 :         42 :   ~FastBufferResource() { pool_delete(share.load(std::memory_order_acquire)); }
     511                 :            : 
     512                 :            :   zerocopy::FastBuffer::Buffer native;
     513                 :            :   std::atomic<size_t> references{1};
     514                 :            :   std::atomic<FastBufferShare*> share{nullptr};
     515                 :            :   FastBufferResource* next{nullptr};
     516                 :            : };
     517                 :            : 
     518                 :            : enum class LocalImport : uint8_t {
     519                 :            :   kMissing = 0,
     520                 :            :   kImported = 1,
     521                 :            :   kExpired = 2,
     522                 :            : };
     523                 :            : 
     524                 :            : // FastBufferManager::Impl
     525                 :            : struct FastBufferManager::Impl final {
     526                 :         20 :   [[nodiscard]] std::mutex& stripe_of(uint64_t id) noexcept { return stripes[id % stripes.size()]; }
     527                 :            : 
     528                 :            :   void free_resource(FastBufferResource* resource) noexcept;
     529                 :            : 
     530                 :            :   void retire_owner(FastBufferResource* resource) noexcept;
     531                 :            : 
     532                 :            :   void reclaim_retired() noexcept;
     533                 :            : 
     534                 :            :   [[nodiscard]] LocalImport import_local(uint64_t id, uint64_t generation,
     535                 :            :                                          zerocopy::FastBuffer::Buffer& buffer) noexcept;
     536                 :            : 
     537                 :            :   void destroy_dead_owner(const FastBufferControl& control, SysSharemem& shared) noexcept;
     538                 :            : 
     539                 :            :   Plugin plugin;
     540                 :            :   std::shared_ptr<FastBufferPluginInterface> interface;
     541                 :            :   uint64_t identity{0};
     542                 :            :   uint64_t pid_namespace{0};
     543                 :            :   uint64_t time_namespace{0};
     544                 :            :   std::mutex mutex;
     545                 :            :   std::unordered_map<uint64_t, FastBufferResource*, std::hash<uint64_t>, std::equal_to<>,
     546                 :            :                      PoolAllocator<std::pair<const uint64_t, FastBufferResource*>>>
     547                 :            :       resources;
     548                 :            :   std::atomic<FastBufferResource*> retired{nullptr};
     549                 :            :   std::array<std::mutex, 64> stripes;
     550                 :            : };
     551                 :            : 
     552                 :         19 : static bool purge_readers(FastBufferControl& control) noexcept {
     553                 :         19 :   bool busy = false;
     554                 :            : 
     555         [ +  + ]:        627 :   for (auto& reader : control.readers) {
     556                 :        608 :     uint64_t identity = reader.load(std::memory_order_seq_cst);
     557                 :            : 
     558         [ +  + ]:        608 :     while (identity != 0) {
     559         [ +  + ]:         10 :       if (is_identity_alive(identity)) {
     560                 :          9 :         busy = true;
     561                 :          9 :         break;
     562                 :            :       }
     563                 :            : 
     564         [ +  - ]:          2 :       if (reader.compare_exchange_strong(identity, 0, std::memory_order_seq_cst)) {
     565                 :          1 :         break;
     566                 :            :       }
     567                 :            :     }
     568                 :            :   }
     569                 :            : 
     570                 :         19 :   return !busy;
     571                 :            : }
     572                 :            : 
     573                 :          7 : void FastBufferManager::Impl::free_resource(FastBufferResource* resource) noexcept {
     574         [ -  + ]:          7 :   if VUNLIKELY (!interface->release(resource->native)) {
     575         [ #  # ]:          0 :     VLOG_E("FastBufferManager: Keeping resource after SDK release failure.");
     576                 :          0 :     return;
     577                 :            :   }
     578                 :            : 
     579                 :          7 :   pool_delete(resource);
     580                 :            : }
     581                 :            : 
     582                 :          7 : void FastBufferManager::Impl::retire_owner(FastBufferResource* resource) noexcept {
     583         [ +  + ]:          7 :   if (purge_readers(*resource->share.load(std::memory_order_acquire)->control)) {
     584                 :          5 :     free_resource(resource);
     585                 :          5 :     return;
     586                 :            :   }
     587                 :            : 
     588                 :          2 :   std::lock_guard lock(mutex);
     589                 :          2 :   resource->next = retired.load(std::memory_order_relaxed);
     590                 :          2 :   retired.store(resource, std::memory_order_relaxed);
     591                 :          2 : }
     592                 :            : 
     593                 :         75 : void FastBufferManager::Impl::reclaim_retired() noexcept {
     594         [ +  + ]:         75 :   if VLIKELY (retired.load(std::memory_order_relaxed) == nullptr) {
     595                 :         70 :     return;
     596                 :            :   }
     597                 :            : 
     598                 :          5 :   FastBufferResource* pending = nullptr;
     599                 :            : 
     600                 :            :   {
     601                 :          5 :     std::lock_guard lock(mutex);
     602                 :          5 :     pending = retired.exchange(nullptr, std::memory_order_relaxed);
     603                 :          5 :   }
     604                 :            : 
     605                 :          5 :   FastBufferResource* busy = nullptr;
     606                 :            : 
     607         [ +  + ]:         10 :   while (pending) {
     608                 :          5 :     auto* resource = pending;
     609                 :          5 :     pending = resource->next;
     610                 :          5 :     resource->next = nullptr;
     611                 :            : 
     612         [ +  + ]:          5 :     if (purge_readers(*resource->share.load(std::memory_order_acquire)->control)) {
     613                 :          1 :       free_resource(resource);
     614                 :            :     } else {
     615                 :          4 :       resource->next = busy;
     616                 :          4 :       busy = resource;
     617                 :            :     }
     618                 :            :   }
     619                 :            : 
     620         [ +  + ]:          5 :   if (busy) {
     621                 :          4 :     auto* tail = busy;
     622                 :            : 
     623         [ -  + ]:          4 :     while (tail->next) {
     624                 :          0 :       tail = tail->next;
     625                 :            :     }
     626                 :            : 
     627                 :          4 :     std::lock_guard lock(mutex);
     628                 :          4 :     tail->next = retired.load(std::memory_order_relaxed);
     629                 :          4 :     retired.store(busy, std::memory_order_relaxed);
     630                 :          4 :   }
     631                 :            : }
     632                 :            : 
     633                 :         23 : LocalImport FastBufferManager::Impl::import_local(uint64_t id, uint64_t generation,
     634                 :            :                                                   zerocopy::FastBuffer::Buffer& buffer) noexcept {
     635                 :         23 :   std::lock_guard lock(mutex);
     636                 :         23 :   const auto iter = resources.find(id);
     637                 :            : 
     638         [ +  + ]:         23 :   if (iter == resources.end()) {
     639                 :         16 :     return LocalImport::kMissing;
     640                 :            :   }
     641                 :            : 
     642                 :          7 :   auto* resource = iter->second;
     643                 :          7 :   const auto* share = resource->share.load(std::memory_order_acquire);
     644                 :            : 
     645         [ +  + ]:         14 :   if VUNLIKELY (share->control->generation.load(std::memory_order_acquire) != generation) {
     646                 :          3 :     return LocalImport::kExpired;
     647                 :            :   }
     648                 :            : 
     649                 :          4 :   resource->references.fetch_add(1, std::memory_order_relaxed);
     650                 :          4 :   buffer = resource->native;
     651                 :          4 :   buffer.handle = resource;
     652                 :          4 :   return LocalImport::kImported;
     653                 :         23 : }
     654                 :            : 
     655                 :          2 : void FastBufferManager::Impl::destroy_dead_owner(const FastBufferControl& control, SysSharemem& shared) noexcept {
     656   [ +  -  +  -  :          2 :   if (control.descriptor_size != 0 && control.descriptor_size <= shared.size() - sizeof(FastBufferControl)) {
                   +  - ]
     657                 :          4 :     (void)interface->destroy_handle(Bytes::shallow_copy(reinterpret_cast<const uint8_t*>(&control + 1),
     658                 :          2 :                                                         static_cast<size_t>(control.descriptor_size)));
     659                 :            :   }
     660                 :            : 
     661                 :          2 :   (void)shared.detach();
     662                 :          2 : }
     663                 :            : 
     664                 :            : // FastBufferManager
     665                 :        300 : FastBufferManager& FastBufferManager::get() {
     666   [ +  +  +  -  :        300 :   static FastBufferManager global_fast_buffer;
             +  -  -  - ]
     667                 :        300 :   return global_fast_buffer;
     668                 :            : }
     669                 :            : 
     670                 :          5 : bool FastBufferManager::is_valid() const { return impl_->interface != nullptr; }
     671                 :            : 
     672                 :        151 : FastBufferPluginInterface* FastBufferManager::get_interface() const { return impl_->interface.get(); }
     673                 :            : 
     674                 :        101 : const zerocopy::FastBuffer::Buffer& FastBufferManager::native_buffer(
     675                 :            :     const zerocopy::FastBuffer::Buffer& buffer) noexcept {
     676                 :        101 :   return static_cast<const FastBufferResource*>(buffer.handle)->native;
     677                 :            : }
     678                 :            : 
     679                 :         33 : bool FastBufferManager::create(size_t size, const zerocopy::FastBuffer::Config& config,
     680                 :            :                                zerocopy::FastBuffer::Buffer& buffer) noexcept {
     681                 :         33 :   impl_->reclaim_retired();
     682                 :            : 
     683                 :         33 :   PoolPtr<FastBufferResource> resource(pool_new<FastBufferResource>());
     684                 :            : 
     685   [ +  -  +  +  :         33 :   if VUNLIKELY (!resource || !impl_->interface->create(size, config, resource->native)) {
                   +  + ]
     686                 :          5 :     return false;
     687                 :            :   }
     688                 :            : 
     689                 :         28 :   buffer = resource->native;
     690                 :         28 :   buffer.handle = resource.release();
     691                 :         28 :   return true;
     692                 :         33 : }
     693                 :            : 
     694                 :         13 : void FastBufferManager::retain(const zerocopy::FastBuffer::Buffer& source,
     695                 :            :                                zerocopy::FastBuffer::Buffer& buffer) noexcept {
     696                 :         13 :   auto* resource = static_cast<FastBufferResource*>(source.handle);
     697                 :         13 :   resource->references.fetch_add(1, std::memory_order_relaxed);
     698                 :         13 :   buffer = source;
     699                 :         13 : }
     700                 :            : 
     701                 :         50 : void FastBufferManager::release(zerocopy::FastBuffer::Buffer& buffer) noexcept {
     702                 :         50 :   auto* resource = static_cast<FastBufferResource*>(buffer.handle);
     703                 :         50 :   buffer = {};
     704                 :         50 :   size_t references = resource->references.load(std::memory_order_acquire);
     705                 :            : 
     706         [ +  + ]:         50 :   while (references > 1) {
     707         [ +  - ]:         34 :     if (resource->references.compare_exchange_weak(references, references - 1, std::memory_order_acq_rel,
     708                 :            :                                                    std::memory_order_acquire)) {
     709                 :         24 :       return;
     710                 :            :     }
     711                 :            :   }
     712                 :            : 
     713                 :         33 :   impl_->reclaim_retired();
     714                 :            : 
     715                 :         33 :   auto* share = resource->share.load(std::memory_order_acquire);
     716         [ +  + ]:         33 :   std::unique_lock stripe(share ? impl_->stripe_of(share->id) : impl_->stripes[0], std::defer_lock);
     717                 :         33 :   std::unique_lock registry(impl_->mutex, std::defer_lock);
     718                 :            : 
     719         [ +  + ]:         33 :   if (share) {
     720         [ +  + ]:         12 :     if (!share->owner) {
     721                 :          5 :       stripe.lock();
     722                 :            :     }
     723                 :            : 
     724                 :         12 :     registry.lock();
     725                 :            :   }
     726                 :            : 
     727         [ -  + ]:         66 :   if (resource->references.fetch_sub(1, std::memory_order_acq_rel) != 1) {
     728                 :          0 :     return;
     729                 :            :   }
     730                 :            : 
     731         [ +  + ]:         33 :   if (share) {
     732                 :         12 :     impl_->resources.erase(share->id);
     733                 :            : 
     734         [ +  + ]:         12 :     if (share->owner) {
     735                 :          7 :       share->control->generation.fetch_add(1, std::memory_order_seq_cst);
     736                 :            :     }
     737                 :            : 
     738                 :         12 :     registry.unlock();
     739                 :            :   }
     740                 :            : 
     741         [ -  + ]:         33 :   if VUNLIKELY (!impl_->interface->synchronize(resource->native)) {
     742         [ #  # ]:          0 :     VLOG_E("FastBufferManager: Keeping resource after synchronization failure.");
     743                 :          0 :     return;
     744                 :            :   }
     745                 :            : 
     746   [ +  +  +  + ]:         33 :   if (share && share->owner) {
     747                 :          7 :     impl_->retire_owner(resource);
     748                 :          7 :     return;
     749                 :            :   }
     750                 :            : 
     751         [ -  + ]:         26 :   if VUNLIKELY (!impl_->interface->release(resource->native)) {
     752         [ #  # ]:          0 :     VLOG_E("FastBufferManager: Keeping resource after SDK release failure.");
     753                 :          0 :     return;
     754                 :            :   }
     755                 :            : 
     756         [ +  + ]:         26 :   if (share) {
     757                 :          5 :     share->control->readers[share->reader].store(0, std::memory_order_release);
     758                 :            :   }
     759                 :            : 
     760                 :         26 :   pool_delete(resource);
     761   [ +  +  +  + ]:         40 : }
     762                 :            : 
     763                 :         21 : bool FastBufferManager::reclaim(const zerocopy::FastBuffer::Buffer& buffer) noexcept {
     764                 :         21 :   auto* resource = static_cast<FastBufferResource*>(buffer.handle);
     765                 :            : 
     766         [ +  + ]:         42 :   if (resource->references.load(std::memory_order_acquire) != 1) {
     767                 :          7 :     return false;
     768                 :            :   }
     769                 :            : 
     770                 :         14 :   const auto* share = resource->share.load(std::memory_order_acquire);
     771                 :            : 
     772         [ +  + ]:         14 :   if (!share) {
     773                 :          7 :     return impl_->interface->synchronize(resource->native);
     774                 :            :   }
     775                 :            : 
     776                 :          7 :   auto& control = *share->control;
     777                 :            : 
     778         [ +  + ]:          7 :   if (!purge_readers(control)) {
     779                 :          3 :     return false;
     780                 :            :   }
     781                 :            : 
     782                 :            :   {
     783                 :          4 :     std::lock_guard lock(impl_->mutex);
     784                 :            : 
     785         [ -  + ]:          8 :     if (resource->references.load(std::memory_order_relaxed) != 1) {
     786                 :          0 :       return false;
     787                 :            :     }
     788                 :            : 
     789                 :          4 :     control.generation.fetch_add(1, std::memory_order_seq_cst);
     790         [ +  - ]:          4 :   }
     791                 :            : 
     792         [ +  + ]:        132 :   for (const auto& reader : control.readers) {
     793         [ -  + ]:        256 :     if (reader.load(std::memory_order_seq_cst) != 0) {
     794                 :          0 :       return false;
     795                 :            :     }
     796                 :            :   }
     797                 :            : 
     798                 :          4 :   return impl_->interface->synchronize(resource->native);
     799                 :            : }
     800                 :            : 
     801                 :          1 : bool FastBufferManager::import_native(const void* handle, zerocopy::FastBuffer::Buffer& buffer) noexcept {
     802                 :          1 :   PoolPtr<FastBufferResource> resource(pool_new<FastBufferResource>());
     803                 :            : 
     804   [ +  -  +  -  :          1 :   if VUNLIKELY (!resource || !impl_->interface->import_native(handle, resource->native)) {
                   +  - ]
     805                 :          1 :     return false;
     806                 :            :   }
     807                 :            : 
     808                 :          0 :   buffer = resource->native;
     809                 :          0 :   buffer.handle = resource.release();
     810                 :          0 :   return true;
     811                 :          1 : }
     812                 :            : 
     813                 :         22 : size_t FastBufferManager::get_handle_size(const zerocopy::FastBuffer::Buffer& buffer) const noexcept {
     814                 :            : #if defined(__ANDROID__)
     815                 :            :   (void)buffer;
     816                 :            :   return 0;
     817                 :            : #else
     818                 :         22 :   const size_t size = impl_->interface->get_handle_size(native_buffer(buffer));
     819   [ +  +  -  + ]:         22 :   return size == 0 || impl_->identity == 0 ? 0 : kFastBufferHandleSize;
     820                 :            : #endif
     821                 :            : }
     822                 :            : 
     823                 :         11 : bool FastBufferManager::export_handle(const zerocopy::FastBuffer::Buffer& buffer, Bytes& descriptor) noexcept {
     824                 :         11 :   auto* resource = static_cast<FastBufferResource*>(buffer.handle);
     825                 :            : 
     826         [ -  + ]:         11 :   if VUNLIKELY (!impl_->interface->synchronize(resource->native)) {
     827                 :          0 :     return false;
     828                 :            :   }
     829                 :            : 
     830                 :         11 :   auto* share = resource->share.load(std::memory_order_acquire);
     831                 :            : 
     832         [ +  + ]:         11 :   if (!share) {
     833                 :          7 :     std::lock_guard stripe(impl_->stripes[std::hash<const void*>{}(resource) % impl_->stripes.size()]);
     834                 :          7 :     share = resource->share.load(std::memory_order_acquire);
     835                 :            : 
     836         [ +  - ]:          7 :     if (!share) {
     837                 :          7 :       const size_t size = impl_->interface->get_handle_size(resource->native);
     838                 :            : 
     839         [ -  + ]:          7 :       if VUNLIKELY (size == 0) {
     840                 :          0 :         return false;
     841                 :            :       }
     842                 :            : 
     843                 :          7 :       PoolPtr<FastBufferShare> created(pool_new<FastBufferShare>());
     844                 :            : 
     845         [ -  + ]:          7 :       if VUNLIKELY (!created) {
     846                 :          0 :         return false;
     847                 :            :       }
     848                 :            : 
     849                 :          7 :       created->id = generate_id();
     850                 :            :       char name[kFastBufferNameSize];
     851                 :          7 :       format_name(kFastBufferControlPrefix, created->id, name);
     852                 :            : 
     853         [ -  + ]:          7 :       if VUNLIKELY (!created->shared.create(name, sizeof(FastBufferControl) + size)) {
     854                 :          0 :         return false;
     855                 :            :       }
     856                 :            : 
     857                 :          7 :       created->owner = true;
     858                 :          7 :       created->control = new (created->shared.data()) FastBufferControl;
     859                 :          7 :       created->control->protocol.store(impl_->interface->get_protocol_id(), std::memory_order_relaxed);
     860                 :          7 :       created->control->size = resource->native.size;
     861                 :          7 :       created->control->descriptor_size = size;
     862                 :          7 :       created->control->pid_namespace.store(impl_->pid_namespace, std::memory_order_relaxed);
     863                 :          7 :       created->control->time_namespace.store(impl_->time_namespace, std::memory_order_relaxed);
     864                 :          7 :       created->control->generation.store(1, std::memory_order_relaxed);
     865                 :          7 :       created->control->owner.store(impl_->identity, std::memory_order_release);
     866                 :          7 :       auto native_descriptor = Bytes::shallow_copy(reinterpret_cast<uint8_t*>(created->control + 1), size);
     867                 :            : 
     868         [ -  + ]:          7 :       if VUNLIKELY (!impl_->interface->export_handle(resource->native, native_descriptor)) {
     869                 :          0 :         return false;
     870                 :            :       }
     871                 :            : 
     872                 :          7 :       share = created.release();
     873                 :            : 
     874                 :            :       {
     875                 :          7 :         std::lock_guard registry(impl_->mutex);
     876                 :          7 :         impl_->resources.emplace(share->id, resource);
     877                 :          7 :       }
     878                 :            : 
     879                 :          7 :       resource->share.store(share, std::memory_order_release);
     880   [ +  -  +  - ]:          7 :     }
     881         [ +  - ]:          7 :   }
     882                 :            : 
     883                 :         11 :   FastBufferSystemDescriptor exported;
     884                 :         11 :   format_name(kFastBufferControlPrefix, share->id, exported.name);
     885         [ +  + ]:         20 :   exported.generation = share->owner ? share->control->generation.load(std::memory_order_acquire) : share->generation;
     886                 :         11 :   std::memcpy(descriptor.data(), &exported, sizeof(exported));
     887                 :         11 :   return true;
     888                 :            : }
     889                 :            : 
     890                 :         15 : bool FastBufferManager::import_handle(const Bytes& descriptor, zerocopy::FastBuffer::Buffer& buffer) noexcept {
     891                 :            : #if defined(__ANDROID__)
     892                 :            :   (void)descriptor;
     893                 :            :   (void)buffer;
     894                 :            :   return false;
     895                 :            : #else
     896   [ +  -  -  +  :         15 :   if VUNLIKELY (descriptor.size() != kFastBufferHandleSize || impl_->identity == 0) {
                   -  + ]
     897                 :          0 :     return false;
     898                 :            :   }
     899                 :            : 
     900                 :         15 :   FastBufferSystemDescriptor decoded;
     901                 :         15 :   std::memcpy(&decoded, descriptor.data(), sizeof(decoded));
     902                 :         15 :   uint64_t id = 0;
     903                 :            : 
     904   [ +  -  -  +  :         15 :   if VUNLIKELY (!parse_name(decoded.name, kFastBufferControlPrefix, id) || decoded.generation == 0) {
                   -  + ]
     905                 :          0 :     return false;
     906                 :            :   }
     907                 :            : 
     908                 :         15 :   LocalImport local = impl_->import_local(id, decoded.generation, buffer);
     909                 :            : 
     910         [ +  + ]:         15 :   if (local != LocalImport::kMissing) {
     911                 :          7 :     return local == LocalImport::kImported;
     912                 :            :   }
     913                 :            : 
     914                 :          8 :   std::lock_guard stripe(impl_->stripe_of(id));
     915                 :          8 :   local = impl_->import_local(id, decoded.generation, buffer);
     916                 :            : 
     917         [ -  + ]:          8 :   if (local != LocalImport::kMissing) {
     918                 :          0 :     return local == LocalImport::kImported;
     919                 :            :   }
     920                 :            : 
     921                 :          8 :   PoolPtr<FastBufferResource> resource(pool_new<FastBufferResource>());
     922                 :          8 :   PoolPtr<FastBufferShare> share(pool_new<FastBufferShare>());
     923                 :            : 
     924   [ +  -  -  +  :          8 :   if VUNLIKELY (!resource || !share) {
                   -  + ]
     925                 :          0 :     return false;
     926                 :            :   }
     927                 :            : 
     928   [ +  +  -  +  :          8 :   if VUNLIKELY (!share->shared.attach(decoded.name) || share->shared.size() <= sizeof(FastBufferControl)) {
          +  -  +  -  +  
                      + ]
     929                 :          1 :     return false;
     930                 :            :   }
     931                 :            : 
     932                 :          7 :   share->control = static_cast<FastBufferControl*>(share->shared.data());
     933                 :          7 :   auto& control = *share->control;
     934                 :            : 
     935                 :          7 :   const uint64_t owner = control.owner.load(std::memory_order_acquire);
     936                 :            : 
     937   [ +  -  -  +  :         28 :   if VUNLIKELY (owner == 0 || control.protocol.load(std::memory_order_relaxed) != impl_->interface->get_protocol_id() ||
          +  -  -  +  +  
          -  -  +  +  -  
          -  +  +  -  -  
          +  +  -  -  +  
                   -  + ]
     938                 :            :                 control.pid_namespace.load(std::memory_order_relaxed) != impl_->pid_namespace ||
     939                 :            :                 control.time_namespace.load(std::memory_order_relaxed) != impl_->time_namespace || control.size == 0 ||
     940                 :            :                 control.descriptor_size == 0 ||
     941                 :            :                 control.descriptor_size > share->shared.size() - sizeof(FastBufferControl)) {
     942                 :          0 :     return false;
     943                 :            :   }
     944                 :            : 
     945         [ +  + ]:          7 :   if VUNLIKELY (!is_identity_alive(owner)) {
     946                 :          1 :     impl_->destroy_dead_owner(control, share->shared);
     947                 :          1 :     return false;
     948                 :            :   }
     949                 :            : 
     950                 :          6 :   size_t index = 0;
     951                 :            : 
     952         [ +  - ]:          6 :   for (; index < kFastBufferMaxReaders; ++index) {
     953                 :          6 :     uint64_t expected = 0;
     954                 :            : 
     955         [ +  - ]:         12 :     if (control.readers[index].compare_exchange_strong(expected, impl_->identity, std::memory_order_seq_cst)) {
     956                 :          6 :       break;
     957                 :            :     }
     958                 :            :   }
     959                 :            : 
     960         [ -  + ]:          6 :   if VUNLIKELY (index == kFastBufferMaxReaders) {
     961         [ #  # ]:          0 :     VLOG_E("FastBufferManager: Reader table of the shared allocation is full.");
     962                 :          0 :     return false;
     963                 :            :   }
     964                 :            : 
     965                 :          6 :   auto& reader = control.readers[index];
     966                 :            : 
     967         [ +  + ]:         12 :   if VUNLIKELY (control.generation.load(std::memory_order_seq_cst) != decoded.generation) {
     968                 :          1 :     reader.store(0, std::memory_order_release);
     969                 :          1 :     return false;
     970                 :            :   }
     971                 :            : 
     972                 :         10 :   const auto native_descriptor = Bytes::shallow_copy(reinterpret_cast<const uint8_t*>(share->control + 1),
     973                 :          5 :                                                      static_cast<size_t>(control.descriptor_size));
     974                 :            : 
     975         [ -  + ]:          5 :   if VUNLIKELY (!impl_->interface->import_handle(native_descriptor, resource->native)) {
     976                 :          0 :     reader.store(0, std::memory_order_release);
     977                 :          0 :     return false;
     978                 :            :   }
     979                 :            : 
     980         [ -  + ]:          5 :   if VUNLIKELY (resource->native.size != control.size) {
     981         [ #  # ]:          0 :     if (impl_->interface->release(resource->native)) {
     982                 :          0 :       reader.store(0, std::memory_order_release);
     983                 :            :     } else {
     984         [ #  # ]:          0 :       VLOG_E("FastBufferManager: Leaking rejected import after SDK release failure.");
     985                 :            :     }
     986                 :            : 
     987                 :          0 :     return false;
     988                 :            :   }
     989                 :            : 
     990                 :          5 :   share->id = id;
     991                 :          5 :   share->generation = decoded.generation;
     992                 :          5 :   share->reader = index;
     993                 :          5 :   resource->share.store(share.release(), std::memory_order_release);
     994                 :            : 
     995                 :            :   {
     996                 :          5 :     std::lock_guard registry(impl_->mutex);
     997                 :          5 :     impl_->resources.emplace(id, resource.get());
     998                 :          5 :   }
     999                 :            : 
    1000                 :          5 :   buffer = resource->native;
    1001                 :          5 :   buffer.handle = resource.release();
    1002                 :          5 :   return true;
    1003                 :            : #endif
    1004                 :          8 : }
    1005                 :            : 
    1006                 :          9 : FastBufferManager::FastBufferManager() : impl_(std::make_unique<Impl>()) {
    1007         [ +  - ]:          9 :   (void)MemoryPool::global_instance();
    1008                 :          9 :   impl_->identity = process_identity(Utils::get_pid());
    1009                 :          9 :   impl_->pid_namespace = namespace_inode("/proc/self/ns/pid");
    1010                 :          9 :   impl_->time_namespace = namespace_inode("/proc/self/ns/time");
    1011                 :            : 
    1012         [ -  + ]:          9 :   if VUNLIKELY (impl_->identity == 0) {
    1013   [ #  #  #  #  :          0 :     VLOG_W("FastBufferManager: Process identity is unavailable; descriptor sharing is disabled.");
                   #  # ]
    1014                 :            :   }
    1015                 :            : 
    1016   [ +  -  +  - ]:         18 :   const std::string plugin_name = Utils::get_env("VLINK_FASTBUFFER_PLUGIN");
    1017                 :            : 
    1018         [ +  + ]:          9 :   if (plugin_name.empty()) {
    1019         [ +  - ]:          2 :     impl_->interface = std::make_shared<HostBufferPlugin>();
    1020         [ +  + ]:          7 :   } else if (plugin_name == "shm") {
    1021                 :          6 :     impl_->interface =
    1022         [ +  - ]:         12 :         std::make_shared<SharedHostBufferPlugin>(impl_->identity, impl_->pid_namespace, impl_->time_namespace);
    1023                 :            :   } else {
    1024   [ +  -  +  -  :          1 :     impl_->interface = impl_->plugin.load<FastBufferPluginInterface>(plugin_name, 1, 0);
             +  -  +  - ]
    1025                 :            : 
    1026   [ -  +  -  -  :          1 :     if VUNLIKELY (impl_->interface && impl_->interface->get_protocol_id() == 0) {
                   -  + ]
    1027   [ #  #  #  #  :          0 :       VLOG_E("FastBufferManager: Plugin descriptor protocol ID must not be zero.");
                   #  # ]
    1028                 :          0 :       impl_->interface.reset();
    1029                 :            :     }
    1030                 :            :   }
    1031                 :            : 
    1032   [ +  +  +  -  :          9 :   if (impl_->interface && impl_->identity != 0) {
                   +  + ]
    1033                 :          8 :     sweep_segments(kFastBufferControlPrefix, [this](SysSharemem& shared) {
    1034         [ -  + ]:          3 :       if (shared.size() <= sizeof(FastBufferControl)) {
    1035                 :          0 :         return;
    1036                 :            :       }
    1037                 :            : 
    1038                 :          3 :       const auto& control = *static_cast<const FastBufferControl*>(shared.data());
    1039                 :          3 :       const uint64_t owner = control.owner.load(std::memory_order_acquire);
    1040                 :            : 
    1041         [ +  - ]:          6 :       if (owner != 0 && control.protocol.load(std::memory_order_relaxed) == impl_->interface->get_protocol_id() &&
    1042         [ +  - ]:          6 :           control.pid_namespace.load(std::memory_order_relaxed) == impl_->pid_namespace &&
    1043   [ +  -  +  -  :         12 :           control.time_namespace.load(std::memory_order_relaxed) == impl_->time_namespace &&
                   +  + ]
    1044         [ +  + ]:          3 :           !is_identity_alive(owner)) {
    1045                 :          1 :         impl_->destroy_dead_owner(control, shared);
    1046                 :            :       }
    1047                 :            :     });
    1048                 :            :   }
    1049                 :          9 : }
    1050                 :            : 
    1051                 :          9 : FastBufferManager::~FastBufferManager() {
    1052                 :            : #ifdef _WIN32
    1053                 :            :   if (Utils::is_terminating()) {
    1054                 :            :     (void)impl_.release();
    1055                 :            :     return;
    1056                 :            :   }
    1057                 :            : #endif
    1058                 :            : 
    1059                 :          9 :   impl_->reclaim_retired();
    1060                 :          9 :   auto* pending = impl_->retired.exchange(nullptr, std::memory_order_relaxed);
    1061                 :            : 
    1062         [ +  + ]:         10 :   while (pending) {
    1063                 :          1 :     auto* resource = pending;
    1064                 :          1 :     pending = resource->next;
    1065         [ +  - ]:          2 :     VLOG_W("FastBufferManager: Releasing a retired allocation still imported by a live process at shutdown.");
    1066                 :          1 :     impl_->free_resource(resource);
    1067                 :            :   }
    1068                 :            : 
    1069                 :          9 :   impl_->interface.reset();
    1070                 :          9 :   impl_->plugin.clear();
    1071                 :          9 : }
    1072                 :            : 
    1073                 :            : }  // namespace vlink

Generated by: LCOV version 1.14