| // Copyright 2022 The Centipede Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef THIRD_PARTY_CENTIPEDE_SHARED_MEMORY_BLOB_SEQUENCE_H_ |
| #define THIRD_PARTY_CENTIPEDE_SHARED_MEMORY_BLOB_SEQUENCE_H_ |
| |
| #include <climits> |
| #include <cstddef> |
| #include <cstdint> |
| #include <type_traits> |
| |
| // This library must not depend on anything other than libc, |
| // so that it does not introduce any dependencies to its users. |
| // Any such dependencies may get coverage-instrumented, introducing noise |
| // into coverage reporting. |
| // Small exceptions for header-only parts of STL may be possible. |
| |
| namespace centipede { |
| |
| // Simple TLV (tag-length-value) data structure. |
| // Blob does not own the memory in `data`, just references it. |
| // `size` is the number of bytes in `data`. |
| // A blob with zero tag is considered invalid. |
| // A blob with zero size and non-zero tag is valid but this contradicts |
| // the current use. |
| // TODO(kcc): [impl] replace uses of (blob.size == 0) with (!blob.IsValid()). |
| // TODO(kcc): [impl] consider making it a class. |
| struct Blob { |
| using SizeAndTagT = size_t; |
| Blob(SizeAndTagT tag, SizeAndTagT size, const uint8_t *data) |
| : tag(tag), size(size), data(data) {} |
| Blob() = default; // Construct an invalid Blob. |
| bool IsValid() const { return tag != 0; } |
| |
| const SizeAndTagT tag = 0; |
| const SizeAndTagT size = 0; |
| const uint8_t *data = nullptr; |
| }; |
| |
| // The BlobSequence is a consecutive sequence of Blobs. |
| class BlobSequence { |
| public: |
| // Creates a new blob sequence of arbitrary `data` with given `size` in bytes, |
| // must be >= 8. Aborts on any failure. The amount of actual data that can be |
| // written is slightly less. |
| explicit BlobSequence(uint8_t *data, size_t size); |
| |
| // Writes the contents of `blob` to the blob sequence. |
| // Returns true on success. |
| // Returns false when the blob sequence is full. |
| // A failed Write does not change the internal state. |
| // Must not be called after Read() w/o first calling Reset(). |
| bool Write(Blob blob); |
| |
| // Writes `tag`/`value` as a blob. `T` should be a POD. |
| // Returns true on success. |
| template <typename T> |
| bool Write(Blob::SizeAndTagT tag, T value) { |
| static_assert(std::is_pod_v<T>, "T must be a POD"); |
| return Write( |
| {tag, sizeof(value), reinterpret_cast<const uint8_t *>(&value)}); |
| } |
| |
| // Reads the next blob from the sequence. |
| // If no more blobs are left, returns a blob with size = 0. |
| // Must not be called after Write() w/o first calling Reset(). |
| Blob Read(); |
| |
| // Resets the internal state, allowing to read from or write to |
| // starting from the beginning of the blob sequence. |
| void Reset(); |
| |
| // The position after last Write (or last Read). |
| size_t offset() const { return offset_; } |
| |
| protected: |
| // Default constructor to create an empty blob sequence. It is used by the |
| // constructors of child classes. |
| explicit BlobSequence() = default; |
| |
| // data_ contains a sequence of {size, payload} pairs, |
| // where size is 8 bytes and payload is size bytes. |
| // After writing a blob, we also write 0 in place of the next blob's size, |
| // if there is space left so that to overwrite any stale data left there. |
| uint8_t *data_ = nullptr; |
| size_t size_ = 0; |
| |
| private: |
| // offset_ points to the position in data_ after last Write (or last Read). |
| size_t offset_ = 0; |
| bool had_reads_after_reset_ = false; |
| bool had_writes_after_reset_ = false; |
| }; |
| |
| // SharedMemoryBlobSequence: |
| // enables inter-process communication via shared memory. |
| // |
| // It allows one process to write some data, then another process to read it. |
| // SharedMemoryBlobSequence is thread-compatible. |
| // It does not perform any inter-process synchronization itself, but relies on |
| // external synchronization e.g. via process fork/join or semaphores. |
| // |
| // Typical usage is to create a SharedMemoryBlobSequence in one process and then |
| // open SharedMemoryBlobSequence with the same name in another process. |
| // But it can be done in the same process too. |
| // |
| // Usage example: |
| // void ParentProcess() { |
| // // Create a new blob sequence. |
| // SharedMemoryBlobSequence parent("/foo", 1000); |
| // |
| // // Parent process writes some data to the shared blob: |
| // parent.Write({some_data, some_data_size}); |
| // parent.Write({some_other_data, some_other_data_size}); |
| // |
| // // Run the child process. |
| // ExecuteChildProcessAndWaitUntilItIsDone(); |
| // } |
| // |
| // void Child() { |
| // // Open an existing blob sequence. |
| // SharedMemoryBlobSequence child("/foo"); |
| // |
| // // Read the data written by parent. |
| // while (true) { |
| // auto blob = parent.Read(); |
| // if (!blob.size) break; |
| // Use({blob.data, blob.size}); |
| // } |
| // } |
| // |
| class SharedMemoryBlobSequence : public BlobSequence { |
| public: |
| // Creates a new shared blob sequence with `name` (for debugging only, not an |
| // actual path). Aborts on any failure. `size` is the size of the shared |
| // memory region in bytes, must be >= 8. The amount of actual data that can be |
| // written is slightly less. |
| // The `use_posix_shmem` argument specifies which API to use to allocate the |
| // shared memory. When true, shm_open(2) will be used, otherwise |
| // memfd_create(2). |
| SharedMemoryBlobSequence(const char *name, size_t size, bool use_posix_shmem); |
| |
| // Opens an existing shared blob sequence with the file `path`. |
| // Aborts on any failure. |
| explicit SharedMemoryBlobSequence(const char *path); |
| |
| // Releases all resources. |
| ~SharedMemoryBlobSequence(); |
| |
| // Releases shared memory used by `this`. |
| void ReleaseSharedMemory(); |
| |
| // Returns the number of bytes used by the shared mapping. |
| // It will be zero just after creation and after the call to |
| // ReleaseSharedMemory(). |
| size_t NumBytesUsed() const; |
| |
| // Gets the file path that can be used to create new instances. |
| const char *path() const { return path_; } |
| |
| private: |
| // mmaps `size_` bytes from `fd_`, assigns to `data_`. Crashes if mmap failed. |
| void MmapData(); |
| |
| // Will be initialized as a generated internal path or a copy of `path` |
| // passed in. |
| char path_[PATH_MAX] = {0}; |
| int fd_ = -1; // file descriptor used to mmap the shared memory region. |
| // Whether the file pointed to by path_ is owned by this and needs to be |
| // deallocated on destruction. |
| bool path_is_owned_ = false; |
| }; |
| |
| } // namespace centipede |
| |
| #endif // THIRD_PARTY_CENTIPEDE_SHARED_MEMORY_BLOB_SEQUENCE_H_ |