# custom-rwmutex ## Overview `CustomRwMutex` is a lightweight, type-safe wrapper around Go’s standard `sync.RWMutex` that provides safer, more ergonomic access patterns through handler functions. Instead of manually calling `Lock()`/`RLock()` and `Unlock()`/`RUnlock()`, you pass a function to be executed under the appropriate lock, ensuring proper resource management and reducing the risk of deadlocks or unlocked access. This pattern enforces **scope-bound locking**, similar to a `defer`-based RAII pattern, making concurrent code more readable and less error-prone. ## Features - ✅ **Thread-safe**: Built on `sync.RWMutex`, guarantees safe concurrent access. - ✅ **Handler-based API**: Prevents manual lock/unlock mistakes. - ✅ **Error propagation**: Functions passed in can return errors; they’re propagated up cleanly. - ✅ **No external dependencies**: Pure Go, zero dependencies. - ✅ **Minimal overhead**: Just a thin wrapper around standard library primitives. ### Real-World Example: Shared Config ```go type Config struct { Host string Port int } func main() { config := &Config{Host: "localhost", Port: 8080} mu := custom_rwmutex.NewCustomRwMutex() // Update config (writer) err := mu.WriteHandler(func() error { config.Host = "api.example.com" config.Port = 443 return nil }) if err != nil { log.Fatal(err) } // Read config (reader) err = mu.ReadHandler(func() error { fmt.Printf("Current config: %s:%d\n", config.Host, config.Port) return nil }) if err != nil { log.Fatal(err) } } ``` > **Note**: Since `config` is captured in closures, ensure it’s not modified outside the lock context. For full safety, consider returning copies of data from `ReadHandler`. ## Why Use `CustomRwMutex`? | Problem with `sync.RWMutex` | How `CustomRwMutex` Solves It | |-----------------------------|-------------------------------| | Manual `Lock()`/`Unlock()` can be forgotten or mismatched | Automatically handled via `defer` | | Risk of leaving locks held due to early returns | `defer` guarantees unlock even on error | | No built-in error handling for operations | Functions return errors; easily propagated | | Verbose and error-prone patterns | Clean, functional-style API | ## Best Practices - ✅ **Keep handlers short and fast** — long operations under write locks block all readers and writers. - ✅ **Avoid blocking calls** like `time.Sleep()` or HTTP requests in locked sections. - ✅ **Return errors from handlers** — they’re preserved and returned from `ReadHandler`/`WriteHandler`. - ✅ **Do not hold references to external data** outside the handler unless you are *certain* they are thread-safe or copied. - ✅ **For heavy read workloads**, consider returning a copy of the data instead of exposing references: ```go var config Data err := mu.ReadHandler(func() error { result := config // copy the data // do work on result return nil }) ```