/// <summary>
        /// Erzeugt eine neue Hintergrundaufgabe.
        /// </summary>
        /// <param name="worker">Der Algorithmus zur Ausführung.</param>
        /// <returns>Die neue Aufgabe.</returns>
        public static CancellableTask <TResultType> Run(Func <CancellationToken, TResultType> worker)
        {
            // Validate
            if (worker == null)
            {
                throw new ArgumentException("no task worker provided", "worker");
            }

            // Allow self reference to new instance
            CancellableTask <TResultType> task = null;

            // Create instance
            task =
                new CancellableTask <TResultType>(() =>
            {
                // Provide cleanup of cancel source
                using (var controller = task.m_cancel)
                    try
                    {
                        // Try to process without generating exception - if possible
                        return(worker(controller.Token));
                    }
                    catch (Exception)
                    {
                        // In case of any error just report nothing - not applyable to value types
                        return(null);
                    }
                finally
                {
                    // Never use cancel source again
                    task.m_cancel = null;
                }
            });

            // Make sure we clean up in case we are not able to start the task
            try
            {
                // Request start
                task.Start();
            }
            catch (Exception)
            {
                // Forward with cleanup
                using (task.m_cancel)
                    throw;
            }

            // Report
            return(task);
        }
        /// <summary>
        /// Initialisiert eine Hintergrundaufgabe.
        /// </summary>
        /// <typeparam name="TResultType">Die Art des Ergebnisses der Aufgabe.</typeparam>
        /// <param name="reader">Die aktuelle Hintergrundaufgabe.</param>
        /// <param name="factory">Optional die Methode zur Neuinitialisierung.</param>
        /// <returns>Die neue Hintergrundaufgabe.</returns>
        private CancellableTask <TResultType> ResetReader <TResultType>(ref CancellableTask <TResultType> reader, Func <CancellableTask <TResultType> > factory = null) where TResultType : class
        {
            // Wipe out
            var previous = Interlocked.Exchange(ref reader, null);

            // Stop it
            if (previous != null)
            {
                previous.Cancel();
            }

            // New one
            if (factory != null)
            {
                reader = factory();
            }

            // Report
            return(reader);
        }
        /// <summary>
        /// Beginnt mit dem Auslesen der Quelldaten.
        /// </summary>
        /// <param name="source">Die gewünschte Quelle.</param>
        /// <param name="device">Das zu verwendende Gerät.</param>
        /// <param name="profile">Optional das zu berücksichtigende Geräteprofil.</param>
        /// <returns>Die Hintergrundaufgabe zum Auslesen der Quelledaten.</returns>
        public static CancellableTask <SourceInformation> GetSourceInformationAsync(this Hardware device, SourceIdentifier source, Profile profile = null)
        {
            // Validate
            if (device == null)
            {
                throw new ArgumentNullException("no hardware to use", "device");
            }
            if (source == null)
            {
                throw new ArgumentException("no source to get information for", "source");
            }

            // Attach to tasks
            var patReader   = device.AssociationTableReader;
            var groupReader = device.GroupReader;

            // Start
            return
                (CancellableTask <SourceInformation> .Run(cancel =>
            {
                // Check tasks
                if (groupReader == null)
                {
                    return null;
                }
                if (patReader == null)
                {
                    return null;
                }

                // Wait on tasks
                if (!groupReader.CancellableWait(cancel))
                {
                    return null;
                }
                if (!patReader.CancellableWait(cancel))
                {
                    return null;
                }

                // Get the current group information
                var groupInfo = groupReader.Result;
                if (groupInfo == null)
                {
                    return null;
                }

                // See if group exists
                if (!groupInfo.Sources.Any(source.Equals))
                {
                    return null;
                }

                // Find the stream identifier for the service
                var pmtIdentifier = patReader.Result.FindService(source.Service);
                if (!pmtIdentifier.HasValue)
                {
                    return null;
                }

                // Wait for mapping table
                var pmtReader = device.GetTableAsync <PMT>(pmtIdentifier.Value);
                if (!pmtReader.CancellableWait(cancel))
                {
                    return null;
                }

                // Request table
                var pmts = pmtReader.Result;
                if (pmts == null)
                {
                    return null;
                }

                // Create dummy
                var currentSettings = new SourceInformation {
                    Source = source, VideoType = VideoTypes.NoVideo
                };

                // Process all PMT - actually should be only one
                foreach (var pmt in pmts)
                {
                    // Overwrite encryption if CA descriptor is present
                    if (pmt.Table.Descriptors != null)
                    {
                        currentSettings.IsEncrypted = pmt.Table.Descriptors.Any(descriptor => EPG.DescriptorTags.CA == descriptor.Tag);
                    }

                    // Process the program entries
                    foreach (var program in pmt.Table.ProgramEntries)
                    {
                        currentSettings.Update(program);
                    }
                }

                // Find the related station information
                var station = groupInfo.Sources.FirstOrDefault(source.Equals) as Station;
                if (station != null)
                {
                    // Take data from there
                    currentSettings.Provider = station.Provider;
                    currentSettings.Name = station.Name;

                    // See if this is a service
                    currentSettings.IsService = station.IsService;

                    // Overwrite encryption if regular service entry exists
                    if (!currentSettings.IsService)
                    {
                        currentSettings.IsEncrypted = station.IsEncrypted;
                    }
                }

                // See if profile is attached
                if (profile != null)
                {
                    // Apply the modifier
                    var modifier = profile.GetFilter(currentSettings.Source);
                    if (modifier != null)
                    {
                        modifier.ApplyTo(currentSettings);
                    }
                }

                // Report
                return currentSettings;
            }));
        }
        /// <summary>
        /// Erzeugt eine neue Hintergrundaufgabe zum Auslesen von SI Tabellen.
        /// </summary>
        /// <param name="device">Das zu verwendende Gerät.</param>
        /// <param name="stream">Die Datenstromkennung, die überwacht werden soll.</param>
        /// <returns>Die neue Aufgabe.</returns>
        public static CancellableTask <TTableType[]> GetTableAsync <TTableType>(this Hardware device, ushort stream) where TTableType : Table
        {
            // Validate
            if (device == null)
            {
                throw new ArgumentException("no hardware to use", "device");
            }

            // Create the task
            return
                (CancellableTask <TTableType[]> .Run(cancel =>
            {
                // Termination synchronisation
                var sync = new object();
                var done = false;

                // Parsing state
                var tables = default(TTableType[]);
                var expectedVersion = 0;
                var collectedParts = 0;

                // Create parser
                var parser = TableParser.Create(( TTableType table ) =>
                {
                    // Disabled
                    if (done)
                    {
                        return;
                    }

                    // Check version
                    if (tables != null)
                    {
                        if (table.Version != expectedVersion)
                        {
                            tables = null;
                        }
                    }

                    // Discard on count mismatch
                    if (tables != null)
                    {
                        if (tables.Length != (table.LastSection + 1))
                        {
                            tables = null;
                        }
                    }

                    // Discard on overrun
                    if (tables != null)
                    {
                        if (table.CurrentSection >= tables.Length)
                        {
                            tables = null;
                        }
                    }

                    // Discard on duplicates
                    if (tables != null)
                    {
                        if (tables[table.CurrentSection] != null)
                        {
                            tables = null;
                        }
                    }

                    // Create once
                    if (tables == null)
                    {
                        // Create in full size
                        tables = new TTableType[table.LastSection + 1];
                        expectedVersion = table.Version;
                        collectedParts = 0;
                    }

                    // Add it
                    tables[table.CurrentSection] = table;

                    // Mark as done
                    if (++collectedParts >= tables.Length)
                    {
                        lock (sync)
                        {
                            done = true;

                            // Our wake up call
                            Monitor.Pulse(sync);
                        }
                    }
                });

                // Register with cleanup
                var tableType = Table.GetIsExtendedTable <TTableType>() ? StreamTypes.ExtendedTable : StreamTypes.StandardTable;
                var registration = device.AddConsumer(stream, tableType, parser.AddPayload);
                try
                {
                    // Start receiving data
                    device.SetConsumerState(registration, true);

                    // Wait for end and fully process the cancellation token
                    using (cancel.Register(() => { lock (sync) Monitor.Pulse(sync); }))
                        lock (sync)
                            while (!done)
                            {
                                if (cancel.IsCancellationRequested)
                                {
                                    return null;
                                }
                                else
                                {
                                    Monitor.Wait(sync);
                                }
                            }
                }
                finally
                {
                    // Cleanup
                    device.SetConsumerState(registration, null);
                }

                // Report
                return tables;
            }));
        }