Author: bonefish Date: 2009-12-30 20:38:41 +0100 (Wed, 30 Dec 2009) New Revision: 34827 Changeset: http://dev.haiku-os.org/changeset/34827/haiku Added: haiku/trunk/src/tests/system/kernel/unit/ haiku/trunk/src/tests/system/kernel/unit/Jamfile haiku/trunk/src/tests/system/kernel/unit/Test.cpp haiku/trunk/src/tests/system/kernel/unit/Test.h haiku/trunk/src/tests/system/kernel/unit/TestContext.cpp haiku/trunk/src/tests/system/kernel/unit/TestContext.h haiku/trunk/src/tests/system/kernel/unit/TestError.cpp haiku/trunk/src/tests/system/kernel/unit/TestError.h haiku/trunk/src/tests/system/kernel/unit/TestManager.cpp haiku/trunk/src/tests/system/kernel/unit/TestManager.h haiku/trunk/src/tests/system/kernel/unit/TestOutput.cpp haiku/trunk/src/tests/system/kernel/unit/TestOutput.h haiku/trunk/src/tests/system/kernel/unit/TestSuite.cpp haiku/trunk/src/tests/system/kernel/unit/TestSuite.h haiku/trunk/src/tests/system/kernel/unit/TestThread.h haiku/trunk/src/tests/system/kernel/unit/TestVisitor.cpp haiku/trunk/src/tests/system/kernel/unit/TestVisitor.h haiku/trunk/src/tests/system/kernel/unit/kernel_unit_tests.cpp haiku/trunk/src/tests/system/kernel/unit/lock/ haiku/trunk/src/tests/system/kernel/unit/lock/Jamfile haiku/trunk/src/tests/system/kernel/unit/lock/LockTestSuite.cpp haiku/trunk/src/tests/system/kernel/unit/lock/LockTestSuite.h haiku/trunk/src/tests/system/kernel/unit/lock/RWLockTests.cpp haiku/trunk/src/tests/system/kernel/unit/lock/RWLockTests.h Modified: haiku/trunk/src/tests/system/kernel/Jamfile Log: * Added a framework for unit tests in the kernel. The beast is implemented as a driver which publishes a device as "/dev/kernel_unit_tests". Commands can be issued by writing to the device (e.g. "echo help > /dev/kernel_unit_tests"), output is written to serial port/ syslog. * Added a few tests for rw_lock. Modified: haiku/trunk/src/tests/system/kernel/Jamfile =================================================================== --- haiku/trunk/src/tests/system/kernel/Jamfile 2009-12-30 15:17:09 UTC (rev 34826) +++ haiku/trunk/src/tests/system/kernel/Jamfile 2009-12-30 19:38:41 UTC (rev 34827) @@ -85,4 +85,5 @@ SubInclude HAIKU_TOP src tests system kernel scheduler ; SubInclude HAIKU_TOP src tests system kernel slab ; SubInclude HAIKU_TOP src tests system kernel swap ; +SubInclude HAIKU_TOP src tests system kernel unit ; SubInclude HAIKU_TOP src tests system kernel util ; Added: haiku/trunk/src/tests/system/kernel/unit/Jamfile =================================================================== --- haiku/trunk/src/tests/system/kernel/unit/Jamfile (rev 0) +++ haiku/trunk/src/tests/system/kernel/unit/Jamfile 2009-12-30 19:38:41 UTC (rev 34827) @@ -0,0 +1,23 @@ +SubDir HAIKU_TOP src tests system kernel unit ; + +UsePrivateKernelHeaders ; + + +KernelAddon kernel_unit_tests : + kernel_unit_tests.cpp + Test.cpp + TestContext.cpp + TestError.cpp + TestManager.cpp + TestOutput.cpp + TestSuite.cpp + TestVisitor.cpp + + : + <nogrist>kernel_unit_tests_lock.o + + $(TARGET_STATIC_LIBSUPC++) +; + + +HaikuSubInclude lock ; Added: haiku/trunk/src/tests/system/kernel/unit/Test.cpp =================================================================== --- haiku/trunk/src/tests/system/kernel/unit/Test.cpp (rev 0) +++ haiku/trunk/src/tests/system/kernel/unit/Test.cpp 2009-12-30 19:38:41 UTC (rev 34827) @@ -0,0 +1,93 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include "Test.h" + +#include "TestVisitor.h" + + +// #pragma mark - Test + + +Test::Test(const char* name) + : + fName(name), + fSuite(NULL) +{ +} + + +Test::~Test() +{ +} + + +void +Test::SetSuite(TestSuite* suite) +{ + fSuite = suite; +} + + +bool +Test::IsLeafTest() const +{ + return true; +} + + +status_t +Test::Setup(TestContext& context) +{ + return B_OK; +} + + +bool +Test::Run(TestContext& context, const char* name) +{ +// TODO: Report error! + return false; +} + + +void +Test::Cleanup(TestContext& context, bool setupOK) +{ +} + + +Test* +Test::Visit(TestVisitor& visitor) +{ + return visitor.VisitTest(this) ? this : NULL; +} + + +// #pragma mark - StandardTestDelegate + + +StandardTestDelegate::StandardTestDelegate() +{ +} + + +StandardTestDelegate::~StandardTestDelegate() +{ +} + + +status_t +StandardTestDelegate::Setup(TestContext& context) +{ + return B_OK; +} + + +void +StandardTestDelegate::Cleanup(TestContext& context, bool setupOK) +{ +} Added: haiku/trunk/src/tests/system/kernel/unit/Test.h =================================================================== --- haiku/trunk/src/tests/system/kernel/unit/Test.h (rev 0) +++ haiku/trunk/src/tests/system/kernel/unit/Test.h 2009-12-30 19:38:41 UTC (rev 34827) @@ -0,0 +1,135 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef TEST_H +#define TEST_H + + +#include "TestContext.h" + + +class TestSuite; +class TestVisitor; + + +class Test { +public: + Test(const char* name); + virtual ~Test(); + + const char* Name() const { return fName; } + + TestSuite* Suite() const { return fSuite; } + void SetSuite(TestSuite* suite); + + virtual bool IsLeafTest() const; + virtual status_t Setup(TestContext& context); + virtual bool Run(TestContext& context) = 0; + virtual bool Run(TestContext& context, const char* name); + virtual void Cleanup(TestContext& context, bool setupOK); + + virtual Test* Visit(TestVisitor& visitor); + +private: + const char* fName; + TestSuite* fSuite; +}; + + +class StandardTestDelegate { +public: + StandardTestDelegate(); + virtual ~StandardTestDelegate(); + + virtual status_t Setup(TestContext& context); + virtual void Cleanup(TestContext& context, bool setupOK); +}; + + +template<typename TestClass> +class StandardTest : public Test { +public: + StandardTest(const char* name, + TestClass* object, + bool (TestClass::*method)(TestContext&)); + virtual ~StandardTest(); + + virtual status_t Setup(TestContext& context); + virtual bool Run(TestContext& context); + virtual void Cleanup(TestContext& context, bool setupOK); + +private: + TestClass* fObject; + bool (TestClass::*fMethod)(TestContext&); +}; + + +template<typename TestClass> +StandardTest<TestClass>::StandardTest(const char* name, TestClass* object, + bool (TestClass::*method)(TestContext&)) + : + Test(name), + fObject(object), + fMethod(method) +{ +} + + +template<typename TestClass> +StandardTest<TestClass>::~StandardTest() +{ + delete fObject; +} + + +template<typename TestClass> +status_t +StandardTest<TestClass>::Setup(TestContext& context) +{ + return fObject->Setup(context); +} + + +template<typename TestClass> +bool +StandardTest<TestClass>::Run(TestContext& context) +{ + return (fObject->*fMethod)(context); +} + + +template<typename TestClass> +void +StandardTest<TestClass>::Cleanup(TestContext& context, bool setupOK) +{ + fObject->Cleanup(context, setupOK); +} + + +#define TEST_ASSERT(condition) \ + do { \ + if (!(condition)) { \ + TestContext::Current()->AssertFailed(__FILE__, __LINE__, \ + __PRETTY_FUNCTION__, #condition); \ + return false; \ + } \ + } while (false) + +#define TEST_ASSERT_PRINT(condition, format...) \ + do { \ + if (!(condition)) { \ + TestContext::Current()->AssertFailed(__FILE__, __LINE__, \ + __PRETTY_FUNCTION__, #condition, format); \ + return false; \ + } \ + } while (false) + +#define TEST_PROPAGATE(result) \ + do { \ + if (!(result)) \ + return false; \ + } while (false) + + +#endif // TEST_H Added: haiku/trunk/src/tests/system/kernel/unit/TestContext.cpp =================================================================== --- haiku/trunk/src/tests/system/kernel/unit/TestContext.cpp (rev 0) +++ haiku/trunk/src/tests/system/kernel/unit/TestContext.cpp 2009-12-30 19:38:41 UTC (rev 34827) @@ -0,0 +1,283 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include "TestContext.h" + +#include <util/AutoLock.h> + +#include "Test.h" +#include "TestError.h" + + +static spinlock sLock = B_SPINLOCK_INITIALIZER; + + +// #pragma mark - TestOptions + + +TestOptions::TestOptions() + : + panicOnFailure(false), + quitAfterFailure(false) +{ +} + + +// #pragma mark - GlobalTestContext + + +struct GlobalTestContext::ThreadCookie { + GlobalTestContext* context; + thread_func function; + void* arg; + + ThreadCookie(GlobalTestContext* context, thread_func function, void* arg) + : + context(context), + function(function), + arg(arg) + { + } +}; + + +/*static*/ GlobalTestContext::ThreadEntry* GlobalTestContext::sGlobalThreads + = NULL; + + +GlobalTestContext::GlobalTestContext(TestOutput& output, TestOptions& options) + : + fThreads(NULL), + fThreadEntry(this), + fOutput(output), + fOptions(options), + fCurrentContext(NULL), + fTotalTests(0), + fFailedTests(0) +{ + _SetCurrent(&fThreadEntry); +} + + +GlobalTestContext::~GlobalTestContext() +{ + InterruptsSpinLocker locker(sLock); + + // remove all of our entries from the global list + ThreadEntry** entry = &sGlobalThreads; + while (*entry != NULL) { + if ((*entry)->context == this) + *entry = (*entry)->globalNext; + else + entry = &(*entry)->globalNext; + } +} + + +/*static*/ GlobalTestContext* +GlobalTestContext::Current() +{ + thread_id thread = find_thread(NULL); + + InterruptsSpinLocker locker(sLock); + + ThreadEntry* entry = sGlobalThreads; + while (entry != NULL) { + if (entry->thread == thread) + return entry->context; + entry = entry->globalNext; + } + + return NULL; +} + + +void +GlobalTestContext::SetCurrentContext(TestContext* context) +{ + fCurrentContext = context; +} + + +void +GlobalTestContext::TestDone(bool success) +{ + fTotalTests++; + if (!success) + fFailedTests++; +} + + +thread_id +GlobalTestContext::SpawnThread(thread_func function, const char* name, + int32 priority, void* arg) +{ + ThreadCookie* cookie = new(std::nothrow) ThreadCookie(this, function, arg); + if (cookie == NULL) + return B_NO_MEMORY; + + thread_id thread = spawn_kernel_thread(_ThreadEntry, name, priority, + cookie); + if (thread < 0) { + delete cookie; + return thread; + } + + return thread; +} + + +/*static*/ void +GlobalTestContext::_SetCurrent(ThreadEntry* entry) +{ + InterruptsSpinLocker locker(sLock); + + entry->contextNext = entry->context->fThreads; + entry->context->fThreads = entry; + + entry->globalNext = sGlobalThreads; + sGlobalThreads = entry; +} + + +/*static*/ void +GlobalTestContext::_UnsetCurrent(ThreadEntry* entryToRemove) +{ + InterruptsSpinLocker locker(sLock); + + // remove from the global list + ThreadEntry** entry = &sGlobalThreads; + while (*entry != NULL) { + if (*entry == entryToRemove) { + *entry = (*entry)->globalNext; + break; + } + + entry = &(*entry)->globalNext; + } + + // remove from the context's list + entry = &entryToRemove->context->fThreads; + while (*entry != NULL) { + if (*entry == entryToRemove) { + *entry = (*entry)->contextNext; + break; + } + + entry = &(*entry)->contextNext; + } +} + + +/*static*/ status_t +GlobalTestContext::_ThreadEntry(void* data) +{ + ThreadCookie* cookie = (ThreadCookie*)data; + + ThreadEntry entry(cookie->context); + _SetCurrent(&entry); + + thread_func function = cookie->function; + void* arg = cookie->arg; + delete cookie; + + status_t result = function(arg); + + _UnsetCurrent(&entry); + + return result; +} + + +// #pragma mark - TestContext + + +TestContext::TestContext(GlobalTestContext* globalContext) + : + fGlobalContext(globalContext), + fParent(NULL), + fTest(NULL), + fErrors(NULL), + fLevel(0) +{ + fGlobalContext->SetCurrentContext(this); +} + + +TestContext::TestContext(TestContext& parent, Test* test) + : + fGlobalContext(parent.GlobalContext()), + fParent(&parent), + fTest(test), + fErrors(NULL), + fLevel(parent.Level() + 1) +{ + fGlobalContext->SetCurrentContext(this); + + if (fTest != NULL) { + if (fTest->IsLeafTest()) + Print("%*s%s...", fLevel * 2, "", test->Name()); + else + Print("%*s%s:\n", fLevel * 2, "", test->Name()); + } +} + + +TestContext::~TestContext() +{ + fGlobalContext->SetCurrentContext(fParent); + + while (fErrors != NULL) { + TestError* error = fErrors; + fErrors = error->ListLink(); + delete error; + } +} + + +void +TestContext::TestDone(bool success) +{ + if (fTest != NULL && fTest->IsLeafTest()) { + if (success) { + Print(" ok\n"); + } else { + Print(" FAILED\n"); + TestError* error = fErrors; + while (error != NULL) { + Print("%s", error->Message()); + error = error->ListLink(); + } + } + + fGlobalContext->TestDone(success); + } +} + + +void +TestContext::ErrorArgs(const char* format, va_list args) +{ + int size = vsnprintf(NULL, 0, format, args) + 1; + char* buffer = (char*)malloc(size); + if (buffer == NULL) + return; + + vsnprintf(buffer, size, format, args); + + TestError* error = new(std::nothrow) TestError(fTest, buffer); + if (error == NULL) { + free(buffer); + return; + } + + InterruptsSpinLocker locker(sLock); + error->ListLink() = fErrors; + fErrors = error; + + if (Options().panicOnFailure) + panic("Test check failed: %s", error->Message()); +} Added: haiku/trunk/src/tests/system/kernel/unit/TestContext.h =================================================================== --- haiku/trunk/src/tests/system/kernel/unit/TestContext.h (rev 0) +++ haiku/trunk/src/tests/system/kernel/unit/TestContext.h 2009-12-30 19:38:41 UTC (rev 34827) @@ -0,0 +1,191 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef TEST_CONTEXT_H +#define TEST_CONTEXT_H + + +#include <stdarg.h> +#include <stdio.h> + +#include <KernelExport.h> + +#include "TestOutput.h" + + +class Test; +class TestContext; +class TestError; + + +struct TestOptions { + bool panicOnFailure; + bool quitAfterFailure; + + public: + TestOptions(); +}; + + +struct GlobalTestContext { + GlobalTestContext(TestOutput& output, + TestOptions& options); + ~GlobalTestContext(); + + static GlobalTestContext* Current(); + + TestOutput& Output() const { return fOutput; } + TestOptions& Options() const { return fOptions; } + + TestContext* CurrentContext() const + { return fCurrentContext; } + void SetCurrentContext(TestContext* context); + + int32 TotalTests() const { return fTotalTests; } + int32 FailedTests() const { return fFailedTests; } + + void TestDone(bool success); + + thread_id SpawnThread(thread_func function, + const char* name, int32 priority, + void* arg); + +private: + struct ThreadEntry { + ThreadEntry* globalNext; + ThreadEntry* contextNext; + GlobalTestContext* context; + thread_id thread; + + ThreadEntry(GlobalTestContext* context) + : + globalNext(NULL), + contextNext(NULL), + context(context), + thread(find_thread(NULL)) + { + } + }; + + struct ThreadCookie; + +private: + static void _SetCurrent(ThreadEntry* entry); + static void _UnsetCurrent(ThreadEntry* entry); + static status_t _ThreadEntry(void* data); + +private: + static ThreadEntry* sGlobalThreads; + ThreadEntry* fThreads; + ThreadEntry fThreadEntry; + TestOutput& fOutput; + TestOptions& fOptions; + TestContext* fCurrentContext; + int32 fTotalTests; + int32 fFailedTests; +}; + + +class TestContext { +public: + TestContext(GlobalTestContext* globalContext); + TestContext(TestContext& parent, Test* test); + ~TestContext(); + + static TestContext* Current(); + + GlobalTestContext* GlobalContext() const { return fGlobalContext; } + TestContext* Parent() const { return fParent; } + int32 Level() const { return fLevel; } + TestOutput& Output() const + { return fGlobalContext->Output(); } + TestOptions& Options() const + { return fGlobalContext->Options(); } + + inline int PrintArgs(const char* format, va_list args); + inline int Print(const char* format,...); + + void ErrorArgs(const char* format, va_list args); + inline void Error(const char* format,...); + + inline void AssertFailed(const char* file, int line, + const char* function, + const char* condition); + inline void AssertFailed(const char* file, int line, + const char* function, + const char* condition, + const char* format,...); + + void TestDone(bool success); + +private: + GlobalTestContext* fGlobalContext; + TestContext* fParent; + Test* fTest; + TestError* fErrors; + int32 fLevel; +}; + + +/*static*/ inline TestContext* +TestContext::Current() +{ + return GlobalTestContext::Current()->CurrentContext(); +} + + +int +TestContext::PrintArgs(const char* format, va_list args) +{ + return Output().PrintArgs(format, args); +} + + +int +TestContext::Print(const char* format,...) +{ + va_list args; + va_start(args, format); + int result = Output().PrintArgs(format, args); + va_end(args); + + return result; +} + + +void +TestContext::Error(const char* format,...) +{ + va_list args; + va_start(args, format); + ErrorArgs(format, args); + va_end(args); +} + + +void +TestContext::AssertFailed(const char* file, int line, const char* function, + const char* condition) +{ + Error("ASSERT FAILED at %s:%d %s: %s\n", file, line, function, condition); +} + + +void +TestContext::AssertFailed(const char* file, int line, const char* function, + const char* condition, const char* format,...) +{ + char buffer[256]; + + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + + Error("ASSERT FAILED at %s:%d %s: %s; %s\n", file, line, function, + condition, buffer); +} + + +#endif // TEST_CONTEXT_H Added: haiku/trunk/src/tests/system/kernel/unit/TestError.cpp =================================================================== --- haiku/trunk/src/tests/system/kernel/unit/TestError.cpp (rev 0) +++ haiku/trunk/src/tests/system/kernel/unit/TestError.cpp 2009-12-30 19:38:41 UTC (rev 34827) @@ -0,0 +1,23 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include "TestError.h" + +#include <stdlib.h> + + +TestError::TestError(Test* test, char* message) + : + fTest(test), + fMessage(message) +{ +} + + +TestError::~TestError() +{ + free(fMessage); +} Added: haiku/trunk/src/tests/system/kernel/unit/TestError.h =================================================================== --- haiku/trunk/src/tests/system/kernel/unit/TestError.h (rev 0) +++ haiku/trunk/src/tests/system/kernel/unit/TestError.h 2009-12-30 19:38:41 UTC (rev 34827) @@ -0,0 +1,32 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef TEST_ERROR_H +#define TEST_ERROR_H + + +#include <SupportDefs.h> + + +class Test; + + +class TestError { +public: + TestError(Test* test, char* message); + ~TestError(); + + Test* GetTest() const { return fTest; } + const char* Message() const { return fMessage; } + + TestError*& ListLink() { return fNext; } + +private: + TestError* fNext; + Test* fTest; + char* fMessage; +}; + + +#endif // TEST_ERROR_H Added: haiku/trunk/src/tests/system/kernel/unit/TestManager.cpp =================================================================== --- haiku/trunk/src/tests/system/kernel/unit/TestManager.cpp (rev 0) +++ haiku/trunk/src/tests/system/kernel/unit/TestManager.cpp 2009-12-30 19:38:41 UTC (rev 34827) @@ -0,0 +1,88 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include "TestManager.h" + +#include <string.h> + +#include "TestOutput.h" +#include "TestVisitor.h" + + +TestManager::TestManager() + : + TestSuite("all") +{ +} + + +TestManager::~TestManager() +{ +} + + +void +TestManager::ListTests(TestOutput& output) +{ + struct Visitor : TestVisitor { + Visitor(TestOutput& output) + : + fOutput(output), + fLevel(0) + { + } + + virtual bool VisitTest(Test* test) + { + fOutput.Print("%*s%s\n", fLevel * 2, "", test->Name()); + return false; + } + + virtual bool VisitTestSuitePre(TestSuite* suite) + { + if (fLevel > 0) + VisitTest(suite); + fLevel++; + return false; + } + + virtual bool VisitTestSuitePost(TestSuite* suite) + { + fLevel--; + return false; + } + + private: + TestOutput& fOutput; + int fLevel; + } visitor(output); + + output.Print("Available tests:\n"); + Visit(visitor); +} + + +void +TestManager::RunTests(GlobalTestContext& globalContext, + const char* const* tests, int testCount) +{ + TestContext context(&globalContext); + + context.Print("Running tests:\n"); + + if (testCount == 0 || (testCount == 1 && strcmp(tests[0], "all") == 0)) { + Run(context); + } else { + for (int i = 0; i < testCount; i++) { + bool result = Run(context, tests[i]); + if (!result && context.Options().quitAfterFailure) + break; + } + } + + context.Print("run tests: %ld, failed tests: %ld\n", + globalContext.TotalTests(), globalContext.FailedTests()); +} Added: haiku/trunk/src/tests/system/kernel/unit/TestManager.h =================================================================== --- haiku/trunk/src/tests/system/kernel/unit/TestManager.h (rev 0) +++ haiku/trunk/src/tests/system/kernel/unit/TestManager.h 2009-12-30 19:38:41 UTC (rev 34827) @@ -0,0 +1,27 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef TEST_MANAGER_H +#define TEST_MANAGER_H + + +#include "TestSuite.h" + + +class GlobalTestContext; +class TestOutput; + + +class TestManager : public TestSuite { +public: + TestManager(); + ~TestManager(); + + void ListTests(TestOutput& output); + void RunTests(GlobalTestContext& globalContext, + const char* const* tests, int testCount); +}; + + +#endif // TEST_MANAGER_H Added: haiku/trunk/src/tests/system/kernel/unit/TestOutput.cpp =================================================================== --- haiku/trunk/src/tests/system/kernel/unit/TestOutput.cpp (rev 0) +++ haiku/trunk/src/tests/system/kernel/unit/TestOutput.cpp 2009-12-30 19:38:41 UTC (rev 34827) @@ -0,0 +1,50 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include "TestOutput.h" + +#include <stdio.h> + +#include <util/AutoLock.h> + + +// #pragma mark - TestOutput + + +TestOutput::TestOutput() +{ +} + + +TestOutput::~TestOutput() +{ +} + + +// #pragma mark - DebugTestOutput + + +DebugTestOutput::DebugTestOutput() +{ + B_INITIALIZE_SPINLOCK(&fLock); +} + + [... truncated: 1113 lines follow ...]