Source Code Analysis of SchduleUpdate

Preface

In the game, the timer is used to update the picture and action.
The ccschedule class of the system is used.
Next, we will talk about the implementation mechanism of timers.

Definition

    scheduler = new (std::nothrow) Scheduler();
    _actionManager = new (std::nothrow) ActionManager();
    _scheduler->scheduleUpdate(_actionManager, Scheduler::PRIORITY_SYSTEM, false);

In the director class, the timer of the action manager is defined and initialized.
The value of priority is INT_MIN.
You can also see that the priority of this scheme is the lowest, which will be discussed in the next article.
(The lower the value of priority, the higher the priority)

Analysis

Let's first look at the scheduleUpdate function.

template <class T>
    void scheduleUpdate(T *target, int priority, bool paused)
    {
        this->schedulePerFrame([target](float dt){
            target->update(dt);
        }, target, priority, paused);
    }

The three parameters passed in mean the target to be updated, the priority (for comparison), and whether a pause is required.
As you can see, schedulePerFrame is invoked for each frame to rank priorities.
Then the update function is called to update itself.

void Scheduler::schedulePerFrame(const ccSchedulerFunc& callback, void *target, int priority, bool paused)
{
    tHashUpdateEntry *hashElement = nullptr;
    HASH_FIND_PTR(_hashForUpdates, &target, hashElement);//Find the elements to sort according to the hash table
    if (hashElement)//If it already exists
    {
        // change priority: should unschedule it first
        if (hashElement->entry->priority != priority)
        {
            unscheduleUpdate(target);//Dispatch cancellation
        } 
        else
        {
            // don't add it again
            CCLOG("warning: don't update it again");
            return;
        }
    }
    // most of the updates are going to be 0, that's way there
    // is an special list for updates with priority 0
    if (priority == 0)
    {
        appendIn(&_updates0List, callback, target, paused);//Priority is 0, then insert data directly
    }
    else if (priority < 0)
    {
        priorityIn(&_updatesNegList, callback, target, priority, paused);//Not 0, judge priority and insert again
    }
    else
    {
        // priority > 0
        priorityIn(&_updatesPosList, callback, target, priority, paused);
    }
}

THashUpdate Entry is a structure defined as follows:

typedef struct _hashUpdateEntry
{
    tListEntry          **list;        // Which list does it belong to ?
    tListEntry          *entry;        // entry in the list
    void                *target;
    ccSchedulerFunc     callback;
    UT_hash_handle      hh;
} tHashUpdateEntry;
{% endhighlight %}
//It has a nested structure tListEntry;
{% highlight cpp %}
typedef struct _listEntry
{
    struct _listEntry   *prev, *next;
    ccSchedulerFunc     callback;
    void                *target;
    int                 priority;
    bool                paused;
    bool                markedForDeletion; // selector will no longer be called and entry will be removed at end of the next tick
} tListEntry;
{% endhighlight %}
//It's easy to see that this is a double linked list.  
hashUpdateEntry Used to quickly retrieve double-linked lists.  
{% highlight cpp %}
    struct _listEntry *_updatesNegList;        // list of priority < 0
    struct _listEntry *_updates0List;            // list priority == 0
    struct _listEntry *_updatesPosList;        // list priority > 0
{% endhighlight %}
//By viewing definitions,Understanding that the system uses three linked lists to store elements with different priorities.  
//Compare appendIn and priorityIn functions:  
{% highlight cpp %}
    void Scheduler::priorityIn(tListEntry **list, const ccSchedulerFunc& callback, void *target, int priority, bool paused)
{
    tListEntry *listElement = new (std::nothrow) tListEntry();
    listElement->callback = callback;
    listElement->target = target;
    listElement->priority = priority;
    listElement->paused = paused;
    listElement->next = listElement->prev = nullptr;
    listElement->markedForDeletion = false;//Assignment to linked list
    // empty list ?
    if (! *list)
    {
        DL_APPEND(*list, listElement);//If empty, insert data directly backwards
    }
    else
    {
        bool added = false;
        for (tListEntry *element = *list; element; element = element->next)
        {
            if (priority < element->priority)//Compare and insert according to priority
            {
                if (element == *list)
                {
                    DL_PREPEND(*list, listElement);
                }
                else
                {
                    listElement->next = element;
                    listElement->prev = element->prev;
                    element->prev->next = listElement;
                    element->prev = listElement;
                }
                added = true;
                break;
            }
        }
        if (! added)
        {
            DL_APPEND(*list, listElement);
        }
    }
    // update hash entry for quick access
    tHashUpdateEntry *hashElement = (tHashUpdateEntry *)calloc(sizeof(*hashElement), 1);
    hashElement->target = target;
    hashElement->list = list;
    hashElement->entry = listElement;
    memset(&hashElement->hh, 0, sizeof(hashElement->hh));
    HASH_ADD_PTR(_hashForUpdates, target, hashElement);
}
    void Scheduler::appendIn(_listEntry **list, const ccSchedulerFunc& callback, void *target, bool paused)
{
    tListEntry *listElement = new (std::nothrow) tListEntry();//Ditto
    listElement->callback = callback;
    listElement->target = target;
    listElement->paused = paused;
    listElement->priority = 0;
    listElement->markedForDeletion = false;
    DL_APPEND(*list, listElement);
    // update hash entry for quicker access
    tHashUpdateEntry *hashElement = (tHashUpdateEntry *)calloc(sizeof(*hashElement), 1);
    hashElement->target = target;
    hashElement->list = list;
    hashElement->entry = listElement;
    memset(&hashElement->hh, 0, sizeof(hashElement->hh));
    HASH_ADD_PTR(_hashForUpdates, target, hashElement);
}

The differences can be found by comparing:
One is inserting by priority, the other is inserting directly at the end of the list.

Epilogue

The update function corresponding to the target is called to implement the specific update logic.
The timer part is over.
In addition, there is a system-defined schedule, which uses the Timer class to implement.
It's much the same.

summary

Schdule compares and inserts linked lists by calling each frame.
In this way, the priority sequence can be sorted to manage the order of updates.
The whole process is almost like this, there are omissions to add it.

The blog was launched at github
Reprinted please indicate the source

Keywords: github

Added by kraadde on Sun, 04 Aug 2019 15:35:09 +0300