Commit c030439d0b556a831bd004276716f8e86fce5d57

Con Kolivas 2013-11-05T12:46:34

Do get_work in fill_queue without holding other locks.

diff --git a/cgminer.c b/cgminer.c
index ad1ef95..42ee76d 100644
--- a/cgminer.c
+++ b/cgminer.c
@@ -6503,10 +6503,28 @@ static void hash_sole_work(struct thr_info *mythr)
 static void fill_queue(struct thr_info *mythr, struct cgpu_info *cgpu, struct device_drv *drv, const int thr_id)
 {
 	do {
-		wr_lock(&cgpu->qlock);
-		if (!cgpu->unqueued_work)
-			cgpu->unqueued_work = get_work(mythr, thr_id);
-		wr_unlock(&cgpu->qlock);
+		bool need_work;
+
+		/* Do this lockless just to know if we need more unqueued work. */
+		need_work = (!cgpu->unqueued_work);
+
+		/* get_work is a blocking function so do it outside of lock
+		 * to prevent deadlocks with other locks. */
+		if (need_work) {
+			struct work *work = get_work(mythr, thr_id);
+
+			wr_lock(&cgpu->qlock);
+			/* Check we haven't grabbed work somehow between
+			 * checking and picking up the lock. */
+			if (likely(!cgpu->unqueued_work))
+				cgpu->unqueued_work = work;
+			else
+				need_work = false;
+			wr_unlock(&cgpu->qlock);
+
+			if (unlikely(!need_work))
+				discard_work(work);
+		}
 		/* The queue_full function should be used by the driver to
 		 * actually place work items on the physical device if it
 		 * does have a queue. */