wp
This commit is contained in:
163
pagecache/pagecache.go
Normal file
163
pagecache/pagecache.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package pagecache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"gordenko.dev/dima/qb"
|
||||
)
|
||||
|
||||
type readResult struct {
|
||||
Data []byte
|
||||
Err error
|
||||
}
|
||||
|
||||
// INDEX PAGES
|
||||
|
||||
type _page struct {
|
||||
PageNo uint32
|
||||
Buf []byte
|
||||
ReferenceCount int
|
||||
}
|
||||
|
||||
type PageCache struct {
|
||||
mutex sync.Mutex
|
||||
pageSize int
|
||||
verifyPageCRC func([]byte) error
|
||||
file *os.File
|
||||
pages map[uint32]*_page
|
||||
pageWaits map[uint32][]chan readResult
|
||||
pagesToRead []uint32
|
||||
readSignalCh chan struct{}
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
File *os.File
|
||||
PageSize int
|
||||
VerifyPageCRC func([]byte) error
|
||||
}
|
||||
|
||||
func New(opt Options) (*PageCache, error) {
|
||||
if opt.File == nil {
|
||||
return nil, errors.New("File option is required")
|
||||
}
|
||||
if opt.PageSize <= 0 {
|
||||
return nil, errors.New("PageSize option is required")
|
||||
}
|
||||
if opt.VerifyPageCRC == nil {
|
||||
return nil, errors.New("VerifyPageCRC option is required")
|
||||
}
|
||||
s := &PageCache{
|
||||
file: opt.File,
|
||||
pageSize: opt.PageSize,
|
||||
verifyPageCRC: opt.VerifyPageCRC,
|
||||
pages: make(map[uint32]*_page),
|
||||
pageWaits: make(map[uint32][]chan readResult),
|
||||
readSignalCh: make(chan struct{}, 1),
|
||||
}
|
||||
go s.pageReader()
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *PageCache) FetchPage(pageNo uint32) ([]byte, error) {
|
||||
s.mutex.Lock()
|
||||
p, ok := s.pages[pageNo]
|
||||
if ok {
|
||||
p.ReferenceCount++
|
||||
s.mutex.Unlock()
|
||||
return p.Buf, nil
|
||||
}
|
||||
|
||||
resultCh := make(chan readResult, 1)
|
||||
s.pageWaits[pageNo] = append(s.pageWaits[pageNo], resultCh)
|
||||
if len(s.pageWaits[pageNo]) == 1 {
|
||||
s.pagesToRead = append(s.pagesToRead, pageNo)
|
||||
s.mutex.Unlock()
|
||||
|
||||
select {
|
||||
case s.readSignalCh <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
s.mutex.Unlock()
|
||||
}
|
||||
|
||||
result := <-resultCh
|
||||
if result.Err == nil {
|
||||
result.Err = s.verifyPageCRC(result.Data)
|
||||
}
|
||||
return result.Data, result.Err
|
||||
}
|
||||
|
||||
func (s *PageCache) ReleasePage(pageNo uint32) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
p, ok := s.pages[pageNo]
|
||||
if ok {
|
||||
if p.ReferenceCount > 0 {
|
||||
p.ReferenceCount--
|
||||
return
|
||||
} else {
|
||||
qb.Abort(
|
||||
qb.ReferenceCountBug,
|
||||
fmt.Errorf("call releasePage on page %d with reference count = %d",
|
||||
pageNo, p.ReferenceCount),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PageCache) pageReader() {
|
||||
for {
|
||||
select {
|
||||
case <-s.readSignalCh:
|
||||
s.readPages()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PageCache) readPages() {
|
||||
s.mutex.Lock()
|
||||
if len(s.pagesToRead) == 0 {
|
||||
s.mutex.Unlock()
|
||||
return
|
||||
}
|
||||
pagesToRead := s.pagesToRead
|
||||
s.pagesToRead = nil
|
||||
s.mutex.Unlock()
|
||||
|
||||
for _, pageNo := range pagesToRead {
|
||||
buf := make([]byte, s.pageSize)
|
||||
off := int(pageNo-1) * s.pageSize
|
||||
n, err := s.file.ReadAt(buf, int64(off))
|
||||
if n != s.pageSize {
|
||||
err = fmt.Errorf("read %d instead of %d", n, s.pageSize)
|
||||
}
|
||||
s.mutex.Lock()
|
||||
resultChannels := s.pageWaits[pageNo]
|
||||
delete(s.pageWaits, pageNo)
|
||||
if err != nil {
|
||||
s.mutex.Unlock()
|
||||
for _, resultCh := range resultChannels {
|
||||
resultCh <- readResult{
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.pages[pageNo] = &_page{
|
||||
PageNo: pageNo,
|
||||
Buf: buf,
|
||||
ReferenceCount: len(resultChannels),
|
||||
}
|
||||
s.mutex.Unlock()
|
||||
for _, resultCh := range resultChannels {
|
||||
resultCh <- readResult{
|
||||
Data: buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user