Changed all locks to SemaphoreSlims, Slight musicbuffer changes.

This commit is contained in:
Kwoth 2016-07-23 15:04:33 +02:00
parent 106a779552
commit e66615b056
15 changed files with 173 additions and 135 deletions

View File

@ -6,6 +6,8 @@ using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Classes
{
@ -27,9 +29,12 @@ namespace NadekoBot.Classes
{
configs = JsonConvert
.DeserializeObject<ConcurrentDictionary<ulong, ServerSpecificConfig>>(
File.ReadAllText(filePath), new JsonSerializerSettings() {
Error = (s,e) => {
if (e.ErrorContext.Member.ToString() == "GenerateCurrencyChannels") {
File.ReadAllText(filePath), new JsonSerializerSettings()
{
Error = (s, e) =>
{
if (e.ErrorContext.Member.ToString() == "GenerateCurrencyChannels")
{
e.ErrorContext.Handled = true;
}
}
@ -52,14 +57,19 @@ namespace NadekoBot.Classes
public ServerSpecificConfig Of(ulong id) =>
configs.GetOrAdd(id, _ => new ServerSpecificConfig());
private readonly object saveLock = new object();
private readonly SemaphoreSlim saveLock = new SemaphoreSlim(1, 1);
public void Save()
public async Task Save()
{
lock (saveLock)
await saveLock.WaitAsync();
try
{
File.WriteAllText(filePath, JsonConvert.SerializeObject(configs, Formatting.Indented));
}
finally
{
saveLock.Release();
}
}
}
@ -245,7 +255,7 @@ namespace NadekoBot.Classes
LogserverIgnoreChannels = new ObservableCollection<ulong>();
}
public event PropertyChangedEventHandler PropertyChanged = delegate { SpecificConfigurations.Default.Save(); };
public event PropertyChangedEventHandler PropertyChanged = async delegate { await SpecificConfigurations.Default.Save().ConfigureAwait(false); };
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{

View File

@ -70,7 +70,7 @@ namespace NadekoBot.Modules.Administration
{
var conf = SpecificConfigurations.Default.Of(e.Server.Id);
conf.AutoDeleteMessagesOnCommand = !conf.AutoDeleteMessagesOnCommand;
Classes.JSONModels.ConfigHandler.SaveConfig();
await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false);
if (conf.AutoDeleteMessagesOnCommand)
await e.Channel.SendMessage("❗`Now automatically deleting successfull command invokations.`");
else

View File

@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Administration.Commands
NadekoBot.Config.CustomReactions[name].Add(message);
else
NadekoBot.Config.CustomReactions.Add(name, new System.Collections.Generic.List<string>() { message });
await Task.Run(() => Classes.JSONModels.ConfigHandler.SaveConfig()).ConfigureAwait(false);
await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"Added {name} : {message}").ConfigureAwait(false);
});
@ -140,7 +140,7 @@ namespace NadekoBot.Modules.Administration.Commands
index = index - 1;
NadekoBot.Config.CustomReactions[name][index] = msg;
await Task.Run(() => Classes.JSONModels.ConfigHandler.SaveConfig()).ConfigureAwait(false);
await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"Edited response #{index + 1} from `{name}`").ConfigureAwait(false);
});
@ -183,7 +183,7 @@ namespace NadekoBot.Modules.Administration.Commands
NadekoBot.Config.CustomReactions.Remove(name);
message = $"Deleted custom reaction: `{name}`";
}
await Task.Run(() => Classes.JSONModels.ConfigHandler.SaveConfig()).ConfigureAwait(false);
await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage(message).ConfigureAwait(false);
});
}

View File

@ -7,8 +7,10 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using Timer = System.Timers.Timer;
namespace NadekoBot.Modules.Administration.Commands
{
@ -36,7 +38,7 @@ namespace NadekoBot.Modules.Administration.Commands
{"%trivia%", () => Games.Commands.TriviaCommands.RunningTrivias.Count.ToString()}
};
private readonly object playingPlaceholderLock = new object();
private readonly SemaphoreSlim playingPlaceholderLock = new SemaphoreSlim(1,1);
public PlayingRotate(DiscordModule module) : base(module)
{
@ -47,7 +49,9 @@ namespace NadekoBot.Modules.Administration.Commands
{
i++;
var status = "";
lock (playingPlaceholderLock)
//wtf am i doing, just use a queue ffs
await playingPlaceholderLock.WaitAsync().ConfigureAwait(false);
try
{
if (PlayingPlaceholders.Count == 0
|| NadekoBot.Config.RotatingStatuses.Count == 0
@ -59,6 +63,7 @@ namespace NadekoBot.Modules.Administration.Commands
status = PlayingPlaceholders.Aggregate(status,
(current, kvp) => current.Replace(kvp.Key, kvp.Value()));
}
finally { playingPlaceholderLock.Release(); }
if (string.IsNullOrWhiteSpace(status))
return;
await Task.Run(() => { NadekoBot.Client.SetGame(status); });
@ -71,14 +76,18 @@ namespace NadekoBot.Modules.Administration.Commands
public Func<CommandEventArgs, Task> DoFunc() => async e =>
{
lock (playingPlaceholderLock)
await playingPlaceholderLock.WaitAsync().ConfigureAwait(false);
try
{
if (timer.Enabled)
timer.Stop();
else
timer.Start();
NadekoBot.Config.IsRotatingStatus = timer.Enabled;
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
}
finally {
playingPlaceholderLock.Release();
}
await e.Channel.SendMessage($"❗`Rotating playing status has been {(timer.Enabled ? "enabled" : "disabled")}.`").ConfigureAwait(false);
};
@ -102,10 +111,15 @@ namespace NadekoBot.Modules.Administration.Commands
var arg = e.GetArg("text");
if (string.IsNullOrWhiteSpace(arg))
return;
lock (playingPlaceholderLock)
await playingPlaceholderLock.WaitAsync().ConfigureAwait(false);
try
{
NadekoBot.Config.RotatingStatuses.Add(arg);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig();
}
finally
{
playingPlaceholderLock.Release();
}
await e.Channel.SendMessage("🆗 `Added a new playing string.`").ConfigureAwait(false);
});
@ -137,14 +151,15 @@ namespace NadekoBot.Modules.Administration.Commands
var arg = e.GetArg("number");
int num;
string str;
lock (playingPlaceholderLock)
{
await playingPlaceholderLock.WaitAsync().ConfigureAwait(false);
try {
if (!int.TryParse(arg.Trim(), out num) || num <= 0 || num > NadekoBot.Config.RotatingStatuses.Count)
return;
str = NadekoBot.Config.RotatingStatuses[num - 1];
NadekoBot.Config.RotatingStatuses.RemoveAt(num - 1);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
}
finally { playingPlaceholderLock.Release(); }
await e.Channel.SendMessage($"🆗 `Removed playing string #{num}`({str})").ConfigureAwait(false);
});
}

View File

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using NadekoBot.Modules.Permissions.Classes;
using System.Threading;
namespace NadekoBot.Modules.ClashOfClans
{
@ -16,8 +17,6 @@ namespace NadekoBot.Modules.ClashOfClans
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; } = new ConcurrentDictionary<ulong, List<ClashWar>>();
private readonly object writeLock = new object();
public override void Install(ModuleManager manager)
{
manager.CreateCommands("", cgb =>

View File

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games.Commands
@ -59,7 +60,7 @@ namespace NadekoBot.Modules.Games.Commands
//channelid/messageid pair
ConcurrentDictionary<ulong, IEnumerable<Message>> plantedFlowerChannels = new ConcurrentDictionary<ulong, IEnumerable<Message>>();
private object locker = new object();
private SemaphoreSlim locker = new SemaphoreSlim(1,1);
internal override void Init(CommandGroupBuilder cgb)
{
@ -84,32 +85,34 @@ namespace NadekoBot.Modules.Games.Commands
cgb.CreateCommand(Module.Prefix + "plant")
.Description("Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost)")
.Do(e =>
.Do(async e =>
{
lock (locker)
await locker.WaitAsync().ConfigureAwait(false);
try
{
if (plantedFlowerChannels.ContainsKey(e.Channel.Id))
{
e.Channel.SendMessage($"There is already a {NadekoBot.Config.CurrencyName} in this channel.");
await e.Channel.SendMessage($"There is already a {NadekoBot.Config.CurrencyName} in this channel.").ConfigureAwait(false);
return;
}
var removed = FlowersHandler.RemoveFlowers(e.User, "Planted a flower.", 1, true).GetAwaiter().GetResult();
var removed = await FlowersHandler.RemoveFlowers(e.User, "Planted a flower.", 1, true).ConfigureAwait(false);
if (!removed)
{
e.Channel.SendMessage($"You don't have any {NadekoBot.Config.CurrencyName}s.").Wait();
await e.Channel.SendMessage($"You don't have any {NadekoBot.Config.CurrencyName}s.").ConfigureAwait(false);
return;
}
var file = GetRandomCurrencyImagePath();
Message msg;
if (file == null)
msg = e.Channel.SendMessage(NadekoBot.Config.CurrencySign).GetAwaiter().GetResult();
msg = await e.Channel.SendMessage(NadekoBot.Config.CurrencySign).ConfigureAwait(false);
else
msg = e.Channel.SendFile(file).GetAwaiter().GetResult();
msg = await e.Channel.SendFile(file).ConfigureAwait(false);
var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.Config.CurrencyName[0]);
var msg2 = e.Channel.SendMessage($"Oh how Nice! **{e.User.Name}** planted {(vowelFirst ? "an" : "a")} {NadekoBot.Config.CurrencyName}. Pick it using {Module.Prefix}pick").GetAwaiter().GetResult();
var msg2 = await e.Channel.SendMessage($"Oh how Nice! **{e.User.Name}** planted {(vowelFirst ? "an" : "a")} {NadekoBot.Config.CurrencyName}. Pick it using {Module.Prefix}pick").ConfigureAwait(false);
plantedFlowerChannels.TryAdd(e.Channel.Id, new[] { msg, msg2 });
}
finally { locker.Release(); }
});
cgb.CreateCommand(Prefix + "gencurrency")
@ -129,12 +132,12 @@ namespace NadekoBot.Modules.Games.Commands
int throwaway;
if (config.GenerateCurrencyChannels.TryRemove(e.Channel.Id, out throwaway))
{
await e.Channel.SendMessage("`Currency generation disabled on this channel.`");
await e.Channel.SendMessage("`Currency generation disabled on this channel.`").ConfigureAwait(false);
}
else
{
if (config.GenerateCurrencyChannels.TryAdd(e.Channel.Id, cd))
await e.Channel.SendMessage($"`Currency generation enabled on this channel. Cooldown is {cd} minutes.`");
await e.Channel.SendMessage($"`Currency generation enabled on this channel. Cooldown is {cd} minutes.`").ConfigureAwait(false);
}
});
}

View File

@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Games.Commands.Trivia
{
internal class TriviaGame
{
private readonly object _guessLock = new object();
private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1,1);
private Server server { get; }
private Channel channel { get; }
@ -113,7 +113,8 @@ namespace NadekoBot.Modules.Games.Commands.Trivia
if (e.User.Id == NadekoBot.Client.CurrentUser.Id) return;
var guess = false;
lock (_guessLock)
await _guessLock.WaitAsync().ConfigureAwait(false);
try
{
if (GameActive && CurrentQuestion.IsAnswerCorrect(e.Message.Text) && !triviaCancelSource.IsCancellationRequested)
{
@ -122,6 +123,7 @@ namespace NadekoBot.Modules.Games.Commands.Trivia
guess = true;
}
}
catch { _guessLock.Release(); }
if (!guess) return;
triviaCancelSource.Cancel();
await channel.SendMessage($"☑️ {e.User.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);

View File

@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Music.Classes
public int BufferSize { get; }
private readonly object readWriteLock = new object();
private readonly SemaphoreSlim readWriteLock = new SemaphoreSlim(1, 1);
public PoopyBuffer(int size)
{
@ -32,12 +32,15 @@ namespace NadekoBot.Modules.Music.Classes
ringBuffer = new byte[size];
}
public int Read(byte[] buffer, int count)
public Task<int> ReadAsync(byte[] buffer, int count)
{
return Task.Run(async () =>
{
if (buffer.Length < count)
throw new ArgumentException();
//Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***");
lock (readWriteLock)
await readWriteLock.WaitAsync().ConfigureAwait(false);
try
{
//read as much as you can if you're reading too much
if (count > ContentLength)
@ -77,6 +80,9 @@ namespace NadekoBot.Modules.Music.Classes
ReadPosition = readFromStart;
return count;
}
finally { readWriteLock.Release(); }
});
}
public async Task WriteAsync(byte[] buffer, int count, CancellationToken cancelToken)
@ -89,9 +95,12 @@ namespace NadekoBot.Modules.Music.Classes
if (cancelToken.IsCancellationRequested)
return;
}
await Task.Run(async () =>
{
//the while above assures that i cannot write past readposition with my write, so i don't have to check
// *unless its multithreaded or task is not awaited
lock (readWriteLock)
await readWriteLock.WaitAsync().ConfigureAwait(false);
try
{
// if i can just write without hitting buffer.length, do it
if (WritePosition + count < BufferSize)
@ -115,6 +124,8 @@ namespace NadekoBot.Modules.Music.Classes
WritePosition = wroteFromStart;
}
finally { readWriteLock.Release(); }
});
}
}
}

View File

@ -155,7 +155,7 @@ namespace NadekoBot.Modules.Music.Classes
{
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
byte[] buffer = new byte[blockSize];
var read = songBuffer.Read(buffer, blockSize);
var read = await songBuffer.ReadAsync(buffer, blockSize).ConfigureAwait(false);
unchecked
{
bytesSent += (ulong)read;

View File

@ -689,7 +689,7 @@ namespace NadekoBot.Modules.Permissions
if (!e.Message.MentionedUsers.Any()) return;
var usr = e.Message.MentionedUsers.First();
NadekoBot.Config.UserBlacklist.Add(usr.Id);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"`Sucessfully blacklisted user {usr.Name}`").ConfigureAwait(false);
}).ConfigureAwait(false);
});
@ -707,7 +707,7 @@ namespace NadekoBot.Modules.Permissions
if (NadekoBot.Config.UserBlacklist.Contains(usr.Id))
{
NadekoBot.Config.UserBlacklist.Remove(usr.Id);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"`Sucessfully unblacklisted user {usr.Name}`").ConfigureAwait(false);
}
else
@ -727,7 +727,7 @@ namespace NadekoBot.Modules.Permissions
if (!e.Message.MentionedChannels.Any()) return;
var ch = e.Message.MentionedChannels.First();
NadekoBot.Config.UserBlacklist.Add(ch.Id);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"`Sucessfully blacklisted channel {ch.Name}`").ConfigureAwait(false);
}).ConfigureAwait(false);
});
@ -742,7 +742,7 @@ namespace NadekoBot.Modules.Permissions
if (!e.Message.MentionedChannels.Any()) return;
var ch = e.Message.MentionedChannels.First();
NadekoBot.Config.UserBlacklist.Remove(ch.Id);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"`Sucessfully blacklisted channel {ch.Name}`").ConfigureAwait(false);
}).ConfigureAwait(false);
});
@ -767,7 +767,7 @@ namespace NadekoBot.Modules.Permissions
}
var serverId = server.Id;
NadekoBot.Config.ServerBlacklist.Add(serverId);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
//cleanup trivias and typeracing
Modules.Games.Commands.Trivia.TriviaGame trivia;
TriviaCommands.RunningTrivias.TryRemove(serverId, out trivia);

View File

@ -30,8 +30,6 @@ namespace NadekoBot.Modules.Searches.Commands
}
private static Dictionary<string, CachedChampion> CachedChampionImages = new Dictionary<string, CachedChampion>();
private readonly object cacheLock = new object();
private System.Timers.Timer clearTimer { get; } = new System.Timers.Timer();
public LoLCommands(DiscordModule module) : base(module)
@ -42,7 +40,6 @@ namespace NadekoBot.Modules.Searches.Commands
{
try
{
lock (cacheLock)
CachedChampionImages = CachedChampionImages
.Where(kvp => DateTime.Now - kvp.Value.AddedAt > new TimeSpan(1, 0, 0))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
@ -87,10 +84,8 @@ namespace NadekoBot.Modules.Searches.Commands
var resolvedRole = role;
var name = e.GetArg("champ").Replace(" ", "").ToLower();
CachedChampion champ = null;
lock (cacheLock)
{
CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ);
}
if(CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ))
if (champ != null)
{
champ.ImageStream.Position = 0;
@ -121,13 +116,9 @@ namespace NadekoBot.Modules.Searches.Commands
role = allData[0]["role"].ToString();
resolvedRole = ResolvePos(role);
}
lock (cacheLock)
{
CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ);
}
if(CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ))
if (champ != null)
{
Console.WriteLine("Sending lol image from cache.");
champ.ImageStream.Position = 0;
await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false);
return;

View File

@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Searches.Commands
}
}
catch { }
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
};
checkTimer.Start();
}
@ -254,7 +254,7 @@ namespace NadekoBot.Modules.Searches.Commands
}
config.ObservingStreams.Remove(toRemove);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($":ok: Removed `{toRemove.Username}`'s stream from notifications.").ConfigureAwait(false);
});

View File

@ -197,7 +197,7 @@ namespace NadekoBot
return;
}
#if NADEKO_RELEASE
await Task.Delay(90000).ConfigureAwait(false);
await Task.Delay(100000).ConfigureAwait(false);
#else
await Task.Delay(1000).ConfigureAwait(false);
#endif

View File

@ -78,13 +78,14 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>

View File

@ -4,6 +4,8 @@ using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Classes.JSONModels
{
@ -187,13 +189,17 @@ Nadeko Support Server: <https://discord.gg/0ehQwTK2RBjAxzEY>";
public static class ConfigHandler
{
private static readonly object configLock = new object();
public static void SaveConfig()
private static readonly SemaphoreSlim configLock = new SemaphoreSlim(1, 1);
public static async Task SaveConfig()
{
lock (configLock)
await configLock.WaitAsync();
try
{
File.WriteAllText("data/config.json", JsonConvert.SerializeObject(NadekoBot.Config, Formatting.Indented));
}
finally {
configLock.Release();
}
}
public static bool IsBlackListed(MessageEventArgs evArgs) => IsUserBlacklisted(evArgs.User.Id) ||