1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include "storbios.hxx"
24 #include <sal/types.h>
25 #include <sal/log.hxx>
27 #include <rtl/alloc.h>
28 #include <rtl/ref.hxx>
30 #include <osl/diagnose.h>
31 #include <osl/mutex.hxx>
33 #include <store/types.h>
34 #include "lockbyte.hxx"
35 #include "storcach.hxx"
37 using namespace store
;
39 /*========================================================================
43 *======================================================================*/
44 #define STORE_MAGIC_SUPERBLOCK sal_uInt32(0x484D5343)
48 struct OStoreSuperBlock
50 typedef OStorePageGuard G
;
51 typedef OStorePageDescriptor D
;
52 typedef OStorePageLink L
;
65 static const size_t theSize
= sizeof(G
) + sizeof(D
) + 2 * (sizeof(L
) + sizeof(sal_uInt32
));
69 explicit OStoreSuperBlock (sal_uInt16 nPageSize
)
70 : m_aGuard (STORE_MAGIC_SUPERBLOCK
),
71 m_aDescr (nPageSize
, nPageSize
, STORE_MINIMUM_PAGESIZE
),
72 m_nMarked (store::htonl(0)),
74 m_nUnused (store::htonl(0)),
80 bool operator== (const OStoreSuperBlock
& rhs
) const
82 return ((m_aGuard
== rhs
.m_aGuard
) &&
83 (m_aDescr
== rhs
.m_aDescr
) &&
84 (m_nMarked
== rhs
.m_nMarked
) &&
85 (m_aMarked
== rhs
.m_aMarked
) &&
86 (m_nUnused
== rhs
.m_nUnused
) &&
87 (m_aUnused
== rhs
.m_aUnused
) );
90 /** unused(Count|Head|Insert|Remove|Reset).
92 sal_uInt32
unusedCount() const
94 return store::ntohl(m_nUnused
);
96 const L
& unusedHead() const
100 void unusedInsert (const L
& rLink
)
102 sal_uInt32 nUnused
= unusedCount();
103 m_nUnused
= store::htonl(nUnused
+ 1);
106 void unusedRemove (const L
& rLink
)
108 sal_uInt32 nUnused
= unusedCount();
109 m_nUnused
= store::htonl(nUnused
- 1);
114 m_nUnused
= store::htonl(0);
118 /** guard (external representation).
122 sal_uInt32 nCRC32
= rtl_crc32 (0, &m_aGuard
.m_nMagic
, sizeof(sal_uInt32
));
123 nCRC32
= rtl_crc32 (nCRC32
, &m_aDescr
, static_cast<sal_uInt32
>(theSize
- sizeof(G
)));
124 m_aGuard
.m_nCRC32
= store::htonl(nCRC32
);
127 /** verify (external representation).
129 storeError
verify() const
131 sal_uInt32 nMagic
= store::ntohl(m_aGuard
.m_nMagic
);
132 if (nMagic
!= STORE_MAGIC_SUPERBLOCK
)
133 return store_E_WrongFormat
;
135 sal_uInt32 nCRC32
= rtl_crc32 (0, &m_aGuard
.m_nMagic
, sizeof(sal_uInt32
));
136 nCRC32
= rtl_crc32 (nCRC32
, &m_aDescr
, static_cast<sal_uInt32
>(theSize
- sizeof(G
)));
137 if (m_aGuard
.m_nCRC32
!= store::htonl(nCRC32
))
138 return store_E_InvalidChecksum
;
146 /*========================================================================
148 * SuperBlockPage interface.
150 *======================================================================*/
154 struct SuperBlockPage
156 typedef OStoreSuperBlock SuperBlock
;
160 SuperBlock m_aSuperOne
;
161 SuperBlock m_aSuperTwo
;
165 static const size_t theSize
= 2 * SuperBlock::theSize
;
166 static const sal_uInt16 thePageSize
= theSize
;
167 static_assert(STORE_MINIMUM_PAGESIZE
>= thePageSize
, "must be at least thePageSize");
171 static void * operator new (size_t n
)
173 return std::malloc(sal::static_int_cast
<sal_Size
>(n
));
175 static void operator delete (void * p
)
180 static void * operator new (SAL_UNUSED_PARAMETER
size_t, sal_uInt16 nPageSize
)
182 return rtl_allocateZeroMemory (sal::static_int_cast
<sal_Size
>(nPageSize
));
184 static void operator delete (void * p
, SAL_UNUSED_PARAMETER sal_uInt16
)
191 explicit SuperBlockPage (sal_uInt16 nPageSize
= thePageSize
)
192 : m_aSuperOne(nPageSize
),
193 m_aSuperTwo(nPageSize
)
198 storeError
save (OStorePageBIOS
const & rBIOS
, sal_uInt32 nSize
= theSize
)
201 m_aSuperTwo
= m_aSuperOne
;
202 return rBIOS
.write (0, this, nSize
);
207 storeError
unusedHead (
208 OStorePageBIOS
const & rBIOS
,
209 PageData
& rPageHead
);
211 storeError
unusedPop (
212 OStorePageBIOS
const & rBIOS
,
213 PageData
const & rPageHead
);
215 storeError
unusedPush (
216 OStorePageBIOS
const & rBIOS
,
219 /** verify (with repair).
221 storeError
verify (OStorePageBIOS
const & rBIOS
);
226 /*========================================================================
228 * SuperBlockPage implementation.
230 *======================================================================*/
232 * unusedHead(): get freelist head (alloc page, step 1).
234 storeError
SuperBlockPage::unusedHead (OStorePageBIOS
const & rBIOS
, PageData
& rPageHead
)
236 storeError eErrCode
= verify (rBIOS
);
237 if (eErrCode
!= store_E_None
)
240 // Check freelist head.
241 OStorePageLink
const aListHead (m_aSuperOne
.unusedHead());
242 if (aListHead
.location() == 0)
244 // Freelist empty, see SuperBlock::ctor().
245 rPageHead
.location (STORE_PAGE_NULL
);
250 eErrCode
= rBIOS
.read (aListHead
.location(), &rPageHead
, PageData::theSize
);
251 if (eErrCode
!= store_E_None
)
254 eErrCode
= rPageHead
.verify (aListHead
.location());
255 if (eErrCode
!= store_E_None
)
258 // Verify page is unused.
259 sal_uInt32
const nAddr
= rPageHead
.m_aUnused
.location();
260 if (nAddr
== STORE_PAGE_NULL
)
262 SAL_WARN("store", "store::SuperBlock::unusedHead(): page not free");
265 rPageHead
.location (STORE_PAGE_NULL
);
267 // Recovery: Reset freelist to empty.
268 m_aSuperOne
.unusedReset();
269 eErrCode
= save (rBIOS
);
275 * unusedPop(): pop freelist head (alloc page, step 2).
277 storeError
SuperBlockPage::unusedPop (OStorePageBIOS
const & rBIOS
, PageData
const & rPageHead
)
279 sal_uInt32
const nAddr
= rPageHead
.m_aUnused
.location();
280 OSL_PRECOND(nAddr
!= STORE_PAGE_NULL
, "store::SuperBlock::unusedPop(): page not free");
281 if (nAddr
== STORE_PAGE_NULL
)
282 return store_E_CantSeek
;
284 // Pop from FreeList.
285 OStorePageLink
const aListHead (nAddr
);
286 m_aSuperOne
.unusedRemove (aListHead
);
291 * unusedPush(): push new freelist head.
293 storeError
SuperBlockPage::unusedPush (OStorePageBIOS
const & rBIOS
, sal_uInt32 nAddr
)
295 storeError eErrCode
= verify (rBIOS
);
296 if (eErrCode
!= store_E_None
)
300 eErrCode
= rBIOS
.read (nAddr
, &aPageHead
, PageData::theSize
);
301 if (eErrCode
!= store_E_None
)
304 eErrCode
= aPageHead
.verify (nAddr
);
305 if (eErrCode
!= store_E_None
)
308 aPageHead
.m_aUnused
= m_aSuperOne
.unusedHead();
309 aPageHead
.guard (nAddr
);
311 eErrCode
= rBIOS
.write (nAddr
, &aPageHead
, PageData::theSize
);
312 if (eErrCode
!= store_E_None
)
315 OStorePageLink
const aListHead (nAddr
);
316 m_aSuperOne
.unusedInsert(aListHead
);
321 * verify (with repair).
323 storeError
SuperBlockPage::verify (OStorePageBIOS
const & rBIOS
)
326 storeError eErrCode
= m_aSuperOne
.verify();
327 if (eErrCode
== store_E_None
)
329 // Ok. Verify 2nd copy.
330 eErrCode
= m_aSuperTwo
.verify();
331 if (eErrCode
== store_E_None
)
333 // Ok. Ensure identical copies (1st copy wins).
334 if (!(m_aSuperOne
== m_aSuperTwo
))
336 // Different. Replace 2nd copy with 1st copy.
337 m_aSuperTwo
= m_aSuperOne
;
340 if (rBIOS
.isWriteable())
341 eErrCode
= rBIOS
.write (0, this, theSize
);
343 eErrCode
= store_E_None
;
348 // Failure. Replace 2nd copy with 1st copy.
349 m_aSuperTwo
= m_aSuperOne
;
352 if (rBIOS
.isWriteable())
353 eErrCode
= rBIOS
.write (0, this, theSize
);
355 eErrCode
= store_E_None
;
360 // Failure. Verify 2nd copy.
361 eErrCode
= m_aSuperTwo
.verify();
362 if (eErrCode
== store_E_None
)
364 // Ok. Replace 1st copy with 2nd copy.
365 m_aSuperOne
= m_aSuperTwo
;
368 if (rBIOS
.isWriteable())
369 eErrCode
= rBIOS
.write (0, this, theSize
);
371 eErrCode
= store_E_None
;
376 SAL_WARN("store", "OStoreSuperBlockPage::verify(): double failure.");
384 /*========================================================================
386 * OStorePageBIOS::Ace implementation.
388 *======================================================================*/
389 OStorePageBIOS::Ace::Ace()
390 : m_next (this), m_prev (this), m_addr (STORE_PAGE_NULL
), m_used (0)
393 OStorePageBIOS::Ace::~Ace()
395 m_next
->m_prev
= m_prev
;
396 m_prev
->m_next
= m_next
;
400 SAL_CALL
OStorePageBIOS::Ace::constructor (
401 void * obj
, SAL_UNUSED_PARAMETER
void * /* arg */)
403 Ace
* ace
= static_cast<Ace
*>(obj
);
404 ace
->m_next
= ace
->m_prev
= ace
;
408 OStorePageBIOS::Ace
*
409 OStorePageBIOS::Ace::find (OStorePageBIOS::Ace
* head
, sal_uInt32 addr
)
411 OStorePageBIOS::Ace
* entry
;
412 for (entry
= head
->m_next
; entry
!= head
; entry
= entry
->m_next
)
414 if (entry
->m_addr
>= addr
)
421 OStorePageBIOS::Ace::insert (OStorePageBIOS::Ace
* head
, OStorePageBIOS::Ace
* entry
)
423 // insert entry at queue tail (before head).
424 entry
->m_next
= head
;
425 entry
->m_prev
= head
->m_prev
;
426 head
->m_prev
= entry
;
427 entry
->m_prev
->m_next
= entry
;
430 /*========================================================================
432 * OStorePageBIOS::AceCache interface.
434 *======================================================================*/
438 class OStorePageBIOS::AceCache
440 rtl_cache_type
* m_ace_cache
;
443 static AceCache
& get();
445 OStorePageBIOS::Ace
*
446 create (sal_uInt32 addr
);
449 destroy (OStorePageBIOS::Ace
* ace
);
458 /*========================================================================
460 * OStorePageBIOS::AceCache implementation.
462 *======================================================================*/
464 OStorePageBIOS::AceCache
&
465 OStorePageBIOS::AceCache::get()
467 static AceCache g_ace_cache
;
471 OStorePageBIOS::AceCache::AceCache()
473 m_ace_cache
= rtl_cache_create (
475 sizeof (OStorePageBIOS::Ace
),
477 OStorePageBIOS::Ace::constructor
,
478 nullptr, // destructor,
481 nullptr, // default source,
486 OStorePageBIOS::AceCache::~AceCache()
488 rtl_cache_destroy (m_ace_cache
);
489 m_ace_cache
= nullptr;
492 OStorePageBIOS::Ace
*
493 OStorePageBIOS::AceCache::create (sal_uInt32 addr
)
495 Ace
* ace
= static_cast<Ace
*>(rtl_cache_alloc (m_ace_cache
));
498 // verify invariant state.
499 OSL_ASSERT((ace
->m_next
== ace
) && (ace
->m_prev
== ace
));
509 OStorePageBIOS::AceCache::destroy (OStorePageBIOS::Ace
* ace
)
513 // remove from queue (if any).
514 ace
->m_next
->m_prev
= ace
->m_prev
;
515 ace
->m_prev
->m_next
= ace
->m_next
;
517 // restore invariant state.
518 ace
->m_next
= ace
->m_prev
= ace
;
521 rtl_cache_free (m_ace_cache
, ace
);
525 /*========================================================================
527 * OStorePageBIOS implementation.
529 *======================================================================*/
533 OStorePageBIOS::OStorePageBIOS()
534 : m_bWriteable (false)
541 OStorePageBIOS::~OStorePageBIOS()
550 storeError
OStorePageBIOS::initialize (
551 ILockBytes
* pLockBytes
,
552 storeAccessMode eAccessMode
,
553 sal_uInt16
& rnPageSize
)
555 // Acquire exclusive access.
556 osl::MutexGuard
aGuard (m_aMutex
);
559 storeError eErrCode
= initialize_Impl (pLockBytes
, eAccessMode
, rnPageSize
);
560 if (eErrCode
!= store_E_None
)
570 * Internal: Precond: exclusive access.
572 storeError
OStorePageBIOS::initialize_Impl (
573 ILockBytes
* pLockBytes
,
574 storeAccessMode eAccessMode
,
575 sal_uInt16
& rnPageSize
)
581 m_xLockBytes
= pLockBytes
;
582 if (!m_xLockBytes
.is())
583 return store_E_InvalidParameter
;
584 m_bWriteable
= (eAccessMode
!= storeAccessMode::ReadOnly
);
586 // Check access mode.
587 storeError eErrCode
= store_E_None
;
588 if (eAccessMode
!= storeAccessMode::Create
)
590 // Load SuperBlock page.
591 m_pSuper
.reset(new SuperBlockPage());
593 eErrCode
= read (0, m_pSuper
.get(), SuperBlockPage::theSize
);
594 if (eErrCode
== store_E_None
)
596 // Verify SuperBlock page (with repair).
597 eErrCode
= m_pSuper
->verify (*this);
602 // Truncate to zero length.
603 eErrCode
= m_xLockBytes
->setSize(0);
604 if (eErrCode
!= store_E_None
)
607 // Mark as not existing.
608 eErrCode
= store_E_NotExists
;
611 if (eErrCode
!= store_E_None
)
614 if (eErrCode
!= store_E_NotExists
)
618 if (eAccessMode
== storeAccessMode::ReadOnly
)
619 return store_E_NotExists
;
620 if (eAccessMode
== storeAccessMode::ReadWrite
)
621 return store_E_NotExists
;
624 if ((STORE_MINIMUM_PAGESIZE
> rnPageSize
) || (rnPageSize
> STORE_MAXIMUM_PAGESIZE
))
625 return store_E_InvalidParameter
;
626 rnPageSize
= ((rnPageSize
+ STORE_MINIMUM_PAGESIZE
- 1) & ~(STORE_MINIMUM_PAGESIZE
- 1));
628 // Create initial page (w/ SuperBlock).
629 m_pSuper
.reset(new(rnPageSize
) SuperBlockPage(rnPageSize
));
630 eErrCode
= m_pSuper
->save (*this, rnPageSize
);
632 if (eErrCode
== store_E_None
)
635 rnPageSize
= store::ntohs(m_pSuper
->m_aSuperOne
.m_aDescr
.m_nSize
);
637 // Create page allocator.
638 eErrCode
= m_xLockBytes
->initialize (m_xAllocator
, rnPageSize
);
639 if (eErrCode
!= store_E_None
)
642 // Create page cache.
643 eErrCode
= PageCache_createInstance (m_xCache
, rnPageSize
);
650 * Internal: Precond: exclusive access.
652 void OStorePageBIOS::cleanup_Impl()
654 // Check referer count.
655 if (m_ace_head
.m_used
> 0)
657 // Report remaining referer count.
658 SAL_INFO("store", "referer count: " << m_ace_head
.m_used
);
659 for (Ace
* ace
= m_ace_head
.m_next
; ace
!= &m_ace_head
; ace
= m_ace_head
.m_next
)
661 m_ace_head
.m_used
-= ace
->m_used
;
662 AceCache::get().destroy (ace
);
664 OSL_ENSURE(m_ace_head
.m_used
== 0, "store::PageBIOS::cleanup_Impl(): logic error");
667 // Release SuperBlock page.
670 // Release PageCache.
673 // Release PageAllocator.
674 m_xAllocator
.clear();
676 // Release LockBytes.
677 m_xLockBytes
.clear();
682 * Low Level: Precond: initialized, exclusive access.
684 storeError
OStorePageBIOS::read (
685 sal_uInt32 nAddr
, void *pData
, sal_uInt32 nSize
) const
688 if (!m_xLockBytes
.is())
689 return store_E_InvalidAccess
;
692 return m_xLockBytes
->readAt (nAddr
, pData
, nSize
);
697 * Low Level: Precond: initialized, writeable, exclusive access.
699 storeError
OStorePageBIOS::write (
700 sal_uInt32 nAddr
, const void *pData
, sal_uInt32 nSize
) const
703 if (!m_xLockBytes
.is())
704 return store_E_InvalidAccess
;
706 return store_E_AccessViolation
;
709 return m_xLockBytes
->writeAt (nAddr
, pData
, nSize
);
714 * Precond: initialized.
716 storeError
OStorePageBIOS::acquirePage (
717 const OStorePageDescriptor
& rDescr
, storeAccessMode eMode
)
719 // Acquire exclusive access.
720 osl::MutexGuard
aGuard (m_aMutex
);
723 if (!m_xLockBytes
.is())
724 return store_E_InvalidAccess
;
726 // Check access mode.
727 if (!(m_bWriteable
|| (eMode
== storeAccessMode::ReadOnly
)))
728 return store_E_AccessViolation
;
730 // Find access control list entry.
731 Ace
* ace
= Ace::find (&m_ace_head
, rDescr
.m_nAddr
);
732 if (ace
->m_addr
== rDescr
.m_nAddr
)
734 // Acquire existing entry (with ShareDenyWrite).
735 if (eMode
== storeAccessMode::ReadOnly
)
738 return store_E_AccessViolation
;
743 Ace
* entry
= AceCache::get().create (rDescr
.m_nAddr
);
745 return store_E_OutOfMemory
;
746 Ace::insert (ace
, entry
);
749 // Increment total referer count and finish.
750 m_ace_head
.m_used
+= 1;
756 * Precond: initialized.
758 storeError
OStorePageBIOS::releasePage (const OStorePageDescriptor
& rDescr
)
760 // Acquire exclusive access.
761 osl::MutexGuard
aGuard (m_aMutex
);
764 if (!m_xLockBytes
.is())
765 return store_E_InvalidAccess
;
767 // Find access control list entry.
768 Ace
* ace
= Ace::find (&m_ace_head
, rDescr
.m_nAddr
);
769 if (ace
->m_addr
!= rDescr
.m_nAddr
)
770 return store_E_NotExists
;
772 // Release existing entry.
776 AceCache::get().destroy (ace
);
778 // Decrement total referer count and finish.
779 m_ace_head
.m_used
-= 1;
785 * Precond: initialized, writeable.
787 storeError
OStorePageBIOS::allocate (
788 OStorePageObject
& rPage
)
790 // Acquire exclusive access.
791 osl::MutexGuard
aGuard (m_aMutex
);
794 if (!m_xLockBytes
.is())
795 return store_E_InvalidAccess
;
797 return store_E_AccessViolation
;
799 // Check allocation type.
800 storeError eErrCode
= store_E_None
;
801 // Try freelist head.
803 eErrCode
= m_pSuper
->unusedHead (*this, aPageHead
);
804 if (eErrCode
!= store_E_None
)
807 sal_uInt32
const nAddr
= aPageHead
.location();
808 if (nAddr
!= STORE_PAGE_NULL
)
811 eErrCode
= saveObjectAt_Impl (rPage
, nAddr
);
812 if (eErrCode
!= store_E_None
)
815 // Pop freelist head and finish.
816 return m_pSuper
->unusedPop (*this, aPageHead
);
819 // Allocate from EOF. Determine current size.
820 sal_uInt32 nSize
= STORE_PAGE_NULL
;
821 eErrCode
= m_xLockBytes
->getSize (nSize
);
822 if (eErrCode
!= store_E_None
)
825 // Save page at current EOF.
826 return saveObjectAt_Impl (rPage
, nSize
);
831 * Precond: initialized, writeable.
833 storeError
OStorePageBIOS::free (sal_uInt32 nAddr
)
835 // Acquire exclusive access.
836 osl::MutexGuard
aGuard (m_aMutex
);
839 if (!m_xLockBytes
.is())
840 return store_E_InvalidAccess
;
842 return store_E_AccessViolation
;
845 (void) m_xCache
->removePageAt (nAddr
);
847 // Push onto freelist.
848 return m_pSuper
->unusedPush (*this, nAddr
);
853 * Precond: initialized, readable.
855 storeError
OStorePageBIOS::loadObjectAt (OStorePageObject
& rPage
, sal_uInt32 nAddr
)
857 // Acquire exclusive access.
858 osl::MutexGuard
aGuard (m_aMutex
);
861 if (!m_xLockBytes
.is())
862 return store_E_InvalidAccess
;
864 return loadObjectAt_Impl (rPage
, nAddr
);
869 * Internal: Precond: initialized, readable, exclusive access.
871 storeError
OStorePageBIOS::loadObjectAt_Impl (OStorePageObject
& rPage
, sal_uInt32 nAddr
) const
873 storeError eErrCode
= m_xCache
->lookupPageAt (rPage
.get(), nAddr
);
874 if (eErrCode
!= store_E_NotExists
)
878 eErrCode
= m_xLockBytes
->readPageAt (rPage
.get(), nAddr
);
879 if (eErrCode
!= store_E_None
)
883 eErrCode
= rPage
.verify (nAddr
);
884 if (eErrCode
!= store_E_None
)
887 // Mark page as clean.
891 return m_xCache
->insertPageAt (rPage
.get(), nAddr
);
896 * Precond: initialized, writeable.
898 storeError
OStorePageBIOS::saveObjectAt (OStorePageObject
& rPage
, sal_uInt32 nAddr
)
900 // Acquire exclusive access.
901 osl::MutexGuard
aGuard (m_aMutex
);
904 if (!m_xLockBytes
.is())
905 return store_E_InvalidAccess
;
907 return store_E_AccessViolation
;
910 return saveObjectAt_Impl (rPage
, nAddr
);
915 * Internal: Precond: initialized, writeable, exclusive access.
917 storeError
OStorePageBIOS::saveObjectAt_Impl (OStorePageObject
& rPage
, sal_uInt32 nAddr
) const
919 // Guard page (incl. set location).
920 storeError eErrCode
= rPage
.guard (nAddr
);
921 if (eErrCode
!= store_E_None
)
925 eErrCode
= m_xLockBytes
->writePageAt(rPage
.get(), nAddr
);
926 if (eErrCode
!= store_E_None
)
929 // Mark page as clean.
933 return m_xCache
->updatePageAt (rPage
.get(), nAddr
);
940 storeError
OStorePageBIOS::close()
942 // Acquire exclusive access.
943 osl::MutexGuard
aGuard (m_aMutex
);
954 * Precond: initialized.
956 storeError
OStorePageBIOS::flush()
958 // Acquire exclusive access.
959 osl::MutexGuard
aGuard (m_aMutex
);
962 if (!m_xLockBytes
.is())
963 return store_E_InvalidAccess
;
965 // Flush LockBytes and finish.
966 return m_xLockBytes
->flush();
969 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */