Skip to content

fix: WSurfaceItemContent buffer lifecycle management#815

Open
Dami-star wants to merge 1 commit intolinuxdeepin:masterfrom
Dami-star:fix-crash
Open

fix: WSurfaceItemContent buffer lifecycle management#815
Dami-star wants to merge 1 commit intolinuxdeepin:masterfrom
Dami-star:fix-crash

Conversation

@Dami-star
Copy link
Copy Markdown
Collaborator

@Dami-star Dami-star commented Apr 2, 2026

Root cause:
WSurfaceItemContent held buffer locks but didn't manage n_ignore_locks, causing wlr_client_buffer_apply_damage to see effective_locks > 1 and refuse to reuse buffers. This led to creating new client_buffer on every commit, triggering frequent buffer destruction and crashes in:

  • assert(buffer->n_locks > 0) in wlr_buffer_unlock
  • heap corruption in Mesa/OpenGL texture cleanup
  • assert(!buffer->accessing_data_ptr) in SHM buffers

Solution:
Added adjustClientBufferIgnoreLocks() helper to manage n_ignore_locks symmetrically with n_locks:

  • Increment n_ignore_locks when acquiring buffer (alongside lock())
  • Decrement n_ignore_locks when releasing buffer (alongside unlock())
  • Handle n_ignore_locks in destructor , dontCacheLastBuffer, and swapBufferIfNeeded()

This allows wlroots to correctly calculate effective_locks and reuse buffers, eliminating the frequent creation/destruction cycle.

Summary by Sourcery

Fix client buffer lifecycle handling in WSurfaceItemContent to keep wlroots buffer lock counters consistent and allow buffer reuse.

Bug Fixes:

  • Ensure n_ignore_locks is updated alongside buffer lock/unlock operations in WSurfaceItemContent to prevent wlroots from constantly recreating client buffers and triggering buffer-related crashes.

Enhancements:

  • Introduce a helper for centrally adjusting client buffer n_ignore_locks and integrate it into WSurfaceItemContent buffer swap, cache, and destruction paths.

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Dami-star

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 2, 2026

Reviewer's Guide

Manages wlroots client buffer lock accounting by introducing adjustClientBufferIgnoreLocks() and applying it consistently when acquiring, releasing, and swapping WSurfaceItemContent buffers so wlroots can correctly reuse client buffers instead of constantly recreating them.

Sequence diagram for updated buffer lock and ignore lock management

sequenceDiagram
    actor Client
    participant WSurfaceItemContent
    participant WSurfaceItemContentPrivate as ContentPriv
    participant Buffer as qw_buffer
    participant ClientBuffer as qw_client_buffer
    participant Handle as ClientBufferHandle
    participant WlrootsDamage as wlr_client_buffer_apply_damage

    Client->>WSurfaceItemContent: commit surface with newBuffer
    WSurfaceItemContent->>ContentPriv: updateSurface(newBuffer)

    alt live mode and buffer changed
        ContentPriv->>Buffer: lock()
        ContentPriv->>ClientBuffer: get(Buffer)
        ClientBuffer-->>ContentPriv: cb
        ContentPriv->>Handle: adjustClientBufferIgnoreLocks(Buffer, +1)
        Note over Handle: n_ignore_locks++
        ContentPriv->>Handle: adjustClientBufferIgnoreLocks(old buffer, -1)
        Note over Handle: n_ignore_locks--
        ContentPriv->>ContentPriv: buffer.reset(newBuffer)
    else non live mode and pendingBuffer changed
        ContentPriv->>Buffer: lock()
        ContentPriv->>ClientBuffer: get(Buffer)
        ClientBuffer-->>ContentPriv: cb
        ContentPriv->>Handle: adjustClientBufferIgnoreLocks(Buffer, +1)
        ContentPriv->>Handle: adjustClientBufferIgnoreLocks(old pendingBuffer, -1)
        ContentPriv->>ContentPriv: pendingBuffer.reset(newBuffer)
    end

    Client->>WSurfaceItemContent: later frame display / damage apply
    WSurfaceItemContent->>WlrootsDamage: apply_damage(client_buffer)
    WlrootsDamage->>Handle: read n_locks, n_ignore_locks
    WlrootsDamage-->>WlrootsDamage: effective_locks = n_locks - n_ignore_locks
    WlrootsDamage-->>Client: buffer reused when effective_locks == 1

    Client->>WSurfaceItemContent: item destroyed or buffer dropped
    WSurfaceItemContent->>ContentPriv: destructor / dontCacheLastBuffer / swapBufferIfNeeded
    ContentPriv->>Handle: adjustClientBufferIgnoreLocks(buffer, -1)
    Note over Handle: n_ignore_locks--
    ContentPriv->>ContentPriv: buffer.reset()  (unique_ptr deleter calls unlock())
Loading

Class diagram for updated WSurfaceItemContent buffer lifecycle

classDiagram
    class WSurfaceItemContent {
    }

    class WSurfaceItemContentPrivate {
        +~WSurfaceItemContentPrivate()
        +cleanTextureProvider()
        +dontCacheLastBuffer : bool
        +live : bool
        +buffer : qw_buffer_ptr
        +pendingBuffer : qw_buffer_ptr
        +swapBufferIfNeeded()
        +updateSurface(newBuffer : qw_buffer)
        +adjustClientBufferIgnoreLocks(buf : qw_buffer, delta : int) static
    }

    class qw_buffer {
        +lock()
        +unlock()
    }

    class qw_client_buffer {
        +get(buf : qw_buffer) qw_client_buffer
        +handle() ClientBufferHandle
    }

    class ClientBufferHandle {
        +n_locks : int
        +n_ignore_locks : int
    }

    WSurfaceItemContent "1" o-- "1" WSurfaceItemContentPrivate
    WSurfaceItemContentPrivate --> qw_buffer : buffer
    WSurfaceItemContentPrivate --> qw_buffer : pendingBuffer
    qw_client_buffer --> qw_buffer : wraps
    qw_client_buffer --> ClientBufferHandle : handle
Loading

File-Level Changes

Change Details Files
Introduce helper to adjust wlroots client buffer n_ignore_locks alongside lock/unlock calls and apply it in buffer lifecycle paths.
  • Add adjustClientBufferIgnoreLocks() static helper that looks up qw_client_buffer from qw_buffer and adjusts n_ignore_locks by a delta if present
  • Call adjustClientBufferIgnoreLocks() with -1 for both buffer and pendingBuffer in the WSurfaceItemContentPrivate destructor to drop ignore locks on destruction
  • Update dontCacheLastBuffer path to decrement n_ignore_locks when discarding the cached buffer
  • Change non-live path to lock newBuffer and increment n_ignore_locks before replacing pendingBuffer, and to decrement ignore locks on the old pendingBuffer when it is replaced
  • Change live path to lock newBuffer and increment n_ignore_locks before replacing buffer, and to decrement ignore locks on the old buffer when it is replaced
  • Update swapBufferIfNeeded() to decrement n_ignore_locks on the old buffer before taking ownership from pendingBuffer
waylib/src/server/qtquick/wsurfaceitem.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Root cause:
WSurfaceItemContent held buffer locks but didn't manage n_ignore_locks,
causing wlr_client_buffer_apply_damage to see effective_locks > 1 and
refuse to reuse buffers. This led to creating new client_buffer on every
commit, triggering frequent buffer destruction and crashes in:
- assert(buffer->n_locks > 0) in wlr_buffer_unlock
- heap corruption in Mesa/OpenGL texture cleanup
- assert(!buffer->accessing_data_ptr) in SHM buffers

Solution:
Added adjustClientBufferIgnoreLocks() helper to manage n_ignore_locks
symmetrically with n_locks:
- Increment n_ignore_locks when acquiring buffer (alongside lock())
- Decrement n_ignore_locks when releasing buffer (alongside unlock())
- Handle n_ignore_locks in destructor , dontCacheLastBuffer, and swapBufferIfNeeded()

This allows wlroots to correctly calculate effective_locks and reuse
buffers, eliminating the frequent creation/destruction cycle.
Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The destructor unconditionally calls adjustClientBufferIgnoreLocks(..., -1) on buffer and pendingBuffer, but those same pointers may already have been decremented in dontCacheLastBuffer() / swapBufferIfNeeded(), which risks driving n_ignore_locks negative or otherwise unbalanced; consider centralizing the lifetime management so each lock/unlock pair has exactly one corresponding ignore-lock adjustment.
  • To guard against subtle misuse of adjustClientBufferIgnoreLocks, it might be safer to add assertions or defensive checks (e.g. that n_ignore_locks does not underflow when applying a negative delta) so issues are caught early rather than silently corrupting wlroots’ lock accounting.
  • Since lock()/unlock() and n_ignore_locks adjustments always need to stay in sync, consider wrapping them in small helper functions or an RAII type instead of manually duplicating the lock() + adjust(..., +1) and adjust(..., -1) patterns in multiple branches.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The destructor unconditionally calls `adjustClientBufferIgnoreLocks(..., -1)` on `buffer` and `pendingBuffer`, but those same pointers may already have been decremented in `dontCacheLastBuffer()` / `swapBufferIfNeeded()`, which risks driving `n_ignore_locks` negative or otherwise unbalanced; consider centralizing the lifetime management so each lock/unlock pair has exactly one corresponding ignore-lock adjustment.
- To guard against subtle misuse of `adjustClientBufferIgnoreLocks`, it might be safer to add assertions or defensive checks (e.g. that `n_ignore_locks` does not underflow when applying a negative delta) so issues are caught early rather than silently corrupting wlroots’ lock accounting.
- Since `lock()`/`unlock()` and `n_ignore_locks` adjustments always need to stay in sync, consider wrapping them in small helper functions or an RAII type instead of manually duplicating the `lock()` + `adjust(..., +1)` and `adjust(..., -1)` patterns in multiple branches.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@Dami-star Dami-star requested a review from zccrs April 2, 2026 02:04
@Dami-star
Copy link
Copy Markdown
Collaborator Author

codedump: #659 (comment)

}

// Adjust n_ignore_locks to allow wlr_client_buffer_apply_damage to reuse client buffers.
static void adjustClientBufferIgnoreLocks(qw_buffer *buf, int delta) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

这样做不好维护,加一个新的struct或class,定位为干净卫生的持有一个wlr buffer,在构造里支持自动lock且更新 n_ignore_locks,析构时自动unlock和更新 n_ignore_locks。此外支持reset可以重新设置一个新的对象持有。

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.

3 participants