office-gobmx/store/source/storbios.cxx
Chris Sherlock 983aa4a107 store: remove useless comments from store
Change-Id: I4b2582d3a7314d8752a56e83a4af7618b053abb5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/128170
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2022-06-20 08:54:08 +02:00

890 lines
22 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include "storbios.hxx"
#include <sal/types.h>
#include <sal/log.hxx>
#include <rtl/alloc.h>
#include <rtl/ref.hxx>
#include <osl/diagnose.h>
#include <osl/mutex.hxx>
#include <store/types.h>
#include "lockbyte.hxx"
#include "storcach.hxx"
using namespace store;
constexpr sal_uInt32 STORE_MAGIC_SUPERBLOCK = 0x484D5343;
namespace {
struct OStoreSuperBlock
{
typedef OStorePageGuard G;
typedef OStorePageDescriptor D;
typedef OStorePageLink L;
G m_aGuard;
D m_aDescr;
sal_uInt32 m_nMarked;
L m_aMarked;
sal_uInt32 m_nUnused;
L m_aUnused;
static const size_t theSize = sizeof(G) + sizeof(D) + 2 * (sizeof(L) + sizeof(sal_uInt32));
explicit OStoreSuperBlock (sal_uInt16 nPageSize)
: m_aGuard (STORE_MAGIC_SUPERBLOCK),
m_aDescr (nPageSize, nPageSize, STORE_MINIMUM_PAGESIZE),
m_nMarked (store::htonl(0)),
m_aMarked (0),
m_nUnused (store::htonl(0)),
m_aUnused (0)
{}
bool operator== (const OStoreSuperBlock & rhs) const
{
return ((m_aGuard == rhs.m_aGuard ) &&
(m_aDescr == rhs.m_aDescr ) &&
(m_nMarked == rhs.m_nMarked) &&
(m_aMarked == rhs.m_aMarked) &&
(m_nUnused == rhs.m_nUnused) &&
(m_aUnused == rhs.m_aUnused) );
}
sal_uInt32 unusedCount() const
{
return store::ntohl(m_nUnused);
}
const L& unusedHead() const
{
return m_aUnused;
}
void unusedInsert (const L& rLink)
{
sal_uInt32 nUnused = unusedCount();
m_nUnused = store::htonl(nUnused + 1);
m_aUnused = rLink;
}
void unusedRemove (const L& rLink)
{
sal_uInt32 nUnused = unusedCount();
m_nUnused = store::htonl(nUnused - 1);
m_aUnused = rLink;
}
void unusedReset()
{
m_nUnused = store::htonl(0);
m_aUnused = L(0);
}
void guard()
{
sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
nCRC32 = rtl_crc32 (nCRC32, &m_aDescr, static_cast<sal_uInt32>(theSize - sizeof(G)));
m_aGuard.m_nCRC32 = store::htonl(nCRC32);
}
storeError verify() const
{
sal_uInt32 nMagic = store::ntohl(m_aGuard.m_nMagic);
if (nMagic != STORE_MAGIC_SUPERBLOCK)
return store_E_WrongFormat;
sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
nCRC32 = rtl_crc32 (nCRC32, &m_aDescr, static_cast<sal_uInt32>(theSize - sizeof(G)));
if (m_aGuard.m_nCRC32 != store::htonl(nCRC32))
return store_E_InvalidChecksum;
else
return store_E_None;
}
};
}
namespace store
{
struct SuperBlockPage
{
typedef OStoreSuperBlock SuperBlock;
SuperBlock m_aSuperOne;
SuperBlock m_aSuperTwo;
static const size_t theSize = 2 * SuperBlock::theSize;
static const sal_uInt16 thePageSize = theSize;
static_assert(STORE_MINIMUM_PAGESIZE >= thePageSize, "must be at least thePageSize");
static void * operator new (size_t n)
{
return std::malloc(sal::static_int_cast<sal_Size>(n));
}
static void operator delete (void * p)
{
std::free (p);
}
static void * operator new (SAL_UNUSED_PARAMETER size_t, sal_uInt16 nPageSize)
{
return rtl_allocateZeroMemory (sal::static_int_cast<sal_Size>(nPageSize));
}
static void operator delete (void * p, SAL_UNUSED_PARAMETER sal_uInt16)
{
std::free (p);
}
explicit SuperBlockPage (sal_uInt16 nPageSize = thePageSize)
: m_aSuperOne(nPageSize),
m_aSuperTwo(nPageSize)
{}
storeError save (OStorePageBIOS const & rBIOS, sal_uInt32 nSize = theSize)
{
m_aSuperOne.guard();
m_aSuperTwo = m_aSuperOne;
return rBIOS.write (0, this, nSize);
}
/** Page allocation.
*/
storeError unusedHead (
OStorePageBIOS const & rBIOS,
PageData & rPageHead);
storeError unusedPop (
OStorePageBIOS const & rBIOS,
PageData const & rPageHead);
storeError unusedPush (
OStorePageBIOS const & rBIOS,
sal_uInt32 nAddr);
storeError verify (OStorePageBIOS const & rBIOS);
};
} // namespace store
/**
Get freelist head (alloc page, step 1).
*/
storeError SuperBlockPage::unusedHead (OStorePageBIOS const & rBIOS, PageData & rPageHead)
{
storeError eErrCode = verify (rBIOS);
if (eErrCode != store_E_None)
return eErrCode;
// Check freelist head.
OStorePageLink const aListHead (m_aSuperOne.unusedHead());
if (aListHead.location() == 0)
{
// Freelist empty, see SuperBlock::ctor().
rPageHead.location (STORE_PAGE_NULL);
return store_E_None;
}
// Load PageHead.
eErrCode = rBIOS.read (aListHead.location(), &rPageHead, PageData::theSize);
if (eErrCode != store_E_None)
return eErrCode;
eErrCode = rPageHead.verify (aListHead.location());
if (eErrCode != store_E_None)
return eErrCode;
// Verify page is unused.
sal_uInt32 const nAddr = rPageHead.m_aUnused.location();
if (nAddr == STORE_PAGE_NULL)
{
SAL_WARN("store", "store::SuperBlock::unusedHead(): page not free");
// Page in use.
rPageHead.location (STORE_PAGE_NULL);
// Recovery: Reset freelist to empty.
m_aSuperOne.unusedReset();
eErrCode = save (rBIOS);
}
return eErrCode;
}
/**
Pop freelist head (alloc page, step 2).
*/
storeError SuperBlockPage::unusedPop (OStorePageBIOS const & rBIOS, PageData const & rPageHead)
{
sal_uInt32 const nAddr = rPageHead.m_aUnused.location();
OSL_PRECOND(nAddr != STORE_PAGE_NULL, "store::SuperBlock::unusedPop(): page not free");
if (nAddr == STORE_PAGE_NULL)
return store_E_CantSeek;
// Pop from FreeList.
OStorePageLink const aListHead (nAddr);
m_aSuperOne.unusedRemove (aListHead);
return save (rBIOS);
}
/**
Push new freelist head.
*/
storeError SuperBlockPage::unusedPush (OStorePageBIOS const & rBIOS, sal_uInt32 nAddr)
{
storeError eErrCode = verify (rBIOS);
if (eErrCode != store_E_None)
return eErrCode;
PageData aPageHead;
eErrCode = rBIOS.read (nAddr, &aPageHead, PageData::theSize);
if (eErrCode != store_E_None)
return eErrCode;
eErrCode = aPageHead.verify (nAddr);
if (eErrCode != store_E_None)
return eErrCode;
aPageHead.m_aUnused = m_aSuperOne.unusedHead();
aPageHead.guard (nAddr);
eErrCode = rBIOS.write (nAddr, &aPageHead, PageData::theSize);
if (eErrCode != store_E_None)
return eErrCode;
OStorePageLink const aListHead (nAddr);
m_aSuperOne.unusedInsert(aListHead);
return save (rBIOS);
}
/**
Verify (with repair).
*/
storeError SuperBlockPage::verify (OStorePageBIOS const & rBIOS)
{
// Verify 1st copy.
storeError eErrCode = m_aSuperOne.verify();
if (eErrCode == store_E_None)
{
// Ok. Verify 2nd copy.
eErrCode = m_aSuperTwo.verify();
if (eErrCode == store_E_None)
{
// Ok. Ensure identical copies (1st copy wins).
if (!(m_aSuperOne == m_aSuperTwo))
{
// Different. Replace 2nd copy with 1st copy.
m_aSuperTwo = m_aSuperOne;
// Write back.
if (rBIOS.isWriteable())
eErrCode = rBIOS.write (0, this, theSize);
else
eErrCode = store_E_None;
}
}
else
{
// Failure. Replace 2nd copy with 1st copy.
m_aSuperTwo = m_aSuperOne;
// Write back.
if (rBIOS.isWriteable())
eErrCode = rBIOS.write (0, this, theSize);
else
eErrCode = store_E_None;
}
}
else
{
// Failure. Verify 2nd copy.
eErrCode = m_aSuperTwo.verify();
if (eErrCode == store_E_None)
{
// Ok. Replace 1st copy with 2nd copy.
m_aSuperOne = m_aSuperTwo;
// Write back.
if (rBIOS.isWriteable())
eErrCode = rBIOS.write (0, this, theSize);
else
eErrCode = store_E_None;
}
else
{
// Double Failure.
SAL_WARN("store", "OStoreSuperBlockPage::verify(): double failure.");
}
}
// Done.
return eErrCode;
}
OStorePageBIOS::Ace::Ace()
: m_next (this), m_prev (this), m_addr (STORE_PAGE_NULL), m_used (0)
{}
OStorePageBIOS::Ace::~Ace()
{
m_next->m_prev = m_prev;
m_prev->m_next = m_next;
}
int
SAL_CALL OStorePageBIOS::Ace::constructor (
void * obj, SAL_UNUSED_PARAMETER void*)
{
Ace * ace = static_cast<Ace*>(obj);
ace->m_next = ace->m_prev = ace;
return 1;
}
OStorePageBIOS::Ace *
OStorePageBIOS::Ace::find (OStorePageBIOS::Ace * head, sal_uInt32 addr)
{
OStorePageBIOS::Ace * entry;
for (entry = head->m_next; entry != head; entry = entry->m_next)
{
if (entry->m_addr >= addr)
return entry;
}
return head;
}
void
OStorePageBIOS::Ace::insert (OStorePageBIOS::Ace * head, OStorePageBIOS::Ace * entry)
{
// insert entry at queue tail (before head).
entry->m_next = head;
entry->m_prev = head->m_prev;
head->m_prev = entry;
entry->m_prev->m_next = entry;
}
namespace store
{
class OStorePageBIOS::AceCache
{
rtl_cache_type * m_ace_cache;
public:
static AceCache & get();
OStorePageBIOS::Ace *
create (sal_uInt32 addr);
void
destroy (OStorePageBIOS::Ace * ace);
protected:
AceCache();
~AceCache();
};
} // namespace store
OStorePageBIOS::AceCache &
OStorePageBIOS::AceCache::get()
{
static AceCache g_ace_cache;
return g_ace_cache;
}
OStorePageBIOS::AceCache::AceCache()
{
m_ace_cache = rtl_cache_create (
"store_ace_cache",
sizeof (OStorePageBIOS::Ace),
0, // objalign
OStorePageBIOS::Ace::constructor,
nullptr, // destructor,
nullptr, // reclaim,
nullptr, // userarg,
nullptr, // default source,
0 // flags
);
}
OStorePageBIOS::AceCache::~AceCache()
{
rtl_cache_destroy (m_ace_cache);
m_ace_cache = nullptr;
}
OStorePageBIOS::Ace *
OStorePageBIOS::AceCache::create (sal_uInt32 addr)
{
Ace * ace = static_cast<Ace*>(rtl_cache_alloc (m_ace_cache));
if (ace != nullptr)
{
// verify invariant state.
OSL_ASSERT((ace->m_next == ace) && (ace->m_prev == ace));
// initialize.
ace->m_addr = addr;
ace->m_used = 1;
}
return ace;
}
void
OStorePageBIOS::AceCache::destroy (OStorePageBIOS::Ace * ace)
{
if (ace != nullptr)
{
// remove from queue (if any).
ace->m_next->m_prev = ace->m_prev;
ace->m_prev->m_next = ace->m_next;
// restore invariant state.
ace->m_next = ace->m_prev = ace;
// return to cache.
rtl_cache_free (m_ace_cache, ace);
}
}
OStorePageBIOS::OStorePageBIOS()
: m_bWriteable (false)
{
}
OStorePageBIOS::~OStorePageBIOS()
{
cleanup_Impl();
}
storeError OStorePageBIOS::initialize (
ILockBytes * pLockBytes,
storeAccessMode eAccessMode,
sal_uInt16 & rnPageSize)
{
// Acquire exclusive access.
osl::MutexGuard aGuard (m_aMutex);
// Initialize.
storeError eErrCode = initialize_Impl (pLockBytes, eAccessMode, rnPageSize);
if (eErrCode != store_E_None)
{
// Cleanup.
cleanup_Impl();
}
return eErrCode;
}
/**
initialize_Impl.
@pre exclusive access
*/
storeError OStorePageBIOS::initialize_Impl (
ILockBytes * pLockBytes,
storeAccessMode eAccessMode,
sal_uInt16 & rnPageSize)
{
// Cleanup.
cleanup_Impl();
// Initialize.
m_xLockBytes = pLockBytes;
if (!m_xLockBytes.is())
return store_E_InvalidParameter;
m_bWriteable = (eAccessMode != storeAccessMode::ReadOnly);
// Check access mode.
storeError eErrCode = store_E_None;
if (eAccessMode != storeAccessMode::Create)
{
// Load SuperBlock page.
m_pSuper.reset(new SuperBlockPage());
eErrCode = read (0, m_pSuper.get(), SuperBlockPage::theSize);
if (eErrCode == store_E_None)
{
// Verify SuperBlock page (with repair).
eErrCode = m_pSuper->verify (*this);
}
}
else
{
// Truncate to zero length.
eErrCode = m_xLockBytes->setSize(0);
if (eErrCode != store_E_None)
return eErrCode;
// Mark as not existing.
eErrCode = store_E_NotExists;
}
if (eErrCode != store_E_None)
{
// Check reason.
if (eErrCode != store_E_NotExists)
return eErrCode;
// Check mode.
if (eAccessMode == storeAccessMode::ReadOnly)
return store_E_NotExists;
if (eAccessMode == storeAccessMode::ReadWrite)
return store_E_NotExists;
// Check PageSize.
if ((STORE_MINIMUM_PAGESIZE > rnPageSize) || (rnPageSize > STORE_MAXIMUM_PAGESIZE))
return store_E_InvalidParameter;
rnPageSize = ((rnPageSize + STORE_MINIMUM_PAGESIZE - 1) & ~(STORE_MINIMUM_PAGESIZE - 1));
// Create initial page (w/ SuperBlock).
m_pSuper.reset(new(rnPageSize) SuperBlockPage(rnPageSize));
eErrCode = m_pSuper->save (*this, rnPageSize);
}
if (eErrCode == store_E_None)
{
// Obtain page size.
rnPageSize = store::ntohs(m_pSuper->m_aSuperOne.m_aDescr.m_nSize);
// Create page allocator.
eErrCode = m_xLockBytes->initialize (m_xAllocator, rnPageSize);
if (eErrCode != store_E_None)
return eErrCode;
// Create page cache.
eErrCode = PageCache_createInstance (m_xCache, rnPageSize);
}
return eErrCode;
}
/*
@pre exclusive access.
*/
void OStorePageBIOS::cleanup_Impl()
{
// Check referer count.
if (m_ace_head.m_used > 0)
{
// Report remaining referer count.
SAL_INFO("store", "referer count: " << m_ace_head.m_used);
for (Ace * ace = m_ace_head.m_next; ace != &m_ace_head; ace = m_ace_head.m_next)
{
m_ace_head.m_used -= ace->m_used;
AceCache::get().destroy (ace);
}
OSL_ENSURE(m_ace_head.m_used == 0, "store::PageBIOS::cleanup_Impl(): logic error");
}
// Release SuperBlock page.
m_pSuper.reset();
// Release PageCache.
m_xCache.clear();
// Release PageAllocator.
m_xAllocator.clear();
// Release LockBytes.
m_xLockBytes.clear();
}
/**
@pre initialized, exclusive access.
*/
storeError OStorePageBIOS::read (
sal_uInt32 nAddr, void *pData, sal_uInt32 nSize) const
{
// Check precond.
if (!m_xLockBytes.is())
return store_E_InvalidAccess;
// Read Data.
return m_xLockBytes->readAt (nAddr, pData, nSize);
}
/**
@pre initialized, writeable, exclusive access.
*/
storeError OStorePageBIOS::write (
sal_uInt32 nAddr, const void *pData, sal_uInt32 nSize) const
{
// Check precond.
if (!m_xLockBytes.is())
return store_E_InvalidAccess;
if (!m_bWriteable)
return store_E_AccessViolation;
// Write Data.
return m_xLockBytes->writeAt (nAddr, pData, nSize);
}
/**
@pre initialized.
*/
storeError OStorePageBIOS::acquirePage (
const OStorePageDescriptor& rDescr, storeAccessMode eMode)
{
// Acquire exclusive access.
osl::MutexGuard aGuard (m_aMutex);
// Check precond.
if (!m_xLockBytes.is())
return store_E_InvalidAccess;
// Check access mode.
if (!(m_bWriteable || (eMode == storeAccessMode::ReadOnly)))
return store_E_AccessViolation;
// Find access control list entry.
Ace * ace = Ace::find (&m_ace_head, rDescr.m_nAddr);
if (ace->m_addr == rDescr.m_nAddr)
{
// Acquire existing entry (with ShareDenyWrite).
if (eMode == storeAccessMode::ReadOnly)
ace->m_used += 1;
else
return store_E_AccessViolation;
}
else
{
// Insert new entry.
Ace * entry = AceCache::get().create (rDescr.m_nAddr);
if (!entry)
return store_E_OutOfMemory;
Ace::insert (ace, entry);
}
// Increment total referer count and finish.
m_ace_head.m_used += 1;
return store_E_None;
}
/**
@pre initialized.
*/
storeError OStorePageBIOS::releasePage (const OStorePageDescriptor& rDescr)
{
// Acquire exclusive access.
osl::MutexGuard aGuard (m_aMutex);
// Check precond.
if (!m_xLockBytes.is())
return store_E_InvalidAccess;
// Find access control list entry.
Ace * ace = Ace::find (&m_ace_head, rDescr.m_nAddr);
if (ace->m_addr != rDescr.m_nAddr)
return store_E_NotExists;
// Release existing entry.
if (ace->m_used > 1)
ace->m_used -= 1;
else
AceCache::get().destroy (ace);
// Decrement total referer count and finish.
m_ace_head.m_used -= 1;
return store_E_None;
}
/**
@pre initialized, writeable.
*/
storeError OStorePageBIOS::allocate (
OStorePageObject& rPage)
{
// Acquire exclusive access.
osl::MutexGuard aGuard (m_aMutex);
// Check precond.
if (!m_xLockBytes.is())
return store_E_InvalidAccess;
if (!m_bWriteable)
return store_E_AccessViolation;
// Check allocation type.
storeError eErrCode = store_E_None;
// Try freelist head.
PageData aPageHead;
eErrCode = m_pSuper->unusedHead (*this, aPageHead);
if (eErrCode != store_E_None)
return eErrCode;
sal_uInt32 const nAddr = aPageHead.location();
if (nAddr != STORE_PAGE_NULL)
{
// Save page.
eErrCode = saveObjectAt_Impl (rPage, nAddr);
if (eErrCode != store_E_None)
return eErrCode;
// Pop freelist head and finish.
return m_pSuper->unusedPop (*this, aPageHead);
}
// Allocate from EOF. Determine current size.
sal_uInt32 nSize = STORE_PAGE_NULL;
eErrCode = m_xLockBytes->getSize (nSize);
if (eErrCode != store_E_None)
return eErrCode;
// Save page at current EOF.
return saveObjectAt_Impl (rPage, nSize);
}
/**
@pre initialized, writeable.
*/
storeError OStorePageBIOS::free (sal_uInt32 nAddr)
{
// Acquire exclusive access.
osl::MutexGuard aGuard (m_aMutex);
// Check precond.
if (!m_xLockBytes.is())
return store_E_InvalidAccess;
if (!m_bWriteable)
return store_E_AccessViolation;
// Invalidate cache.
(void) m_xCache->removePageAt (nAddr);
// Push onto freelist.
return m_pSuper->unusedPush (*this, nAddr);
}
/**
@pre initialized, readable.
*/
storeError OStorePageBIOS::loadObjectAt (OStorePageObject & rPage, sal_uInt32 nAddr)
{
// Acquire exclusive access.
osl::MutexGuard aGuard (m_aMutex);
// Check precond.
if (!m_xLockBytes.is())
return store_E_InvalidAccess;
return loadObjectAt_Impl (rPage, nAddr);
}
/**
@pre initialized, readable, exclusive access.
*/
storeError OStorePageBIOS::loadObjectAt_Impl (OStorePageObject & rPage, sal_uInt32 nAddr) const
{
storeError eErrCode = m_xCache->lookupPageAt (rPage.get(), nAddr);
if (eErrCode != store_E_NotExists)
return eErrCode;
// Read page.
eErrCode = m_xLockBytes->readPageAt (rPage.get(), nAddr);
if (eErrCode != store_E_None)
return eErrCode;
// Verify page.
eErrCode = rPage.verify (nAddr);
if (eErrCode != store_E_None)
return eErrCode;
// Mark page as clean.
rPage.clean();
// Cache page.
return m_xCache->insertPageAt (rPage.get(), nAddr);
}
/**
@pre initialized, writeable.
*/
storeError OStorePageBIOS::saveObjectAt (OStorePageObject & rPage, sal_uInt32 nAddr)
{
// Acquire exclusive access.
osl::MutexGuard aGuard (m_aMutex);
// Check precond.
if (!m_xLockBytes.is())
return store_E_InvalidAccess;
if (!m_bWriteable)
return store_E_AccessViolation;
// Save Page.
return saveObjectAt_Impl (rPage, nAddr);
}
/**
@pre initialized, writeable, exclusive access.
*/
storeError OStorePageBIOS::saveObjectAt_Impl (OStorePageObject & rPage, sal_uInt32 nAddr) const
{
// Guard page (incl. set location).
storeError eErrCode = rPage.guard (nAddr);
if (eErrCode != store_E_None)
return eErrCode;
// Write page.
eErrCode = m_xLockBytes->writePageAt(rPage.get(), nAddr);
if (eErrCode != store_E_None)
return eErrCode;
// Mark page as clean.
rPage.clean();
// Cache page.
return m_xCache->updatePageAt (rPage.get(), nAddr);
}
/**
@pre none.
*/
storeError OStorePageBIOS::close()
{
// Acquire exclusive access.
osl::MutexGuard aGuard (m_aMutex);
// Cleanup.
cleanup_Impl();
// Done.
return store_E_None;
}
/**
@pre initialized.
*/
storeError OStorePageBIOS::flush()
{
// Acquire exclusive access.
osl::MutexGuard aGuard (m_aMutex);
// Check precond.
if (!m_xLockBytes.is())
return store_E_InvalidAccess;
// Flush LockBytes and finish.
return m_xLockBytes->flush();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */