From 93bbb633c19b44e3a1821e76b49f21caab548b73 Mon Sep 17 00:00:00 2001 From: Chris McFarlen Date: Thu, 20 Nov 2025 16:19:25 -0600 Subject: [PATCH] Remove Extendible and AcidPtr (unused code) --- .../internal-libraries/AcidPtr.en.rst | 286 ------- .../internal-libraries/Extendible.en.rst | 329 -------- .../internal-libraries/index.en.rst | 2 - include/tscore/AcidPtr.h | 199 ----- include/tscore/Extendible.h | 776 ------------------ src/tscore/AcidPtr.cc | 43 - src/tscore/CMakeLists.txt | 5 - src/tscore/Extendible.cc | 165 ---- src/tscore/unit_tests/test_AcidPtr.cc | 164 ---- src/tscore/unit_tests/test_Extendible.cc | 520 ------------ 10 files changed, 2489 deletions(-) delete mode 100644 doc/developer-guide/internal-libraries/AcidPtr.en.rst delete mode 100644 doc/developer-guide/internal-libraries/Extendible.en.rst delete mode 100644 include/tscore/AcidPtr.h delete mode 100644 include/tscore/Extendible.h delete mode 100644 src/tscore/AcidPtr.cc delete mode 100644 src/tscore/Extendible.cc delete mode 100644 src/tscore/unit_tests/test_AcidPtr.cc delete mode 100644 src/tscore/unit_tests/test_Extendible.cc diff --git a/doc/developer-guide/internal-libraries/AcidPtr.en.rst b/doc/developer-guide/internal-libraries/AcidPtr.en.rst deleted file mode 100644 index 4bbf7613931..00000000000 --- a/doc/developer-guide/internal-libraries/AcidPtr.en.rst +++ /dev/null @@ -1,286 +0,0 @@ -.. 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 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - -.. include:: ../../common.defs - -.. highlight:: cpp -.. default-domain:: cpp - -.. |AcidPtr| replace:: :class:`AcidPtr` -.. |AcidCommitPtr| replace:: :class:`AcidCommitPtr` - - -.. _ACIDPTR: - -AcidPtr & AcidCommitPtr -*********************** - -Synopsis -++++++++ - -.. code-block:: cpp - - #include "tscore/AcidPtr.h" - -|AcidPtr| provides atomic access to a std::shared_ptr. -|AcidCommitPtr| provides exclusive write access to data. - -Description -+++++++++++ - -Provides transparent interface for "copy on write" and "commit when done" in a familiar unique_ptr style. - -Named after the desirable properties of a database, ACID_ acronym: - -* Atomic - reads and writes avoid skew, by using mutex locks. -* Consistent - data can only be changed by a commit, and only one commit can exist at a time per data. -* Isolated - commits of a single point of data are not concurrent. But commits of separate data can be concurrent. -* Durable - shared_ptr is used to keep older versions of data in memory while references exist. - -.. _ACID: https://en.wikipedia.org/wiki/ACID_(computer_science) - -.. uml: - class AcidPtr { - -std::shared_ptr data - ---- - +AcidPtr() - +AcidPtr(T*) - +std::shared_ptr getPtr() - +void commit(T*) - +AcidCommmitPtr startCommit() - ---- - ~_finishCommit(T*) - } - - class AcidCommitPtr { - -AcidPtr& data_ptr - -std::unique_lock commit_lock - ---- - -AcidCommmitPtr() = delete - +AcidCommmitPtr(AcidPtr&) - +~AcidCommmitPtr() - +void abort() - } - - class std::unique_ptr { - - } - - AcidCommitPtr--|>std::unique_ptr - -Performance ------------ -Note this code is currently implemented with mutex locks, it is expected to be fast due to the very brief duration of each lock. It would be plausible to change the implementation to use atomics or spin locks. - - -|AcidCommitPtr| - -* On construction, duplicate values from a shared_ptr to a unique_ptr. (aka copy read to write memory) -* On destruction, move value ptr to shared_ptr. (aka move write ptr to read ptr) - -The |AcidCommitPtr| executes this transparent to the writer code. It copies the data on construction, and finalizes on destruction. A MemLock is used to allow exclusive read and write access, however the access is made to as fast as possible. - -Use Cases ---------- -Implemented for use |ACIDPTR| interface in :class:`Extendible`. But could be used elsewhere without modification. - -.. uml:: - :align: center - - title Read while Write - - actor ExistingReaders - actor NewReaders - actor Writer - storage AcidPtr - card "x=foo" as Data - card "x=bar" as DataPrime - - ExistingReaders --> Data : read only - AcidPtr -> Data : shared_ptr - NewReaders --> AcidPtr : read only - Writer --> DataPrime : read/write - Data .> DataPrime : copy - -When the writer is done, :func:`~AcidCommitPtr::~AcidCommitPtr()` is called and its |AcidPtr| is updated to point at the written copy, so that future read requests will use it. Existing reader will continue to use the old data. - -.. uml:: - :align: center - - title Write Finalize - - actor ExistingReaders - actor NewReaders - storage AcidPtr - card "x=foo" as Data - card "x=bar" as DataPrime - - ExistingReaders --> Data : read only - AcidPtr -> DataPrime : shared_ptr - NewReaders --> AcidPtr : read only - - -.. uml:: - :align: center - - title AcidPtr Reader/Reader Contention - box "MemLock" - participant "Access" as AccessMutex - end box - participant AcidPtr - actor Reader_A #green - actor Reader_B #red - Reader_A -[#green]> AcidPtr: getPtr() - AcidPtr <[#green]- AccessMutex: lock - activate AccessMutex #green - Reader_B -> AcidPtr: getPtr() - AcidPtr -[#green]> Reader_A: copy const shared_ptr - activate Reader_A - note left - Contention limited to - duration of shared_ptr copy. - (internally defined) - end note - AcidPtr -[#green]> AccessMutex: unlock - deactivate AccessMutex - AcidPtr <- AccessMutex: lock - activate AccessMutex #red - AcidPtr -> Reader_B: copy const shared_ptr - activate Reader_B - AcidPtr -> AccessMutex: unlock - deactivate AccessMutex - - -.. uml:: - :align: center - - Title AcidPtr Writer/Reader Contention - box "MemLock" - participant "Access" as AccessMutex - participant "Write" as WriteMutex - end box - participant AcidPtr - actor Writer #red - actor Reader #green - - Writer -> AcidPtr: startCommit() - AcidPtr <- WriteMutex: lock - activate WriteMutex #red - AcidPtr -> Writer: copy to AcidCommitPtr - activate Writer - hnote over Writer #pink - update AcidCommitPtr - end hnote - Reader -[#green]> AcidPtr: getPtr() - AcidPtr <[#green]- AccessMutex: lock - activate AccessMutex #green - - Writer -> AcidPtr: ~AcidCommitPtr() - deactivate Writer - AcidPtr -[#green]> Reader: copy const shared_ptr - activate Reader - note left - Contention limited to duration - of shared_ptr copy/reset. - (internally defined) - end note - AcidPtr -[#green]> AccessMutex: unlock - deactivate AccessMutex - - AcidPtr <- AccessMutex: lock - activate AccessMutex #red - hnote over Reader #lightgreen - use shared copy - end hnote - AcidPtr -> AcidPtr: reset shared_ptr - AcidPtr -> AccessMutex: unlock - deactivate AccessMutex - AcidPtr -> WriteMutex: unlock - deactivate WriteMutex - - -.. uml:: - :align: center - - Title AcidPtr Writer/Writer Contention - box "MemLock" - participant "Access" as AccessMutex - participant "Write" as WriteMutex - end box - participant AcidPtr - actor Writer_A #red - actor Writer_B #blue - - Writer_A -> AcidPtr: startCommit - AcidPtr <- WriteMutex: lock - activate WriteMutex #red - AcidPtr -> Writer_A: copy to AcidCommitPtr - activate Writer_A - Writer_B -[#blue]> AcidPtr: startCommit - hnote over Writer_A #pink - update AcidCommitPtr - end hnote - note over Writer_A - Contention for duration - of AcidCommitPtr scope. - (externally defined) - end note - Writer_A -> AcidPtr: ~AcidCommitPtr() - deactivate Writer_A - AcidPtr <- AccessMutex: lock - activate AccessMutex #red - AcidPtr -> AcidPtr: reset shared_ptr - AcidPtr -> AccessMutex: unlock - deactivate AccessMutex - AcidPtr -> WriteMutex: unlock - deactivate WriteMutex - - AcidPtr <[#blue]- WriteMutex: lock - activate WriteMutex #blue - AcidPtr -[#blue]> Writer_B: copy to AcidCommitPtr - activate Writer_B - hnote over Writer_B #lightblue - update AcidCommitPtr - end hnote - Writer_B -[#blue]> AcidPtr: ~AcidCommitPtr() - deactivate Writer_B - deactivate AccessMutex - - AcidPtr <[#blue]- AccessMutex: lock - activate AccessMutex #blue - AcidPtr -[#blue]> AcidPtr: reset shared_ptr - AcidPtr -[#blue]> AccessMutex: unlock - deactivate AccessMutex - AcidPtr -[#blue]> WriteMutex: unlock - deactivate WriteMutex - -Reference -+++++++++ - - -.. class:: template AcidPtr - - .. function:: template const std::shared_ptr getPtr() const - -.. class:: template AcidCommitPtr - - .. function:: template<> ~AcidCommitPtr() - - .. function:: template AcidCommitPtr startCommit() - - .. function:: template<> void abort() - diff --git a/doc/developer-guide/internal-libraries/Extendible.en.rst b/doc/developer-guide/internal-libraries/Extendible.en.rst deleted file mode 100644 index 66011d6bbcb..00000000000 --- a/doc/developer-guide/internal-libraries/Extendible.en.rst +++ /dev/null @@ -1,329 +0,0 @@ -.. 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 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - -.. include:: ../../common.defs - -.. highlight:: cpp -.. default-domain:: cpp - -.. |Extendible| replace:: :class:`Extendible` -.. |FieldId| replace:: :class:`template FieldId` -.. |ExtFieldContext| replace:: :type:`ExtFieldContext` -.. |FieldDesc| replace:: :class:`FieldDesc` -.. |Schema| replace:: :class:`Schema` - - -.. _Extendible: - -Extendible -********** - -Synopsis -++++++++ - -.. code-block:: cpp - - #include "tscore/Extendible.h" - -|Extendible| allows Plugins to append additional storage to Core data structures and interface like a map or dictionary. Each additional field is declared during init, so that a custom allocator can malloc one block for the Datatype and its extended fields. -In C++, the |FieldId| are strongly typed field handles, which allows you to use Extendible in multiple inheritance, and at many levels of inheritance hierarchy, with compile time type safety. - -Use Case: - - TSCore - Defines class ``Host`` as |Extendible| - - TSPlugin ``HealthStatus`` - Extend the ``Host`` datatype with field `` down reason code``. API returns a handle. - - Use :arg:`Data` and :arg:`handle` to read & write fields. - - -Description -+++++++++++ - -A data class that inherits from Extendible, uses a CRTP (Curiously Recurring Template Pattern) -so that its static |Schema| instance is unique among other |Extendible| types. Thus all instances of the type implicitly know memory layout of the fields. - -.. code-block:: cpp - - class ExtendibleExample : public ext::Extendible { - int real_member = 0; - } - -The documentation and code refers to the `Derived` type as the class that is inheriting from an |Extendible|. - -During system init, code and plugins add fields to the |Extendible|'s schema. This will update the `Memory Layout`_ of the schema, -and the memory offsets of all fields. The schema does not know the field's type, but it stores the byte size and creates std::functions of -the type's constructor, destructor, and serializer. And to avoid corruption, the code asserts that no instances are in use when adding fields. - -.. code-block:: cpp - - ext::FieldId fld_my_int; - - void PluginInit() { - fld_my_int = ext::fieldAdd(fld_my_int, "my_plugin_int"); - } - - -When an derived class is instantiated, :func:`template<> create()` will allocate a block of memory for the derived class and all added -fields. The only memory overhead per instance is an uint16 used as a offset to the start of the extendible block. Then the constructor of the class -is called, followed by the constructors of each extendible field. - -.. code-block:: cpp - - ExtendibleExample* alloc_example() { - return ext::create(); - } - -Memory Layout -------------- -One block of memory is allocated per |Extendible|, which include all member variables and extended fields. -Within the block, memory is arranged in the following order: - -#. Derived members (+padding align next field) -#. Fields (largest to smallest) -#. Packed Bits - -When using inheritance, all base cases arranged from most super to most derived, -then all |Extendible| blocks are arranged from most super to most derived. -If the fields are aligned, padding will be inserted where needed. - -Strongly Typed Fields ---------------------- -:class:`template FieldId` - -|FieldId| is a templated ``Field_t`` reference. One benefit is that all type casting is internal to the |Extendible|, -which simplifies the code using it. Also this provides compile errors for common misuses and type mismatches. - -.. code-block:: cpp - - // Core code - class Food : public ext::Extendible {}; - class Car : public ext::Extendible {}; - - // Example Plugin - ext::FieldId fld_food_weight; - ext::FieldId fld_expr_date; - ext::FieldId fld_max_speed; - ext::FieldId fld_car_weight; - - PluginInit() { - ext::addField(fld_food_weight, "weight"); - ext::addField(fld_expr_date,"expire date"); - ext::addField(fld_max_speed,"max_speed"); - ext::addField(fld_car_weight,"weight"); // 'weight' is unique within 'Car' - } - - PluginFunc() { - Food *banana = ext::create(); - Car *camry = ext::create(); - - // Common user errors - - float expire_date = ext::get(banana,fld_expr_date); - //^^^ - // Compile error: cannot convert time_t to float - - float speed = ext::get(banana,fld_max_speed); - // ^^^^^^^^^^^^^ - // Compile error: Cannot convert banana to type Extendible - - float weight = ext::get(camry,fld_food_weight); - // ^^^^^^^^^^^^^^^ - // Compile error: Cannot convert camry to type Extendible, even though Car and Food each have a 'weight' field, the FieldId is strongly typed. - - } - - // Inheritance Example - class Fruit : Food, Extendible { - using super_type = Food; - }; - - ext::FieldId fld_has_seeds; - - Fruit.schema.addField(fld_has_seeds, "has_seeds"); - - Fruit mango = ext::create(); - - ext::set(mango, fld_has_seeds) = true; // converts mango to Extendible - ext::set(mango, fld_food_weight) = 2; // converts mango to Extendible - ext::set(mango, fld_max_speed) = 9; - // ^^^^^^^^^^^^^ - // Compile error: Cannot convert mango to type Extendible - - -Inheritance ------------ - -Unfortunately it is non-trivial handle multiple |Extendible| super types in the same inheritance -tree. :func:`template<> create()` handles allocation and initialization of the entire `Derived` -class, but it is dependent on each class defining :code:`using super_type = *some_super_class*;` so -that it recurse through the classes. - -.. code-block:: cpp - - struct A : public Extendible { - uint16_t a = {1}; - }; - - struct B : public A { - using super_type = A; - uint16_t b = {2}; - }; - - struct C : public B, public Extendible { - using super_type = B; - uint16_t c = {3}; - }; - - ext::FieldId> ext_a_1; - ext::FieldId ext_c_1; - - C &x = *(ext::create()); - ext::viewFormat(x); - -:func:`viewFormat` prints a diagram of the position and size of bytes used within the allocated -memory. - -.. code-block:: text - - 1A | EXT | 2b | ##________##__ | - 1A | BASE | 2b | __##__________ | - 1B | BASE | 2b | ____##________ | - 1C | EXT | 2b | ______##____## | - 1C | BASE | 2b | ________##____ | - - -See :ts:git:`src/tscore/unit_tests/test_Extendible.cc` for more examples. - -Reference -+++++++++ - -Namespace `ext` - -.. class:: template FieldId - - The handle used to access a field. These are templated to prevent human error, and branching logic. - - :tparam Derived_t: The class that you want to extend at runtime. - :tparam Field_t: The type of the field. - -.. type:: const void* ExtFieldContext - - The handle used to access a field through C API. Human error not allowed by convention. - - -.. class:: template Extendible - - Allocates block of memory, uses |FieldId| and |Schema| to access slices of memory. - - :tparam Derived_t: The class that you want to extend at runtime. - - .. member:: static Schema schema - - one schema instance per |Extendible| to define contained |FieldDesc| - -.. function:: template Extendible* create() - - To be used in place of `new Derived_t()`. - Allocate a block of memory. Construct the base data. - Recursively construct and initialize `Derived_t::super_type` and its |Extendible| classes. - - :tparam Derived_t: The Derived class to allocate. - -.. function:: template \ - bool fieldAdd(FieldId & field_id, std::string const & field_name) - - Declare a new |FieldId| for Derived_t. - - :tparam Derived_t: The class that uses this field. - :tparam Field_t: The type of the field. - -.. function:: template \ - bool fieldFind(FieldId & field_id, std::string const & field_name) - - Find an existing |FieldId| for Derived_t. - - :tparam Derived_t: The class that uses this field. - :tparam Field_t: The type of the field. - -.. function:: template \ - auto const get(T const &, FieldId) - - Returns T const& value from the field stored in the |Extendible| allocation. - - :tparam T: The class passed in. - :tparam Derived_t: The class that uses this field. - :tparam Field_t: The type of the field. - -.. function:: template \ - T & set(T &, FieldId) - - Returns T & value from the field stored in |Extendible| allocation. - - :tparam T: The class passed in. - :tparam Derived_t: The class that uses this field. - :tparam Field_t: The type of the field. - -.. function:: template size_t sizeOf() - - Recurse through super classes and sum memory needed for allocation. - - Depends on usage of `super_type` in each class. - - :tparam Derived_t: The class to measure. - -.. function:: template void viewFormat() - - Recurse through super classes and prints chart of bytes used within the allocation. - - Depends on usage of `super_type` in each class. - - :tparam Derived_t: The class to analyze. - -.. function:: template std::string toString(T const &t) - - Convert all extendible fields to std::strings (in a YAML-like format) using the :func:`serializeField()` - - :tparam Derived_t: The class to convert to string. - -.. function:: template void serializeField(std::ostream &os, Field_t const &f) - - Converts a single field into a std::string (in a YAML-like format). - - Specialize this template or overload the `operator<<` for your field to convert properly. - - This is very useful when debugging. - - :tparam Derived_t: The field data type. - -Namespace `ext::details` - - -.. class:: FieldDesc - - Defines a span of memory within the allocation, and holds the constructor, destructor and serializer as std::functions. - - Effectively the type-erased version of |FieldId|. - -.. class:: Schema - - Manages a memory layout through a map of |FieldDesc|. - - - diff --git a/doc/developer-guide/internal-libraries/index.en.rst b/doc/developer-guide/internal-libraries/index.en.rst index 45f808280d1..0dc9820afdd 100644 --- a/doc/developer-guide/internal-libraries/index.en.rst +++ b/doc/developer-guide/internal-libraries/index.en.rst @@ -28,9 +28,7 @@ development team. .. toctree:: :maxdepth: 1 - AcidPtr.en ArgParser.en - Extendible.en MemArena.en MemSpan.en TextView.en diff --git a/include/tscore/AcidPtr.h b/include/tscore/AcidPtr.h deleted file mode 100644 index bc9c3f248f8..00000000000 --- a/include/tscore/AcidPtr.h +++ /dev/null @@ -1,199 +0,0 @@ -/** - @file AcidPtr - - @section license License - - 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 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - @section details Details - -//////////////////////////////////////////// - Implements advanced locking techniques: - * LockPool - * Writer_ptr -*/ - -#pragma once -#include -#include -#include - -////////////////////////////////////////////////////////// -// Lock Pool -/// Intended to make datasets thread safe by assigning locks to stripes of data, kind of like a bloom filter. -/** Allocates a fixed number of locks and retrieves one with a hash. - */ -template struct LockPool { - /** - * @param numLocks - use a prime number near the number of concurrent users you expect - */ - LockPool(size_t num_locks) : mutexes(num_locks) {} - - Mutex_t & - getMutex(size_t key_hash) - { - return mutexes[key_hash % size()]; - } - - size_t - size() const - { - return mutexes.size(); - } - - void - lockAll() - { - for (Mutex_t &m : mutexes) { - m.lock(); - } - } - - void - unlockAll() - { - for (Mutex_t &m : mutexes) { - m.unlock(); - } - } - -private: - std::vector mutexes; - - /// use the other constructor to define how many locks you want. - LockPool() = delete; - LockPool(LockPool const &) = delete; -}; - -template class AcidCommitPtr; -template class AcidPtr; - -using AcidPtrMutex = std::mutex; // TODO: use shared_mutex when available -using AcidPtrLock = std::unique_lock; -AcidPtrMutex &AcidPtrMutexGet(void const *ptr); // used for read, and write swap - -using AcidCommitMutex = std::mutex; -using AcidCommitLock = std::unique_lock; -AcidCommitMutex &AcidCommitMutexGet(void const *ptr); // used for write block - -/////////////////////////////////////////// -/// AcidPtr -/** just a thread safe shared pointer. - * - */ - -template class AcidPtr -{ -private: - std::shared_ptr data_ptr; - -public: - AcidPtr(const AcidPtr &) = delete; - AcidPtr &operator=(const AcidPtr &) = delete; - - AcidPtr() : data_ptr(new T()) {} - AcidPtr(T *data) : data_ptr(data) {} - - const std::shared_ptr - getPtr() const - { // wait until we have exclusive pointer access. - auto ptr_lock = AcidPtrLock(AcidPtrMutexGet(&data_ptr)); - // copy the pointer - return data_ptr; - //[end scope] unlock ptr_lock - } - - void - commit(T *data) - { - // wait until existing commits finish, avoid race conditions - auto commit_lock = AcidCommitLock(AcidCommitMutexGet(&data_ptr)); - // wait until we have exclusive pointer access. - auto ptr_lock = AcidPtrLock(AcidPtrMutexGet(&data_ptr)); - // overwrite the pointer - data_ptr.reset(data); - //[end scope] unlock commit_lock & ptr_lock - } - - AcidCommitPtr - startCommit() - { - return AcidCommitPtr(*this); - } - - friend class AcidCommitPtr; - -protected: - void - _finishCommit(T *data) - { - // wait until we have exclusive pointer access. - auto ptr_lock = AcidPtrLock(AcidPtrMutexGet(&data_ptr)); - // overwrite the pointer - data_ptr.reset(data); - //[end scope] unlock ptr_lock - } -}; - -/////////////////////////////////////////// -/// AcidCommitPtr - -/// a globally exclusive pointer, for committing changes to AcidPtr. -/** used for COPY_SWAP functionality. - * 1. copy data (construct) - * 2. overwrite data (scope) - * 3. update live data pointer (destruct) - */ -template class AcidCommitPtr : public std::unique_ptr -{ -private: - AcidCommitLock commit_lock; // block other writers from starting - AcidPtr &data; // data location - -public: - AcidCommitPtr() = delete; - AcidCommitPtr(const AcidCommitPtr &) = delete; - AcidCommitPtr &operator=(const AcidCommitPtr &) = delete; - - AcidCommitPtr(AcidPtr &data_ptr) : commit_lock(AcidCommitMutexGet(&data_ptr)), data(data_ptr) - { - // wait for exclusive commit access to the data - // copy the data to new memory - std::unique_ptr::reset(new T(*data.getPtr())); - } - AcidCommitPtr(AcidCommitPtr &&other) - : std::unique_ptr(std::move(other)), commit_lock(std::move(other.commit_lock)), data(other.data) - { - } - - ~AcidCommitPtr() - { - if (!commit_lock) { - return; // previously aborted - } - - // point the existing read ptr to the newly written data - data._finishCommit(std::unique_ptr::release()); - } - - void - abort() - { - commit_lock.unlock(); // allow other writers to start - std::unique_ptr::reset(); // delete data copy - } -}; diff --git a/include/tscore/Extendible.h b/include/tscore/Extendible.h deleted file mode 100644 index 5ba7c6d9fe5..00000000000 --- a/include/tscore/Extendible.h +++ /dev/null @@ -1,776 +0,0 @@ -/** @file - - Extendible - - @section license License - - 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 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - @section details Details - -//////////////////////////////////////////// - Implements: - * Extendible - * Schema - * fieldAdd - * fieldFind - */ - -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tscore/AtomicBit.h" -#include "tscore/ink_assert.h" -#include "tscore/ink_memory.h" -#include "tscore/ink_defs.h" - -////////////////////////////////////////// -/// SUPPORT MACRO -#define DEF_EXT_NEW_DEL(cls) \ - void *operator new(size_t) \ - { \ - return ats_malloc(ext::sizeOf()); \ - } \ - void *operator new(size_t, void *ptr) \ - { \ - return ptr; \ - } \ - void operator delete(void *ptr) \ - { \ - free(ptr); \ - } - -////////////////////////////////////////// -/// HELPER CLASSES - -///////////////////////// -// Has 'const_iterator' trait class -template class has_const_iterator -{ - template static std::true_type check(typename U::const_iterator *); - template static std::false_type check(...); - -public: - static constexpr decltype(check(nullptr)) value{}; // value is 'constexpr true' if T defines 'const_iterator' -}; -///////////////////////// -// Has 'super_type' trait class -template class has_super_type -{ - template static std::true_type check(typename U::super_type *); - template static std::false_type check(...); - -public: - static constexpr decltype(check(nullptr)) value{}; // value is 'constexpr true' if T defines 'super_type' -}; - -////////////////////////////////////////// -////////////////////////////////////////// -////////////////////////////////////////// - -// C API - -// context (internally FieldDesc*) -using ExtFieldContext = const void *; -using DerivedPtr = void *; -using FieldPtr = void *; - -FieldPtr ExtFieldPtr(DerivedPtr derived, ExtFieldContext field_context, int *size = nullptr); - -////////////////////////////////////////// -////////////////////////////////////////// -////////////////////////////////////////// - -namespace ext -{ -//////////////////////////////////////////////////// -// Forward declarations -template class FieldId; // field handle (strongly type) -template class Extendible; - -namespace details // internal stuff -{ - using Offest_t = uint16_t; // used to store byte offsets to fields - struct FieldDesc; // keeps the field properties, and methods - class Schema; // container of fields and methods - - // forward declare friend functions used outside of details - template uintptr_t initRecurseSuper(T &, uintptr_t); - template FieldPtr FieldPtrGet(Extendible const &, details::FieldDesc const &); - - ////////////////////////////////////////// - - bool &areFieldsFinalized(); - - ///////////////////////////////////////////////////////////////////// - /// ext::details::FieldDesc - type erased field descriptor, with type specific std::functions - struct FieldDesc { - Offest_t ext_loc_offset; ///< byte offset to Extendible._ext_loc - Offest_t field_offset; ///< byte offset from ext_loc to field - std::type_index field_type_idx; ///< data type index - uint16_t size; ///< byte size of field - uint8_t align; ///< alignment of field - uint8_t mask; ///< mask for packed bit operations - - // specialize the following - std::function constructor; - std::function destructor; - std::function serializer; - - FieldDesc() : field_type_idx(typeid(std::nullptr_t)) {} - }; - - ///////////////////////////////////////////////////////////////////// - /// ext::details::Schema manages the a static layout of fields as data structures - class Schema - { - public: - std::unordered_map fields; ///< defined elements of the blob by name - size_t alloc_size = 0; ///< bytes to allocate for fields - uint8_t alloc_align = 1; ///< alignment of block - std::atomic cnt_constructed = {0}; ///< the number of Extendible created. - std::atomic cnt_fld_constructed = {0}; ///< the number of Extendible that constructed fields. - std::atomic cnt_destructed = {0}; ///< the number of Extendible destroyed. - - public: - Schema() {} - ~Schema(); - - /// Testing methods - bool no_instances() const; ///< returns true if there are no instances of Extendible - bool reset(); ///< clears all field definitions. - - size_t fullSize(size_t base_size) const; ///< returns sizeof memory allocated - void updateMemOffsets(); ///< updates memory offsets, alignment, and total allocation size - void callConstructor(uintptr_t ext_start_ptr); ///< calls constructor for each field - void callDestructor(uintptr_t ext_start_ptr); ///< call destructor for each field - - }; // end Schema struct -} // namespace details - -/// ext::Extendible allows code (and Plugins) to declare member-like variables during system init. -/* - * This class uses a special allocator (ext::create) to extend the memory allocated to store run-time static - * variables, which are registered by plugins during system init. The API is in a functional style to support - * multiple inheritance of Extendible classes. This is templated so static variables are instanced per Derived - * type, because we need to have different field schema per type. - * - * @tparam Derived_t - the class that you want to extend at runtime. - * - * @see test_Extendible.cc for examples - * - */ -template class Extendible -{ -public: - using short_ptr_t = uint16_t; - - // static - static details::Schema schema; ///< one schema instance per Derived_t to define contained fields - - // return the address offset of the ext_loc member variable - static constexpr size_t - getLocOffset() - { - return offsetof(Extendible, ext_loc); - } - - // member variables -private: - short_ptr_t ext_loc = 0; ///< byte offset to extendible storage - - // lifetime management - /** don't allow copy construct, that doesn't allow atomicity */ -public: - Extendible(Extendible &) = delete; - -protected: - Extendible(); - // use ext::create() exclusively for allocation and initialization - - /** destruct all fields */ - ~Extendible(); - -private: - /** construct all fields */ - size_t initFields(uintptr_t start_ptr); ///< tell this extendible where it's memory offset start is. - - uintptr_t - getBegin() const - { - return uintptr_t(this) + ext_loc; - } - - template friend T *create(); - template friend uintptr_t details::initRecurseSuper(T &, uintptr_t); - template friend FieldPtr details::FieldPtrGet(Extendible const &, details::FieldDesc const &); - template friend std::string viewFormat(T const &, uintptr_t, int); -}; - -// define the static schema per derived type -template details::Schema Extendible::schema; - -// #################################################### -// #################################################### -// UTILITY Functions - -////////////////////////////////////////////////////// -/// HexToString function for serializing untyped C storage -// TODO: use swoc::bwf::As_Hex() after PR goes through -inline void -hexToStream(std::ostream &os, void const *buf, uint16_t size) -{ - static const char hexDigits[] = "0123456789abcdef"; - - const uint8_t *src = static_cast(buf); - for (int i = 0; i < size; i++) { - os << hexDigits[src[i]]; - } -}; - -template -void -serializeField(std::ostream &os, Field_t const &f) -{ - using namespace std; - // print containers as lists - if constexpr (has_const_iterator::value) { - os << "["; - for (auto const &a : f) { - serializeField(os, a); - os << ", "; - } - os << "]"; - return; - } else { - os << f; - } -} -// #################################################### -// #################################################### -// #################################################### - -///////////////////////////////////////////////////////////////////// -// FieldId -/// a strongly typed pointer to FieldDesc -template class FieldId -{ -public: - ext::details::FieldDesc const *desc = nullptr; - bool isValid() const; - FieldId(ext::details::FieldDesc const &); - FieldId() {} - FieldId(FieldId const &) = default; - FieldId &operator=(FieldId const &) = default; -}; - -namespace details -{ - template - FieldPtr - FieldPtrGet(Extendible const &d, FieldDesc const &desc) - { - return FieldPtr(d.Extendible::getBegin() + desc.field_offset); - } - - // overloadable function to construct and init an FieldId - ////////////////////////////////////////////////////// - /// Type Generic Template - - template - Field_t const & - fieldGet(void const *fld_ptr, FieldId const & /* field ATS_UNUSED */) - { - return *static_cast(fld_ptr); - } - - template - Field_t & - fieldSet(void *fld_ptr, FieldId const & /* field ATS_UNUSED */) - { - return *static_cast(fld_ptr); - } - - template - void - makeFieldId(FieldId &id, FieldDesc &desc) - { - ink_assert(!areFieldsFinalized()); - - desc.field_type_idx = std::type_index(typeid(Field_t)); - desc.ext_loc_offset = Extendible::getLocOffset(); - desc.field_offset = std::numeric_limits::max(); - desc.size = sizeof(Field_t); - desc.align = alignof(Field_t); - desc.mask = 0; - - id = FieldId(desc); - - // - desc.constructor = [](FieldPtr fld_ptr) { new (fld_ptr) Field_t(); }; - desc.destructor = [](FieldPtr fld_ptr) { static_cast(fld_ptr)->~Field_t(); }; - desc.serializer = [id](std::ostream &os, void const *fld_ptr) { serializeField(os, fieldGet(fld_ptr, id)); }; - } - - ////////////////////////////////////////////////////// - /// C API specialization - // no type or constructor. Just a size. - - template - void - makeFieldId(FieldDesc &desc, uint16_t size) - { - ink_assert(!areFieldsFinalized()); - desc.field_type_idx = typeid(void); - desc.ext_loc_offset = Extendible::getLocOffset(); - desc.field_offset = std::numeric_limits::max(); - desc.size = size; - desc.align = 1; - desc.mask = 0; - - // - desc.constructor = nullptr; - desc.destructor = nullptr; - desc.serializer = [size](std::ostream &os, void const *fld_ptr) { hexToStream(os, fld_ptr, size); }; - } - - ////////////////////////////////////////////////////// - /// Bool specializations - - template - bool - fieldGet(const void *fld_ptr, FieldId const &field) - { - return static_cast((*static_cast(fld_ptr)) & field.desc->mask); - } - - template - AtomicBit - fieldSet(FieldPtr fld_ptr, FieldId const &field) - { - return AtomicBit{static_cast(fld_ptr), field.desc->mask}; - } - - template - void - makeFieldId(FieldId &id, FieldDesc &desc) - { - desc.field_type_idx = std::type_index(typeid(bool)); - desc.ext_loc_offset = Extendible::getLocOffset(); - desc.field_offset = std::numeric_limits::max(); - desc.size = 0; - desc.align = 0; - desc.mask = 0; - - id = FieldId(desc); - - // - desc.constructor = nullptr; - desc.destructor = nullptr; - desc.serializer = [id](std::ostream &os, void const *fld_ptr) { serializeField(os, fieldGet(fld_ptr, id)); }; - } - - ////////////////////////////////////////////////////// - /// std::atomic specializations (same as bool) - - template - inline bool - fieldGet(void const *fld_ptr, FieldId> const &field) - { - return bool(fld_ptr & field.mask); - } - - template - inline AtomicBit - fieldSet(FieldPtr fld_ptr, FieldId> const &field) - { - return AtomicBit{fld_ptr, field.mask}; - } - - template - void - makeFieldId(FieldId> &id, FieldDesc &desc) - { - desc.field_type_idx = std::type_index(typeid(std::atomic)); - desc.ext_loc_offset = Extendible::getLocOffset(); - desc.field_offset = std::numeric_limits::max(); - desc.size = 0; - desc.align = 0; - desc.mask = 0; - - id = FieldId>(desc); - - // - desc.constructor = nullptr; - desc.destructor = nullptr; - desc.serializer = [id](std::ostream &os, void const *fld_ptr) { serializeField(os, fieldGet(fld_ptr, id)); }; - } -} // namespace details - -////////////////////////////////////////////////////// -/// safely cast FieldDesc back to FieldId - -template -FieldId::FieldId(ext::details::FieldDesc const &fld_desc) : desc(&fld_desc) -{ - const size_t loc = Extendible::getLocOffset(); - ink_assert(loc == fld_desc.ext_loc_offset); - ink_assert(std::type_index(typeid(Field_t)) == fld_desc.field_type_idx); -} - -template -bool -FieldId::isValid() const -{ - return desc != nullptr; -} - -// #################################################### -// #################################################### -// #################################################### -// Functional API for Extendible Field Access -// - -//////////////////////////////////////////////////// -// Schema Method Definitions -// - -/// Add a new Field to this record type -template -bool -fieldAdd(FieldId &field_id, char const *field_name) -{ - using namespace ext::details; - Schema &schema = Extendible::schema; - ink_release_assert(schema.no_instances()); // it's too late, we already started allocating. - ink_release_assert(!areFieldsFinalized()); // it's too late, Fields must be added during Plugin Init. - - auto field_iter = schema.fields.find(field_name); - if (field_iter != schema.fields.end()) { - return false; - } - - makeFieldId(field_id, schema.fields[field_name]); - schema.updateMemOffsets(); - return true; -} - -/// Add a new Field to Derived_t, this C function uses a fat API -template -ExtFieldContext -fieldAdd(char const *field_name, int size, void (*construct_fn)(void *), void (*destruct_fn)(void *)) -{ - using namespace ext::details; - Schema &schema = Extendible::schema; - ink_release_assert(schema.no_instances()); // it's too late, we already started allocating. - ink_release_assert(!areFieldsFinalized()); // it's too late, Fields must be added during Plugin Init. - ink_release_assert(size >= 0); // non-negative numbers please - - auto field_iter = schema.fields.find(field_name); - if (field_iter != schema.fields.end()) { - return nullptr; - } - if (size == 0) { - FieldId id; - makeFieldId(id, schema.fields[field_name]); - } else { - FieldDesc &desc = schema.fields[field_name]; - makeFieldId(desc, size); - desc.constructor = construct_fn; - desc.destructor = destruct_fn; - } - schema.updateMemOffsets(); - return &schema.fields[field_name]; -} - -template -bool -fieldFind(FieldId &field_id, char const *field_name) -{ - using namespace ext::details; - ink_release_assert(areFieldsFinalized()); - Schema const &schema = Extendible::schema; - auto field_iter = schema.fields.find(field_name); - if (field_iter == schema.fields.end()) { - return false; // didn't find name - } - field_id = FieldId(field_iter->second); - return true; -} - -// each Derived_t will have a DerivedExtfieldFind -template -ExtFieldContext -fieldFind(char const *field_name) -{ - using namespace ext::details; - ink_release_assert(areFieldsFinalized()); - Schema const &schema = Extendible::schema; - auto field_iter = schema.fields.find(field_name); - if (field_iter == schema.fields.end()) { - return nullptr; // didn't find name - } - return &field_iter->second; -} - -//////////////////////////////////////////////////// -/// ext::get & ext::set accessor functions -template -inline decltype(auto) -get(T const &d, FieldId &field) -{ - Extendible const &ext = d; - FieldPtr fld_ptr = ext::details::FieldPtrGet(ext, *field.desc); - return ext::details::fieldGet(fld_ptr, field); -} - -template -inline decltype(auto) -set(T &d, FieldId &field) -{ - Extendible const &ext = d; - FieldPtr fld_ptr = ext::details::FieldPtrGet(ext, *field.desc); - return ext::details::fieldSet(fld_ptr, field); -} - -///////////////////////// -// ext::sizeOf - returns the size of a class + all extensions. -// - -template -inline size_t -sizeOf(size_t size = sizeof(Derived_t)) -{ - // add size of super extendibles - if constexpr (has_super_type::value) { - size = ext::sizeOf(size); - } else { - static_assert(std::is_same::schema)>::value, - "ambiguous schema, Derived_t is missing super_type"); - } - - // add size of this extendible - if constexpr (std::is_base_of, Derived_t>::value) { - size = Extendible::schema.fullSize(size); - // assert that the schema is not ambiguous. - } - return size; -} - -///////////////////////// -// Ext Alloc & Init -// -template Extendible::Extendible() -{ - ink_assert(ext::details::areFieldsFinalized()); - // don't call callConstructor until the derived class is fully constructed. - ++schema.cnt_constructed; -} - -template Extendible::~Extendible() -{ - // assert callConstructors was called. - ink_assert(ext_loc); - schema.callDestructor(uintptr_t(this) + ext_loc); - ++schema.cnt_destructed; - ink_assert(schema.cnt_destructed <= schema.cnt_fld_constructed); -} - -/// tell this extendible where it's memory offset start is. Added to support inheriting from extendible classes -template -uintptr_t -Extendible::initFields(uintptr_t start_ptr) -{ - ink_assert(ext_loc == 0); - start_ptr = ROUNDUP(start_ptr, schema.alloc_align); // pad the previous struct, so that our fields are memaligned correctly - ink_assert(start_ptr - uintptr_t(this) < UINT16_MAX); - ext_loc = static_cast(start_ptr - uintptr_t(this)); // store the offset to be used by ext::get and ext::set - ink_assert(ext_loc > 0); - schema.callConstructor(start_ptr); // construct all fields - return start_ptr + schema.alloc_size; // return the end of the extendible data -} - -namespace details -{ - /// recursively init all extendible structures, and construct fields - template - uintptr_t - initRecurseSuper(Derived_t &derived, uintptr_t tail_ptr /*= 0*/) - { - // track a tail pointer, that starts after the class, and iterate each extendible block - if constexpr (has_super_type::value) { - // init super type, move tail pointer - tail_ptr = initRecurseSuper(derived, tail_ptr); - } - if constexpr (std::is_base_of, Derived_t>::value) { - // set start for this extendible block after the previous extendible, and move tail pointer to after this block - tail_ptr = derived.Extendible::initFields(tail_ptr); - } - return tail_ptr; - } - -} // namespace details - -// allocate and initialize an extendible data structure -template -Derived_t * -create(Args &&...args) -{ - // don't instantiate until all Fields are finalized. - ink_assert(ext::details::areFieldsFinalized()); - - // calculate the memory needed for the class and all Extendible blocks - const size_t type_size = ext::sizeOf(); - - // alloc one block of memory - Derived_t *ptr = static_cast(ats_memalign(alignof(Derived_t), type_size)); - - // construct (recursively super-to-sub class) - new (ptr) Derived_t(std::forward(args)...); - - // define extendible blocks start offsets (recursively super-to-sub class) - details::initRecurseSuper(*ptr, uintptr_t(ptr) + sizeof(Derived_t)); - return ptr; -} - -///////////////////////// -// ExtDebugFormat - print a ascii chart of memory layout of a class -// -// Example layout of C -> B -> A, where C and A are extendible. All contain 1 int. -// See test_Extendible.cc for class implementation. -// 1A | EXT | 2b | ##________##__ -// 1A | BASE | 2b | __##__________ -// 1B | BASE | 2b | ____##________ -// 1C | EXT | 2b | ______##____## -// 1C | BASE | 2b | ________##____ - -template -std::string -viewFormat(T const &t, uintptr_t _base_addr = 0, int _full_size = ext::sizeOf()) -{ - using namespace std; - stringstream ss; - if (_base_addr == 0) { - _base_addr = uintptr_t(&t); - } - int super_size = 0; - if constexpr (has_super_type::value) { - ss << viewFormat(t, _base_addr, _full_size); - super_size += sizeof(typename T::super_type); - } - - if constexpr (is_base_of, T>::value) { - Extendible const *e = &t; - int ptr_start = uintptr_t(e) + Extendible::getLocOffset() - _base_addr; - int ptr_end = ptr_start + sizeof(typename Extendible::short_ptr_t); - int ext_start = e->getBegin() - _base_addr; - int ext_end = Extendible::schema.fullSize(ext_start); - - ink_assert(ptr_end <= ext_start); - ink_assert(ext_end <= _full_size); - - ss << endl << setw(30) << typeid(T).name() << " | EXT | " << setw(5) << ext_end - ext_start << "b |"; - ss << string(ptr_start, '_').c_str(); - ss << string(ptr_end - ptr_start, '#').c_str(); - ss << string(ext_start - ptr_end, '_').c_str(); - ss << string(ext_end - ext_start, '#').c_str(); - ss << string(_full_size - ext_end, '_').c_str(); - - super_size += sizeof(Extendible); - } - - int super_start = uintptr_t(&t) - _base_addr; - int member_start = super_start + super_size; - int member_end = super_start + sizeof(T); - - ink_assert(member_start <= member_end); - ink_assert(member_end <= _full_size); - - ss << endl << setw(30) << typeid(T).name() << " | BASE | " << setw(5) << sizeof(T) - super_size << "b |"; - ss << string(super_start, '_').c_str(); - ss << string(member_start - super_start, '_').c_str(); - ss << string(member_end - member_start, '#').c_str(); - ss << string(_full_size - member_end, '_').c_str(); - - return ss.str(); -} - -namespace details -{ - inline std::string - ltrim(std::string const &str, const std::string &chars = "\t\n\v\f\r ") - { - std::string r(str); - r.erase(0, str.find_first_not_of(chars)); - return r; - } -} // namespace details - -template -void -serialize(std::ostream &os, T const &t) -{ - using namespace std; - size_t indent = os.width(); - os << endl << setw(indent) << "" << details::ltrim(typeid(T).name(), " 0123456789") << ": {" << endl; - indent += 2; - if constexpr (is_base_of, T>::value) { - if constexpr (has_super_type::value) { - serialize(os, t); - } - auto const &schema = T::schema; - size_t name_width = 0; - for (const auto &kv : schema.fields) { - name_width = max(name_width, kv.first.length()); - } - // TODO: clang-5 didn't like the use of a range based for here, change later - for (auto it = schema.fields.begin(); it != schema.fields.end(); ++it) { - auto &fname = it->first; - auto &field = it->second; - ink_assert(field.serializer); - os << setw(indent) << "" << setw(name_width) << right << fname << ": "; - field.serializer(os, details::FieldPtrGet(t, field)); - os << "," << endl; - } - } - indent -= 2; - os << setw(indent + 1) << "}"; -} - -template -std::string -toString(T const &t) -{ - std::stringstream ss; - ss.width(0); - serialize(ss, t); - return ss.str(); -} -} // namespace ext diff --git a/src/tscore/AcidPtr.cc b/src/tscore/AcidPtr.cc deleted file mode 100644 index 5c793609e7e..00000000000 --- a/src/tscore/AcidPtr.cc +++ /dev/null @@ -1,43 +0,0 @@ -/** - @file AcidPtr defines global LockPools for accessing and committing - - @section license License - - 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 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include "tscore/AcidPtr.h" - -const int READLOCKCOUNT = 61; // a prime larger than number of readers - -AcidPtrMutex & -AcidPtrMutexGet(void const *ptr) -{ - static LockPool read_locks(READLOCKCOUNT); - static_assert(sizeof(void *) == sizeof(size_t)); - return read_locks.getMutex(reinterpret_cast(ptr)); -} - -const int WRITELOCKCOUNT = 31; // a prime larger than number of writers - -AcidCommitMutex & -AcidCommitMutexGet(void const *ptr) -{ - static LockPool write_locks(WRITELOCKCOUNT); - static_assert(sizeof(void *) == sizeof(size_t)); - return write_locks.getMutex(reinterpret_cast(ptr)); -} diff --git a/src/tscore/CMakeLists.txt b/src/tscore/CMakeLists.txt index b9f856f56c1..64241e0cf0c 100644 --- a/src/tscore/CMakeLists.txt +++ b/src/tscore/CMakeLists.txt @@ -29,8 +29,6 @@ add_custom_target(ParseRules ALL DEPENDS ParseRulesCType ParseRulesCTypeToUpper add_library( tscore - AcidPtr.cc - AcidPtr.cc Arena.cc ArgParser.cc BaseLogFile.cc @@ -40,7 +38,6 @@ add_library( Diags.cc Encoding.cc EventNotify.cc - Extendible.cc FrequencyCounter.cc Hash.cc HashFNV.cc @@ -148,10 +145,8 @@ endif() if(BUILD_TESTING) add_executable( test_tscore - unit_tests/test_AcidPtr.cc unit_tests/test_ArgParser.cc unit_tests/test_CryptoHash.cc - unit_tests/test_Extendible.cc unit_tests/test_Encoding.cc unit_tests/test_FrequencyCounter.cc unit_tests/test_HashAlgorithms.cc diff --git a/src/tscore/Extendible.cc b/src/tscore/Extendible.cc deleted file mode 100644 index 4e27c249e13..00000000000 --- a/src/tscore/Extendible.cc +++ /dev/null @@ -1,165 +0,0 @@ -/** @file - - Extendible - - @section license License - - 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 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - @section details Details - -//////////////////////////////////////////// - Implements: - * Extendible - * Schema - * fieldAdd - * fieldFind - */ - -#include "tscore/Extendible.h" - -namespace ext -{ -namespace details -{ - //////////////////////////////////////////////////// - // Schema Methods - - Schema::~Schema() {} - - void - Schema::updateMemOffsets() - { - ink_release_assert(cnt_constructed == cnt_destructed); - - uint32_t acc_offset = 0; - alloc_align = 1; - - for (auto &pair_fld : fields) { - alloc_align = std::max(alloc_align, pair_fld.second.align); - } - - // allocate fields from largest to smallest alignment - uint8_t processing_align = alloc_align; - while (processing_align > 0) { - uint8_t next_align = 0; - for (auto &pair_fld : fields) { - auto &fld = pair_fld.second; - if (fld.align == processing_align) { - fld.field_offset = acc_offset; - acc_offset += fld.size; - } else if (fld.align < processing_align) { - next_align = std::max(next_align, fld.align); - } - } - processing_align = next_align; - } - - // align '0' are packed bit allocations. - uint32_t acc_bit_offset = 0; - for (auto &pair_fld : fields) { - auto &fld = pair_fld.second; - if (fld.align == 0) { - fld.field_offset = acc_offset + acc_bit_offset / 8; - fld.mask = 1 << (acc_bit_offset % 8); - ++acc_bit_offset; - } - } - - alloc_size = acc_offset + (acc_bit_offset + 7) / 8; - } - - bool - Schema::reset() - { - if (cnt_constructed > cnt_destructed) { - // free instances before calling this so we don't leak memory - return false; - } - fields.clear(); - updateMemOffsets(); - return true; - } - - void - Schema::callConstructor(uintptr_t ext_loc) - { - ink_assert(ext_loc); - ++cnt_fld_constructed; // don't allow schema modification - ink_assert(cnt_fld_constructed <= cnt_constructed); - - // init all extendible memory to 0, in case constructors don't - memset(reinterpret_cast(ext_loc), 0, alloc_size); - - for (auto const &elm : fields) { - if (elm.second.constructor) { - elm.second.constructor(FieldPtr(ext_loc + elm.second.field_offset)); - } - } - } - - void - Schema::callDestructor(uintptr_t ext_loc) - { - ink_assert(ext_loc); - for (auto const &elm : fields) { - if (elm.second.destructor) { - elm.second.destructor(FieldPtr(ext_loc + elm.second.field_offset)); - } - } - } - - size_t - Schema::fullSize(const size_t base_size) const - { - ink_assert(base_size); - return ROUNDUP(base_size, alloc_align) + alloc_size; - } - - bool - Schema::no_instances() const - { - return cnt_constructed == cnt_destructed; - } - - bool & - areFieldsFinalized() - { - static bool finalized = false; - return finalized; - } -} // namespace details - -} // namespace ext -// C API -// - -FieldPtr -ExtFieldPtr(DerivedPtr derived, ExtFieldContext field_context, int *size /*= nullptr*/) -{ - using namespace ext; - using namespace ext::details; - ink_assert(field_context); - ink_assert(derived); - FieldDesc const &desc = *static_cast(field_context); - if (size) { - *size = desc.size; - } - - Offest_t const *loc = (Offest_t const *)(uintptr_t(derived) + desc.ext_loc_offset); - return FieldPtr(uintptr_t(derived) + (*loc) + desc.field_offset); -} diff --git a/src/tscore/unit_tests/test_AcidPtr.cc b/src/tscore/unit_tests/test_AcidPtr.cc deleted file mode 100644 index 5dbafeac1b8..00000000000 --- a/src/tscore/unit_tests/test_AcidPtr.cc +++ /dev/null @@ -1,164 +0,0 @@ -/** @file - Test file for AcidPtr - @section license License - 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 - Unless REQUIRE by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#include - -#include "tscore/AcidPtr.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -TEST_CASE("AcidPtr Atomicity") -{ - // fail if skew is detected. - constexpr int N = 1000; // Number of threads - constexpr int K = 50; // Size of data sample. - AcidPtr> ptr(new vector(K)); - atomic errors{0}; - atomic count{0}; - condition_variable gate; - mutex gate_mutex; - - auto job_read_write = [&]() { - { - unique_lock gate_lock(gate_mutex); - gate.wait(gate_lock); - } - int r = random(); - AcidCommitPtr> cptr(ptr); - int old = (*cptr)[0]; - for (int &i : *cptr) { - if (i != old) { - errors++; - } - i = r; - } - ++count; - }; - - auto job_read = [&]() { - { - unique_lock gate_lock(gate_mutex); - gate.wait(gate_lock); - } - auto sptr = ptr.getPtr(); - int old = (*sptr)[0]; - for (int const &i : *sptr) { - if (i != old) { - errors++; - } - } - ++count; - }; - - array writers; - array readers; - - // Use a for loop so the threads start in pairs. - for (int i = 0; i < N; i++) { - writers[i] = thread(job_read_write); - readers[i] = thread(job_read); - gate.notify_all(); - } - - while (count < 2 * N) { - gate.notify_all(); - this_thread::sleep_for(chrono::milliseconds{10}); - } - - for (auto &t : readers) { - t.join(); - } - - for (auto &t : writers) { - t.join(); - } - - REQUIRE(errors == 0); // skew detected -} - -TEST_CASE("AcidPtr Isolation") -{ - AcidPtr p; - REQUIRE(p.getPtr() != nullptr); - REQUIRE(p.getPtr().get() != nullptr); - { - AcidCommitPtr w(p); - *w = 40; - } - CHECK(*p.getPtr() == 40); - { - AcidCommitPtr w = p; - *w += 1; - CHECK(*p.getPtr() == 40); // new value not committed until end of scope - } - CHECK(*p.getPtr() == 41); - { - *AcidCommitPtr(p) += 1; // leaves scope immediately if not named. - CHECK(*p.getPtr() == 42); - } - CHECK(*p.getPtr() == 42); -} - -TEST_CASE("AcidPtr persistence") -{ - AcidPtr p(new int(40)); - std::shared_ptr r1, r2, r3, r4; - REQUIRE(p.getPtr() != nullptr); - r1 = p.getPtr(); - { - AcidCommitPtr w = p; - r2 = p.getPtr(); - *w += 1; // update p at end of scope - } - r3 = p.getPtr(); - { - *AcidCommitPtr(p) += 1; // leaves scope immediately if not named. - r4 = p.getPtr(); - } - CHECK(*r1 == 40); // references to data are still valid, but inconsistent. (todo: rename AcidPtr to AiPtr?) - CHECK(*r2 == 40); - CHECK(*r3 == 41); - CHECK(*r4 == 42); -} - -TEST_CASE("AcidPtr Abort") -{ - AcidPtr p; - { - AcidCommitPtr w(p); - *w = 40; - } - CHECK(*p.getPtr() == 40); - { - AcidCommitPtr w = p; - *w += 1; - w.abort(); - CHECK(w == nullptr); - } - CHECK(*p.getPtr() == 40); -} diff --git a/src/tscore/unit_tests/test_Extendible.cc b/src/tscore/unit_tests/test_Extendible.cc deleted file mode 100644 index 38100816844..00000000000 --- a/src/tscore/unit_tests/test_Extendible.cc +++ /dev/null @@ -1,520 +0,0 @@ -/** @file - Test file for Extendible - @INFO license License - 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 - Unless REQUIRE by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "tscore/ink_atomic.h" -#include "tscore/Extendible.h" - -using namespace std; -using namespace ext; - -////////////////////////////////////////////////////// -// AtomicBit Tests -TEST_CASE("AtomicBit Atomic test") -{ - // test the atomicity and isolation of operations - uint32_t bit_storage = 0; - - auto job_set = [&bit_storage](int idx) { - AtomicBit{reinterpret_cast(&bit_storage) + (idx / 8), static_cast(1 << (idx % 8))} = true; - }; - auto job_clear = [&bit_storage](int idx) { - AtomicBit{reinterpret_cast(&bit_storage) + (idx / 8), static_cast(1 << (idx % 8))} = false; - }; - - std::thread jobs[32]; - - // set all bits in parallel - for (int i = 0; i < 32; i++) { - jobs[i] = std::thread(job_set, i); - } - for (auto &job : jobs) { - job.join(); - } - REQUIRE(bit_storage == 0xffffffff); - - // clear all bits in parallel - for (int i = 0; i < 32; i++) { - jobs[i] = std::thread(job_clear, i); - } - for (auto &job : jobs) { - job.join(); - } - REQUIRE(bit_storage == 0); -} - -////////////////////////////////////////////////////// -// Extendible Inheritance Tests - -struct A : public Extendible { - using self_type = A; - DEF_EXT_NEW_DEL(self_type); - uint16_t a = {1}; -}; - -ext::FieldId> ext_a_1; - -class B : public A -{ -public: - using super_type = A; - using self_type = B; - DEF_EXT_NEW_DEL(self_type); - uint16_t b = {2}; -}; - -class C : public B, public Extendible -{ -public: - using super_type = B; - using self_type = C; - DEF_EXT_NEW_DEL(self_type); - uint16_t c = {3}; - - // operator[] - template - decltype(auto) - operator[](F field) const - { - return ext::get(*this, field); - } - template - decltype(auto) - operator[](F field) - { - return ext::set(*this, field); - } -}; - -ext::FieldId> ext_c_1; - -uintptr_t -memDelta(void *p, void *q) -{ - return uintptr_t(q) - uintptr_t(p); -} -A *a_ptr = nullptr; -TEST_CASE("Create A", "") -{ - ext::details::areFieldsFinalized() = true; - a_ptr = ext::create(); - CHECK(Extendible::schema.no_instances() == false); - delete a_ptr; - CHECK(Extendible::schema.no_instances()); -} -TEST_CASE("Create B", "") -{ - a_ptr = ext::create(); - CHECK(Extendible::schema.no_instances() == false); - delete a_ptr; - CHECK(Extendible::schema.no_instances()); -} -TEST_CASE("Create C", "") -{ - a_ptr = ext::create(); - CHECK(Extendible::schema.no_instances() == false); - CHECK(Extendible::schema.no_instances() == false); -} -TEST_CASE("Delete C", "") -{ - delete static_cast(a_ptr); - CHECK(Extendible::schema.no_instances()); - CHECK(Extendible::schema.no_instances()); - CHECK(Extendible::schema.cnt_constructed == 3); - CHECK(Extendible::schema.cnt_fld_constructed == 3); - CHECK(Extendible::schema.cnt_destructed == 3); - CHECK(Extendible::schema.cnt_constructed == 1); - CHECK(Extendible::schema.cnt_fld_constructed == 1); - CHECK(Extendible::schema.cnt_destructed == 1); -} -TEST_CASE("Extendible Memory Allocations", "") -{ - ext::details::areFieldsFinalized() = false; - fieldAdd(ext_a_1, "ext_a_1"); - fieldAdd(ext_c_1, "ext_c_1"); - ext::details::areFieldsFinalized() = true; - - size_t w = sizeof(uint16_t); - CHECK(ext::sizeOf() == w * 3); - CHECK(ext::sizeOf() == w * 4); - CHECK(ext::sizeOf() == w * 7); - - C &x = *(ext::create()); - // 0 1 2 3 4 5 6 - //[ EA*, a, b,EC*, c, EA, EC] - // - uint16_t *mem = reinterpret_cast(&x); - CHECK(memDelta(&x, &x.a) == w * 1); - CHECK(memDelta(&x, &x.b) == w * 2); - CHECK(memDelta(&x, &x.c) == w * 4); - CHECK(mem[0] == w * (5 - 0)); - CHECK(mem[1] == 1); - CHECK(mem[2] == 2); - CHECK(mem[3] == w * (6 - 3)); - CHECK(mem[4] == 3); - CHECK(mem[5] == 0); - CHECK(mem[6] == 0); - - std::string format = "\n 1A | EXT | 2b |##________##__" - "\n 1A | BASE | 2b |__##__________" - "\n 1B | BASE | 2b |____##________" - "\n 1C | EXT | 2b |______##____##" - "\n 1C | BASE | 2b |________##____"; - CHECK(ext::viewFormat(x) == format); - - printf("\n"); - delete &x; -} - -TEST_CASE("Extendible Pointer Math", "") -{ - C &x = *(ext::create()); - - CHECK(x.a == 1); - CHECK(x.b == 2); - CHECK(x.c == 3); - - ext::set(x, ext_a_1) = 4; - CHECK(ext::get(x, ext_a_1) == 4); - x[ext_c_1] = 5; - CHECK(ext::get(x, ext_c_1) == 5); - - CHECK(x.a == 1); - CHECK(x.b == 2); - CHECK(x.c == 3); - CHECK(ext::get(x, ext_a_1) == 4); - CHECK(ext::get(x, ext_c_1) == 5); - - std::string format = "\n 1A | EXT | 2b |##________##__" - "\n 1A | BASE | 2b |__##__________" - "\n 1B | BASE | 2b |____##________" - "\n 1C | EXT | 2b |______##____##" - "\n 1C | BASE | 2b |________##____"; - CHECK(ext::viewFormat(x) == format); - - ext::FieldId a_bit; - ext::FieldId a_int; - static_assert(std::is_same::value); - static_assert(std::is_same::value); - delete &x; -} - -// Extendible is abstract and must be derived in a CRTP -struct Derived : Extendible { - using self_type = Derived; - DEF_EXT_NEW_DEL(self_type); - string m_str; - - // operator[] for shorthand - template - decltype(auto) - operator[](F field) const - { - return ext::get(*this, field); - } - template - decltype(auto) - operator[](F field) - { - return ext::set(*this, field); - } - - static const string - testFormat() - { - const size_t intenal_size = sizeof(Derived) - sizeof(ext::Extendible::short_ptr_t); - std::stringstream format; - format << "\n 7Derived | EXT | 1b |##" << string(intenal_size, '_') << "#" - << "\n 7Derived | BASE | " << setw(5) << intenal_size << "b |__" << string(intenal_size, '#') - << "_"; - return format.str(); - } -}; - -/////////////////////////////////////// -// C API for Derived -// -void * -DerivedExtalloc() -{ - return ext::create(); -} -void -DerivedExtFree(void *ptr) -{ - delete static_cast(ptr); -} - -ExtFieldContext -DerivedExtfieldAdd(char const *field_name, int size, void (*construct_fn)(void *), void (*destruct_fn)(void *)) -{ - // hack to avoid having to repeat this in testing. - ext::details::areFieldsFinalized() = false; - auto r = fieldAdd(field_name, size, construct_fn, destruct_fn); - ext::details::areFieldsFinalized() = true; - return r; -} -ExtFieldContext -DerivedExtfieldFind(char const *field_name) -{ - return fieldFind(field_name); -} - -/////////////////////////////////////// - -// something to store more complex than an int -struct testField { - std::array arr; - static int alive; - testField() - { - uint8_t x = 1; - for (uint8_t &a : arr) { - a = x; - x *= 2; - } - alive++; - } - ~testField() - { - for (uint8_t &a : arr) { - a = 0; - } - alive--; - } -}; -int testField::alive = 0; - -namespace ext -{ -template <> -void -serializeField(ostream &os, testField const &t) -{ - serializeField(os, t.arr); -} -} // namespace ext - -TEST_CASE("Extendible", "") -{ - printf("\nsizeof(string) = %lu", sizeof(std::string)); - printf("\nsizeof(Derived) = %lu", sizeof(Derived)); - - ext::FieldId bit_a, bit_b, bit_c; - ext::FieldId> int_a, int_b, int_c; - Derived *ptr; - - // test cases: - //[constructor] [operator] [type] [access] [capacity] [modifier] [operation] [compare] [find] - // I don't use SECTIONS because this modifies static variables many times, is not thread safe. - INFO("Extendible()"); - { - ptr = ext::create(); - REQUIRE(ptr != nullptr); - } - - INFO("~Extendible"); - { - // - delete ptr; - } - - INFO("Schema Reset"); - { - ptr = ext::create(); - REQUIRE(Derived::schema.no_instances() == false); - REQUIRE(Derived::schema.reset() == false); - delete ptr; - REQUIRE(Derived::schema.no_instances() == true); - ext::details::areFieldsFinalized() = false; - REQUIRE(Derived::schema.reset() == true); - ext::details::areFieldsFinalized() = true; - } - - INFO("shared_ptr"); - { - shared_ptr sptr(ext::create()); - REQUIRE(Derived::schema.no_instances() == false); - REQUIRE(sptr); - } - REQUIRE(Derived::schema.no_instances() == true); - - INFO("add a bit field"); - { - // - ext::details::areFieldsFinalized() = false; - REQUIRE(fieldAdd(bit_a, "bit_a")); - ext::details::areFieldsFinalized() = true; - } - - INFO("Extendible delete ptr"); - { - for (int i = 0; i < 10; i++) { - ptr = ext::create(); - REQUIRE(ptr != nullptr); - INFO(__LINE__); - REQUIRE(Derived::schema.no_instances() == false); - delete ptr; - INFO(__LINE__); - ptr = nullptr; - REQUIRE(Derived::schema.no_instances() == true); - } - } - - INFO("test bit field"); - { - shared_ptr sptr{ext::create()}; - Derived &ref = *sptr; - - CHECK(ext::viewFormat(ref) == Derived::testFormat()); - - AtomicBit bitref = ext::set(ref, bit_a); - bitref = true; - CHECK(bitref == true); - bitref = true; - CHECK(bitref == true); - CHECK(ext::set(ref, bit_a) == true); - CHECK(ext::get(ref, bit_a) == true); - } - - INFO("test bit packing"); - { - struct size_test { - string s; - uint16_t i; - }; - - REQUIRE(Derived::schema.reset() == true); - CHECK(sizeof(Extendible) == sizeof(uint16_t)); - CHECK(sizeof(size_test) == ROUNDUP(sizeof(std::string) + sizeof(uint16_t), alignof(std::string))); - CHECK(sizeof(Derived) == sizeof(size_test)); - CHECK(ext::sizeOf() == sizeof(Derived)); - - ext::details::areFieldsFinalized() = false; - REQUIRE(fieldAdd(bit_a, "bit_a")); - size_t expected_size = sizeof(Derived) + 1; - CHECK(ext::sizeOf() == expected_size); - REQUIRE(fieldAdd(bit_b, "bit_b")); - CHECK(ext::sizeOf() == expected_size); - REQUIRE(fieldAdd(bit_c, "bit_c")); - CHECK(ext::sizeOf() == expected_size); - ext::details::areFieldsFinalized() = true; - - shared_ptr sptr(ext::create()); - Derived &ref = *sptr; - CHECK(ext::viewFormat(ref) == Derived::testFormat()); - using Catch::Matchers::ContainsSubstring; - REQUIRE_THAT(ext::toString(ref), ContainsSubstring("bit_a: 0")); - REQUIRE_THAT(ext::toString(ref), ContainsSubstring("bit_b: 0")); - REQUIRE_THAT(ext::toString(ref), ContainsSubstring("bit_c: 0")); - - ext::set(ref, bit_a) = true; - ext::set(ref, bit_b) = false; - ext::set(ref, bit_c) = true; - CHECK(ext::get(ref, bit_a) == true); - CHECK(ext::get(ref, bit_b) == false); - CHECK(ext::get(ref, bit_c) == true); - REQUIRE_THAT(ext::toString(ref), ContainsSubstring("bit_a: 1")); - REQUIRE_THAT(ext::toString(ref), ContainsSubstring("bit_b: 0")); - REQUIRE_THAT(ext::toString(ref), ContainsSubstring("bit_c: 1")); - } - - INFO("store int field"); - { - ext::details::areFieldsFinalized() = false; - REQUIRE(fieldAdd(int_a, "int_a")); - REQUIRE(fieldAdd(int_b, "int_b")); - ext::details::areFieldsFinalized() = true; - - size_t expected_size = sizeof(Derived) + 1 + sizeof(std::atomic_int) * 2; - CHECK(ext::sizeOf() == expected_size); - - shared_ptr sptr(ext::create()); - Derived &ref = *sptr; - CHECK(ext::get(ref, int_a) == 0); - CHECK(ext::get(ref, int_b) == 0); - ++ext::set(ref, int_a); - ext::set(ref, int_b) = 42; - ref.m_str = "Hello"; - CHECK(ext::get(ref, int_a) == 1); - CHECK(ext::get(ref, int_b) == 42); - CHECK(ref.m_str == "Hello"); - } - - printf("\n"); - - INFO("Extendible Test Complete"); -} - -TEST_CASE("Extendible C API") -{ - ext::details::areFieldsFinalized() = false; - Derived::schema.reset(); - CHECK(Derived::schema.no_instances() == true); - ext::details::areFieldsFinalized() = true; - - INFO("C API alloc instance"); - { - void *d = DerivedExtalloc(); - - CHECK(d != nullptr); - CHECK(Derived::schema.no_instances() == false); - - DerivedExtFree(d); - - CHECK(Derived::schema.no_instances() == true); - } - - INFO("C API add int field"); - { - ExtFieldContext cf_a = DerivedExtfieldAdd("cf_a", 4, nullptr, nullptr); - - size_t expected_size = sizeof(Derived) + 4; - CHECK(ext::sizeOf() == expected_size); - CHECK(DerivedExtfieldFind("cf_a") == cf_a); - } - INFO("C API test int field"); - { - void *d = DerivedExtalloc(); - REQUIRE(d != nullptr); - - ExtFieldContext cf_a = DerivedExtfieldFind("cf_a"); - uint8_t *data8 = static_cast(ExtFieldPtr(d, cf_a)); - - CHECK(data8[0] == 0); - ink_atomic_increment(&data8[0], 1); - data8[1] = 5; - data8[2] = 7; - - uint32_t *data32 = static_cast(ExtFieldPtr(d, cf_a)); - CHECK(*data32 == 0x00070501); - DerivedExtFree(d); - } - INFO("Extendible C API Test Complete"); -} - -//*/