Distributed system architecture - explanation of basic code iunknown.c of samgr/source system service development framework

Keywords: C architecture OpenHarmony

Overview of this article

Based on IUnknown, we can develop external interfaces of services or functions. The essence of interface implementation is the conduction of function address and function call. Therefore, IUnknown is particularly important in the basic code of M-core and A-core system service development framework.
A component publishes interfaces. A component can implement multiple interfaces, that is, it can publish multiple interfaces. The component itself provides an interface for its own query, allowing customers to ask the component whether it supports an interface. After many such queries, customers will have a clearer understanding of the component. This is the core content of COM component object model. Please refer to com programming.
This article will describe the core content of iunknown.c and iunknown.h, and explore the core technology of interface implementation based on the underlying code of interface.

Code framework

iunknown
Keywords - macro definition
│ └── DEFAULT_VERSION
│ └── INHERIT_IUNKNOWN
│ └── INHERIT_IUNKNOWNENTRY
│ └── DEFAULT_IUNKNOWN_IMPL
│ └── IUNKNOWN_ENTRY_BEGIN
│ └── IUNKNOWN_ENTRY_END
│ └── GET_IUNKNOWN
Structure - structure
│ └── IUnknown
│ └── IUnknownEntry
Function - interface function
│ └── IUNKNOWN_QueryInterface
│ └── IUNKNOWN_AddRef
│ └── IUNKNOWN_Release

iunknown.h

iunknown.h contains the related macro definitions of interface objects and classes, the structure of interfaces and interface entries, and some declarations of interface function functions. Through the relevant codes and comments in iunknown.h, we can find that the structure and calling idea of the interface adopt the scheme of COM programming. For details, see:

Macro definition section

/**
 * @brief Defines the default IUnknown IUnknown. You can customize the version.
 * Overview: define the default IUnknown model. You can customize this model
 * The <b>IUnknown</b> interface of the default version can be called only in the current process.
 * Inter-process communication is not supported. \n
 * IUnknown The default model interface can only be called in the current process
 * Inter process communication is not supported
 * @since 1.0
 * @version 1.0
 */
#define DEFAULT_ Version 0x20 / / hexadecimal to hexadecimal to 32

/**
 * @brief Defines the macro for inheriting the <b>IUnknown</b> interface.
 * Overview: define macros to inherit IUnknown interface
 * When developing a subclass of the <b>IUnknown</b> class, you can use this macro to inherit the
 * structures of the <b>IUnknown</b> interface. \n
 * When developing a subclass of IUnknown, you can use this macro to inherit the framework of IUnknown interface
 */
#define INHERIT_IUNKNOWN                                                   \
    int (*QueryInterface)(IUnknown *iUnknown, int version, void **target); \
    int (*AddRef)(IUnknown *iUnknown);                                     \
    int (*Release)(IUnknown *iUnknown)

/**
 * @brief Defines the macro for inheriting the classes that implement the <b>IUnknown</b>
 * interface.
 * Overview: define the class used by macro to inherit and implement IUnknown interface
 * When developing a subclass of a class that implements the <b>IUnknown</b> interface, you can use
 * this macro to inherit the structures of the <b>IUnknown</b> implementation class. \n
 * When developing a subclass that implements the IUnknown interface, you can use this macro to inherit the framework of the IUnknown implementation class
 */
#define INHERIT_IUNKNOWNENTRY(T) \
    uint16 ver;                   \
    int16 ref;                   \
    T iUnknown

/**
 * @brief Defines the default marco for initializing the <b>IUnknown</b> interface.
 * Overview: define the default marco (macro) to initialize IUnknown interface
 * When creating a subclass object of the <b>IUnknown</b> interface, you can use this macro to
 * initialize members of the <b>IUnknown</b> interface to their default values. \n
 * When creating a subclass of IUnknown interface, you can use this macro to initialize the default values of IUnknown interface members
 */
#define DEFAULT_IUNKNOWN_IMPL                  \
    .QueryInterface = IUNKNOWN_QueryInterface, \
    .AddRef = IUNKNOWN_AddRef,                 \
    .Release = IUNKNOWN_Release

/**
 * @brief Defines the macro for initializing the classes that implement the <b>IUnknown</b>
 * interface.
 * Overview: define a macro to initialize the class that implements IUnknown interface
 * When creating a subclass object of a class that implements the <b>IUnknown</b> interface, you
 * can use this macro to initialize members of the <b>IUnknown</b> implementation class to their
 * default values. \n
 * You need to add the initialization of the customized member variable. \n
 * When creating a subclass object of a class that implements the IUnknown interface, you can use this macro to initialize the default values of IUnknown implementation class members
 */
#define IUNKNOWN_ENTRY_BEGIN(version)   \
    .ver = (version),                   \
    .ref = 1,                           \
    .iUnknown = {                       \
        DEFAULT_IUNKNOWN_IMPL

/**
 * @brief IUnknown Defines the end macro for initializing the <b>IUnknown</b> implementation
 * object.
 * Overview: IUnknown defines an end tag macro for initializing IUnknown implementation objects
 * This macro is used when a subclass object of the <b>IUnknown</b> implementation class is
 * initialized. \n
 * This macro is called when the IUnknown implementation class of the subclass object is initialized
 */
#define IUNKNOWN_ENTRY_END }

#define DEFAULT_IUNKNOWN_ENTRY_BEGIN IUNKNOWN_ENTRY_BEGIN(DEFAULT_VERSION)

#define DEFAULT_IUNKNOWN_ENTRY_END IUNKNOWN_ENTRY_END

/**
 * @brief Obtains the pointer of the <b>IUnknown</b> interface object from the subclass object T
 *  (generic macro) of the <b>IUnknown</b> implementation class.
 * Overview: get the pointer of IUnknown interface object from subclass object T
 * Use this macro when registering <b>IUnknown</b> interfaces with Samgr so that you can obtain
 * the interfaces from the subclass objects of different <b>IUnknown</b> implementation classes. \n
 * Use this macro when registering IUnknown interfaces with Samgr, so that you can get interfaces from different IUnknown implementation classes of subclass objects
 * @since 1.0
 * @version 1.0
 */
#define GET_IUNKNOWN(T) (IUnknown *)(&((T).iUnknown))

Structure part

Through the macro definition and structure, we can find that IUnknownEntry is an inheritance from IUnknown, and the IUnknown interface is a framework. When implementing the IUnknown interface class, it needs to be expanded on the basis of this framework. The detailed code is as follows:

/**
 * @brief Defines the <b>IUnknown</b> class.
 * Overview: define IUnknown class
 * You need to inherit this structure when developing a subclass of the <b>IUnknown</b> interface. \n
 * When developing an IUnknown subclass interface, you need to inherit this framework
 */
struct IUnknown {
    /**
     * Queries the subclass object of the <b>IUnknown</b> interface of a specified version
     * (downcasting).
     * Query subclass objects of IUnknown interface of specified version
     */
    int (*QueryInterface)(IUnknown *iUnknown, int version, void **target);
    /** Adds the reference count. Increment a reference count*/
    int (*AddRef)(IUnknown *iUnknown);
    /** Release the reference to an <b>IUnknown</b> interface. Release an IUnknown interface reference*/
    int (*Release)(IUnknown *iUnknown);
};

/**
 * @brief Defines the <b>IUnknown</b> implementation .
 * Overview: define an IUnknown implementation class entry
 * You need to inherit this structure when developing a subclass of the <b>IUnknown</b>
 * implementation class. \n
 * You need to inherit this class when developing a subclass of IUnknown implementation class
 * Each <b>IUnknown</b> interface must correspond to one or more <b>IUnknown</b> implementation
 * classes. \n
 * Each IUnknown interface must correspond to one or more IUnknown implementation classes
 */
typedef struct IUnknownEntry {
    /** Version information of <b>IUnknown</b> interface. IUnknown Version information of the interface */
    uint16 ver;
    /** Reference count of <b>IUnknown</b> interface. IUnknown Reference count for interface */
    int16 ref;
    /**
     * Implementation of <b>IUnknown</b> interface, which is related to the specific definition
     * implementation.
     * IUnknown The implementation of the interface is related to the specific implementation definition
     */
    IUnknown iUnknown;
} IUnknownEntry;

Function declaration part

The function declaration part is explained together with the definition of the function, so it is omitted here first.

iunknown.c

iunknown interface implementation framework is based on QueryInterface and AddRefRelease in COM programming. Here we analyze the codes of these three function functions in detail:

IUNKNOWN_QueryInterface

Function declaration and comments:

/**
 * @brief Queries the <b>IUnknown</b> interfaces of a specified version (downcasting).
 * Overview: query the IUnknown interface of the specified version
 * 
 * After obtaining the <b>IUnknown</b> interface object, the function caller uses
 * <b>QueryInterface</b> to convert the object to the required subclass type. \n
 * After obtaining the IUnknown interface object, the function calls QueryInterface to convert the object to the required subtype
 * The system converts {@link DEFAULT_VERSION} into the subclass type required by the caller.
 * The system converts {@ link DEFAULT_VERSION} to the subclass type required by the caller
 * If the type conversion requirements cannot be met, you need to re-implement this function. \n
 * If the type conversion requirements cannot be met, you need to re implement the function.
 * 
 * @param iUnknown Indicates the pointer to the <b>IUnknown</b> interface.
 * @param version Indicates the version of the <b>IUnknown</b> interface object to be converted.
 * @param target Indicates the <b>IUnknown</b> subclass type required by the caller. This is an
 * output parameter.
 * target Represents the IUnknown subclass type required by the caller. This is an output parameter
 * 
 * @return Returns <b>EC_SUCCESS</b> if the conversion is successful; returns other error codes
 * if the conversion fails.
 *
 * @since 1.0
 * @version 1.0
 */
int IUNKNOWN_QueryInterface(IUnknown *iUnknown, int ver, void **target);

Function body and notes:

//Query IUnknown interface of specified version
int IUNKNOWN_QueryInterface(IUnknown *iUnknown, int ver, void **target)
{
    if (iUnknown == NULL || target == NULL) {
        return EC_INVALID;
    }
    //First get an object, and then judge whether the IUnknown entry type is correct
    IUnknownEntry *entry = GET_OBJECT(iUnknown, IUnknownEntry, iUnknown);
    if ((entry->ver & (uint16)ver) != ver) {
        return EC_INVALID;
    }

    //Then check the type information
    if (ver == OLD_VERSION &&
        entry->ver != OLD_VERSION &&
        (entry->ver & (uint16)DEFAULT_VERSION) != DEFAULT_VERSION) {
        return EC_INVALID;
    }

    //Assign the IUnknown subclass type interface value required by the caller
    *target = iUnknown;
    //Add a reference to the IUnknown interface structure
    iUnknown->AddRef(iUnknown);
    return EC_SUCCESS;
}

IUNKNOWN_AddRef

Function declaration and comments:

/**
 * @brief Increments the reference count in this <b>IUnknown</b> interface.
 * Overview: increase reference count in IUnknown interface
 * 
 * This function is called in <b>QueryInterface</b>. Do not call this function in the
 * <b>IUnknown<b> interface. \n
 * This function is called in QueryInterface. Do not call this function in the IUnknown interface.
 * 
 * When the <b>QueryInterface</b> function is re-implemented, you need to call this function
 * in the new <b>QueryInterface</b>.
 * When the QueryInterface function is re implemented, you need to call this function in the new QueryInterface.
 * 
 * The system does not provide a lock to protect functions. Therefore, you need to re-implement
 * functions if multiple developers are using them. \n
 * The system does not provide lock protection. Therefore, if multiple developers are using these functions, they need to be re implemented
 * 
 * @param iUnknown Indicates the pointer to the <b>IUnknown<b> interface object.
 * Parameter iUnknown: pointer representing iUnknown of the interface object.
 * @return Indicates the number of objects that reference the <b>IUnknown<b> interface.
 * Return value: the number of nodes referencing the IUnknown interface
 * @since 1.0
 * @version 1.0
 */
int IUNKNOWN_AddRef(IUnknown *iUnknown);

Code and comments of function body:

int IUNKNOWN_AddRef(IUnknown *iUnknown)
{
    //Check whether the incoming interface address is empty
    if (iUnknown == NULL) {
        return EC_INVALID;
    }

    //Define an IUnknown entry and turn the IUnknown pointer down to the address of the T member variable
    IUnknownEntry *entry = GET_OBJECT(iUnknown, IUnknownEntry, iUnknown);
    //Count references in the iUnknown entry + 1
    entry->ref++;
    //Returns the number of iUnknown references after implementation
    return entry->ref;
}

IUNKNOWN_Release

Function declaration and comments:

/**
 * @brief Releases a reference to an <b>IUnknown</b> interface that is no longer used.
 * Overview: release a reference to an IUnknown interface that is no longer used.
 * In the default implementation provided by the system, if the reference count is <b>0</b>,
 * the memory of the <b>IUnknown</b> interface object and implementation object is not released. \n
 * In the system default implementation, if the number of references is 0, the IUnknown interface object and implementation object in memory will not be released
 * 
 * If the memory of the <b>IUnknown</b> interface object and implementation object is dynamically
 * allocated, this function needs to be re-implemented. \n
 * If the memory IUnknown interface object and application object are dynamically allocated, this function needs to be re implemented
 * If the reference count is <b>0</b>, the memory of the <b>IUnknown</b> interface object and
 * implementation object is released. \n
 * If the number of references is 0, the IUnknown interface object and application object in memory need to be released
 *
 * @param iUnknown Indicates the pointer to the <b>IUnknown<b> interface object.
 * @return Indicates the number of <b>IUnknown<b> interface objects that are referenced after the
 * current reference is released.
 *
 * @since 1.0
 * @version 1.0
 */
int IUNKNOWN_Release(IUnknown *iUnknown);

Code and comments of function body:

//Release a reference to an IUnknown interface that is no longer in use
int IUNKNOWN_Release(IUnknown *iUnknown)
{
    //Judge whether the incoming iUnknown is empty
    if (iUnknown == NULL) {
        return EC_INVALID;
    }

    //After the object is obtained, the IUnknown entry interface is referenced
    IUnknownEntry *entry = GET_OBJECT(iUnknown, IUnknownEntry, iUnknown);
    int ref = entry->ref - 1;
    if (ref < 0) {
        // The IUnknown is already free, there is some exception.iunknown has been released with some exceptions;
    } else {
        if (ref == 0) {
            // Nobody reference to the unknown, should delete it.
            // But iUnknown may be global variable, so the default version don`t delete it.
            // However, iUnknown may be a global variable, so it will not be deleted by the default version.
        } else {
            entry->ref = ref;
        }
    }
    return ref;
}

Summary of this paper

After carefully interpreting the interface code, we believe that we have a clearer understanding of the essence of the external interface for developing services or functions, and a deeper understanding of the position of the interface in serving functions, which is helpful for us to further interpret the system service development framework.

Posted by riffy on Fri, 29 Oct 2021 20:28:01 -0700