steamcompmgr: Avoid deadlock between import_commit and destroy_buffer
Very rarely we can see a deadlock between import_commit and destroy_buffer where: - destroy_buffer is waiting on `wlr_buffer_map_lock` and has `wlserver_lock` (from up the chain) - import_commit has `wlr_buffer_map_lock` and is waiting on `wlserver_lock` To avoid this, we simply replace the lock_guard in import_commit with a unique_lock and manually unlock this before going into the section where we need to lock the wl_server. This is safe for a few reasons: - 1: All accesses to wlr_buffer_map are done before this lock. - 2: destroy_buffer cannot be called from this buffer before now as it only happens because of the signal added below. - 3: "References to elements in the unordered_map container remain valid in all cases, even after a rehash."
This commit is contained in:
parent
952949f1a1
commit
a5af1a78c1
1 changed files with 19 additions and 5 deletions
|
@ -661,10 +661,7 @@ release_commit( commit_t &commit )
|
||||||
static bool
|
static bool
|
||||||
import_commit ( struct wlr_buffer *buf, commit_t &commit )
|
import_commit ( struct wlr_buffer *buf, commit_t &commit )
|
||||||
{
|
{
|
||||||
/* Keep this locked during the entire function so we
|
std::unique_lock<std::mutex> lock( wlr_buffer_map_lock );
|
||||||
* avoid racing the creation of the commit. Unlikely to
|
|
||||||
* come up with the current callers but a safe default. */
|
|
||||||
std::lock_guard<std::mutex> lock( wlr_buffer_map_lock );
|
|
||||||
|
|
||||||
commit.buf = buf;
|
commit.buf = buf;
|
||||||
|
|
||||||
|
@ -674,6 +671,12 @@ import_commit ( struct wlr_buffer *buf, commit_t &commit )
|
||||||
commit.vulkanTex = it->second.vulkanTex;
|
commit.vulkanTex = it->second.vulkanTex;
|
||||||
commit.fb_id = it->second.fb_id;
|
commit.fb_id = it->second.fb_id;
|
||||||
|
|
||||||
|
/* Unlock here to avoid deadlock [1],
|
||||||
|
* drm_lock_fbid calls wlserver_lock.
|
||||||
|
* Map is no longer used here and the element
|
||||||
|
* is no longer accessed. */
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
if (commit.fb_id)
|
if (commit.fb_id)
|
||||||
{
|
{
|
||||||
drm_lock_fbid( &g_DRM, commit.fb_id );
|
drm_lock_fbid( &g_DRM, commit.fb_id );
|
||||||
|
@ -682,6 +685,18 @@ import_commit ( struct wlr_buffer *buf, commit_t &commit )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_buffer_map_entry& entry = wlr_buffer_map[buf];
|
||||||
|
/* [1]
|
||||||
|
* Need to unlock the wlr_buffer_map_lock to avoid
|
||||||
|
* a deadlock on destroy_buffer if it owns the wlserver_lock.
|
||||||
|
* This is safe for a few reasons:
|
||||||
|
* - 1: All accesses to wlr_buffer_map are done before this lock.
|
||||||
|
* - 2: destroy_buffer cannot be called from this buffer before now
|
||||||
|
* as it only happens because of the signal added below.
|
||||||
|
* - 3: "References to elements in the unordered_map container remain
|
||||||
|
* valid in all cases, even after a rehash." */
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
commit.vulkanTex = vulkan_create_texture_from_wlr_buffer( buf );
|
commit.vulkanTex = vulkan_create_texture_from_wlr_buffer( buf );
|
||||||
assert( commit.vulkanTex != 0 );
|
assert( commit.vulkanTex != 0 );
|
||||||
|
|
||||||
|
@ -700,7 +715,6 @@ import_commit ( struct wlr_buffer *buf, commit_t &commit )
|
||||||
commit.fb_id = 0;
|
commit.fb_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_buffer_map_entry& entry = wlr_buffer_map[buf];
|
|
||||||
entry.listener.notify = destroy_buffer;
|
entry.listener.notify = destroy_buffer;
|
||||||
entry.buf = buf;
|
entry.buf = buf;
|
||||||
entry.vulkanTex = commit.vulkanTex;
|
entry.vulkanTex = commit.vulkanTex;
|
||||||
|
|
Loading…
Reference in a new issue