Draw: Avoid hang when depsgraph update happens during draw

This should not happen and any failure here should be considered a bug.
But for end users better not to hang Blender, and to have a better
diagnostic for developers in bug reports.

Ref #82483

Pull Request: https://projects.blender.org/blender/blender/pulls/123023
This commit is contained in:
Brecht Van Lommel 2024-06-10 18:11:41 +02:00 committed by Brecht Van Lommel
parent 4f593b5710
commit 27f467e028
3 changed files with 41 additions and 5 deletions

@ -144,6 +144,7 @@ typedef struct TicketMutex TicketMutex;
TicketMutex *BLI_ticket_mutex_alloc(void);
void BLI_ticket_mutex_free(TicketMutex *ticket);
void BLI_ticket_mutex_lock(TicketMutex *ticket);
bool BLI_ticket_mutex_lock_check_recursive(TicketMutex *ticket);
void BLI_ticket_mutex_unlock(TicketMutex *ticket);
/* Condition */

@ -504,6 +504,8 @@ struct TicketMutex {
pthread_cond_t cond;
pthread_mutex_t mutex;
uint queue_head, queue_tail;
pthread_t owner;
bool has_owner;
};
TicketMutex *BLI_ticket_mutex_alloc()
@ -524,24 +526,46 @@ void BLI_ticket_mutex_free(TicketMutex *ticket)
MEM_freeN(ticket);
}
void BLI_ticket_mutex_lock(TicketMutex *ticket)
static bool ticket_mutex_lock(TicketMutex *ticket, const bool check_recursive)
{
uint queue_me;
pthread_mutex_lock(&ticket->mutex);
/* Check for recursive locks, for debugging only. */
if (check_recursive && ticket->has_owner && pthread_equal(pthread_self(), ticket->owner)) {
pthread_mutex_unlock(&ticket->mutex);
return false;
}
queue_me = ticket->queue_tail++;
while (queue_me != ticket->queue_head) {
pthread_cond_wait(&ticket->cond, &ticket->mutex);
}
ticket->owner = pthread_self();
ticket->has_owner = true;
pthread_mutex_unlock(&ticket->mutex);
return true;
}
void BLI_ticket_mutex_lock(TicketMutex *ticket)
{
ticket_mutex_lock(ticket, false);
}
bool BLI_ticket_mutex_lock_check_recursive(TicketMutex *ticket)
{
return ticket_mutex_lock(ticket, true);
}
void BLI_ticket_mutex_unlock(TicketMutex *ticket)
{
pthread_mutex_lock(&ticket->mutex);
ticket->queue_head++;
ticket->has_owner = false;
pthread_cond_broadcast(&ticket->cond);
pthread_mutex_unlock(&ticket->mutex);
}

@ -8,6 +8,8 @@
#include <cstdio>
#include "CLG_log.h"
#include "BLI_alloca.h"
#include "BLI_listbase.h"
#include "BLI_memblock.h"
@ -103,6 +105,8 @@
#include "DRW_select_buffer.hh"
static CLG_LogRef LOG = {"draw.manager"};
/** Render State: No persistent data between draw calls. */
DRWManager DST = {nullptr};
@ -1327,10 +1331,17 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx)
const bool gpencil_engine_needed = drw_gpencil_engine_needed(depsgraph, v3d);
/* XXX Really nasty locking. But else this could
* be executed by the material previews thread
* while rendering a viewport. */
BLI_ticket_mutex_lock(DST.system_gpu_context_mutex);
/* XXX Really nasty locking. But else this could be executed by the
* material previews thread while rendering a viewport.
*
* Check for recursive lock which can deadlock. This should not
* happen, but in case there is a bug where depsgraph update is called
* during drawing we try not to hang Blender. */
if (!BLI_ticket_mutex_lock_check_recursive(DST.system_gpu_context_mutex)) {
CLOG_ERROR(&LOG, "GPU context already bound");
BLI_assert_unreachable();
return;
}
/* Reset before using it. */
drw_state_prepare_clean_for_draw(&DST);