Skip to content

Adjust acquisition thread to never overwrite circular buffer, keep track of pending frames, always fetch oldest frame. - #47

Open
Jakub Wlodek (jwlodek) wants to merge 8 commits into
NSLS2:mainfrom
jwlodek:eof-ring-buffer-fix
Open

Jakub Wlodek (jwlodek) wants to merge 8 commits into
NSLS2:mainfrom
jwlodek:eof-ring-buffer-fix

Conversation

@jwlodek

Copy link
Copy Markdown
Collaborator
  • Use CIRC_NO_OVERWRITE mode, and keep track of frames pending in circular buffer. Also track dropped frames
  • Remove defaults

Copilot AI balanced review requested due to automatic review settings October 13, 2026 16:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Error and synchronization paths can leave frame-buffer state invalid or discard valid EOF notifications.

3 open findings
What changed in this PR

Updates continuous acquisition to preserve buffered frames and expose queue diagnostics/configuration.

Changes:

  • Uses non-overwriting circular buffers and retrieves the oldest frame.
  • Tracks pending and dropped frames.
  • Adds configurable buffer sizing and unique-ID sources.
File Description
kinetixApp/​src/​ADKinetixDS.h Adds pending-frame state.
kinetixApp/​src/​ADKinetix.h Defines new parameters and unique-ID options.
kinetixApp/​src/​ADKinetix.cpp Implements buffered frame retrieval and diagnostics.
kinetixApp/​Db/​ADKinetix.template Adds corresponding EPICS records.
kinetixApp/​Db/​ADKinetix_settings.req Persists new configurable settings.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

INFO_ARGS("Allocated circular buffer for %d frames. Total size: %d bytes.",
KTX_CIRC_BUFF_SIZE, frameBufferSize * KTX_CIRC_BUFF_SIZE);
// Allocate entire circular buffer; PVCam takes its total size as uns32
uint64_t circBuffBytes = (uint64_t) circBuffSize * frameBufferSize;
Comment on lines +987 to +990
{
std::lock_guard<std::mutex> lock(this->cameraContext->eofEvent.mutex);
this->cameraContext->pendingFrames = 0;
}
Comment on lines +1043 to +1044
if (continuousMode && PV_OK != pl_exp_unlock_oldest_frame(this->cameraContext->hcam))
reportKinetixError(functionName);
Copilot AI balanced review requested due to automatic review settings October 13, 2026 16:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Failed dequeues can erase concurrently delivered EOF notifications and strand buffered frames.

3 open findings

🧠 Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 14, 2026 20:22

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Concurrent stop/start requests can create overlapping acquisition workers and compromise shared buffer ownership.

4 open findings

🧠 Review effort: Balanced

Comment on lines +775 to +777
this->unlock();
epicsThreadMustJoin(this->acquisitionThreadId);
this->lock();
Copilot AI balanced review requested due to automatic review settings October 14, 2026 20:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Frame-number arithmetic can overflow, and startup failures do not retain an error status.

4 open findings
Previously missed (2)

In code that hasn't changed since last review

Medium severity Report allocation failures before publishing acquisition status

kinetixApp/​src/​ADKinetix.cpp:974

When buffer allocation or frame-info creation fails, execution still publishes ADStatusWaiting or ADStatusAcquire, skips the acquisition loop, and ends with ADStatusIdle. This hides the startup failure from status consumers. Clean up and return with ADStatusError before publishing an acquiring status.

Medium severity Prevent signed overflow at INT32_MAX frame number

kinetixApp/​src/​ADKinetix.cpp:1009

If a continuous acquisition reaches INT32_MAX, the next comparison evaluates lastFrameNr + 1 with signed 32-bit arithmetic, causing undefined behavior. Promote the addition to 64 bits to keep this comparison safe at the frame-number boundary.

🧠 Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 16, 2026 20:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread kinetixApp/src/ADKinetix.cpp Outdated
(long long) pFrameInfo->TimeStamp,
(frameNr != pFrameInfo->FrameNr) ? " MISMATCH" : "");

if (lastFrameNr >= 0 && frameNr > lastFrameNr + 1) {
Copilot AI balanced review requested due to automatic review settings October 16, 2026 20:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Buffer-allocation errors are hidden from status clients, and unconditional per-frame console output can stall acquisition.

6 open findings
Previously missed (2)

In code that hasn't changed since last review

Medium severity Startup failure status is overwritten before publication

kinetixApp/​src/​ADKinetix.cpp:962

For an invalid circular-buffer size or allocation failure, this error status is overwritten by the Waiting/Acquire assignment below before any parameter callback publishes it. Cleanup then reports Idle, so PV clients see a normal completion with zero frames rather than a startup failure. Clear ADAcquire, publish ADStatusError, and release the port lock before returning from this failure path.

Medium severity Per-frame stdout logging stalls readout under locks

kinetixApp/​src/​ADKinetix.cpp:1014

This writes to stdout for every continuous frame while both the port lock and the oldest buffer slot are held. A slow or blocked IOC console can stall readout and prevent timely slot release, and disabling asyn tracing does not disable this output. Use the existing trace-controlled DEBUG_ARGS logging instead.

🧠 Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 16, 2026 20:22

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Buffer-size and allocation failures overwrite the error status before clients receive it.

5 open findings
1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Failed buffer allocation status is overwritten before publication

kinetixApp/​src/​ADKinetix.cpp:962

If the configured buffer exceeds UINT32_MAX bytes or calloc fails, ADStatusError is overwritten by Acquire/Waiting before callParamCallbacks(), then by Idle during cleanup. Clients therefore never receive the failed-start status. Clear ADAcquire, publish the error, release the port lock, and return from this branch; neither the frame buffer nor FRAME_INFO has been allocated here.

🧠 Review effort: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants