From 991d93c826077b8054122acbe8524ef8b0ad0a54 Mon Sep 17 00:00:00 2001 From: Kah Date: Mon, 18 Mar 2019 21:46:04 +0100 Subject: [PATCH 1/2] Make the accounts thread safe --- .../economy/account/LiteUniqueAccount.java | 50 +++++++++++++++++++ .../economy/account/LiteVirtualAccount.java | 50 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteUniqueAccount.java b/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteUniqueAccount.java index e2bd0a8..b48e871 100644 --- a/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteUniqueAccount.java +++ b/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteUniqueAccount.java @@ -31,12 +31,17 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Semaphore; public class LiteUniqueAccount implements UniqueAccount { private PlayerEconService playerService = EconomyLite.getPlayerService(); private CurrencyEconService currencyService = EconomyLite.getCurrencyService(); + static private Semaphore lock = new Semaphore(1); + static private long threadID = 0; + static private int numLock = 0; + private UUID uuid; private String name; @@ -76,9 +81,11 @@ public boolean hasBalance(Currency currency, Set contexts) { @Override public BigDecimal getBalance(Currency currency, Set contexts) { + acquireLock(); if (!hasBalance(currency, contexts)) { playerService.setBalance(uuid, getDefaultBalance(currency), currency, CauseFactory.create("New Account")); } + releaseLock(); return playerService.getBalance(uuid, currency, CauseFactory.create("Get Balance")); } @@ -99,9 +106,12 @@ public TransactionResult setBalance(Currency currency, BigDecimal amount, Cause if (amount.compareTo(BigDecimal.ZERO) == -1 || amount.compareTo(BigDecimal.valueOf(999999999)) == 1) { return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.DEPOSIT, cause); } + acquireLock(); if (playerService.setBalance(uuid, amount, currency, cause)) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.DEPOSIT, cause); } else { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.DEPOSIT, cause); } } @@ -109,6 +119,7 @@ public TransactionResult setBalance(Currency currency, BigDecimal amount, Cause @Override public Map resetBalances(Cause cause, Set contexts) { HashMap results = new HashMap<>(); + acquireLock(); for (Currency currency : currencyService.getCurrencies()) { if (playerService.accountExists(uuid, currency, cause)) { if (playerService.setBalance(uuid, getDefaultBalance(currency), currency, cause)) { @@ -118,64 +129,82 @@ public Map resetBalances(Cause cause, Set } } } + releaseLock(); return results; } @Override public TransactionResult resetBalance(Currency currency, Cause cause, Set contexts) { + acquireLock(); if (playerService.setBalance(uuid, getDefaultBalance(currency), currency, cause)) { + releaseLock(); return resultAndEvent(this, BigDecimal.ZERO, currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause); } else { + releaseLock(); return resultAndEvent(this, BigDecimal.ZERO, currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause); } } @Override public TransactionResult deposit(Currency currency, BigDecimal amount, Cause cause, Set contexts) { + acquireLock(); BigDecimal newBal = getBalance(currency).add(amount); // Check if the new balance is in bounds if (newBal.compareTo(BigDecimal.ZERO) == -1 || newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.DEPOSIT, cause); } if (playerService.deposit(uuid, amount, currency, cause)) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.DEPOSIT, cause); } else { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.DEPOSIT, cause); } } @Override public TransactionResult withdraw(Currency currency, BigDecimal amount, Cause cause, Set contexts) { + acquireLock(); BigDecimal newBal = getBalance(currency).subtract(amount); // Check if the new balance is in bounds if (newBal.compareTo(BigDecimal.ZERO) == -1) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_FUNDS, TransactionTypes.WITHDRAW, cause); } if (newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.WITHDRAW, cause); } if (playerService.withdraw(uuid, amount, currency, cause)) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause); } else { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause); } } @Override public TransferResult transfer(Account to, Currency currency, BigDecimal amount, Cause cause, Set contexts) { + acquireLock(); BigDecimal newBal = to.getBalance(currency).add(amount); // Check if the new balance is in bounds if (newBal.compareTo(BigDecimal.ZERO) == -1 || newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, to, cause); } // Check if the account has enough funds if (amount.compareTo(getBalance(currency)) == 1) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_FUNDS, to, cause); } if (withdraw(currency, amount, cause).getResult().equals(ResultType.SUCCESS) && to.deposit(currency, amount, cause).getResult().equals(ResultType.SUCCESS)) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.SUCCESS, to, cause); } else { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.FAILED, to, cause); } } @@ -208,4 +237,25 @@ public UUID getUniqueId() { return uuid; } + static private void acquireLock() { + if (threadID > 0 && Thread.currentThread().getId() == threadID) { + numLock++; + return; + } + try { + lock.acquire(); + threadID = Thread.currentThread().getId(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + static private void releaseLock() { + if (numLock == 0) { + threadID = 0; + lock.release(); + } else { + numLock--; + } + } } diff --git a/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteVirtualAccount.java b/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteVirtualAccount.java index add6589..edd5918 100644 --- a/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteVirtualAccount.java +++ b/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteVirtualAccount.java @@ -29,12 +29,17 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.Semaphore; public class LiteVirtualAccount implements VirtualAccount { private VirtualEconService virtualService = EconomyLite.getVirtualService(); private CurrencyEconService currencyService = EconomyLite.getCurrencyService(); + static private Semaphore lock = new Semaphore(1); + static private long threadID = 0; + static private int numLock = 0; + private String name; public LiteVirtualAccount(String id) { @@ -63,9 +68,11 @@ public boolean hasBalance(Currency currency, Set contexts) { @Override public BigDecimal getBalance(Currency currency, Set contexts) { + acquireLock(); if (!hasBalance(currency, contexts)) { virtualService.setBalance(name, getDefaultBalance(currency), currency, CauseFactory.create("New Account")); } + releaseLock(); return virtualService.getBalance(name, currency, CauseFactory.create("Get Balance")); } @@ -86,9 +93,12 @@ public TransactionResult setBalance(Currency currency, BigDecimal amount, Cause if (amount.compareTo(BigDecimal.ZERO) == -1 || amount.compareTo(BigDecimal.valueOf(999999999)) == 1) { return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.DEPOSIT, cause); } + acquireLock(); if (virtualService.setBalance(name, amount, currency, cause)) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.DEPOSIT, cause); } else { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.DEPOSIT, cause); } } @@ -96,6 +106,7 @@ public TransactionResult setBalance(Currency currency, BigDecimal amount, Cause @Override public Map resetBalances(Cause cause, Set contexts) { HashMap results = new HashMap<>(); + acquireLock(); for (Currency currency : currencyService.getCurrencies()) { if (virtualService.accountExists(name, currency, cause)) { if (virtualService.setBalance(name, getDefaultBalance(currency), currency, cause)) { @@ -105,64 +116,82 @@ public Map resetBalances(Cause cause, Set } } } + releaseLock(); return results; } @Override public TransactionResult resetBalance(Currency currency, Cause cause, Set contexts) { + acquireLock(); if (virtualService.setBalance(name, getDefaultBalance(currency), currency, cause)) { + releaseLock(); return resultAndEvent(this, BigDecimal.ZERO, currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause); } else { + releaseLock(); return resultAndEvent(this, BigDecimal.ZERO, currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause); } } @Override public TransactionResult deposit(Currency currency, BigDecimal amount, Cause cause, Set contexts) { + acquireLock(); BigDecimal newBal = getBalance(currency).add(amount); // Check if the new balance is in bounds if (newBal.compareTo(BigDecimal.ZERO) == -1 || newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.DEPOSIT, cause); } if (virtualService.deposit(name, amount, currency, cause)) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.DEPOSIT, cause); } else { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.DEPOSIT, cause); } } @Override public TransactionResult withdraw(Currency currency, BigDecimal amount, Cause cause, Set contexts) { + acquireLock(); BigDecimal newBal = getBalance(currency).subtract(amount); // Check if the new balance is in bounds if (newBal.compareTo(BigDecimal.ZERO) == -1) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_FUNDS, TransactionTypes.WITHDRAW, cause); } if (newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.WITHDRAW, cause); } if (virtualService.withdraw(name, amount, currency, cause)) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause); } else { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause); } } @Override public TransferResult transfer(Account to, Currency currency, BigDecimal amount, Cause cause, Set contexts) { + acquireLock(); BigDecimal newBal = to.getBalance(currency).add(amount); // Check if the new balance is in bounds if (newBal.compareTo(BigDecimal.ZERO) == -1 || newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, to, cause); } // Check if the account has enough funds if (amount.compareTo(getBalance(currency)) == 1) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_FUNDS, to, cause); } if (withdraw(currency, amount, cause).getResult().equals(ResultType.SUCCESS) && to.deposit(currency, amount, cause).getResult().equals(ResultType.SUCCESS)) { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.SUCCESS, to, cause); } else { + releaseLock(); return resultAndEvent(this, amount, currency, ResultType.FAILED, to, cause); } } @@ -190,4 +219,25 @@ private TransferResult resultAndEvent(Account account, BigDecimal amount, Curren return result; } + static private void acquireLock() { + if (threadID > 0 && Thread.currentThread().getId() == threadID) { + numLock++; + return; + } + try { + lock.acquire(); + threadID = Thread.currentThread().getId(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + static private void releaseLock() { + if (numLock == 0) { + threadID = 0; + lock.release(); + } else { + numLock--; + } + } } From f572be2a5c81c66e7a7170e70a10b9fec1b0ca8b Mon Sep 17 00:00:00 2001 From: Kah Date: Tue, 19 Mar 2019 23:26:47 +0100 Subject: [PATCH 2/2] More maintainable implementation of Semaphores --- .../github/flibio/economylite/LockUtils.java | 35 ++++ .../economy/account/LiteUniqueAccount.java | 170 +++++++----------- .../economy/account/LiteVirtualAccount.java | 168 +++++++---------- 3 files changed, 168 insertions(+), 205 deletions(-) create mode 100644 src/main/java/io/github/flibio/economylite/LockUtils.java diff --git a/src/main/java/io/github/flibio/economylite/LockUtils.java b/src/main/java/io/github/flibio/economylite/LockUtils.java new file mode 100644 index 0000000..ccf5e9c --- /dev/null +++ b/src/main/java/io/github/flibio/economylite/LockUtils.java @@ -0,0 +1,35 @@ +/* + * This file is part of EconomyLite, licensed under the MIT License (MIT). See the LICENSE file at the root of this project for more information. + */ +package io.github.flibio.economylite; + +import java.util.concurrent.Semaphore; + +public class LockUtils implements AutoCloseable { + static private Semaphore lock = new Semaphore(1); + static private long threadID = 0; + static private int numLock = 0; + + public LockUtils() { + if (threadID > 0 && Thread.currentThread().getId() == threadID) { + numLock++; + return; + } + try { + lock.acquire(); + threadID = Thread.currentThread().getId(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Override + public void close() { + if (numLock == 0) { + threadID = 0; + lock.release(); + } else { + numLock--; + } + } +} diff --git a/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteUniqueAccount.java b/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteUniqueAccount.java index b48e871..60f6ffe 100644 --- a/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteUniqueAccount.java +++ b/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteUniqueAccount.java @@ -5,6 +5,7 @@ import io.github.flibio.economylite.CauseFactory; import io.github.flibio.economylite.EconomyLite; +import io.github.flibio.economylite.LockUtils; import io.github.flibio.economylite.api.CurrencyEconService; import io.github.flibio.economylite.api.PlayerEconService; import io.github.flibio.economylite.impl.economy.event.LiteEconomyTransactionEvent; @@ -31,17 +32,12 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; -import java.util.concurrent.Semaphore; public class LiteUniqueAccount implements UniqueAccount { private PlayerEconService playerService = EconomyLite.getPlayerService(); private CurrencyEconService currencyService = EconomyLite.getCurrencyService(); - static private Semaphore lock = new Semaphore(1); - static private long threadID = 0; - static private int numLock = 0; - private UUID uuid; private String name; @@ -81,11 +77,11 @@ public boolean hasBalance(Currency currency, Set contexts) { @Override public BigDecimal getBalance(Currency currency, Set contexts) { - acquireLock(); - if (!hasBalance(currency, contexts)) { - playerService.setBalance(uuid, getDefaultBalance(currency), currency, CauseFactory.create("New Account")); + try (LockUtils lock = new LockUtils()) { + if (!hasBalance(currency, contexts)) { + playerService.setBalance(uuid, getDefaultBalance(currency), currency, CauseFactory.create("New Account")); + } } - releaseLock(); return playerService.getBalance(uuid, currency, CauseFactory.create("Get Balance")); } @@ -106,106 +102,96 @@ public TransactionResult setBalance(Currency currency, BigDecimal amount, Cause if (amount.compareTo(BigDecimal.ZERO) == -1 || amount.compareTo(BigDecimal.valueOf(999999999)) == 1) { return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.DEPOSIT, cause); } - acquireLock(); - if (playerService.setBalance(uuid, amount, currency, cause)) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.DEPOSIT, cause); - } else { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.DEPOSIT, cause); + try (LockUtils lock = new LockUtils()) { + if (playerService.setBalance(uuid, amount, currency, cause)) { + return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.DEPOSIT, cause); + } else { + return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.DEPOSIT, cause); + } } } @Override public Map resetBalances(Cause cause, Set contexts) { HashMap results = new HashMap<>(); - acquireLock(); - for (Currency currency : currencyService.getCurrencies()) { - if (playerService.accountExists(uuid, currency, cause)) { - if (playerService.setBalance(uuid, getDefaultBalance(currency), currency, cause)) { - results.put(currency, resultAndEvent(this, getBalance(currency), currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause)); - } else { - results.put(currency, resultAndEvent(this, getBalance(currency), currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause)); + try (LockUtils lock = new LockUtils()) { + for (Currency currency : currencyService.getCurrencies()) { + if (playerService.accountExists(uuid, currency, cause)) { + if (playerService.setBalance(uuid, getDefaultBalance(currency), currency, cause)) { + results.put(currency, resultAndEvent(this, getBalance(currency), currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause)); + } else { + results.put(currency, resultAndEvent(this, getBalance(currency), currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause)); + } } } + return results; } - releaseLock(); - return results; } @Override public TransactionResult resetBalance(Currency currency, Cause cause, Set contexts) { - acquireLock(); - if (playerService.setBalance(uuid, getDefaultBalance(currency), currency, cause)) { - releaseLock(); - return resultAndEvent(this, BigDecimal.ZERO, currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause); - } else { - releaseLock(); - return resultAndEvent(this, BigDecimal.ZERO, currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause); + try (LockUtils lock = new LockUtils()) { + if (playerService.setBalance(uuid, getDefaultBalance(currency), currency, cause)) { + return resultAndEvent(this, BigDecimal.ZERO, currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause); + } else { + return resultAndEvent(this, BigDecimal.ZERO, currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause); + } } } @Override public TransactionResult deposit(Currency currency, BigDecimal amount, Cause cause, Set contexts) { - acquireLock(); - BigDecimal newBal = getBalance(currency).add(amount); - // Check if the new balance is in bounds - if (newBal.compareTo(BigDecimal.ZERO) == -1 || newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.DEPOSIT, cause); - } - if (playerService.deposit(uuid, amount, currency, cause)) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.DEPOSIT, cause); - } else { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.DEPOSIT, cause); + try (LockUtils lock = new LockUtils()) { + BigDecimal newBal = getBalance(currency).add(amount); + // Check if the new balance is in bounds + if (newBal.compareTo(BigDecimal.ZERO) == -1 || newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { + return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.DEPOSIT, cause); + } + if (playerService.deposit(uuid, amount, currency, cause)) { + return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.DEPOSIT, cause); + } else { + return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.DEPOSIT, cause); + } } } @Override public TransactionResult withdraw(Currency currency, BigDecimal amount, Cause cause, Set contexts) { - acquireLock(); - BigDecimal newBal = getBalance(currency).subtract(amount); - // Check if the new balance is in bounds - if (newBal.compareTo(BigDecimal.ZERO) == -1) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_FUNDS, TransactionTypes.WITHDRAW, cause); - } - if (newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.WITHDRAW, cause); - } - if (playerService.withdraw(uuid, amount, currency, cause)) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause); - } else { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause); + try (LockUtils lock = new LockUtils()) { + BigDecimal newBal = getBalance(currency).subtract(amount); + // Check if the new balance is in bounds + if (newBal.compareTo(BigDecimal.ZERO) == -1) { + return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_FUNDS, TransactionTypes.WITHDRAW, cause); + } + if (newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { + return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.WITHDRAW, cause); + } + if (playerService.withdraw(uuid, amount, currency, cause)) { + return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause); + } else { + return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause); + } } } @Override public TransferResult transfer(Account to, Currency currency, BigDecimal amount, Cause cause, Set contexts) { - acquireLock(); - BigDecimal newBal = to.getBalance(currency).add(amount); - // Check if the new balance is in bounds - if (newBal.compareTo(BigDecimal.ZERO) == -1 || newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, to, cause); - } - // Check if the account has enough funds - if (amount.compareTo(getBalance(currency)) == 1) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_FUNDS, to, cause); - } - if (withdraw(currency, amount, cause).getResult().equals(ResultType.SUCCESS) - && to.deposit(currency, amount, cause).getResult().equals(ResultType.SUCCESS)) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.SUCCESS, to, cause); - } else { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.FAILED, to, cause); + try (LockUtils lock = new LockUtils()) { + BigDecimal newBal = to.getBalance(currency).add(amount); + // Check if the new balance is in bounds + if (newBal.compareTo(BigDecimal.ZERO) == -1 || newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { + return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, to, cause); + } + // Check if the account has enough funds + if (amount.compareTo(getBalance(currency)) == 1) { + return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_FUNDS, to, cause); + } + if (withdraw(currency, amount, cause).getResult().equals(ResultType.SUCCESS) + && to.deposit(currency, amount, cause).getResult().equals(ResultType.SUCCESS)) { + return resultAndEvent(this, amount, currency, ResultType.SUCCESS, to, cause); + } else { + return resultAndEvent(this, amount, currency, ResultType.FAILED, to, cause); + } } } @@ -236,26 +222,4 @@ private TransferResult resultAndEvent(Account account, BigDecimal amount, Curren public UUID getUniqueId() { return uuid; } - - static private void acquireLock() { - if (threadID > 0 && Thread.currentThread().getId() == threadID) { - numLock++; - return; - } - try { - lock.acquire(); - threadID = Thread.currentThread().getId(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - static private void releaseLock() { - if (numLock == 0) { - threadID = 0; - lock.release(); - } else { - numLock--; - } - } } diff --git a/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteVirtualAccount.java b/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteVirtualAccount.java index edd5918..1f7f10c 100644 --- a/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteVirtualAccount.java +++ b/src/main/java/io/github/flibio/economylite/impl/economy/account/LiteVirtualAccount.java @@ -5,6 +5,7 @@ import io.github.flibio.economylite.CauseFactory; import io.github.flibio.economylite.EconomyLite; +import io.github.flibio.economylite.LockUtils; import io.github.flibio.economylite.api.CurrencyEconService; import io.github.flibio.economylite.api.VirtualEconService; import io.github.flibio.economylite.impl.economy.event.LiteEconomyTransactionEvent; @@ -29,17 +30,12 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.concurrent.Semaphore; public class LiteVirtualAccount implements VirtualAccount { private VirtualEconService virtualService = EconomyLite.getVirtualService(); private CurrencyEconService currencyService = EconomyLite.getCurrencyService(); - static private Semaphore lock = new Semaphore(1); - static private long threadID = 0; - static private int numLock = 0; - private String name; public LiteVirtualAccount(String id) { @@ -68,11 +64,11 @@ public boolean hasBalance(Currency currency, Set contexts) { @Override public BigDecimal getBalance(Currency currency, Set contexts) { - acquireLock(); - if (!hasBalance(currency, contexts)) { - virtualService.setBalance(name, getDefaultBalance(currency), currency, CauseFactory.create("New Account")); + try (LockUtils lock = new LockUtils()) { + if (!hasBalance(currency, contexts)) { + virtualService.setBalance(name, getDefaultBalance(currency), currency, CauseFactory.create("New Account")); + } } - releaseLock(); return virtualService.getBalance(name, currency, CauseFactory.create("Get Balance")); } @@ -93,106 +89,96 @@ public TransactionResult setBalance(Currency currency, BigDecimal amount, Cause if (amount.compareTo(BigDecimal.ZERO) == -1 || amount.compareTo(BigDecimal.valueOf(999999999)) == 1) { return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.DEPOSIT, cause); } - acquireLock(); - if (virtualService.setBalance(name, amount, currency, cause)) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.DEPOSIT, cause); - } else { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.DEPOSIT, cause); + try (LockUtils lock = new LockUtils()) { + if (virtualService.setBalance(name, amount, currency, cause)) { + return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.DEPOSIT, cause); + } else { + return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.DEPOSIT, cause); + } } } @Override public Map resetBalances(Cause cause, Set contexts) { HashMap results = new HashMap<>(); - acquireLock(); - for (Currency currency : currencyService.getCurrencies()) { - if (virtualService.accountExists(name, currency, cause)) { - if (virtualService.setBalance(name, getDefaultBalance(currency), currency, cause)) { - results.put(currency, resultAndEvent(this, getBalance(currency), currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause)); - } else { - results.put(currency, resultAndEvent(this, getBalance(currency), currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause)); + try (LockUtils lock = new LockUtils()) { + for (Currency currency : currencyService.getCurrencies()) { + if (virtualService.accountExists(name, currency, cause)) { + if (virtualService.setBalance(name, getDefaultBalance(currency), currency, cause)) { + results.put(currency, resultAndEvent(this, getBalance(currency), currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause)); + } else { + results.put(currency, resultAndEvent(this, getBalance(currency), currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause)); + } } } } - releaseLock(); return results; } @Override public TransactionResult resetBalance(Currency currency, Cause cause, Set contexts) { - acquireLock(); - if (virtualService.setBalance(name, getDefaultBalance(currency), currency, cause)) { - releaseLock(); - return resultAndEvent(this, BigDecimal.ZERO, currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause); - } else { - releaseLock(); - return resultAndEvent(this, BigDecimal.ZERO, currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause); + try (LockUtils lock = new LockUtils()) { + if (virtualService.setBalance(name, getDefaultBalance(currency), currency, cause)) { + return resultAndEvent(this, BigDecimal.ZERO, currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause); + } else { + return resultAndEvent(this, BigDecimal.ZERO, currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause); + } } } @Override public TransactionResult deposit(Currency currency, BigDecimal amount, Cause cause, Set contexts) { - acquireLock(); - BigDecimal newBal = getBalance(currency).add(amount); - // Check if the new balance is in bounds - if (newBal.compareTo(BigDecimal.ZERO) == -1 || newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.DEPOSIT, cause); - } - if (virtualService.deposit(name, amount, currency, cause)) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.DEPOSIT, cause); - } else { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.DEPOSIT, cause); + try (LockUtils lock = new LockUtils()) { + BigDecimal newBal = getBalance(currency).add(amount); + // Check if the new balance is in bounds + if (newBal.compareTo(BigDecimal.ZERO) == -1 || newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { + return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.DEPOSIT, cause); + } + if (virtualService.deposit(name, amount, currency, cause)) { + return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.DEPOSIT, cause); + } else { + return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.DEPOSIT, cause); + } } } @Override public TransactionResult withdraw(Currency currency, BigDecimal amount, Cause cause, Set contexts) { - acquireLock(); - BigDecimal newBal = getBalance(currency).subtract(amount); - // Check if the new balance is in bounds - if (newBal.compareTo(BigDecimal.ZERO) == -1) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_FUNDS, TransactionTypes.WITHDRAW, cause); - } - if (newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.WITHDRAW, cause); - } - if (virtualService.withdraw(name, amount, currency, cause)) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause); - } else { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause); + try (LockUtils lock = new LockUtils()) { + BigDecimal newBal = getBalance(currency).subtract(amount); + // Check if the new balance is in bounds + if (newBal.compareTo(BigDecimal.ZERO) == -1) { + return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_FUNDS, TransactionTypes.WITHDRAW, cause); + } + if (newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { + return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, TransactionTypes.WITHDRAW, cause); + } + if (virtualService.withdraw(name, amount, currency, cause)) { + return resultAndEvent(this, amount, currency, ResultType.SUCCESS, TransactionTypes.WITHDRAW, cause); + } else { + return resultAndEvent(this, amount, currency, ResultType.FAILED, TransactionTypes.WITHDRAW, cause); + } } } @Override public TransferResult transfer(Account to, Currency currency, BigDecimal amount, Cause cause, Set contexts) { - acquireLock(); - BigDecimal newBal = to.getBalance(currency).add(amount); - // Check if the new balance is in bounds - if (newBal.compareTo(BigDecimal.ZERO) == -1 || newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, to, cause); - } - // Check if the account has enough funds - if (amount.compareTo(getBalance(currency)) == 1) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_FUNDS, to, cause); - } - if (withdraw(currency, amount, cause).getResult().equals(ResultType.SUCCESS) - && to.deposit(currency, amount, cause).getResult().equals(ResultType.SUCCESS)) { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.SUCCESS, to, cause); - } else { - releaseLock(); - return resultAndEvent(this, amount, currency, ResultType.FAILED, to, cause); + try (LockUtils lock = new LockUtils()) { + BigDecimal newBal = to.getBalance(currency).add(amount); + // Check if the new balance is in bounds + if (newBal.compareTo(BigDecimal.ZERO) == -1 || newBal.compareTo(BigDecimal.valueOf(999999999)) == 1) { + return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_SPACE, to, cause); + } + // Check if the account has enough funds + if (amount.compareTo(getBalance(currency)) == 1) { + return resultAndEvent(this, amount, currency, ResultType.ACCOUNT_NO_FUNDS, to, cause); + } + if (withdraw(currency, amount, cause).getResult().equals(ResultType.SUCCESS) + && to.deposit(currency, amount, cause).getResult().equals(ResultType.SUCCESS)) { + return resultAndEvent(this, amount, currency, ResultType.SUCCESS, to, cause); + } else { + return resultAndEvent(this, amount, currency, ResultType.FAILED, to, cause); + } } } @@ -218,26 +204,4 @@ private TransferResult resultAndEvent(Account account, BigDecimal amount, Curren Sponge.getEventManager().post(new LiteEconomyTransactionEvent(result, cause)); return result; } - - static private void acquireLock() { - if (threadID > 0 && Thread.currentThread().getId() == threadID) { - numLock++; - return; - } - try { - lock.acquire(); - threadID = Thread.currentThread().getId(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - static private void releaseLock() { - if (numLock == 0) { - threadID = 0; - lock.release(); - } else { - numLock--; - } - } }