Files
custom-rwmutex/README.md

74 lines
2.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# custom-rwmutex
## Overview
`CustomRwMutex` is a lightweight, type-safe wrapper around Gos 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; theyre 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 its 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** — theyre 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
})
```