| Directory: | cvmfs/ |
|---|---|
| File: | cvmfs/bundle_mgr.h |
| Date: | 2026-07-26 02:35:14 |
| Exec | Total | Coverage | |
|---|---|---|---|
| Lines: | 27 | 28 | 96.4% |
| Branches: | 9 | 18 | 50.0% |
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * This file is part of the CernVM File System. | ||
| 3 | */ | ||
| 4 | |||
| 5 | #ifndef CVMFS_BUNDLE_MGR_H_ | ||
| 6 | #define CVMFS_BUNDLE_MGR_H_ | ||
| 7 | |||
| 8 | #include <limits.h> | ||
| 9 | #include <pthread.h> | ||
| 10 | |||
| 11 | #include <cstddef> | ||
| 12 | #include <memory> | ||
| 13 | #include <type_traits> | ||
| 14 | #include <vector> | ||
| 15 | |||
| 16 | #include "duplex_testing.h" | ||
| 17 | #include "file_bundle.h" | ||
| 18 | #include "mountpoint.h" | ||
| 19 | #include "shortstring.h" | ||
| 20 | #include "util/posix.h" | ||
| 21 | #include "util/single_copy.h" | ||
| 22 | |||
| 23 | class MockFetcher; | ||
| 24 | |||
| 25 | class BundleMgr : SingleCopy { | ||
| 26 | friend class T_BundleMgr; | ||
| 27 | FRIEND_TEST(T_BundleMgr, ExchangeCT); | ||
| 28 | FRIEND_TEST(T_BundleMgr, ExchangePathString); | ||
| 29 | FRIEND_TEST(T_BundleMgr, ReceivePathLongerThanPipeBuf); | ||
| 30 | FRIEND_TEST(T_BundleMgr, Fetch); | ||
| 31 | FRIEND_TEST(T_BundleMgr, FetchChunked); | ||
| 32 | |||
| 33 | public: | ||
| 34 | BundleMgr(MountPoint *mp, const PathString &path); | ||
| 35 | 760 | virtual ~BundleMgr() { | |
| 36 | 380 | JoinFetcherPool(); | |
| 37 | 380 | pthread_mutex_destroy(&worker_read_mutex_); | |
| 38 |
1/2✓ Branch 0 taken 190 times.
✗ Branch 1 not taken.
|
380 | delete bfm_; |
| 39 | 760 | } | |
| 40 | void Fetch(); | ||
| 41 | ✗ | explicit operator bool() const { return is_valid_; } | |
| 42 | |||
| 43 | private: | ||
| 44 | static void *MainBundleMgrFetcher(void *data); | ||
| 45 | void SpawnFetcherPool(); | ||
| 46 | void JoinFetcherPool(); | ||
| 47 | PathString ReceivePath(int fd) const; | ||
| 48 | bool TrySendPath(int fd, const PathString &path) const; | ||
| 49 | PathString NormalizeDependencyPath(const PathString &path) const; | ||
| 50 | |||
| 51 | void FetchPath(const PathString &path); | ||
| 52 | |||
| 53 | // CT stands for contiguous type | ||
| 54 | template<typename CT, | ||
| 55 | typename = typename std::enable_if< | ||
| 56 | std::is_trivially_copyable<CT>::value>::type> | ||
| 57 | 760 | void BlockingSend(int fd, const CT &obj, size_t size = sizeof(CT)) const { | |
| 58 | typedef typename std::remove_cv<CT>::type T; | ||
| 59 | static_assert( | ||
| 60 | std::is_trivially_copyable<T>::value, | ||
| 61 | "Can't directly send non trivially copyable types over a pipe"); | ||
| 62 | static_assert(sizeof(T) == sizeof(CT), "CT illformed"); | ||
| 63 | static_assert( | ||
| 64 | sizeof(T) <= PIPE_BUF, | ||
| 65 | "Type too big to be guaranteed atomic transmission over a pipe"); | ||
| 66 | |||
| 67 | 760 | const T *ptr = reinterpret_cast<const T *>(&obj); | |
| 68 | 760 | WritePipe(fd, ptr, size); | |
| 69 | 760 | } | |
| 70 | |||
| 71 | 304 | void BlockingSend(int fd, const PathString &path) const { | |
| 72 | 304 | const size_t size = path.GetLength(); | |
| 73 |
1/2✓ Branch 1 taken 304 times.
✗ Branch 2 not taken.
|
304 | BlockingSend(fd, size); |
| 74 |
1/2✓ Branch 2 taken 304 times.
✗ Branch 3 not taken.
|
304 | WritePipe(fd, path.GetChars(), size * sizeof(char)); |
| 75 | 304 | } | |
| 76 | |||
| 77 | 38 | void BlockingSend(int fd, const std::string &string) const { | |
| 78 | 38 | const size_t size = string.size(); | |
| 79 |
1/2✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
|
38 | BlockingSend(fd, size); |
| 80 |
1/2✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
|
38 | WritePipe(fd, string.data(), size * sizeof(char)); |
| 81 | 38 | } | |
| 82 | |||
| 83 | template<typename CT, | ||
| 84 | typename = typename std::enable_if< | ||
| 85 | std::is_trivially_copyable<CT>::value>::type> | ||
| 86 | 760 | CT BlockingReceive(int fd) const { | |
| 87 | typedef typename std::remove_cv<CT>::type T; | ||
| 88 | static_assert( | ||
| 89 | sizeof(T) <= PIPE_BUF, | ||
| 90 | "Type too big to be guaranteed atomic transmission over a pipe"); | ||
| 91 | CT item; | ||
| 92 |
1/2✓ Branch 1 taken 380 times.
✗ Branch 2 not taken.
|
760 | ReadPipe(fd, static_cast<void *>(&item), sizeof(CT)); |
| 93 | 760 | return item; | |
| 94 | } | ||
| 95 | |||
| 96 | 342 | std::string BlockingReceive(int fd) const { | |
| 97 | 342 | const size_t size = BlockingReceive<size_t>(fd); | |
| 98 |
1/2✓ Branch 2 taken 342 times.
✗ Branch 3 not taken.
|
342 | std::string result(size, '\t'); |
| 99 |
2/4✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 342 times.
✗ Branch 5 not taken.
|
342 | ReadPipe(fd, static_cast<void *>(&result[0]), size * sizeof(char)); |
| 100 | 342 | return result; | |
| 101 | } | ||
| 102 | |||
| 103 | MountPoint *mount_point_; | ||
| 104 | PathString path_; | ||
| 105 | NameString fname_; | ||
| 106 | PathString parent_path_; | ||
| 107 | |||
| 108 | // The file that contains the dependences | ||
| 109 | PathString bundle_file_path_; | ||
| 110 | BundleFileMgr *bfm_; | ||
| 111 | |||
| 112 | // Pool of fetcher threads. All workers share pipe_bm_[0] (read end) | ||
| 113 | // and serialize their reads via worker_read_mutex_ so cmd+payload | ||
| 114 | // pairs are received atomically. | ||
| 115 | std::vector<std::unique_ptr<pthread_t> > fetcher_threads_; | ||
| 116 | pthread_mutex_t worker_read_mutex_; | ||
| 117 | size_t pool_size_; | ||
| 118 | int back_channel_; | ||
| 119 | |||
| 120 | enum class Command { | ||
| 121 | kTerminate, | ||
| 122 | kFetch | ||
| 123 | }; | ||
| 124 | |||
| 125 | /** | ||
| 126 | * Work queue (a pipe). Main thread writes Command + path payload to | ||
| 127 | * pipe_bm_[1]; workers read from pipe_bm_[0] under worker_read_mutex_. | ||
| 128 | */ | ||
| 129 | int pipe_bm_[2]; | ||
| 130 | bool is_valid_ = true; | ||
| 131 | }; | ||
| 132 | #endif // CVMFS_BUNDLE_MGR_H_ | ||
| 133 | |||
| 134 |