New evaluator that uses only 133 MB of memory and improves performance of 6- and 7-card hand evaluations
This commit is contained in:
parent
696bf46555
commit
7819b1ee27
18 changed files with 4367 additions and 7701 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1,283 +1,77 @@
|
|||
//! # Error Handling for the LUT Hand Evaluator System
|
||||
//!
|
||||
//! Comprehensive error types and handling for the lookup table hand evaluation system.
|
||||
//! This module provides detailed error reporting for all potential failure modes in
|
||||
//! the evaluator, including file I/O errors, memory allocation failures, and
|
||||
//! invalid input validation.
|
||||
//!
|
||||
//! ## Error Categories
|
||||
//!
|
||||
//! The evaluator can encounter several categories of errors:
|
||||
//!
|
||||
//! ### File I/O Errors
|
||||
//! - **File not found**: Lookup table files missing or inaccessible
|
||||
//! - **File corruption**: Checksum validation failures or malformed data
|
||||
//! - **Permission errors**: Insufficient permissions for file operations
|
||||
//! - **Disk space**: Insufficient space for table generation or caching
|
||||
//!
|
||||
//! ### Memory Management Errors
|
||||
//! - **Allocation failures**: System unable to allocate memory for tables
|
||||
//! - **Out of memory**: Table initialization exceeds available memory
|
||||
//! - **Memory corruption**: Invalid memory access patterns detected
|
||||
//!
|
||||
//! ### Input Validation Errors
|
||||
//! - **Invalid card count**: Wrong number of cards for evaluation type
|
||||
//! - **Malformed cards**: Invalid card data or encoding errors
|
||||
//! - **Hand configuration**: Invalid hand structure or card combinations
|
||||
//!
|
||||
//! ### Table Integrity Errors
|
||||
//! - **Checksum failures**: File integrity validation errors
|
||||
//! - **Format errors**: Incompatible file format or version mismatches
|
||||
//! - **Initialization failures**: Table generation or loading errors
|
||||
//!
|
||||
//! ## Error Handling Strategy
|
||||
//!
|
||||
//! The evaluator implements a robust error handling strategy:
|
||||
//!
|
||||
//! 1. **Graceful Degradation**: System continues operation with partial failures
|
||||
//! 2. **Automatic Recovery**: Attempts to regenerate corrupted tables
|
||||
//! 3. **Detailed Reporting**: Comprehensive error messages with context
|
||||
//! 4. **Type Safety**: Strongly typed error variants for better error handling
|
||||
//!
|
||||
//! ## Usage Examples
|
||||
//!
|
||||
//! ### Basic Error Handling
|
||||
//!
|
||||
//! ```rust
|
||||
//! use math::{Evaluator, evaluator::errors::EvaluatorError};
|
||||
//!
|
||||
//! // Attempt to create evaluator (may fail if tables are corrupted)
|
||||
//! match Evaluator::new() {
|
||||
//! Ok(evaluator) => {
|
||||
//! println!("Evaluator initialized successfully");
|
||||
//! // Use evaluator...
|
||||
//! }
|
||||
//! Err(EvaluatorError::FileNotFound(msg)) => {
|
||||
//! println!("Table files missing: {}", msg);
|
||||
//! println!("Tables will be generated automatically on first use");
|
||||
//! }
|
||||
//! Err(EvaluatorError::ChecksumValidationFailed(msg)) => {
|
||||
//! println!("Table file corrupted: {}", msg);
|
||||
//! println!("Tables will be regenerated");
|
||||
//! }
|
||||
//! Err(e) => {
|
||||
//! println!("Unexpected error: {}", e);
|
||||
//! return;
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### File I/O Error Recovery
|
||||
//!
|
||||
//! ```rust
|
||||
//! use math::evaluator::file_io::{LutFileManager, TableType};
|
||||
//! use math::evaluator::errors::EvaluatorError;
|
||||
//!
|
||||
//! let manager = LutFileManager::default();
|
||||
//!
|
||||
//! // Attempt to read a table file
|
||||
//! match manager.read_table(TableType::FiveCard) {
|
||||
//! Ok(table) => {
|
||||
//! println!("Table loaded successfully");
|
||||
//! }
|
||||
//! Err(EvaluatorError::FileNotFound(_)) => {
|
||||
//! println!("Table file not found - will be generated");
|
||||
//! }
|
||||
//! Err(EvaluatorError::ChecksumValidationFailed(msg)) => {
|
||||
//! println!("Table corrupted: {} - regenerating", msg);
|
||||
//! // Trigger table regeneration
|
||||
//! let mut evaluator = Evaluator::instance();
|
||||
//! // Note: Would need mutable reference for regeneration
|
||||
//! }
|
||||
//! Err(e) => {
|
||||
//! println!("Cannot recover from error: {}", e);
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Input Validation
|
||||
//!
|
||||
//! ```rust
|
||||
//! use math::Evaluator;
|
||||
//! use holdem_core::Card;
|
||||
//! use math::evaluator::errors::EvaluatorError;
|
||||
//!
|
||||
//! let evaluator = Evaluator::instance();
|
||||
//!
|
||||
//! // Invalid card count
|
||||
//! let invalid_cards = vec![Card::new(0, 0).unwrap()]; // Only 1 card
|
||||
//! // This would cause an error in actual usage since evaluate_5_card expects exactly 5 cards
|
||||
//!
|
||||
//! // Proper error handling for invalid inputs
|
||||
//! let cards = [Card::new(0, 0).unwrap(); 5]; // Valid 5 cards
|
||||
//! let result = evaluator.evaluate_5_card(&cards);
|
||||
//! // Result is always valid - no error return for evaluation functions
|
||||
//! ```
|
||||
//!
|
||||
//! ## Error Prevention
|
||||
//!
|
||||
//! The evaluator includes several mechanisms to prevent errors:
|
||||
//!
|
||||
//! ### Automatic Table Management
|
||||
//! - **Lazy initialization**: Tables generated only when needed
|
||||
//! - **Atomic writes**: Safe concurrent file operations with rollback
|
||||
//! - **Checksum validation**: File integrity verification on every read
|
||||
//! - **Version checking**: Format compatibility validation
|
||||
//!
|
||||
//! ### Input Validation
|
||||
//! - **Bounds checking**: All array accesses validated
|
||||
//! - **Card validation**: Card data integrity verification
|
||||
//! - **Hash validation**: Perfect hash indices verified within bounds
|
||||
//! - **Memory safety**: All memory operations checked for safety
|
||||
//!
|
||||
//! ### Recovery Mechanisms
|
||||
//! - **Table regeneration**: Automatic regeneration of corrupted tables
|
||||
//! - **Fallback strategies**: Graceful degradation with partial failures
|
||||
//! - **Retry logic**: Automatic retry for transient failures
|
||||
//! - **Diagnostic information**: Detailed logging for troubleshooting
|
||||
//!
|
||||
//! ## Performance Impact
|
||||
//!
|
||||
//! Error handling has minimal performance impact during normal operation:
|
||||
//!
|
||||
//! - **Validation overhead**: <1% of total evaluation time
|
||||
//! - **Memory checks**: Zero-cost in release builds with panic-on-error
|
||||
//! - **File I/O errors**: Only occur during initialization, not evaluation
|
||||
//! - **Bounds checking**: Optimized away in release builds where possible
|
||||
//!
|
||||
//! ## Integration with Application Error Handling
|
||||
//!
|
||||
//! The evaluator errors integrate well with application error handling:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use math::evaluator::errors::EvaluatorError;
|
||||
//! use std::error::Error;
|
||||
//!
|
||||
//! fn handle_poker_error(err: EvaluatorError) -> String {
|
||||
//! match err {
|
||||
//! EvaluatorError::FileNotFound(msg) => {
|
||||
//! format!("Poker tables not found: {}. Please ensure tables are generated.", msg)
|
||||
//! }
|
||||
//! EvaluatorError::MemoryAllocationFailed(msg) => {
|
||||
//! format!("Insufficient memory for poker evaluation: {}. Consider reducing memory usage.", msg)
|
||||
//! }
|
||||
//! EvaluatorError::InvalidCardCount { expected, actual } => {
|
||||
//! format!("Invalid hand size: expected {} cards, got {}", expected, actual)
|
||||
//! }
|
||||
//! _ => format!("Poker evaluation error: {}", err)
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//! Error types for the poker evaluator system
|
||||
|
||||
use std::fmt;
|
||||
// Define a simple error type since thiserror is not available
|
||||
|
||||
/// Errors that can occur during hand evaluation
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum EvaluatorError {
|
||||
/// Invalid number of cards provided
|
||||
InvalidCardCount { expected: usize, actual: usize },
|
||||
|
||||
/// Card not found in deck
|
||||
CardNotFound(String),
|
||||
|
||||
/// Invalid card combination or hand
|
||||
InvalidHand(String),
|
||||
/// Table initialization failed
|
||||
TableInitializationFailed(String),
|
||||
|
||||
/// Memory allocation error
|
||||
MemoryAllocationFailed(String),
|
||||
|
||||
/// Invalid hand configuration
|
||||
InvalidHandConfiguration(String),
|
||||
|
||||
TableInitFailed(String),
|
||||
/// File I/O error
|
||||
FileIoError(String),
|
||||
|
||||
/// Checksum validation error
|
||||
ChecksumValidationFailed(String),
|
||||
|
||||
/// File format error
|
||||
FileFormatError(String),
|
||||
|
||||
/// File not found error
|
||||
FileNotFound(String),
|
||||
/// Invalid rank or suit value
|
||||
InvalidCardValue(String),
|
||||
/// Memory allocation error
|
||||
MemoryAllocationError(String),
|
||||
/// Evaluation algorithm error
|
||||
EvaluationError(String),
|
||||
}
|
||||
|
||||
impl std::error::Error for EvaluatorError {}
|
||||
|
||||
impl fmt::Display for EvaluatorError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
EvaluatorError::InvalidCardCount { expected, actual } => {
|
||||
write!(
|
||||
f,
|
||||
"Invalid number of cards: expected {}, got {}",
|
||||
expected, actual
|
||||
)
|
||||
}
|
||||
EvaluatorError::CardNotFound(msg) => write!(f, "Card not found: {}", msg),
|
||||
EvaluatorError::TableInitializationFailed(msg) => {
|
||||
write!(f, "Failed to initialize lookup tables: {}", msg)
|
||||
}
|
||||
EvaluatorError::MemoryAllocationFailed(msg) => {
|
||||
write!(f, "Memory allocation failed: {}", msg)
|
||||
}
|
||||
EvaluatorError::InvalidHandConfiguration(msg) => {
|
||||
write!(f, "Invalid hand configuration: {}", msg)
|
||||
EvaluatorError::InvalidHand(msg) => write!(f, "Invalid hand: {}", msg),
|
||||
EvaluatorError::TableInitFailed(msg) => {
|
||||
write!(f, "Table initialization failed: {}", msg)
|
||||
}
|
||||
EvaluatorError::FileIoError(msg) => write!(f, "File I/O error: {}", msg),
|
||||
EvaluatorError::ChecksumValidationFailed(msg) => {
|
||||
write!(f, "Checksum validation failed: {}", msg)
|
||||
EvaluatorError::InvalidCardValue(msg) => write!(f, "Invalid card value: {}", msg),
|
||||
EvaluatorError::MemoryAllocationError(msg) => {
|
||||
write!(f, "Memory allocation error: {}", msg)
|
||||
}
|
||||
EvaluatorError::FileFormatError(msg) => write!(f, "File format error: {}", msg),
|
||||
EvaluatorError::FileNotFound(msg) => write!(f, "File not found: {}", msg),
|
||||
EvaluatorError::EvaluationError(msg) => write!(f, "Evaluation error: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for EvaluatorError {}
|
||||
|
||||
impl EvaluatorError {
|
||||
/// Create an invalid card count error
|
||||
pub fn invalid_card_count(expected: usize, actual: usize) -> Self {
|
||||
Self::InvalidCardCount { expected, actual }
|
||||
/// Create a new invalid hand error
|
||||
pub fn invalid_hand(msg: &str) -> Self {
|
||||
EvaluatorError::InvalidHand(msg.to_string())
|
||||
}
|
||||
|
||||
/// Create a card not found error
|
||||
pub fn card_not_found(card: &str) -> Self {
|
||||
Self::CardNotFound(card.to_string())
|
||||
}
|
||||
|
||||
/// Create a table initialization error
|
||||
/// Create a new table initialization error
|
||||
pub fn table_init_failed(msg: &str) -> Self {
|
||||
Self::TableInitializationFailed(msg.to_string())
|
||||
EvaluatorError::TableInitFailed(msg.to_string())
|
||||
}
|
||||
|
||||
/// Create a memory allocation error
|
||||
pub fn memory_alloc_failed(msg: &str) -> Self {
|
||||
Self::MemoryAllocationFailed(msg.to_string())
|
||||
}
|
||||
|
||||
/// Create an invalid hand configuration error
|
||||
pub fn invalid_hand_config(msg: &str) -> Self {
|
||||
Self::InvalidHandConfiguration(msg.to_string())
|
||||
}
|
||||
|
||||
/// Create a file I/O error
|
||||
/// Create a new file I/O error
|
||||
pub fn file_io_error(msg: &str) -> Self {
|
||||
Self::FileIoError(msg.to_string())
|
||||
EvaluatorError::FileIoError(msg.to_string())
|
||||
}
|
||||
|
||||
/// Create a checksum validation error
|
||||
pub fn checksum_validation_failed(msg: &str) -> Self {
|
||||
Self::ChecksumValidationFailed(msg.to_string())
|
||||
/// Create a new invalid card value error
|
||||
pub fn invalid_card_value(msg: &str) -> Self {
|
||||
EvaluatorError::InvalidCardValue(msg.to_string())
|
||||
}
|
||||
|
||||
/// Create a file format error
|
||||
pub fn file_format_error(msg: &str) -> Self {
|
||||
Self::FileFormatError(msg.to_string())
|
||||
/// Create a new memory allocation error
|
||||
pub fn memory_allocation_error(msg: &str) -> Self {
|
||||
EvaluatorError::MemoryAllocationError(msg.to_string())
|
||||
}
|
||||
|
||||
/// Create a file not found error
|
||||
pub fn file_not_found(msg: &str) -> Self {
|
||||
Self::FileNotFound(msg.to_string())
|
||||
/// Create a new evaluation error
|
||||
pub fn evaluation_error(msg: &str) -> Self {
|
||||
EvaluatorError::EvaluationError(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for EvaluatorError {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
EvaluatorError::FileIoError(err.to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,37 +1,38 @@
|
|||
//! # Evaluator Module
|
||||
//! # Hand Evaluation Module
|
||||
//!
|
||||
//! High-performance poker hand evaluation system with lookup tables
|
||||
//! This module provides comprehensive poker hand evaluation capabilities,
|
||||
//! including both traditional lookup table approaches and advanced jump table
|
||||
//! implementations for optimal performance.
|
||||
//!
|
||||
//! ## Architecture Overview
|
||||
//!
|
||||
//! The evaluator module is organized into several sub-modules:
|
||||
//!
|
||||
//! - **`tables`**: Lookup table implementations and jump table structures
|
||||
//! - **`integration`**: Integration utilities and compatibility layers
|
||||
//! - **`property_tests`**: Property-based testing for evaluation correctness
|
||||
//! - **`examples`**: Usage examples and performance benchmarks
|
||||
|
||||
/// Core evaluator functionality
|
||||
pub mod errors;
|
||||
pub mod evaluator;
|
||||
|
||||
/// Lookup table structures and algorithms
|
||||
pub mod examples;
|
||||
pub mod file_io;
|
||||
pub mod integration;
|
||||
pub mod property_tests;
|
||||
pub mod singleton;
|
||||
pub mod tables;
|
||||
|
||||
/// Thread-safe singleton pattern implementation
|
||||
pub mod singleton;
|
||||
|
||||
/// Comprehensive error handling
|
||||
pub mod errors;
|
||||
|
||||
/// Integration utilities for holdem_core types
|
||||
pub mod integration;
|
||||
|
||||
/// Atomic file I/O operations with checksum validation
|
||||
pub mod file_io;
|
||||
|
||||
/// Property-based testing framework
|
||||
#[cfg(test)]
|
||||
pub mod property_tests;
|
||||
|
||||
/// Usage examples and demonstrations
|
||||
pub mod examples;
|
||||
|
||||
/// Performance benchmarks and profiling
|
||||
pub mod benchmarks;
|
||||
|
||||
// Re-export key types for convenience
|
||||
// Re-export commonly used types from local modules
|
||||
pub use errors::EvaluatorError;
|
||||
pub use evaluator::{Evaluator, HandRank, HandValue};
|
||||
pub use file_io::{LutFileManager, TableInfo, TableType};
|
||||
pub use integration::{HandEvaluation, HandEvaluator, HoleCardsEvaluation};
|
||||
pub use singleton::EvaluatorSingleton;
|
||||
|
||||
// Re-export math-specific types
|
||||
pub use tables::JumpTable;
|
||||
|
||||
// Module-level constants
|
||||
pub const MAX_CARDS_IN_HAND: usize = 7;
|
||||
pub const TOTAL_CARDS_IN_DECK: usize = 52;
|
||||
pub const CARDS_PER_SUIT: usize = 13;
|
||||
|
||||
/// Version of the evaluator system
|
||||
pub const EVALUATOR_VERSION: &str = "2.0.0";
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,369 +1,35 @@
|
|||
//! # Thread-Safe Singleton Pattern for Hand Evaluator
|
||||
//!
|
||||
//! Implements a robust singleton pattern for the hand evaluator using
|
||||
//! `once_cell::sync::Lazy` for thread-safe lazy initialization. This module
|
||||
//! ensures that the evaluator instance is created exactly once and safely
|
||||
//! shared across unlimited concurrent threads.
|
||||
//!
|
||||
//! ## Design Philosophy
|
||||
//!
|
||||
//! The singleton pattern is implemented with several key principles:
|
||||
//!
|
||||
//! ### Thread Safety
|
||||
//! - **Lock-free access**: No runtime locks during normal operation
|
||||
//! - **Atomic initialization**: Safe concurrent first-time initialization
|
||||
//! - **Memory safety**: Proper synchronization without data races
|
||||
//! - **Panic safety**: System remains stable under initialization failures
|
||||
//!
|
||||
//! ### Performance Optimization
|
||||
//! - **Lazy initialization**: Tables generated only when first accessed
|
||||
//! - **Zero-cost abstraction**: No overhead after initialization
|
||||
//! - **Efficient cloning**: Arc-based sharing with minimal overhead
|
||||
//! - **Cache locality**: Single instance promotes CPU cache efficiency
|
||||
//!
|
||||
//! ### Reliability Engineering
|
||||
//! - **Fail-fast initialization**: Clear error reporting for setup failures
|
||||
//! - **Graceful degradation**: System continues operation after initialization
|
||||
//! - **Resource management**: Proper cleanup and memory management
|
||||
//! - **Diagnostic capabilities**: Reference counting for debugging
|
||||
//!
|
||||
//! ## Implementation Details
|
||||
//!
|
||||
//! ### Lazy Initialization Strategy
|
||||
//! ```rust
|
||||
//! use once_cell::sync::Lazy;
|
||||
//! use std::sync::Arc;
|
||||
//!
|
||||
//! static EVALUATOR_INSTANCE: Lazy<Arc<Evaluator>> = Lazy::new(|| {
|
||||
//! Arc::new(Evaluator::new().unwrap_or_else(|e| {
|
||||
//! eprintln!("Failed to initialize evaluator singleton: {}", e);
|
||||
//! panic!("Evaluator initialization failed");
|
||||
//! }))
|
||||
//! });
|
||||
//! ```
|
||||
//!
|
||||
//! This approach provides:
|
||||
//! - **Thread-safe initialization**: Multiple threads can safely call `instance()`
|
||||
//! - **Atomic creation**: Instance created exactly once, even under contention
|
||||
//! - **Memory efficiency**: No heap allocation until first access
|
||||
//! - **Panic safety**: Initialization failures are handled gracefully
|
||||
//!
|
||||
//! ### Reference Counting
|
||||
//! The singleton uses `Arc<Evaluator>` for efficient sharing:
|
||||
//!
|
||||
//! - **Strong references**: Track active users of the evaluator
|
||||
//! - **Automatic cleanup**: Memory freed when last reference dropped
|
||||
//! - **Thread safety**: Safe concurrent access without external synchronization
|
||||
//! - **Performance**: Minimal overhead for reference counting operations
|
||||
//!
|
||||
//! ## Usage Patterns
|
||||
//!
|
||||
//! ### Basic Usage
|
||||
//! ```rust
|
||||
//! use math::evaluator::singleton::EvaluatorSingleton;
|
||||
//!
|
||||
//! // Get the global evaluator instance
|
||||
//! let evaluator = EvaluatorSingleton::instance();
|
||||
//!
|
||||
//! // Use for hand evaluation
|
||||
//! let hand_value = evaluator.evaluate_5_card(&cards);
|
||||
//! ```
|
||||
//!
|
||||
//! ### Advanced Usage with Reference Counting
|
||||
//! ```rust
|
||||
//! use math::evaluator::singleton::EvaluatorSingleton;
|
||||
//!
|
||||
//! fn demonstrate_reference_counting() {
|
||||
//! println!("Initial reference count: {}", EvaluatorSingleton::reference_count());
|
||||
//!
|
||||
//! {
|
||||
//! let evaluator1 = EvaluatorSingleton::instance();
|
||||
//! println!("After first instance: {}", EvaluatorSingleton::reference_count());
|
||||
//!
|
||||
//! {
|
||||
//! let evaluator2 = EvaluatorSingleton::instance();
|
||||
//! println!("After second instance: {}", EvaluatorSingleton::reference_count());
|
||||
//! // Both evaluator1 and evaluator2 point to same instance
|
||||
//! }
|
||||
//!
|
||||
//! println!("After inner scope: {}", EvaluatorSingleton::reference_count());
|
||||
//! }
|
||||
//!
|
||||
//! println!("After outer scope: {}", EvaluatorSingleton::reference_count());
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Integration with Application Architecture
|
||||
//! ```rust
|
||||
//! use math::evaluator::singleton::{EvaluatorSingleton, EvaluatorAccess};
|
||||
//! use std::sync::Arc;
|
||||
//!
|
||||
//! // Method 1: Direct singleton access
|
||||
//! fn evaluate_with_singleton(cards: &[Card; 5]) -> HandValue {
|
||||
//! let evaluator = EvaluatorSingleton::instance();
|
||||
//! evaluator.evaluate_5_card(cards)
|
||||
//! }
|
||||
//!
|
||||
//! // Method 2: Using the EvaluatorAccess trait
|
||||
//! fn evaluate_with_trait(cards: &[Card; 5]) -> HandValue {
|
||||
//! let evaluator = <() as EvaluatorAccess>::evaluator();
|
||||
//! evaluator.evaluate_5_card(cards)
|
||||
//! }
|
||||
//!
|
||||
//! // Method 3: Dependency injection pattern
|
||||
//! struct PokerGame {
|
||||
//! evaluator: Arc<Evaluator>,
|
||||
//! }
|
||||
//!
|
||||
//! impl PokerGame {
|
||||
//! fn new() -> Self {
|
||||
//! Self {
|
||||
//! evaluator: EvaluatorSingleton::instance(),
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn evaluate_hand(&self, cards: &[Card; 5]) -> HandValue {
|
||||
//! self.evaluator.evaluate_5_card(cards)
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Performance Characteristics
|
||||
//!
|
||||
//! ### Initialization Performance
|
||||
//! - **First access**: 1-3 seconds (table loading/generation)
|
||||
//! - **Subsequent access**: <1 nanosecond (Arc clone)
|
||||
//! - **Memory allocation**: ~1KB overhead for Arc management
|
||||
//! - **Lock contention**: Zero after initialization
|
||||
//!
|
||||
//! ### Runtime Performance
|
||||
//! - **Instance access**: ~1-2 nanoseconds per call
|
||||
//! - **Reference counting**: ~5-10 nanoseconds per clone/drop
|
||||
//! - **Memory overhead**: 8 bytes per Arc instance (pointer)
|
||||
//! - **Cache efficiency**: Single instance promotes cache locality
|
||||
//!
|
||||
//! ## Thread Safety Analysis
|
||||
//!
|
||||
//! ### Initialization Phase
|
||||
//! During the first call to `instance()`:
|
||||
//! - **Multiple threads**: All block until initialization completes
|
||||
//! - **Single creation**: Only one thread performs actual initialization
|
||||
//! - **Atomic completion**: Other threads see completed instance immediately
|
||||
//! - **Panic handling**: Initialization panics are propagated correctly
|
||||
//!
|
||||
//! ### Post-Initialization Phase
|
||||
//! After successful initialization:
|
||||
//! - **Unlimited readers**: Any number of threads can access simultaneously
|
||||
//! - **Lock-free access**: No synchronization primitives used during access
|
||||
//! - **Memory safety**: Arc ensures safe concurrent access to evaluator
|
||||
//! - **Performance**: No contention or waiting during normal operation
|
||||
//!
|
||||
//! ## Error Handling
|
||||
//!
|
||||
//! The singleton handles initialization errors robustly:
|
||||
//!
|
||||
//! ### Initialization Failures
|
||||
//! - **File I/O errors**: Logged and panicked with clear message
|
||||
//! - **Memory allocation**: Propagated as panic with context
|
||||
//! - **Table corruption**: Automatic regeneration attempted
|
||||
//! - **Recovery mechanisms**: Clear error messages for troubleshooting
|
||||
//!
|
||||
//! ### Runtime Safety
|
||||
//! - **Panic recovery**: System remains stable if user code panics
|
||||
//! - **Resource cleanup**: Proper cleanup even under error conditions
|
||||
//! - **Diagnostic information**: Reference counting for debugging
|
||||
//! - **Graceful degradation**: System continues operation after errors
|
||||
//!
|
||||
//! ## Testing Support
|
||||
//!
|
||||
//! The singleton includes testing utilities:
|
||||
//!
|
||||
//! ### Reference Count Monitoring
|
||||
//! ```rust
|
||||
//! #[cfg(test)]
|
||||
//! mod tests {
|
||||
//! use super::*;
|
||||
//!
|
||||
//! #[test]
|
||||
//! fn test_reference_counting() {
|
||||
//! let initial_count = EvaluatorSingleton::reference_count();
|
||||
//!
|
||||
//! {
|
||||
//! let _instance = EvaluatorSingleton::instance();
|
||||
//! assert_eq!(EvaluatorSingleton::reference_count(), initial_count + 1);
|
||||
//! }
|
||||
//!
|
||||
//! assert_eq!(EvaluatorSingleton::reference_count(), initial_count);
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Thread Safety Testing
|
||||
//! ```rust
|
||||
//! #[cfg(test)]
|
||||
//! mod tests {
|
||||
//! use super::*;
|
||||
//! use std::thread;
|
||||
//!
|
||||
//! #[test]
|
||||
//! fn test_thread_safety() {
|
||||
//! let handles: Vec<_> = (0..10).map(|_| {
|
||||
//! thread::spawn(|| {
|
||||
//! let instance = EvaluatorSingleton::instance();
|
||||
//! // All instances should be identical
|
||||
//! let base_ptr = Arc::as_ptr(&EvaluatorSingleton::instance());
|
||||
//! assert_eq!(Arc::as_ptr(&instance), base_ptr);
|
||||
//! })
|
||||
//! }).collect();
|
||||
//!
|
||||
//! for handle in handles {
|
||||
//! handle.join().unwrap();
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Memory Management
|
||||
//!
|
||||
//! ### Arc-Based Sharing
|
||||
//! The singleton uses `Arc<Evaluator>` for efficient sharing:
|
||||
//!
|
||||
//! - **Shallow cloning**: Only pointer and reference count copied
|
||||
//! - **Atomic operations**: Thread-safe reference count updates
|
||||
//! - **Memory efficiency**: Single evaluator instance in memory
|
||||
//! - **Automatic cleanup**: Memory freed when last Arc dropped
|
||||
//!
|
||||
//! ### Memory Layout
|
||||
//! ```text
|
||||
//! Arc<Evaluator> Memory Layout:
|
||||
//! ┌─────────────────┬─────────────────┐
|
||||
//! │ Arc Header │ Evaluator Data │
|
||||
//! │ Strong Count: 3 │ │
|
||||
//! │ Weak Count: 0 │ Lookup Tables │
|
||||
//! │ Pointer to Data │ File Manager │
|
||||
//! └─────────────────┴─────────────────┘
|
||||
//! ```
|
||||
//!
|
||||
//! ## Best Practices
|
||||
//!
|
||||
//! ### Application Integration
|
||||
//! - **Store Arc reference**: Keep evaluator instance in application structs
|
||||
//! - **Avoid repeated calls**: Cache the instance rather than calling repeatedly
|
||||
//! - **Use dependency injection**: Pass evaluator to components that need it
|
||||
//! - **Monitor reference counts**: Use for debugging memory usage
|
||||
//!
|
||||
//! ### Performance Optimization
|
||||
//! - **Cache instance**: Store in static or application state
|
||||
//! - **Minimize cloning**: Reuse Arc references when possible
|
||||
//! - **Batch operations**: Evaluate multiple hands with single instance
|
||||
//! - **Monitor initialization**: Log initialization time for optimization
|
||||
//!
|
||||
//! ### Error Handling
|
||||
//! - **Handle initialization failures**: Implement proper error handling for startup
|
||||
//! - **Monitor file system**: Check table file integrity periodically
|
||||
//! - **Graceful degradation**: Continue operation with reduced functionality
|
||||
//! - **User feedback**: Provide clear error messages for initialization failures
|
||||
//! Singleton pattern implementation for the poker evaluator
|
||||
|
||||
use super::errors::EvaluatorError;
|
||||
use super::Evaluator;
|
||||
use std::sync::{Arc, Once};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
/// Thread-safe singleton instance using std::sync::Once
|
||||
static INIT: Once = Once::new();
|
||||
static mut EVALUATOR_INSTANCE: Option<Arc<Evaluator>> = None;
|
||||
|
||||
/// Singleton manager for the evaluator
|
||||
pub struct EvaluatorSingleton;
|
||||
/// Singleton instance of the poker evaluator
|
||||
#[derive(Debug)]
|
||||
pub struct EvaluatorSingleton {
|
||||
/// The underlying evaluator instance
|
||||
evaluator: Arc<Evaluator>,
|
||||
}
|
||||
|
||||
impl EvaluatorSingleton {
|
||||
/// Get the global evaluator instance
|
||||
///
|
||||
/// This method provides thread-safe access to the singleton evaluator instance.
|
||||
/// The instance is created lazily on first access and reused for all subsequent calls.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use holdem_core::evaluator::singleton::EvaluatorSingleton;
|
||||
///
|
||||
/// let evaluator = EvaluatorSingleton::instance();
|
||||
/// // Use evaluator for hand evaluation
|
||||
/// ```
|
||||
pub fn instance() -> Arc<Evaluator> {
|
||||
unsafe {
|
||||
INIT.call_once(|| {
|
||||
EVALUATOR_INSTANCE = Some(Arc::new(Evaluator::new().unwrap_or_else(|e| {
|
||||
eprintln!("Failed to initialize evaluator singleton: {:?}", e);
|
||||
panic!("Evaluator initialization failed");
|
||||
})));
|
||||
});
|
||||
EVALUATOR_INSTANCE.as_ref().unwrap().clone()
|
||||
}
|
||||
static INSTANCE: OnceLock<EvaluatorSingleton> = OnceLock::new();
|
||||
let singleton = INSTANCE.get_or_init(|| EvaluatorSingleton {
|
||||
evaluator: Arc::new(Evaluator::new().expect("Failed to create evaluator")),
|
||||
});
|
||||
singleton.evaluator.clone()
|
||||
}
|
||||
|
||||
/// Get the number of active references to the evaluator
|
||||
///
|
||||
/// This is primarily useful for testing and debugging purposes.
|
||||
pub fn reference_count() -> usize {
|
||||
unsafe {
|
||||
EVALUATOR_INSTANCE
|
||||
.as_ref()
|
||||
.map_or(0, |arc| Arc::strong_count(arc))
|
||||
}
|
||||
/// Create a new singleton instance (mainly for testing)
|
||||
pub fn new() -> Result<Self, EvaluatorError> {
|
||||
Ok(EvaluatorSingleton {
|
||||
evaluator: Arc::new(Evaluator::new()?),
|
||||
})
|
||||
}
|
||||
|
||||
/// Reset the singleton instance (for testing purposes)
|
||||
///
|
||||
/// This function is unsafe and should only be used in test environments.
|
||||
/// It will drop the current instance and allow a new one to be created.
|
||||
#[cfg(test)]
|
||||
pub unsafe fn reset() {
|
||||
// Note: once_cell doesn't provide a direct way to reset Lazy instances
|
||||
// This is a limitation for testing, but ensures thread safety in production
|
||||
// For testing, we recommend using mock instances instead
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience trait for easy access to evaluator functionality
|
||||
pub trait EvaluatorAccess {
|
||||
/// Get the evaluator instance
|
||||
fn evaluator() -> Arc<Evaluator>;
|
||||
}
|
||||
|
||||
impl EvaluatorAccess for () {
|
||||
fn evaluator() -> Arc<Evaluator> {
|
||||
EvaluatorSingleton::instance()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_singleton_creation() {
|
||||
let instance1 = EvaluatorSingleton::instance();
|
||||
let instance2 = EvaluatorSingleton::instance();
|
||||
|
||||
// Both instances should be the same object
|
||||
assert_eq!(Arc::as_ptr(&instance1), Arc::as_ptr(&instance2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reference_counting() {
|
||||
let initial_count = EvaluatorSingleton::reference_count();
|
||||
|
||||
{
|
||||
let _instance = EvaluatorSingleton::instance();
|
||||
assert_eq!(EvaluatorSingleton::reference_count(), initial_count + 1);
|
||||
}
|
||||
|
||||
// Note: Reference counting test may not work exactly the same with Once
|
||||
// since we can't easily reset the instance for testing
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_evaluator_access_trait() {
|
||||
let evaluator = <() as EvaluatorAccess>::evaluator();
|
||||
assert!(Arc::strong_count(&evaluator) >= 1);
|
||||
/// Get a reference to the evaluator
|
||||
pub fn evaluator(&self) -> &Evaluator {
|
||||
&self.evaluator
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -77,13 +77,13 @@ pub use board::Street;
|
|||
pub use errors::PokerError;
|
||||
|
||||
/// Re-export evaluator types for convenience
|
||||
pub use evaluator::{Evaluator, HandRank, HandValue};
|
||||
pub use evaluator::evaluator::{Evaluator, HandRank, HandValue};
|
||||
|
||||
/// Re-export singleton functionality
|
||||
pub use evaluator::singleton::EvaluatorSingleton;
|
||||
|
||||
/// Re-export integration utilities
|
||||
pub use evaluator::integration::{HandEvaluation, HandEvaluator, HoleCardsEvaluation};
|
||||
pub use evaluator::integration::{EvaluatorComparison, MathEvaluator};
|
||||
|
||||
/// Re-export file I/O functionality
|
||||
pub use evaluator::file_io::{LutFileManager, TableInfo, TableType};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue