-
-
Notifications
You must be signed in to change notification settings - Fork 713
Add solution for Challenge 7 by nzamulov #683
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| // Package challenge7 contains the solution for Challenge 7: Bank Account with Error Handling. | ||
| package challenge7 | ||
|
|
||
| import "sync" | ||
|
|
||
| // BankAccount represents a bank account with balance management and minimum balance requirements. | ||
| type BankAccount struct { | ||
| ID string | ||
| Owner string | ||
| Balance float64 | ||
| MinBalance float64 | ||
| mu sync.Mutex // For thread safety | ||
| } | ||
|
|
||
| // Constants for account operations | ||
| const ( | ||
| MaxTransactionAmount = 10000.0 // Example limit for deposits/withdrawals | ||
| ) | ||
|
|
||
| // AccountError is a general error type for bank account operations. | ||
| type AccountError struct { | ||
| err string | ||
| } | ||
|
|
||
| func NewAccountError(err string) AccountError { | ||
| return AccountError{ | ||
| err: err, | ||
| } | ||
| } | ||
|
|
||
| func (e AccountError) Error() string { | ||
| return "Account error: " + e.err | ||
| } | ||
|
|
||
| // InsufficientFundsError occurs when a withdrawal or transfer would bring the balance below minimum. | ||
| type InsufficientFundsError struct {} | ||
|
|
||
| func (e InsufficientFundsError) Error() string { | ||
| return "Insufficient funds error" | ||
| } | ||
|
|
||
| // NegativeAmountError occurs when an amount for deposit, withdrawal, or transfer is negative. | ||
| type NegativeAmountError struct {} | ||
|
|
||
| func (e NegativeAmountError) Error() string { | ||
| return "Negative amount error" | ||
| } | ||
|
|
||
| // ExceedsLimitError occurs when a deposit or withdrawal amount exceeds the defined limit. | ||
| type ExceedsLimitError struct {} | ||
|
|
||
| func (e ExceedsLimitError) Error() string { | ||
| return "Exceeds limit error" | ||
| } | ||
|
|
||
| // NewBankAccount creates a new bank account with the given parameters. | ||
| // It returns an error if any of the parameters are invalid. | ||
| func NewBankAccount(id, owner string, initialBalance, minBalance float64) (*BankAccount, error) { | ||
| if len(id) == 0 { | ||
| return nil, NewAccountError("empty id") | ||
| } | ||
| if len(owner) == 0 { | ||
| return nil, NewAccountError("empty owner") | ||
| } | ||
| if initialBalance < 0 { | ||
| return nil, NegativeAmountError{} | ||
| } | ||
| if minBalance < 0 { | ||
| return nil, NegativeAmountError{} | ||
| } | ||
| if initialBalance < minBalance { | ||
| return nil, InsufficientFundsError{} | ||
| } | ||
| return &BankAccount{ | ||
| ID: id, | ||
| Owner: owner, | ||
| Balance: initialBalance, | ||
| MinBalance: minBalance, | ||
| }, nil | ||
| } | ||
|
|
||
| // Deposit adds the specified amount to the account balance. | ||
| // It returns an error if the amount is invalid or exceeds the transaction limit. | ||
| func (a *BankAccount) Deposit(amount float64) error { | ||
| if amount < 0 { | ||
| return NegativeAmountError{} | ||
| } | ||
| if amount > MaxTransactionAmount { | ||
| return ExceedsLimitError{} | ||
| } | ||
|
|
||
| a.mu.Lock() | ||
| defer a.mu.Unlock() | ||
|
|
||
| a.Balance += amount | ||
| return nil | ||
| } | ||
|
|
||
| // Withdraw removes the specified amount from the account balance. | ||
| // It returns an error if the amount is invalid, exceeds the transaction limit, | ||
| // or would bring the balance below the minimum required balance. | ||
| func (a *BankAccount) Withdraw(amount float64) error { | ||
| if amount < 0 { | ||
| return NegativeAmountError{} | ||
| } | ||
| if amount > MaxTransactionAmount { | ||
| return ExceedsLimitError{} | ||
| } | ||
|
|
||
| a.mu.Lock() | ||
| defer a.mu.Unlock() | ||
|
|
||
| if a.Balance - amount < a.MinBalance { | ||
| return InsufficientFundsError{} | ||
| } | ||
|
|
||
| a.Balance -= amount | ||
| return nil | ||
| } | ||
|
|
||
| // Transfer moves the specified amount from this account to the target account. | ||
| // It returns an error if the amount is invalid, exceeds the transaction limit, | ||
| // or would bring the balance below the minimum required balance. | ||
| func (a *BankAccount) Transfer(amount float64, target *BankAccount) error { | ||
| if target == nil { | ||
| return NewAccountError("target account is nil") | ||
| } | ||
| if a == target { | ||
| return NewAccountError("cannot transfer to same account") | ||
| } | ||
| if amount < 0 { | ||
| return NegativeAmountError{} | ||
| } | ||
| if amount > MaxTransactionAmount { | ||
| return ExceedsLimitError{} | ||
| } | ||
|
|
||
| first, second := a, target | ||
| if a.ID > target.ID { | ||
| first, second = target, a | ||
| } | ||
|
|
||
| first.mu.Lock() | ||
| defer first.mu.Unlock() | ||
| second.mu.Lock() | ||
| defer second.mu.Unlock() | ||
|
|
||
| if a.Balance - amount < a.MinBalance { | ||
| return InsufficientFundsError{} | ||
| } | ||
|
|
||
| a.Balance -= amount | ||
| target.Balance += amount | ||
| return nil | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deadlock risk when two accounts share the same ID
Lock ordering swaps only when
a.ID > target.ID. If two distinct accounts happen to share the same ID, goroutine A transferring from account1→account2 locks in the sequence (account1, account2) while goroutine B transferring from account2→account1 also locks (account2, account1). Both goroutines then block forever waiting on the other mutex. Because ID uniqueness is not enforced anywhere, this deadlock is reachable at runtime. To make the ordering unambiguous, add a deterministic tie-breaker (e.g., pointer address) when IDs compare equal so every transfer uses the same lock order.🤖 Prompt for AI Agents