public TaskSlim<MessageDelivery> Call(string exchange, string routing, BasicProperties properties, ArraySegment<byte> buffer)
		{
			_semaphoreSlim.Wait();

			var task = _taskResultPool.GetObject();

			uint correlationId;
			if (!SecureSpotAndUniqueCorrelationId(task, out correlationId))
			{
				_semaphoreSlim.Release();

				Console.WriteLine("max calls reached!");
				task.SetException(new Exception("reached max calls"));
				return task;
			}

			task.Id = correlationId; // so we can confirm we have the right instance later
			task.Started = DateTime.Now.Ticks;

			try
			{
				var prop = properties ?? _channel.RentBasicProperties();
				prop.CorrelationId = correlationId.ToString();
				prop.ReplyTo = _replyQueueName.Name;
				// TODO: confirm this doesnt cause more overhead to rabbitmq
				prop.Expiration = _timeoutInMs.ToString();

				_channel.BasicPublishFast(exchange, routing, true, false, properties, buffer);
			}
			catch (Exception ex)
			{
				// release spot
				Interlocked.Exchange(ref _pendingCalls[correlationId % _maxConcurrentCalls], null);

				_semaphoreSlim.Release();

				task.SetException(ex);
			}

			return task;
		}
Exemple #2
0
        internal void DispatchDeliveredMessage(
            string consumerTag, ulong deliveryTag, bool redelivered,
            string exchange, string routingKey, int bodySize,
            BasicProperties properties, BaseLightStream lightStream)
        {
            BasicConsumerSubscriptionInfo consumer;

            if (!_consumerSubscriptions.TryGetValue(consumerTag, out consumer))
            {
                // received msg but nobody was subscribed to get it (?)
                LogAdapter.LogWarn(LogSource, "Received message without a matching subscription. Discarding. " +
                                   "Exchange: " + exchange + " routing: " + routingKey +
                                   " consumer tag: " + consumerTag + " and channel " + this.ChannelNumber);

                // Ensure moved cursor ahead
                var marker = new RingBufferPositionMarker(lightStream);
                marker.EnsureConsumed(bodySize);
                return;
            }

            var delivery = new MessageDelivery
            {
                bodySize    = bodySize,
                properties  = properties,
                routingKey  = routingKey,
                deliveryTag = deliveryTag + this._deliveryTagOffset,                 // adds tag offset
                redelivered = redelivered,
            };

            var mode             = consumer.Mode;
            var cb               = consumer.Callback;
            var consumerInstance = consumer._consumer;

            if (mode == ConsumeMode.SingleThreaded)
            {
                // run with scissors, we're letting
                // the user code mess with the ring buffer in the name of performance
                delivery.stream = bodySize == 0 ? EmptyStream : lightStream;

                // upon return it's assumed the user has consumed from the stream and is done with it
                var marker = new RingBufferPositionMarker(lightStream);

                try
                {
                    if (cb != null)
                    {
                        cb(delivery).GetAwaiter().GetResult();
                    }
                    else
                    {
                        consumerInstance.Consume(delivery).GetAwaiter().GetResult();
                    }
                }
                finally
                {
                    if (!delivery.TakenOver)
                    {
                        delivery.Dispose();
                    }

                    // fingers crossed the user cloned the buffer if she needs it later
                    marker.EnsureConsumed(bodySize);
                }
            }
            else
            {
                // parallel mode. it cannot hold the frame handler, so we copy the buffer (yuck) and move forward

                if (mode == ConsumeMode.ParallelWithBufferCopy ||
                    mode == ConsumeMode.SerializedWithBufferCopy)
                {
                    delivery.stream = delivery.bodySize == 0
                                                ? EmptyStream
                                                : lightStream.CloneStream(bodySize);
                }

                if (mode == ConsumeMode.SerializedWithBufferCopy)                 // Posts to a thread
                {
                    if (consumer._consumerThread == null)
                    {
                        // Initialization. safe since this delivery call always happen from the same thread
                        consumer._receivedMessages   = new ConcurrentQueue <MessageDelivery>();
                        consumer._messageAvailableEv = new AutoResetEvent(false);
                        consumer._consumerThread     = ThreadFactory.BackgroundThread(SerializedDelivery, "Delivery_" + consumer.ConsumerTag,
                                                                                      consumer);
                    }

                    consumer._receivedMessages.Enqueue(delivery);
                    consumer._messageAvailableEv.Set();
                }
                else if (mode == ConsumeMode.ParallelWithBufferCopy)                 // Posts to TaskScheduler
                {
                    new Task(async state =>
                    {
                        var tuple       = (Tuple <MessageDelivery, Func <MessageDelivery, Task>, IQueueConsumer, Channel>)state;
                        var delivery1   = tuple.Item1;
                        var cb1         = tuple.Item2;
                        var conInstance = tuple.Item3;

                        try
                        {
                            if (cb1 != null)
                            {
                                await cb1(delivery1).ConfigureAwait(false);
                            }
                            else
                            {
                                await conInstance.Consume(delivery1).ConfigureAwait(false);
                            }
                        }
                        catch (Exception e)
                        {
                            if (LogAdapter.IsErrorEnabled)
                            {
                                LogAdapter.LogError(LogSource, "Error processing message (user code)", e);
                            }
                        }
                        finally
                        {
                            if (!delivery1.TakenOver)
                            {
                                delivery1.Dispose();
                            }
                        }
                    }, Tuple.Create(delivery, cb, consumerInstance, this))                     // tuple avoids the closure capture
                    // Start task in the given scheduler
                    .Start(_schedulerToDeliverMessages);
                }
            }
        }
 public static TaskSlim BasicPublish(this Channel source,
                                     string exchange, string routingKey, bool mandatory, bool immediate,
                                     BasicProperties properties, byte[] buffer)
 {
     return(source.BasicPublish(exchange, routingKey, mandatory, immediate, properties, new ArraySegment <byte>(buffer)));
 }
 public static void BasicPublishFast(this Channel source,
                                     string exchange, string routingKey, bool mandatory,
                                     BasicProperties properties, byte[] buffer)
 {
     source.BasicPublishFast(exchange, routingKey, mandatory, false, properties, new ArraySegment <byte>(buffer));
 }
//		private static async Task StartRpc()
//		{
//			Connection conn1 = null;
//			Connection conn2 = null;
//			try
//			{
//				conn1 = await new ConnectionFactory().Connect(TargetHost, vhost: VHost, username: _username, password: _password);
//				conn2 = await new ConnectionFactory().Connect(TargetHost, vhost: VHost, username: _username, password: _password);
//
//				Console.WriteLine("[Connected]");
//
//				var newChannel = await conn1.CreateChannel();
//				Console.WriteLine("[channel created] " + newChannel.ChannelNumber);
//				await newChannel.BasicQos(0, Prefetch, false);
//
//				await newChannel.ExchangeDeclare("test_ex", "direct", true, false, null, true);
//
//				var qInfo = await newChannel.QueueDeclare("rpc1", false, true, false, false, null, true);
//
//				Console.WriteLine("[qInfo] " + qInfo);
//
//				await newChannel.QueueBind("rpc1", "test_ex", "rpc1", null, true);
//
//				var temp = new byte[1000];
//
//				Console.WriteLine("Starting Rpc channel consumer...");
//				await newChannel.BasicConsume(ConsumeMode.SingleThreaded, delivery =>
//				{
//					if (delivery.stream != null)
//						delivery.stream.Read(temp, 0, (int) delivery.bodySize);
////					else 
////						temp = delivery.Body;
//
//					var replyProp = new BasicProperties()
//					{
//						CorrelationId = delivery.properties.CorrelationId
//					};
//
//					newChannel.BasicPublishFast("", 
//						delivery.properties.ReplyTo, false, false, 
//						replyProp, new ArraySegment<byte>(temp, 0, 4));
//
//					return Task.CompletedTask;
//
//				}, "rpc1", "", true, false, null, waitConfirmation: true);
//
//				Console.WriteLine("Starting Rpc calls...");
//
//				var newChannel2 = await conn2.CreateChannel();
//				Console.WriteLine("[channel created] " + newChannel2.ChannelNumber);
//				await newChannel2.BasicQos(0, Prefetch, false);
//
//				var rpcHelper = await newChannel2.CreateRpcHelper(ConsumeMode.SingleThreaded);
//
//				var watch = new Stopwatch();
//				watch.Start();
//
//				var totalReceived = 0;
//				var req = new byte[100];
//				var reply = new byte[100];
//				for (int i = 0; i < TotalPublish; i++)
//				{
//					var prop2 = new BasicProperties();
//					req[3] = (byte)((i & 0xFF000000) >> 24);
//					req[2] = (byte)((i & 0x00FF0000) >> 16);
//					req[1] = (byte)((i & 0x0000FF00) >> 8);
//					req[0] = (byte)((i & 0x000000FF));
//
//					var rpcCallResult = await rpcHelper.Call("test_ex", "rpc1", prop2, new ArraySegment<byte>(req, 0, 4));
//					if (rpcCallResult.stream != null)
//					{
//						rpcCallResult.stream.Read(reply, 0, rpcCallResult.bodySize);
//						var x = BitConverter.ToInt32(reply, 0);
//						if (x != i) throw new Exception("Invalid result for call");
//					}
////					else if (rpcCallResult.Body != null)
////					{
////						var x = BitConverter.ToInt32(rpcCallResult.Body, 0);
////						if (x != i) throw new Exception("Invalid result for call");
////					}
//
//					// var rpcCallResult2 = await rpcHelper.Call("test_ex", "rpc1", prop2, new ArraySegment<byte>(req, 0, 4));
//
//					var val = Interlocked.Increment(ref totalReceived);
//
//					if (val == TotalPublish)
//					{
//						watch.Stop();
//						Console.WriteLine("Rpc stress. Took " +
//										  watch.Elapsed.TotalMilliseconds +
//										  "ms - rate of " + (TotalPublish / watch.Elapsed.TotalSeconds) + " message per second");
//						totalReceived = 0;
//					}
//				}
//
////				watch = Stopwatch.StartNew();
////				int totalReceived = 0;
//
////				Console.WriteLine("[subscribed to queue] " + sub);
//
//				await Task.Delay(TimeSpan.FromMinutes(5));
//
//				await newChannel.Close();
//				await newChannel2.Close();
//			}
//			catch (AggregateException ex)
//			{
//				Console.WriteLine("[Captured error] " + ex.Flatten().Message);
//			}
//			catch (Exception ex)
//			{
//				Console.WriteLine("[Captured error 2] " + ex.Message);
//			}
//
//			if (conn1 != null)
//				await conn1.Close();
//			if (conn2 != null)
//				await conn2.Close();
//		}

		private static async Task Start()
		{
			Connection conn = null;
			try
			{
				conn = await new ConnectionFactory().Connect(TargetHost, vhost: VHost, username: _username, password: _password);

				Console.WriteLine("[Connected]");

				var newChannel = await conn.CreateChannel();
//				var newChannel = await conn.CreateChannelWithPublishConfirmation();
				Console.WriteLine("[channel created] " + newChannel.ChannelNumber);
				await newChannel.BasicQos(0, Prefetch, false);

				await newChannel.ExchangeDeclare("test_ex", "direct", true, false, null, true);

				var qInfo = await newChannel.QueueDeclare("queue1", false, true, false, false, null, true);

				Console.WriteLine("[qInfo] " + qInfo);

				await newChannel.QueueBind("queue1", "test_ex", "routing1", null, true);

				var prop = new BasicProperties()
				{
					// DeliveryMode = 2,
					Type = "type1",
					Headers = new Dictionary<string, object> {{"serialization", 0}}
				};

				newChannel.MessageUndeliveredHandler = (undelivered) =>
				{
					Console.WriteLine("\t(Ops, message not routed: " + 
						undelivered.replyCode + " " + 
						undelivered.replyText + " " + 
						undelivered.routingKey + ")");

					return Task.CompletedTask;
				};

				Console.WriteLine("Started Publishing...");

				var watch = Stopwatch.StartNew();
				for (int i = 0; i < TotalPublish; i++)
				{
					prop.Headers["serialization"] = i;
					// var buffer = Encoding.ASCII.GetBytes("The " + i + " " + Message);
					await newChannel.BasicPublish("test_ex", "routing1", true, false, prop, new ArraySegment<byte>(MessageContent));

					// await Task.Delay(TimeSpan.FromMilliseconds(100));
				}
				watch.Stop();

				Console.WriteLine(DateTime.Now.TimeOfDay.TotalSeconds + " BasicPublish stress. Took " + watch.Elapsed.TotalMilliseconds + 
								  "ms - rate of " + (TotalPublish / watch.Elapsed.TotalSeconds) + " message per second");

				await Task.Delay(TimeSpan.FromMilliseconds(200));

				var newChannel2 = await conn.CreateChannel();
				Console.WriteLine("[channel created] " + newChannel2.ChannelNumber);
				await newChannel2.BasicQos(0, Prefetch, false);
 
				var temp = new byte[1000000];

				watch = Stopwatch.StartNew();
				int totalReceived = 0;

				Console.WriteLine("[subscribing to queue] ");
				var sub = await newChannel2.BasicConsume(ConsumeMode.SingleThreaded, async (delivery) =>
				{
					// var len = await delivery.stream.ReadAsync(temp, 0, (int) delivery.bodySize);
					// var str = Encoding.UTF8.GetString(temp, 0, len);
					// Console.WriteLine("Received : " + str.Length);

					if (WithAcks)
					{
						if (totalReceived % 2 == 0)
							newChannel2.BasicAck(delivery.deliveryTag, false);
						else
							newChannel2.BasicNAck(delivery.deliveryTag, false, false);
					}

					var val = Interlocked.Increment(ref totalReceived);

					if (val == TotalPublish)
					{
						watch.Stop();
						Console.WriteLine("Consume stress. Took " + 
										  watch.Elapsed.TotalMilliseconds + 
										  "ms - rate of " + (TotalPublish / watch.Elapsed.TotalSeconds) + " message per second");
						totalReceived = 0;
					}

					// return Task.CompletedTask;

				}, "queue1", "tag123", !WithAcks, false, null, true);

				Console.WriteLine("[subscribed to queue] " + sub);

				await Task.Delay(TimeSpan.FromMinutes(5));

				await newChannel.Close();
				await newChannel2.Close();
			}
			catch (AggregateException ex)
			{
				Console.WriteLine("[Captured error] " + ex.Flatten().Message);
			}
			catch (Exception ex)
			{
				Console.WriteLine("[Captured error 2] " + ex.Message);
			}

			if (conn != null)
				conn.Dispose();
		}
//		private static async Task StartConcurrentRpc()
//		{
//			Connection conn2 = null;
//			try
//			{
//				conn2 = await new ConnectionFactory().Connect(TargetHost,
//					vhost: VHost, username: _username, password: _password);
//
//				Console.WriteLine("[Connected]");
//
//				Console.WriteLine("Starting Rpc Parallel calls...");
//
//				var newChannel2 = await conn2.CreateChannel();
//				Console.WriteLine("[channel created] " + newChannel2.ChannelNumber);
//				await newChannel2.BasicQos(0, Prefetch, false);
//
//				var rpcHelper = await newChannel2.CreateRpcHelper(ConsumeMode.ParallelWithBufferCopy);
//
//				var watch = new Stopwatch();
//				watch.Start();
//
//				var totalReceived = 0;
//
//				
//				var tasks = new Task[ConcurrentCalls];
//
//				for (int i = 0; i < TotalPublish; i += ConcurrentCalls)
//				{
//					for (int j = 0; j < ConcurrentCalls; j++)
//					{
//						var t = MakeCall(rpcHelper, i + j);
//						tasks[j] = t;
//					}
//
//					Task.WaitAll(tasks);
//
//					totalReceived += ConcurrentCalls;
//
////					Console.WriteLine("calls " + totalReceived);
//
//					if (totalReceived >= TotalPublish)
//					{
//						watch.Stop();
//						Console.WriteLine("Rpc stress done. Took " +
//										  watch.Elapsed.TotalMilliseconds +
//										  "ms - rate of " + (TotalPublish / watch.Elapsed.TotalSeconds) + " messages per second");
//						totalReceived = 0;
//					}
//				}
//
//				await Task.Delay(TimeSpan.FromMinutes(5));
//
//				await newChannel2.Close();
//			}
//			catch (AggregateException ex)
//			{
//				Console.WriteLine("[Captured error] " + ex.Message);
//			}
//			catch (Exception ex)
//			{
//				Console.WriteLine("[Captured error 2] " + ex.Message);
//			}
//
//			if (conn2 != null)
//				conn2.Dispose();
//		}

		private static async Task<int> MakeCall(RpcHelper rpcHelper, int y)
		{
			var prop2 = new BasicProperties();
			var req = new byte[4];
			req[3] = (byte)((y & 0xFF000000) >> 24);
			req[2] = (byte)((y & 0x00FF0000) >> 16);
			req[1] = (byte)((y & 0x0000FF00) >> 8);
			req[0] = (byte)((y & 0x000000FF));

			var rpcCallResult = await rpcHelper.Call("test_ex", "rpc1", prop2, new ArraySegment<byte>(req, 0, 4));
			if (rpcCallResult.stream != null)
			{
				var reply = new byte[4];
				rpcCallResult.stream.Read(reply, 0, rpcCallResult.bodySize);
				var x = BitConverter.ToInt32(reply, 0);
				if (x != y) throw new Exception("Invalid result for call");
			}
//			else if (rpcCallResult.Body != null)
//			{
//				var x = BitConverter.ToInt32(rpcCallResult.Body, 0);
//				if (x != y) throw new Exception("Invalid result for call");
//			}

//			Console.WriteLine("Call " + y + " completed");

			return y;
		}
 public static Task <MessageDelivery> Call(this RpcHelper source, string exchange, string routing, BasicProperties properties, byte[] buffer)
 {
     return(source.Call(exchange, routing, properties, new ArraySegment <byte>(buffer)));
 }
 object ICloneable.Clone()
 {
     var cloned = new BasicProperties(false, false)
     {
         _presenceSWord = this._presenceSWord,
         _timestamp = this._timestamp,
         _deliveryMode = this._deliveryMode,
         _priority = this._priority,
         _contentType = this._contentType,
         _contentEncoding = this._contentEncoding,
         _correlationId = this._correlationId,
         _replyTo = this._replyTo,
         _expiration = this._expiration,
         _messageId = this._messageId,
         _type = this._type,
         _userId = this._userId,
         _appId = this._appId,
         _clusterId = this._clusterId,
     };
     if (this._headers != null && this._headers.Count != 0)
     {
         cloned._headers = new Dictionary<string, object>(this._headers);
     }
     return cloned;
 }
Exemple #9
0
        /// <summary>
        /// The request message is expected to have multiple receivers, and multiple replies.
        /// The replies will be aggregated and returned, respecting up to a minimum set by minExpectedReplies, and
        /// if unsuccessful a timeout error will be thrown.
        /// </summary>
        public Task <IEnumerable <MessageDelivery> > CallAggregate(string exchange, string routing,
                                                                   BasicProperties properties,
                                                                   ArraySegment <byte> buffer, int minExpectedReplies, bool runContinuationsAsynchronously = true)
        {
            if (!_operational)
            {
                throw new Exception("Can't make RPC call when connection in recovery");
            }

            _semaphoreSlim.Wait();

            uint correlationId;
            long pos;
            var  tcs = SecureSpotAndUniqueCorrelationId(runContinuationsAsynchronously, exchange, routing, out pos, out correlationId);

            if (tcs == null)
            {
                _semaphoreSlim.Release();

                // NOTE: If our use of semaphore is correct, this should never happen:
                LogAdapter.LogError(LogSource, "Maxed calls: " + _maxConcurrentCalls);
                throw new Exception("reached max calls");
            }

            var cookie = tcs.Task.Id;
            var state  = _pendingAggregationState[pos];

            lock (state)             // bad, but chances of s*** happening are too high
            {
                Interlocked.Exchange(ref state.waitingCount, minExpectedReplies);
                Interlocked.Exchange(ref state.cookie, cookie);
                state.received.Clear();
            }

            try
            {
                var prop = (properties == null || properties == BasicProperties.Empty) ? _channel.RentBasicProperties() : properties;

                prop.CorrelationId = BuildFullCorrelation(cookie, correlationId);                 // correlationId + SeparatorStr + cookie;
                prop.ReplyTo       = _replyQueueName.Name;

                // TODO: confirm this doesnt cause more overhead to rabbitmq
                if (_timeoutInMs.HasValue)
                {
//					prop.Expiration = _timeoutInMs.ToString();
                }

                _channel.BasicPublishFast(exchange, routing, true, prop, buffer);
            }
            catch (Exception ex)
            {
                if (ReleaseSpot(pos, cookie))
                {
                    _semaphoreSlim.Release();
                }

                tcs.TrySetException(ex);
            }

            return(tcs.Task);
        }
        /// <summary>
        /// The request message is expected to have multiple receivers, and multiple replies.
        /// The replies will be aggregated and returned, respecting up to a minimum set by minExpectedReplies, and
        /// if unsuccessful a timeout error will be thrown.
        /// </summary>
        public TaskSlim <IEnumerable <MessageDelivery> > CallAggregate(string exchange, string routing, BasicProperties properties,
                                                                       ArraySegment <byte> buffer, int minExpectedReplies)
        {
            _semaphoreSlim.Wait();

            var task = _taskResultPool.GetObject();

            uint correlationId;
            long pos;

            if (!SecureSpotAndUniqueCorrelationId(task, out pos, out correlationId))
            {
                _semaphoreSlim.Release();

                // NOTE: If our use of semaphore is correct, this should never happen:
                LogAdapter.LogError("RpcAggregateHelper", "Maxed calls: " + _maxConcurrentCalls);
                task.SetException(new Exception("reached max calls"));
                return(task);
            }

            var cookie = Rnd.Next();
            var state  = _pendingAggregationState[pos];

            lock (state)             // bad, but chances of s*** happening are too high
            {
                Interlocked.Exchange(ref state.waitingCount, minExpectedReplies);
                Interlocked.Exchange(ref state.cookie, cookie);
                state.received.Clear();
            }

            task.Id      = correlationId;        // so we can confirm we have the right instance later
            task.Started = DateTime.Now.Ticks;

            try
            {
                var prop = properties ?? _channel.RentBasicProperties();
                prop.CorrelationId = correlationId + Separator[0].ToString() + cookie;
                prop.ReplyTo       = _replyQueueName.Name;

                // TODO: confirm this doesnt cause more overhead to rabbitmq
                if (_timeoutInMs.HasValue)
                {
//					prop.Expiration = _timeoutInMs.ToString();
                }

                _channel.BasicPublishFast(exchange, routing, true, prop, buffer);
            }
            catch (Exception ex)
            {
                // release spot
                Interlocked.Exchange(ref _pendingCalls[pos], null);

                _semaphoreSlim.Release();

                task.SetException(ex);
            }

            return(task);
        }
Exemple #11
0
        internal async Task DispatchDeliveredMessage(string consumerTag, ulong deliveryTag, bool redelivered,
                                                     string exchange, string routingKey, int bodySize, BasicProperties properties, Stream stream)
        {
            BasicConsumerSubscriptionInfo consumer;

            if (_consumerSubscriptions.TryGetValue(consumerTag, out consumer))
            {
                var delivery = new MessageDelivery()
                {
                    bodySize    = bodySize,
                    properties  = properties,
                    routingKey  = routingKey,
                    deliveryTag = deliveryTag,
                    redelivered = redelivered
                };

                var mode             = consumer.Mode;
                var cb               = consumer.Callback;
                var consumerInstance = consumer._consumer;

                if (mode == ConsumeMode.SingleThreaded)
                {
                    // run with scissors, we're letting
                    // the user code mess with the ring buffer in the name of performance
                    delivery.stream = stream;

                    // upon return it's assumed the user has consumed from the stream and is done with it
                    try
                    {
                        if (cb != null)
                        {
                            await cb(delivery);
                        }
                        else
                        {
                            await consumerInstance.Consume(delivery);
                        }
                    }
                    finally
                    {
                        // fingers crossed the user cloned the buffer if she needs it later
                        this.Return(properties);
                    }
                }
                else
                {
                    // parallel mode. it cannot hold the frame handler, so we copy the buffer (yuck) and more forward

                    // since we dont have any control on how the user
                    // will deal with the buffer we cant even re-use/use a pool, etc :-(

                    // Idea: split Ringbuffer consumers, create reader barrier. once they are all done,
                    // move the read pos forward. Shouldnt be too hard to implement and
                    // avoids the new buffer + GC and keeps the api Stream based consistently

                    if (mode == ConsumeMode.ParallelWithBufferCopy)
                    {
                        var bufferCopy = BufferUtil.Copy(stream as RingBufferStreamAdapter, (int)bodySize);
                        var memStream  = new MemoryStream(bufferCopy, writable: false);
                        delivery.stream = memStream;
                    }
//					else if (mode == ConsumeMode.ParallelWithReadBarrier)
//					{
//						var readBarrier = new RingBufferStreamReadBarrier(stream as RingBufferStreamAdapter, delivery.bodySize);
//						delivery.stream = readBarrier;
//					}

#pragma warning disable 4014
                    Task.Factory.StartNew(async() =>
                                          // ThreadPool.UnsafeQueueUserWorkItem((param) =>
#pragma warning restore 4014
                    {
                        try
                        {
                            using (delivery.stream)
                            {
                                if (cb != null)
                                {
                                    await cb(delivery);
                                }
                                else
                                {
                                    await consumerInstance.Consume(delivery);
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("From threadpool " + e);
                        }
                        finally
                        {
                            this.Return(properties);
                        }
                    }, TaskCreationOptions.PreferFairness);
                }
            }
            else
            {
                // received msg but nobody was subscribed to get it (?) TODO: log it at least
            }
        }
		internal void __BasicPublish(string exchange, string routingKey, bool mandatory, bool immediate,
									BasicProperties properties, ArraySegment<byte> buffer)
		{
			if (properties == null)
			{
				properties = BasicProperties.Empty;
			}

			var args = _basicPubArgsPool.GetObject();
			args.exchange = exchange;
			args.immediate = immediate;
			args.routingKey = routingKey;
			args.mandatory = mandatory;
			args.properties = properties;
			args.buffer = buffer;

			_connectionIo.SendCommand(_channelNum, 60, 40,
				null, // writer
				reply: (channel, classMethodId, error) =>
				{
					if (properties.IsReusable)
					{
						_channel.Return(properties); // the tcs is left for the confirmation keeper
					}
					return Task.CompletedTask;
				},
				expectsReply: false,
				optArg: args);
		}
		internal TaskSlim __BasicPublishConfirm(string exchange, string routingKey, bool mandatory, bool immediate,
												BasicProperties properties, ArraySegment<byte> buffer)
		{
			if (properties == null)
			{
				properties = BasicProperties.Empty;
			}

			var confirmationKeeper = _channel._confirmationKeeper;

			TaskSlim tcs = _taskLightPool.GetObject();
			confirmationKeeper.WaitForSemaphore(); // make sure we're not over the limit
			
			var args = _basicPubArgsPool.GetObject();
			args.exchange = exchange;
			args.immediate = immediate;
			args.routingKey = routingKey;
			args.mandatory = mandatory;
			args.properties = properties;
			args.buffer = buffer;

			_connectionIo.SendCommand(_channelNum, 60, 40,
				null, // AmqpChannelLevelFrameWriter.InternalBasicPublish, 
				reply: (channel, classMethodId, error) =>
				{
					if (properties.IsReusable)
					{
						_channel.Return(properties); // the tcs is left for the confirmation keeper
					}
					return Task.CompletedTask;
				},
				expectsReply: false,
				tcsL: null, 
				optArg: args,
				prepare: () => _channel._confirmationKeeper.Add(tcs));

			return tcs;
		}
        public static TaskSlim BasicPublish(this Channel source, 
			string exchange, string routingKey, bool mandatory, bool immediate,
			BasicProperties properties, byte[] buffer)
        {
            return source.BasicPublish(exchange, routingKey, mandatory, immediate, properties, new ArraySegment<byte>(buffer));
        }
        public static void BasicPublishFast(this Channel source,
			string exchange, string routingKey, bool mandatory,
			BasicProperties properties, byte[] buffer)
        {
            source.BasicPublishFast(exchange, routingKey, mandatory, false, properties, new ArraySegment<byte>(buffer));
        }