<commit_before>﻿using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Diagnostics;
using System.Linq;
using System.Threading;

namespace Hangfire.EntityFramework
{
    internal class EntityFrameworkJobStorageDistributedLock : IDisposable
    {
        private static TimeSpan MaxSupportedTimeout = new TimeSpan(TimeSpan.TicksPerMillisecond * int.MaxValue);

        private EntityFrameworkJobStorage Storage { get; }
        private string Resource { get; }
        private TimeSpan Timeout { get; }
        private bool Disposed { get; set; }

        public EntityFrameworkJobStorageDistributedLock(
            EntityFrameworkJobStorage storage,
            string resource,
            TimeSpan timeout)
        {
            if (storage == null) throw new ArgumentNullException(nameof(storage));
            if (resource == null) throw new ArgumentNullException(nameof(resource));
            if (resource == string.Empty)
                throw new ArgumentException(ErrorStrings.StringCannotBeEmpty, nameof(resource));
            if (timeout < TimeSpan.Zero)
                throw new ArgumentOutOfRangeException(nameof(timeout), timeout, ErrorStrings.NeedNonNegativeValue);
            if (timeout > MaxSupportedTimeout) throw new ArgumentOutOfRangeException(nameof(timeout), timeout,
                string.Format(ErrorStrings.Culture, ErrorStrings.TimeoutTooLarge, MaxSupportedTimeout));

            Resource = resource;
            Timeout = timeout;
            Storage = storage;

            Initialize();
        }

        private void Initialize()
        {
            var lockAcquiringTime = Stopwatch.StartNew();

            bool tryAcquireLock = true;

            while (tryAcquireLock)
            {
                TryRemoveDeadlock();

                using (var context = Storage.CreateHangfireDbContext())
                using (var transaction = context.Database.BeginTransaction())
                {
                    if (!context.DistributedLocks.Any(x => x.Resource == Resource))
                    {
                        context.DistributedLocks.Add(new HangfireDistributedLock { Resource = Resource, CreatedAt = DateTime.UtcNow, });

                        bool alreadyLocked = false;
                        try
                        {
                            context.SaveChanges();
                        }
                        catch (DbUpdateConcurrencyException)
                        {
                            alreadyLocked = true;
                        }

                        if(!alreadyLocked)
                            return;
                    }
                }

                if (lockAcquiringTime.ElapsedMilliseconds > Timeout.TotalMilliseconds)
                    tryAcquireLock = false;
                else
                {
                    int sleepDuration = Math.Min(1000, (int)(Timeout.TotalMilliseconds - lockAcquiringTime.ElapsedMilliseconds));
                    if (sleepDuration > 1000) sleepDuration = 1000;
                    if (sleepDuration > 0)
                        Thread.Sleep(sleepDuration);<commit_msg>Fix distributed lock acquiring issue<commit_after>﻿using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Diagnostics;
using System.Linq;
using System.Threading;

namespace Hangfire.EntityFramework
{
    internal class EntityFrameworkJobStorageDistributedLock : IDisposable
    {
        private static TimeSpan MaxSupportedTimeout = new TimeSpan(TimeSpan.TicksPerMillisecond * int.MaxValue);

        private EntityFrameworkJobStorage Storage { get; }
        private string Resource { get; }
        private TimeSpan Timeout { get; }
        private bool Disposed { get; set; }

        public EntityFrameworkJobStorageDistributedLock(
            EntityFrameworkJobStorage storage,
            string resource,
            TimeSpan timeout)
        {
            if (storage == null) throw new ArgumentNullException(nameof(storage));
            if (resource == null) throw new ArgumentNullException(nameof(resource));
            if (resource == string.Empty)
                throw new ArgumentException(ErrorStrings.StringCannotBeEmpty, nameof(resource));
            if (timeout < TimeSpan.Zero)
                throw new ArgumentOutOfRangeException(nameof(timeout), timeout, ErrorStrings.NeedNonNegativeValue);
            if (timeout > MaxSupportedTimeout) throw new ArgumentOutOfRangeException(nameof(timeout), timeout,
                string.Format(ErrorStrings.Culture, ErrorStrings.TimeoutTooLarge, MaxSupportedTimeout));

            Resource = resource;
            Timeout = timeout;
            Storage = storage;

            Initialize();
        }

        private void Initialize()
        {
            var lockAcquiringTime = Stopwatch.StartNew();

            bool tryAcquireLock = true;

            while (tryAcquireLock)
            {
                TryRemoveDeadlock();

                using (var context = Storage.CreateHangfireDbContext())
                using (var transaction = context.Database.BeginTransaction())
                {
                    if (!context.DistributedLocks.Any(x => x.Resource == Resource))
                    {
                        context.DistributedLocks.Add(new HangfireDistributedLock { Resource = Resource, CreatedAt = DateTime.UtcNow, });

                        bool alreadyLocked = false;
                        try
                        {
                            context.SaveChanges();
                        }
                        catch (DbUpdateConcurrencyException)
                        {
                            alreadyLocked = true;
                        }
                        finally
                        {
                            transaction.Commit();
                        }

                        if(!alreadyLocked)
                            return;
                    }
                }

                if (lockAcquiringTime.ElapsedMilliseconds > Timeout.TotalMilliseconds)
                    tryAcquireLock = false;
                else
                {
                    int sleepDuration = Math.Min(1000, (int)(Timeout.TotalMilliseconds - lockAcquiringTime.ElapsedMilliseconds));
                    if (sleepDuration > 1000) sleepDuration = 1000;
                    if (sleepDuration > 0)
                        Thread.Sleep(sleepDuration);
{'fraction_non_alphanumeric': 0.07015917168907433, 'fraction_numerical': 0.00401792613197342, 'mean_word_length': 1.6308943089430894, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3438105', 'n_tokens_mistral': 1477, 'n_tokens_neox': 1370, 'n_words': 315}