---
source: boltffi_bindgen/src/render/csharp/templates.rs
expression: template.render().unwrap()
---
// <auto-generated>
// This file was generated by BoltFFI. Do not edit.
// </auto-generated>
#nullable enable

using System;
using System.Collections.Generic;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace Demo
{
    public sealed class EventBus : IDisposable
    {
        private IntPtr _handle;

        internal EventBus(IntPtr handle)
        {
            _handle = handle;
        }

        /// <remarks>
        /// The returned <see cref="IAsyncEnumerable{T}"/> owns the underlying
        /// subscription. Disposing the owning <see cref="EventBus"/>
        /// instance does not, by itself, end iteration - the producer simply
        /// stops emitting events. Callers must either cancel the supplied
        /// <see cref="CancellationToken"/> or break out of <c>await foreach</c>
        /// to release the subscription.
        /// </remarks>
        public async IAsyncEnumerable<int> SubscribeValues([EnumeratorCancellation] CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();
            IntPtr subscription = NativeMethods.EventBusSubscribeValues(_handle);
            if (subscription == IntPtr.Zero) yield break;

            global::System.Threading.Tasks.Task<int>? _pendingWait = null;
            try
            {
                while (true)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    while (true)
                    {
                        int[] _items = new int[BoltFFIStream.DefaultBatchSize];
                        int _itemCount;
                        UIntPtr _count;
                        unsafe
                        {
                            fixed (int* _itemsPtr = _items)
                            {
                                _count = NativeMethods.EventBusSubscribeValuesPopBatch(subscription, (IntPtr)_itemsPtr, (UIntPtr)_items.Length);
                            }
                        }
                        _itemCount = checked((int)(nuint)_count);
                        if (_itemCount == 0) break;

                        for (int _i = 0; _i < _itemCount; _i++)
                        {
                            cancellationToken.ThrowIfCancellationRequested();
                            yield return _items[_i];
                        }

                        if (_itemCount < BoltFFIStream.DefaultBatchSize) break;
                    }

                    _pendingWait = global::System.Threading.Tasks.Task.Run(
                        () => NativeMethods.EventBusSubscribeValuesWait(subscription, BoltFFIStream.WaitTimeoutMilliseconds),
                        cancellationToken);
                    int _waitResult = await _pendingWait.ConfigureAwait(false);
                    _pendingWait = null;
                    if (_waitResult == BoltFFIStream.WaitResultUnsubscribed) yield break;
                }
            }
            finally
            {
                NativeMethods.EventBusSubscribeValuesUnsubscribe(subscription);
                if (_pendingWait is not null)
                {
                    try { await _pendingWait.ConfigureAwait(false); }
                    catch { /* wait may have observed cancellation; ignore */ }
                }
                NativeMethods.EventBusSubscribeValuesFree(subscription);
            }
        }

        /// <remarks>
        /// The returned <see cref="IAsyncEnumerable{T}"/> owns the underlying
        /// subscription. Disposing the owning <see cref="EventBus"/>
        /// instance does not, by itself, end iteration - the producer simply
        /// stops emitting events. Callers must either cancel the supplied
        /// <see cref="CancellationToken"/> or break out of <c>await foreach</c>
        /// to release the subscription.
        /// </remarks>
        public async IAsyncEnumerable<Point> SubscribePoints([EnumeratorCancellation] CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();
            IntPtr subscription = NativeMethods.EventBusSubscribePoints(_handle);
            if (subscription == IntPtr.Zero) yield break;

            global::System.Threading.Tasks.Task<int>? _pendingWait = null;
            try
            {
                while (true)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    while (true)
                    {
                        Point[] _items = new Point[BoltFFIStream.DefaultBatchSize];
                        int _itemCount;
                        UIntPtr _count;
                        unsafe
                        {
                            fixed (Point* _itemsPtr = _items)
                            {
                                _count = NativeMethods.EventBusSubscribePointsPopBatch(subscription, (IntPtr)_itemsPtr, (UIntPtr)_items.Length);
                            }
                        }
                        _itemCount = checked((int)(nuint)_count);
                        if (_itemCount == 0) break;

                        for (int _i = 0; _i < _itemCount; _i++)
                        {
                            cancellationToken.ThrowIfCancellationRequested();
                            yield return _items[_i];
                        }

                        if (_itemCount < BoltFFIStream.DefaultBatchSize) break;
                    }

                    _pendingWait = global::System.Threading.Tasks.Task.Run(
                        () => NativeMethods.EventBusSubscribePointsWait(subscription, BoltFFIStream.WaitTimeoutMilliseconds),
                        cancellationToken);
                    int _waitResult = await _pendingWait.ConfigureAwait(false);
                    _pendingWait = null;
                    if (_waitResult == BoltFFIStream.WaitResultUnsubscribed) yield break;
                }
            }
            finally
            {
                NativeMethods.EventBusSubscribePointsUnsubscribe(subscription);
                if (_pendingWait is not null)
                {
                    try { await _pendingWait.ConfigureAwait(false); }
                    catch { /* wait may have observed cancellation; ignore */ }
                }
                NativeMethods.EventBusSubscribePointsFree(subscription);
            }
        }

        internal IntPtr RawHandle => _handle;

        private void ThrowIfDisposed()
        {
            if (_handle == IntPtr.Zero) throw new ObjectDisposedException(nameof(EventBus));
        }

        public void Dispose()
        {
            IntPtr handle = Interlocked.Exchange(ref _handle, IntPtr.Zero);
            if (handle == IntPtr.Zero) return;
            NativeMethods.EventBusFree(handle);
            GC.SuppressFinalize(this);
        }

        ~EventBus()
        {
            Dispose();
        }
    }
}
