diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index 568b0360cc4..f9ef0a5be20 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -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 */ diff --git a/source/blender/blenlib/intern/threads.cc b/source/blender/blenlib/intern/threads.cc index 71277b1c786..42f106aeee8 100644 --- a/source/blender/blenlib/intern/threads.cc +++ b/source/blender/blenlib/intern/threads.cc @@ -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); } diff --git a/source/blender/draw/intern/draw_manager_c.cc b/source/blender/draw/intern/draw_manager_c.cc index 9a27203ab9d..458f651cc46 100644 --- a/source/blender/draw/intern/draw_manager_c.cc +++ b/source/blender/draw/intern/draw_manager_c.cc @@ -8,6 +8,8 @@ #include +#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);