예제 #1
0
        public void SetExitTasksEarly(bool exitTasksEarly)
        {
            if (ExitTasksEarly == exitTasksEarly)
            {
                return;
            }

            ExitTasksEarly = exitTasksEarly;

            if (exitTasksEarly)
            {
                Logger.Debug("ExitTasksEarly enabled.");

                lock (_lock)
                {
                    foreach (var task in PendingTasks)
                    {
                        task?.Cancel();
                    }

                    PendingTasks.Clear();

                    foreach (var task in SimilarTasks)
                    {
                        task?.Cancel();
                    }

                    SimilarTasks.Clear();
                }
            }
            else
            {
                Logger.Debug("ExitTasksEarly disabled.");
            }
        }
예제 #2
0
        protected void QueueImageLoadingTask(IImageLoaderTask task)
        {
            if (task.IsCancelled || task.IsCompleted || ExitTasksEarly)
            {
                if (!task.IsCompleted)
                {
                    task.TryDispose();
                }

                return;
            }

            IImageLoaderTask similarRunningTask = null;

            similarRunningTask = PendingTasks.FirstOrDefaultByRawKey(task.KeyRaw);
            if (similarRunningTask == null)
            {
                Interlocked.Increment(ref _statsTotalPending);
                Enqueue(task);
            }
            else
            {
                if (task.Parameters.Priority.HasValue && (!similarRunningTask.Parameters.Priority.HasValue ||
                                                          task.Parameters.Priority.Value > similarRunningTask.Parameters.Priority.Value))
                {
                    similarRunningTask.Parameters.WithPriority(task.Parameters.Priority.Value);
                    PendingTasks.TryUpdatePriority(similarRunningTask, task.Parameters.Priority.Value);
                }

                if (task.Parameters.OnDownloadProgress != null)
                {
                    var similarTaskOnDownloadProgress = similarRunningTask.Parameters.OnDownloadProgress;
                    similarRunningTask.Parameters.DownloadProgress((DownloadProgress obj) =>
                    {
                        similarTaskOnDownloadProgress?.Invoke(obj);
                        task.Parameters.OnDownloadProgress(obj);
                    });
                }
            }

            if (PauseWork)
            {
                return;
            }

            if (similarRunningTask == null || !task.CanUseMemoryCache)
            {
                TakeFromPendingTasksAndRun();
            }
            else
            {
                Interlocked.Increment(ref _statsTotalWaiting);
                Logger.Debug(string.Format("Wait for similar request for key: {0}", task.Key));
                SimilarTasks.Add(task);
            }
        }
예제 #3
0
 public virtual void RemovePendingTask(IImageLoaderTask task)
 {
     if (task != null)
     {
         lock (_lock)
         {
             PendingTasks.TryRemove(task);
             SimilarTasks.Remove(task);
         }
     }
 }
예제 #4
0
        public virtual void Cancel(Func <IImageLoaderTask, bool> predicate)
        {
            lock (_lock)
            {
                foreach (var task in PendingTasks.Where(p => predicate(p)))
                {
                    task?.Cancel();
                }

                SimilarTasks.RemoveAll(predicate);
            }
        }
예제 #5
0
        protected async Task RunImageLoadingTaskAsync(IImageLoaderTask pendingTask)
        {
            string keyRaw = pendingTask.KeyRaw;

            try
            {
                if (_imageLoader.VerbosePerformanceLogging)
                {
                    IPlatformPerformance performance = _imageLoader.Performance;

                    LogSchedulerStats(performance);
                    var stopwatch = Stopwatch.StartNew();

                    await pendingTask.RunAsync().ConfigureAwait(false);

                    stopwatch.Stop();

                    _imageLoader.Logger?.Debug(string.Format("[PERFORMANCE] RunAsync - NetManagedThreadId: {0}, NativeThreadId: {1}, Execution: {2} ms, Key: {3}",
                                                             performance.GetCurrentManagedThreadId(),
                                                             performance.GetCurrentSystemThreadId(),
                                                             stopwatch.Elapsed.Milliseconds,
                                                             pendingTask.Key));
                }
                else
                {
                    await pendingTask.RunAsync().ConfigureAwait(false);
                }
            }
            finally
            {
                lock (_lock)
                {
                    RunningTasks.Remove(keyRaw);

                    if (SimilarTasks.Count > 0)
                    {
                        SimilarTasks.RemoveAll(v => v == null || v.IsCompleted || v.IsCancelled);
                        var similarItems = SimilarTasks.Where(v => v.KeyRaw == keyRaw);
                        foreach (var similar in similarItems)
                        {
                            SimilarTasks.Remove(similar);

                            LoadImage(similar);
                        }
                    }
                }

                pendingTask.TryDispose();
                await TakeFromPendingTasksAndRunAsync().ConfigureAwait(false);
            }
        }
예제 #6
0
        protected void QueueImageLoadingTask(IImageLoaderTask task)
        {
            if (task.IsCancelled || task.IsCompleted || ExitTasksEarly)
            {
                if (!task.IsCompleted)
                {
                    task.TryDispose();
                }

                return;
            }

            IImageLoaderTask?similarRunningTask = null;

            similarRunningTask = PendingTasks.FirstOrDefaultByRawKey(task.KeyRaw);
            if (similarRunningTask == null)
            {
                Interlocked.Increment(ref _statsTotalPending);
                Enqueue(task);
            }
            else
            {
                if (task.Priority.HasValue && (!similarRunningTask.Priority.HasValue ||
                                               task.Priority.Value > similarRunningTask.Priority.Value))
                {
                    similarRunningTask.Priority = task.Priority.Value;
                    PendingTasks.TryUpdatePriority(similarRunningTask, task.Priority.Value);
                }

#if LATER
                similarRunningTask.ImageSource.DownloadProgress += (sender, args) =>
                                                                   task.ImageSource.RaiseDownloadProgress(args);
#endif
            }

            if (PauseWork)
            {
                return;
            }

            if (similarRunningTask == null || !task.CanUseMemoryCache)
            {
                TakeFromPendingTasksAndRun();
            }
            else
            {
                Interlocked.Increment(ref _statsTotalWaiting);
                _imageLoader.Logger?.Debug($"Wait for similar request for key: {task.Key}");
                SimilarTasks.Add(task);
            }
        }
예제 #7
0
        public virtual void Cancel(Func <IImageLoaderTask, bool> predicate)
        {
            lock (_similarTasksLock)
            {
                foreach (var task in PendingTasks.Where(p => predicate(p)))
                {
                    task?.Cancel();
                }

                var items = SimilarTasks.Where(v => predicate(v)).ToList();
                foreach (var item in items)
                {
                    SimilarTasks.Remove(item);
                }
            }
        }
예제 #8
0
        public void SetPauseWork(bool pauseWork, bool cancelExisting = false)
        {
            if (PauseWork == pauseWork)
            {
                return;
            }

            if (cancelExisting)
            {
                lock (_lock)
                {
                    foreach (var task in PendingTasks)
                    {
                        task?.Cancel();
                    }

                    PendingTasks.Clear();

                    foreach (var task in SimilarTasks)
                    {
                        task?.Cancel();
                    }

                    SimilarTasks.Clear();
                }
            }

            PauseWork = pauseWork;

            if (pauseWork)
            {
                Logger.Debug("SetPauseWork enabled.");
            }
            else
            {
                Logger.Debug("SetPauseWork disabled.");
                TakeFromPendingTasksAndRun();
            }
        }
예제 #9
0
        protected async Task TakeFromPendingTasksAndRunAsync()
        {
            if (PendingTasks.Count == 0)
            {
                return;
            }

            Dictionary <string, IImageLoaderTask> tasksToRun = null;

            int preloadOrUrlTasksCount = 0;
            int urlTasksCount          = 0;
            int preloadTasksCount      = 0;

            lock (_lock)
            {
                if (RunningTasks.Count >= MaxParallelTasks)
                {
                    urlTasksCount          = RunningTasks.Count(v => v.Value != null && (!v.Value.Parameters.Preload && v.Value.Parameters.Source == ImageSource.Url));
                    preloadTasksCount      = RunningTasks.Count(v => v.Value != null && v.Value.Parameters.Preload);
                    preloadOrUrlTasksCount = preloadTasksCount + urlTasksCount;

                    if (preloadOrUrlTasksCount == 0 || preloadOrUrlTasksCount != MaxParallelTasks)
                    {
                        return;
                    }

                    // Allow only half of MaxParallelTasks as additional allowed tasks when preloading occurs to prevent starvation
                    if (RunningTasks.Count - Math.Max(1, Math.Min(preloadOrUrlTasksCount, MaxParallelTasks / 2)) >= MaxParallelTasks)
                    {
                        return;
                    }
                }

                int numberOfTasks = MaxParallelTasks - RunningTasks.Count + Math.Min(preloadOrUrlTasksCount, MaxParallelTasks / 2);
                tasksToRun = new Dictionary <string, IImageLoaderTask>();
                IImageLoaderTask task = null;

                while (tasksToRun.Count < numberOfTasks && PendingTasks.TryDequeue(out task))
                {
                    if (task == null || task.IsCancelled || task.IsCompleted)
                    {
                        continue;
                    }

                    // We don't want to load, at the same time, images that have same key or same raw key at the same time
                    // This way we prevent concurrent downloads and benefit from caches
                    string rawKey = task.KeyRaw;
                    if (RunningTasks.ContainsKey(rawKey) || tasksToRun.ContainsKey(rawKey))
                    {
                        SimilarTasks.Add(task);
                        continue;
                    }

                    if (preloadOrUrlTasksCount != 0)
                    {
                        if (!task.Parameters.Preload && (urlTasksCount == 0 || task.Parameters.Source != ImageSource.Url))
                        {
                            tasksToRun.Add(rawKey, task);
                        }
                        else
                        {
                            Enqueue(task);
                            break;
                        }
                    }
                    else
                    {
                        tasksToRun.Add(rawKey, task);
                    }
                }

                foreach (var item in tasksToRun)
                {
                    RunningTasks.Add(item.Key, item.Value);
                    Interlocked.Increment(ref _statsTotalRunning);
                }
            }

            if (tasksToRun != null && tasksToRun.Count > 0)
            {
                var tasks = tasksToRun.Select(p => RunImageLoadingTaskAsync(p.Value));
                await Task.WhenAll(tasks).ConfigureAwait(false);
            }
        }
예제 #10
0
        protected async Task TakeFromPendingTasksAndRunAsync()
        {
            if (PendingTasks.Count == 0)
            {
                return;
            }

            Dictionary <string, IImageLoaderTask>?tasksToRun = null;

            int urlTasksCount = 0;

            lock (_lock)
            {
                if (RunningTasks.Count >= MaxParallelTasks)
                {
                    urlTasksCount = RunningTasks.Count(v => v.Value != null && v.Value.ImageSource.Source is UriSource);

                    if (urlTasksCount == 0 || urlTasksCount != MaxParallelTasks)
                    {
                        return;
                    }

                    // Allow only half of MaxParallelTasks as additional allowed tasks when preloading occurs to prevent starvation
                    if (RunningTasks.Count - Math.Max(1, Math.Min(urlTasksCount, MaxParallelTasks / 2)) >= MaxParallelTasks)
                    {
                        return;
                    }
                }

                int numberOfTasks = MaxParallelTasks - RunningTasks.Count + Math.Min(urlTasksCount, MaxParallelTasks / 2);
                tasksToRun = new Dictionary <string, IImageLoaderTask>();

                while (tasksToRun.Count < numberOfTasks && PendingTasks.TryDequeue(out IImageLoaderTask? task))
                {
                    if (task == null || task.IsCancelled || task.IsCompleted)
                    {
                        continue;
                    }

                    // We don't want to load, at the same time, images that have same key or same raw key at the same time
                    // This way we prevent concurrent downloads and benefit from caches
                    string rawKey = task.KeyRaw;
                    if (RunningTasks.ContainsKey(rawKey) || tasksToRun.ContainsKey(rawKey))
                    {
                        SimilarTasks.Add(task);
                        continue;
                    }

                    if (urlTasksCount != 0)
                    {
                        if (!(task.ImageSource.Source is UriSource))
                        {
                            tasksToRun.Add(rawKey, task);
                        }
                        else
                        {
                            Enqueue(task);
                            break;
                        }
                    }
                    else
                    {
                        tasksToRun.Add(rawKey, task);
                    }
                }

                foreach (var item in tasksToRun)
                {
                    RunningTasks.Add(item.Key, item.Value);
                    Interlocked.Increment(ref _statsTotalRunning);
                }
            }

            if (tasksToRun != null && tasksToRun.Count > 0)
            {
                var tasks = tasksToRun.Select(async p =>
                {
                    await Task.Factory.StartNew(async() =>
                    {
                        try
                        {
                            await RunImageLoadingTaskAsync(p.Value).ConfigureAwait(false);
                        }
                        catch (Exception ex)
                        {
                            _imageLoader.Logger?.Error("TakeFromPendingTasksAndRun exception", ex);
                        }
                    }, CancellationToken.None, TaskCreationOptions.PreferFairness | TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler, TaskScheduler.Default).ConfigureAwait(false);
                });
                await Task.WhenAll(tasks).ConfigureAwait(false);
            }
        }