A high-performance, memory-mapped ring buffer implementation in Go, designed for efficient inter-process or inter-thread communication. This library provides a lock-free (with minimal locking) ring buffer backed by memory-mapped files, making it suitable for high-throughput scenarios.
- Memory-mapped file as a shared ring buffer
- Minimal locking for write operations
- Efficient for high message throughput
go get github.com/EBWi11/mmap_ringbuffer
package main
import (
"fmt"
"github.com/EBWi11/mmap_ringbuffer"
)
func main() {
// Create a new ring buffer with 1MB size
rb, err := ringbuffer.NewRingBuffer("/tmp/rb.mmap", 1024*1024, true)
if err != nil {
panic(err)
}
defer rb.Close()
// Write a message
msg := []byte("hello, mmap ringbuffer!")
ok, err := rb.WriteMsg(msg)
if ok && err == nil {
fmt.Println("Message written successfully")
}
// Read a message
readMsg, err := rb.ReadMsg()
if err == nil {
fmt.Printf("Read message: %s\n", string(readMsg))
}
}
package main
import (
"fmt"
"sync"
"github.com/EBWi11/mmap_ringbuffer"
)
func producer(rb *ringbuffer.RingBuffer, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 1000; i++ {
msg := []byte(fmt.Sprintf("message-%d", i))
ok, err := rb.WriteMsg(msg)
if !ok || err != nil {
fmt.Printf("Failed to write: %v\n", err)
return
}
}
}
func consumer(rb *ringbuffer.RingBuffer, wg *sync.WaitGroup) {
defer wg.Done()
for {
msg, err := rb.ReadMsg()
if err == ringbuffer.ErrBufferEmpty {
continue
}
if err != nil {
fmt.Printf("Failed to read: %v\n", err)
return
}
fmt.Printf("Received: %s\n", string(msg))
}
}
func main() {
rb, err := ringbuffer.NewRingBuffer("/tmp/rb.mmap", 1024*1024, true)
if err != nil {
panic(err)
}
defer rb.Close()
var wg sync.WaitGroup
wg.Add(2)
go producer(rb, &wg)
go consumer(rb, &wg)
wg.Wait()
}
func NewRingBuffer(filename string, size int, remove bool) (*RingBuffer, error)
Creates a new ring buffer backed by a memory-mapped file.
filename
: Path to the memory-mapped filesize
: Size of the buffer in bytesremove
: If true, removes any existing file before creating
func OpenRingBuffer(filename string) (*RingBuffer, error)
Opens an existing ring buffer file.
func (r *RingBuffer) WriteMsg(msg []byte) (bool, error)
Writes a message to the buffer. Returns true
and nil
if successful.
- Returns
ErrBufferFull
if the buffer is full - Returns
ErrInvalidSize
if the message is empty or too large - Returns
ErrClosed
if the buffer is closed
func (r *RingBuffer) ReadMsg() ([]byte, error)
Reads a message from the buffer. Returns the message and nil
if successful.
- Returns
ErrBufferEmpty
if the buffer is empty - Returns
ErrClosed
if the buffer is closed
func (r *RingBuffer) Close() error
Closes the ring buffer and releases the memory-mapped file.
ErrBufferFull
: Returned when trying to write to a full bufferErrInvalidSize
: Returned when trying to write an empty or too large messageErrBufferEmpty
: Returned when trying to read from an empty bufferErrClosed
: Returned when trying to use a closed buffer
- The buffer size should be chosen carefully based on your use case
- For high-throughput scenarios, consider using a larger buffer size
- The buffer uses a header of 8 bytes (4 bytes for head, 4 bytes for tail)
- Maximum message size is limited to half of the buffer size
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.