@@ -47,8 +47,21 @@ namespace asio
4747namespace messages
4848{
4949
50- // TODO: tidy this up and reuse it in TcpConnection and in MessageHandler.
51- // Add proper doxygen comments!
50+ /* !
51+ * \brief Reusable message pool class.
52+ *
53+ * The MessageHandler class now uses this specialised to our MessageHeader.
54+ * This pool is smart, if it cannot acquire a message from the pool, either
55+ * because there are no free messages or the required message size is too bg
56+ * then it falls back to dynamic allocation.
57+ *
58+ * The really nice thing is the pool messages release themselves pack to the
59+ * pool once the last instance of the shared_ptr to the message goes out of
60+ * scope. Dynamically allocated "fallback" messages self-destruct as normal
61+ * through the shared_ptr's default destruction logic.
62+ *
63+ * Header must be default constructable.
64+ */
5265template <typename Header>
5366class ReceivedMessagePool : public std ::enable_shared_from_this<ReceivedMessagePool<Header>>
5467{
@@ -59,6 +72,19 @@ class ReceivedMessagePool : public std::enable_shared_from_this<ReceivedMessageP
5972 using msg_t = defs::ReceivedMessage<Header>;
6073 using msg_sh_ptr_t = std::shared_ptr<msg_t >;
6174
75+ #ifdef USE_DEFAULT_CONSTRUCTOR_
76+ /* ! \brief Default constructor. */
77+ ReceivedMessagePool () {}
78+ #else
79+ /* ! \brief Default constructor. */
80+ ReceivedMessagePool () = default;
81+ #endif
82+
83+ /* !
84+ * \brief Initialisation constructor.
85+ * \param[in] poolSize - How many messages to have in the pool.
86+ * \param[in] reservedBodySize - The maximum size allowed per message.
87+ */
6288 ReceivedMessagePool (size_t poolSize, size_t reservedBodySize)
6389 : m_pool(poolSize)
6490 , m_reservedBodySize(reservedBodySize)
@@ -71,20 +97,45 @@ class ReceivedMessagePool : public std::enable_shared_from_this<ReceivedMessageP
7197 }
7298 }
7399
100+ /* ! \brief Default destructor. */
101+ ~ReceivedMessagePool () = default ;
102+ /* ! \brief Deleted copy constructor. */
103+ ReceivedMessagePool (const ReceivedMessagePool&) = delete ;
104+ /* ! \brief Deleted copy assignment operator. */
105+ ReceivedMessagePool& operator =(const ReceivedMessagePool&) = delete ;
106+
107+ /* !
108+ * \brief Get the pool size
109+ * \return Return the number of messages in the pool.
110+ */
74111 size_t PoolSize () const
75112 {
76113 STD_SHARED_LOCK (m_mutex);
77114 return m_pool.size ();
78115 }
79116
117+ /* !
118+ * \brief Get the reserved message size.
119+ * \return Return the number of bytes reserved per message.
120+ */
80121 size_t ReservedBodySize () const
81122 {
82123 return m_reservedBodySize;
83124 }
84125
85- msg_sh_ptr_t Acquire (std::size_t requiredBodySize)
126+ /* !
127+ * \brief Acquire a message from the pool.
128+ * \param[in] requiredBodySize - The amount of bytes needed to be reserved in the returned message.
129+ * \return Returns a shared_ptr to a pool message.
130+ *
131+ * If the requiredBodySize is larger than ReservedBodySize() or the pool is
132+ * currently exhausted then it returns a dynamically allocated message instead.
133+ */
134+ msg_sh_ptr_t Acquire (size_t requiredBodySize)
86135 {
87- if (requiredBodySize <= m_reservedBodySize)
136+ auto actualReservedBodySize = ReservedBodySize ();
137+
138+ if (requiredBodySize <= actualReservedBodySize)
88139 {
89140 STD_UNIQUE_LOCK (m_mutex);
90141
@@ -112,13 +163,23 @@ class ReceivedMessagePool : public std::enable_shared_from_this<ReceivedMessageP
112163 }
113164
114165private:
166+ /* !
167+ * \brief Make a message dynamically.
168+ * \param[in] requiredBodySize - The amount of bytes needed to be reserved in the returned message.
169+ * \return Returns a shared_ptr to a dynamically allocated message.
170+ */
115171 msg_sh_ptr_t MakeDynamic (size_t requiredBodySize)
116172 {
117173 auto msg = std::make_shared<msg_t >();
118174 msg->body .reserve (requiredBodySize);
119175 return msg;
120176 }
121177
178+ /* !
179+ * \brief Release a message back to the pool for re-use.
180+ * \param[in] index - The pool index to make available again.
181+ * \param[in] msg - The message being released to the pool.
182+ */
122183 void ReleaseToPool (size_t index, msg_t * msg)
123184 {
124185 // Guard against null pointer shenanigans.
@@ -130,15 +191,17 @@ class ReceivedMessagePool : public std::enable_shared_from_this<ReceivedMessageP
130191 msg->header = Header{};
131192 msg->body .clear ();
132193
194+ auto actualReservedBodySize = ReservedBodySize ();
195+
133196 // Shrink oversized buffers before reusing.
134197 //
135198 // This should never get called unless someone tampered with the
136199 // message body after acquiring from the pool, but we can be
137- // defensive here.
138- if (msg->body .capacity () > m_reservedBodySize )
200+ // defensive here anyway, best to be safe .
201+ if (msg->body .capacity () > actualReservedBodySize )
139202 {
140203 defs::char_buffer_t tmp;
141- tmp.reserve (m_reservedBodySize );
204+ tmp.reserve (actualReservedBodySize );
142205 msg->body .swap (tmp);
143206 }
144207
@@ -150,10 +213,15 @@ class ReceivedMessagePool : public std::enable_shared_from_this<ReceivedMessageP
150213 }
151214
152215private:
216+ /* ! \brief Shared mutex. */
153217 mutable std::shared_mutex m_mutex;
218+ /* ! \brief Message pointer definition. */
154219 using msg_ptr_t = std::unique_ptr<msg_t >;
220+ /* ! \brief The actual message pool */
155221 std::vector<msg_ptr_t > m_pool;
222+ /* ! \brief Deque to track available pool indices. */
156223 std::deque<size_t > m_available;
224+ /* ! \brief The reserved size in bytes for pool messages. */
157225 size_t m_reservedBodySize{0 };
158226};
159227
@@ -166,6 +234,7 @@ class ReceivedMessagePool : public std::enable_shared_from_this<ReceivedMessageP
166234 */
167235class CORE_LIBRARY_DLL_SHARED_API MessageHandler final
168236{
237+ /* ! \brief Specialisation of the message pool. */
169238 using msg_pool_t = ReceivedMessagePool<defs::MessageHeader>;
170239
171240public:
0 commit comments