Application Crash calling MibLeaf::replace_value

SNMP++ Version: 3.4.2
Agent++ Version: 4.3.1

I am investigating a series of application crashes occurring in our Agent running Agent++.
The agent has a recurring timer that spawns a thread every 10 seconds. The thread first queries the load on each CPU and then uses those CPU load values to update the host_resources_mib_hrProcessorEntry MibTable via the set_row function (which then calls MibLeaf::replace_value).

Do you have any suggestions on steps to further investigate these crashes?
Are there any code changes you would suggest we make in how the MibTable is updated or how frequently it is being updated to possibly avoid these crashes?

Code Sample:
void CSnmpAgent::OnProcessorLoadUpdated(std::vector vCpuUtilizationPercentages)
{
MibTableRow* hrProcessorEntryMTR = nullptr;

//Loop through the CPU utilization percentages
for (size_t i = 0; i < vCpuUtilizationPercentages.size(); ++i)
{
    int processorHrDeviceTableIndex = m_uHostResourcesDeviceTableProcessorStartIndex + i;

    Agentpp::Oidx processorIndexOidx = Oidx() += std::to_string(processorHrDeviceTableIndex).c_str();

    //Attempt to find an existing entry in the hrProcessorEntry mib table. 
    hrProcessorEntryMTR = host_resources_mib_hrProcessorEntry::instance->find_index(processorIndexOidx);

    if (hrProcessorEntryMTR == 0)
    {
        hrProcessorEntryMTR = host_resources_mib_hrProcessorEntry::instance->add_row(processorIndexOidx);
    }

    **host_resources_mib_hrProcessorEntry::instance->set_row(hrProcessorEntryMTR, "0.0", int(round(vCpuUtilizationPercentages[i])));**
}

}

//From host_resources_mib.cpp obtained from AgenPro Code Samples for AGENT++ - SNMP Tools - AGENTPP
void host_resources_mib_hrProcessorEntry::set_row(MibTableRow* r, const char* p1, long p2)
{
r->get_nth(0)->replace_value(new NS_SNMP Oid(p1));
r->get_nth(1)->replace_value(new NS_SNMP SnmpInt32(p2));
}

Error Message: Unhandled exception at 0x773CFCDD (ntdll.dll) in SnmpAgent.exe.4968.dmp: 0xC0000374: A heap has been corrupted (parameters: 0x7740B960).

Crash Stack Trace:
ntdll.dll!_RtlReportFatalFailure@4()
ntdll.dll!_RtlReportCriticalFailure@12()
ntdll.dll!_RtlpReportHeapFailure@4()
ntdll.dll!_RtlpHpHeapHandleError@12()
ntdll.dll!_RtlpLogHeapFailure@24()
ntdll.dll!_RtlpFreeHeapInternal@20()
ntdll.dll!RtlFreeHeap()
ucrtbase.dll!_free_base()
ucrtbase.dll!_free()
[Inline Frame] Rescue21SnmpAgent.exe!Snmp_pp::Oid::delete_oid_ptr() Line 635
[Inline Frame] Rescue21SnmpAgent.exe!Snmp_pp::Oid::{dtor}() Line 175
[Inline Frame] Rescue21SnmpAgent.exe!Agentpp::Oidx::{dtor}() Line 95
SnmpAgent.exe!Agentpp::Oidx::`scalar deleting destructor’(unsigned int)
SnmpAgent.exe!Agentpp::MibLeaf::replace_value(Snmp_pp::SnmpSyntax * v) Line 230
SnmpAgent.exe!Agentpp::host_resources_mib_hrProcessorEntry::set_row(Agentpp::MibTableRow * r, const char * p1, long p2) Line 1801

I think I may have resolved my issue on my own.

The issue ended up being an error with our timer operation. For some reason which I haven’t determined yet, the normal timer operation is being blocked/halted for several minutes. After the block is resolved, a flood of callbacks are processed on multiple threads at the same time. All of these threads are trying to update the MibTable at the same time and are likely causing the heap corruption.

I plan on adding controls to enforce the desired update frequency as well as a thread guard to ensure only one thread is updating the MibTable at the same time.

This topic can be closed.

OK, please use the following lock scheme when protecting your update threads:
https://doc.snmp.app/pages/viewpage.action?pageId=5799965

Thank you for your response Frank. That resource will be very helpful.

One follow up question:

Our agent is currently set up to access/update the mib tables through the static instance defined on each MibTable subclass. Is that a desired pattern? Or should I swap out the references to the static instance for the mib->get() call as shown in the example you provided?

Using the mib->get() call is cleaner because you are then able to use multiple Mibs in the same process. But there is no need to change it if you do not plan to use multiple Mib instances in the same process.

I have another follow up question on the locking scheme.

Is it safe/desirable to call start_sync() on multiple tables at the same time?
Or should I restrict the updates to a single table per pair of mib->lock_mib() / mib->unlock_mib() calls?
If it is ok to sync multiple tables at the same time, does the order of calls to start_sync() and end_sync() important?

Example Code:
Mib* mib;

...

mib->lock_mib();

// code to lookup a MibEntry (replace "my context" with "" or your context and the OID by the table entry OID, for example):

MibTable* table1 = (MibTable*) mib->get( "my context" , "1.3.6.1.4.1.????.1" );
MibTable* table2 = (MibTable*) mib->get( "my context" , "1.3.6.1.8.1.????.1" );

// enter protected region:

table1->start_synch();
table2->start_synch();

// now you can drop the Mib lock

mib->unlock_mib();

// do the real work on table

...

table1->end_synch(); table2->end_synch();`

Yes, it is safe to start multiple table synch blocks at the same time using the schema you presented here.
The important detail is calling mib->unlock_mib() after any start_synch(). If any start_synch() is protected by a lock_mib(), there cannot be any race conditions on the lock order.