Ok, I&#39;ve gone ahead and implemented what I think should be a channel-based memory pooling, which you can find here:<div><br></div><div><a href="https://github.com/alanxz/rabbitmq-c/tree/channel_owns_memory_pool">https://github.com/alanxz/rabbitmq-c/tree/channel_owns_memory_pool</a></div>
<div><br></div><div>Its not quite API/ABI backwards compatible, the change that breaks things being adding an extra field to the amqp_pool_t struct:</div><div><a href="https://github.com/alanxz/rabbitmq-c/commit/2bc1dc9e22152506cf07f21d10e6a79e7ed2ccc3#diff-2">https://github.com/alanxz/rabbitmq-c/commit/2bc1dc9e22152506cf07f21d10e6a79e7ed2ccc3#diff-2</a>
</div><div><br></div><div>This could be fixed easy enough, the trade-off being some complexity internally.</div><div><br></div><div>The change isn&#39;t a small patch, and should probably have a few more pairs of eyes look at it, despite having a few unit tests written for the�machinery�that moves the internal memory pools around.</div>
<div><br></div><div>For an�explanation�of what my implementation tries to do:</div><div><a href="https://github.com/alanxz/rabbitmq-c/commit/2bc1dc9e22152506cf07f21d10e6a79e7ed2ccc3#L5R119">https://github.com/alanxz/rabbitmq-c/commit/2bc1dc9e22152506cf07f21d10e6a79e7ed2ccc3#L5R119</a>
</div><div><br></div><div>I have no idea how this will scale, especially to things like embedded devices. �Hopefully I have not created a monster.</div><div><br></div><div>If the overarching idea looks sane to people, I will create a pull request and we can see about merging this in.</div>
<div><br></div><div>-Alan</div><div><br><br><div class="gmail_quote">On Fri, Jan 13, 2012 at 5:15 PM, Alexis Richardson <span dir="ltr">&lt;<a href="mailto:alexis@rabbitmq.com">alexis@rabbitmq.com</a>&gt;</span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Alan,<br>
<br>
I&#39;m afraid everyone has been distracted.<br>
<br>
Could anyone on the list opine?<br>
<span class="HOEnZb"><font color="#888888"><br>
alexis<br>
</font></span><div class="HOEnZb"><div class="h5"><br>
<br>
On Fri, Jan 13, 2012 at 9:19 PM, Alan Antonuk &lt;<a href="mailto:alan.antonuk@gmail.com">alan.antonuk@gmail.com</a>&gt; wrote:<br>
&gt; I haven&#39;t really had a huge amount of time to work on it since I sent the<br>
&gt; first message message, with the holidays and starting up a new project at<br>
&gt; work.<br>
&gt;<br>
&gt; Also I was hoping to get some input from the rabbitmq guys so that any<br>
&gt; changes I make would have a better chance of being accepted.<br>
&gt;<br>
&gt; Taking a quick look at what I wrote a month ago, today my plan would be to<br>
&gt; implement having a amqp_pool_t associated with each amqp_channel_t and see<br>
&gt; how far I could get with that.<br>
&gt;<br>
&gt; -Alan<br>
&gt;<br>
&gt; On Thu, Jan 12, 2012 at 11:39 AM, Alexis Richardson &lt;<a href="mailto:alexis@rabbitmq.com">alexis@rabbitmq.com</a>&gt;<br>
&gt; wrote:<br>
&gt;&gt;<br>
&gt;&gt; Alan<br>
&gt;&gt;<br>
&gt;&gt; It appears that you stunned everyone into silence ;-)<br>
&gt;&gt;<br>
&gt;&gt; How are you getting on with this?<br>
&gt;&gt;<br>
&gt;&gt; alexis<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; On Wed, Dec 14, 2011 at 8:15 PM, Alan Antonuk &lt;<a href="mailto:alan.antonuk@gmail.com">alan.antonuk@gmail.com</a>&gt;<br>
&gt;&gt; wrote:<br>
&gt;&gt; &gt; I&#39;ve been working on a c++ wrapper around rabbitmq-c that presents a<br>
&gt;&gt; &gt; &quot;humane&quot; API to the programmer heavily inspired by the Puka project.<br>
&gt;&gt; &gt; (see:�<a href="https://github.com/alanxz/SimpleAmqpClient" target="_blank">https://github.com/alanxz/SimpleAmqpClient</a>).<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; In developing this library I&#39;ve run across several limitations of the<br>
&gt;&gt; &gt; rabbitmq-c when working with multiple channels, the biggest issue being:<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; - There is no way to wait for a list of methods on a channel.<br>
&gt;&gt; &gt; There is amqp_simple_wait_method() - however this suffers from some<br>
&gt;&gt; &gt; serious<br>
&gt;&gt; &gt; drawbacks:<br>
&gt;&gt; &gt; + you can only specify one method to listen for<br>
&gt;&gt; &gt; + it calls abort() if a different method, or a method on a different<br>
&gt;&gt; &gt; channel<br>
&gt;&gt; &gt; is received<br>
&gt;&gt; &gt; A use case for this might be: doing a basic.publish, and you want to<br>
&gt;&gt; &gt; want to<br>
&gt;&gt; &gt; wait for a basic.ack or a basic.return on a channel with confirms<br>
&gt;&gt; &gt; enabled<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; The way I got around this in SimpleAmqpClient was to only use<br>
&gt;&gt; &gt; amqp_simple_wait_frame() and maintain queues of amqp_frame_t for each<br>
&gt;&gt; &gt; channel that I have open.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; However, this comes with one serious drawback: memory management. Each<br>
&gt;&gt; &gt; decoded frame is allocated in a connection-wide amqp_pool_t. �Because of<br>
&gt;&gt; &gt; this - it is impossible to recycle the pool and release memory unless<br>
&gt;&gt; &gt; you<br>
&gt;&gt; &gt; have dealt with all of your pending amqp_frame_t&#39;s. �This becomes a<br>
&gt;&gt; &gt; problem<br>
&gt;&gt; &gt; in�pathological�cases where you have two consumers sending<br>
&gt;&gt; &gt; simultaneously,<br>
&gt;&gt; &gt; you can get in the situation that even though the client program<br>
&gt;&gt; &gt; eventually<br>
&gt;&gt; &gt; deals with every consumed message, memory never gets released, because<br>
&gt;&gt; &gt; there<br>
&gt;&gt; &gt; is always at least one frame queued up.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; The above demonstrates the second biggest issue with the rabbitmq-c API:<br>
&gt;&gt; &gt; memory management when dealing with multiple channels. �There is no way<br>
&gt;&gt; &gt; to<br>
&gt;&gt; &gt; separate out memory allocation on, for example, a per-channel basis<br>
&gt;&gt; &gt; (with<br>
&gt;&gt; &gt; the library client keeping track of the memory pools used for example).<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Before I go on I&#39;d like to mention one feature that I find useful with<br>
&gt;&gt; &gt; the<br>
&gt;&gt; &gt; current API: it is possible to use something like select() before<br>
&gt;&gt; &gt; calling<br>
&gt;&gt; &gt; amqp_simple_wait_frame() to setup a timeout while waiting for a<br>
&gt;&gt; &gt; consumer,<br>
&gt;&gt; &gt; which is useful when developing single-threaded RPC apps.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; So now the interesting part: how could the API be improved?<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Some thoughts I&#39;ve had dealing with the memory management:<br>
&gt;&gt; &gt; 1. Create a amqp_simple_wait_frame() variant that as one of the<br>
&gt;&gt; &gt; parameters<br>
&gt;&gt; &gt; provides a callback to allocate memory, either something like a malloc()<br>
&gt;&gt; &gt; call, which the client would then be responsible for freeing, or perhaps<br>
&gt;&gt; &gt; something like get_amqp_pool(amqp_frame_t) which allows the client to<br>
&gt;&gt; &gt; return<br>
&gt;&gt; &gt; a memory pool which rabbitmq-c would use to allocate memory for that<br>
&gt;&gt; &gt; frame.<br>
&gt;&gt; &gt; �The amqp_frame_t would have to have some minimal amount of information<br>
&gt;&gt; &gt; filled in - such as frame type and channel to be useful.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; 2. amqp_channel_t becomes a struct containing both the the channel_id<br>
&gt;&gt; &gt; and a<br>
&gt;&gt; &gt; amqp_pool_t. �The amqp_pool would be used by the library to allocate<br>
&gt;&gt; &gt; frames<br>
&gt;&gt; &gt; received on that channel. �The client would then be responsible for<br>
&gt;&gt; &gt; calling<br>
&gt;&gt; &gt; amqp_pool_recycle at an appropriate point.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Some thoughts on improving the API to deal with multiple channels:<br>
&gt;&gt; &gt; 1. Add the following to the API:<br>
&gt;&gt; &gt; amqp_simple_wait_frame_on_channel - wait for a frame on a specified<br>
&gt;&gt; &gt; channel<br>
&gt;&gt; &gt; amqp_simple_wait_methods - wait for multiple methods on a specified<br>
&gt;&gt; &gt; channel.<br>
&gt;&gt; &gt; Don&#39;t abort() if the wrong method, or channel is received, instead queue<br>
&gt;&gt; &gt; up<br>
&gt;&gt; &gt; the frame as is done in amqp_simple_rpc<br>
&gt;&gt; &gt; amqp_frames_enqueued_for_channel to add feature parity with<br>
&gt;&gt; &gt; amqp_frames_enqueued<br>
&gt;&gt; &gt; I started to code this up at one point but abandoned it as it didn&#39;t<br>
&gt;&gt; &gt; support<br>
&gt;&gt; &gt; the interesting property of being able to use select() to specify a<br>
&gt;&gt; &gt; timeout<br>
&gt;&gt; &gt; when calling amqp_simple_wait_frame_on_channel. �At least not without<br>
&gt;&gt; &gt; adding<br>
&gt;&gt; &gt; timeout to the rabbitmq-c api, which didn&#39;t fit with how the current<br>
&gt;&gt; &gt; rabbitmq-c api. Here&#39;s the implementation I came up with before I<br>
&gt;&gt; &gt; abandoned<br>
&gt;&gt; &gt; it. I can&#39;t guarantee is completely free of<br>
&gt;&gt; &gt; bugs.�<a href="https://github.com/alanxz/rabbitmq-c/commits/simple_wait_frame/" target="_blank">https://github.com/alanxz/rabbitmq-c/commits/simple_wait_frame/</a><br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; 2. Continue to duck the issue and allow clients to write their own code<br>
&gt;&gt; &gt; to<br>
&gt;&gt; &gt; deal with multiple channels (fixing the memory management issue using<br>
&gt;&gt; &gt; one of<br>
&gt;&gt; &gt; the above techniques)<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; 3. Something I haven&#39;t thought of yet.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; So anyone have any thoughts on all of this?<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; -Alan<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; _______________________________________________<br>
&gt;&gt; &gt; rabbitmq-discuss mailing list<br>
&gt;&gt; &gt; <a href="mailto:rabbitmq-discuss@lists.rabbitmq.com">rabbitmq-discuss@lists.rabbitmq.com</a><br>
&gt;&gt; &gt; <a href="https://lists.rabbitmq.com/cgi-bin/mailman/listinfo/rabbitmq-discuss" target="_blank">https://lists.rabbitmq.com/cgi-bin/mailman/listinfo/rabbitmq-discuss</a><br>
&gt;&gt; &gt;<br>
&gt;<br>
&gt;<br>
</div></div></blockquote></div><br></div>