Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 23 Oct 1998 10:00:00 -0700 (PDT)
From:      "Daniel M. Eischen" <eischen@vigrid.com>
To:        freebsd-bugs@FreeBSD.ORG
Subject:   Re: kern/8375: pthread_cond_wait() spins the CPU
Message-ID:  <199810231700.KAA21223@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
The following reply was made to PR kern/8375; it has been noted by GNATS.

From: "Daniel M. Eischen" <eischen@vigrid.com>
To: freebsd-gnats-submit@freebsd.org, info@highwind.com
Cc: jb@freebsd.org
Subject: Re: kern/8375: pthread_cond_wait() spins the CPU
Date: Fri, 23 Oct 1998 12:40:19 -0400

 This is a multi-part message in MIME format.
 
 --------------446B9B3D2781E494167EB0E7
 Content-Type: text/plain; charset=us-ascii
 Content-Transfer-Encoding: 7bit
 
 The spinlock ordering of the cond_wait and mutex structures
 can lead to a deadlock.  Also, kernel scheduling initiated
 by a SIGVTALRM can cause improper mutex and condition
 variable operation.
 
 Attach is a patch that fixes these problems and also
 makes pthread_mutex[try]lock and pthread_cond_[timed]wait
 return EDEADLK when the caller has not locked the mutex.
 
 Dan Eischen
 eischen@vigrid.com
 
 --------------446B9B3D2781E494167EB0E7
 Content-Type: text/plain; charset=us-ascii; name="uthread.diffs"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: inline; filename="uthread.diffs"
 
 *** pthread_private.h.orig	Thu Oct 22 12:15:26 1998
 --- pthread_private.h	Thu Oct 22 12:16:04 1998
 ***************
 *** 446,451 ****
 --- 446,463 ----
   	/* Signal number when in state PS_SIGWAIT: */
   	int		signo;
   
 + 	/*
 + 	 * Set to non-zero when this thread has locked out thread
 + 	 * scheduling.  We allow this lock to be recursively taken.
 + 	 */
 + 	int		sched_lock;
 + 
 + 	/*
 + 	 * Set to TRUE if this thread should yield after reenabling
 + 	 * thread scheduling.
 + 	 */
 + 	int		yield_on_sched_unlock;
 + 
   	/* Miscellaneous data. */
   	char		flags;
   #define PTHREAD_EXITING		0x0100
 ***************
 *** 655,660 ****
 --- 667,674 ----
   void    _thread_kern_sched(struct sigcontext *);
   void    _thread_kern_sched_state(enum pthread_state,char *fname,int lineno);
   void    _thread_kern_set_timeout(struct timespec *);
 + void    _thread_kern_sched_lock(void);
 + void    _thread_kern_sched_unlock(void);
   void    _thread_sig_handler(int, int, struct sigcontext *);
   void    _thread_start(void);
   void    _thread_start_sig_handler(void);
 *** uthread_cond.c.orig	Sat Oct 17 19:46:57 1998
 --- uthread_cond.c	Thu Oct 22 11:44:46 1998
 ***************
 *** 137,142 ****
 --- 137,150 ----
   	 */
   	else if (*cond != NULL ||
   	    (rval = pthread_cond_init(cond,NULL)) == 0) {
 + 		/*
 + 		 * Disable thread scheduling.  If the thread blocks
 + 		 * in some way, thread scheduling is reenabled.  When
 + 		 * the thread wakes up, thread scheduling will again
 + 		 * be disabled.
 + 		 */
 + 		_thread_kern_sched_lock();
 + 
   		/* Lock the condition variable structure: */
   		_SPINLOCK(&(*cond)->lock);
   
 ***************
 *** 150,184 ****
   			 */
   			_thread_queue_enq(&(*cond)->c_queue, _thread_run);
   
 - 			/* Unlock the mutex: */
 - 			pthread_mutex_unlock(mutex);
 - 
 - 			/* Wait forever: */
 - 			_thread_run->wakeup_time.tv_sec = -1;
 - 
   			/* Unlock the condition variable structure: */
   			_SPINUNLOCK(&(*cond)->lock);
   
 ! 			/* Schedule the next thread: */
 ! 			_thread_kern_sched_state(PS_COND_WAIT,
 ! 			    __FILE__, __LINE__);
   
 ! 			/* Lock the condition variable structure: */
 ! 			_SPINLOCK(&(*cond)->lock);
   
 ! 			/* Lock the mutex: */
 ! 			rval = pthread_mutex_lock(mutex);
   			break;
   
   		/* Trap invalid condition variable types: */
   		default:
   			/* Return an invalid argument error: */
   			rval = EINVAL;
   			break;
   		}
   
 ! 	/* Unlock the condition variable structure: */
 ! 	_SPINUNLOCK(&(*cond)->lock);
   	}
   
   	/* Return the completion status: */
 --- 158,193 ----
   			 */
   			_thread_queue_enq(&(*cond)->c_queue, _thread_run);
   
   			/* Unlock the condition variable structure: */
   			_SPINUNLOCK(&(*cond)->lock);
   
 ! 			/* Unlock the mutex: */
 ! 			if (rval = pthread_mutex_unlock(mutex) == 0) {
 ! 
 ! 				/* Wait forever: */
 ! 				_thread_run->wakeup_time.tv_sec = -1;
   
 ! 				/* Schedule the next thread: */
 ! 				_thread_kern_sched_state(PS_COND_WAIT,
 ! 			 	   __FILE__, __LINE__);
   
 ! 				/* Lock the mutex: */
 ! 				rval = pthread_mutex_lock(mutex);
 ! 			}
   			break;
   
   		/* Trap invalid condition variable types: */
   		default:
 + 			/* Unlock the condition variable structure: */
 + 			_SPINUNLOCK(&(*cond)->lock);
 + 
   			/* Return an invalid argument error: */
   			rval = EINVAL;
   			break;
   		}
   
 ! 		/* Reenable thread scheduling. */
 ! 		_thread_kern_sched_unlock();
   	}
   
   	/* Return the completion status: */
 ***************
 *** 201,206 ****
 --- 210,223 ----
   	 */
   	else if (*cond != NULL ||
   	    (rval = pthread_cond_init(cond,NULL)) == 0) {
 + 		/*
 + 		 * Disable thread scheduling.  If the thread blocks
 + 		 * in some way, thread scheduling is reenabled.  When
 + 		 * the thread wakes up, thread scheduling will again
 + 		 * be disabled.
 + 		 */
 + 		_thread_kern_sched_lock();
 + 
   		/* Lock the condition variable structure: */
   		_SPINLOCK(&(*cond)->lock);
   
 ***************
 *** 221,226 ****
 --- 238,246 ----
   			 */
   			_thread_queue_enq(&(*cond)->c_queue, _thread_run);
   
 + 			/* Unlock the condition structure: */
 + 			_SPINUNLOCK(&(*cond)->lock);
 + 
   			/* Unlock the mutex: */
   			if ((rval = pthread_mutex_unlock(mutex)) != 0) {
   				/*
 ***************
 *** 230,245 ****
   				 */
   				_thread_queue_deq(&(*cond)->c_queue);
   			} else {
 - 				/* Unlock the condition variable structure: */
 - 				_SPINUNLOCK(&(*cond)->lock);
 - 
   				/* Schedule the next thread: */
   				_thread_kern_sched_state(PS_COND_WAIT,
   				    __FILE__, __LINE__);
   
 - 				/* Lock the condition variable structure: */
 - 				_SPINLOCK(&(*cond)->lock);
 - 
   				/* Lock the mutex: */
   				if ((rval = pthread_mutex_lock(mutex)) != 0) {
   				}
 --- 250,259 ----
 ***************
 *** 253,265 ****
   
   		/* Trap invalid condition variable types: */
   		default:
   			/* Return an invalid argument error: */
   			rval = EINVAL;
   			break;
   		}
   
 ! 	/* Unlock the condition variable structure: */
 ! 	_SPINUNLOCK(&(*cond)->lock);
   	}
   
   	/* Return the completion status: */
 --- 267,282 ----
   
   		/* Trap invalid condition variable types: */
   		default:
 + 			/* Unlock the condition structure: */
 + 			_SPINUNLOCK(&(*cond)->lock);
 + 
   			/* Return an invalid argument error: */
   			rval = EINVAL;
   			break;
   		}
   
 ! 		/* Reenable thread scheduling. */
 ! 		_thread_kern_sched_unlock();
   	}
   
   	/* Return the completion status: */
 ***************
 *** 276,281 ****
 --- 293,306 ----
   	if (cond == NULL || *cond == NULL)
   		rval = EINVAL;
   	else {
 + 		/*
 + 		 * Disable thread scheduling.  If the thread blocks
 + 		 * in some way, thread scheduling is reenabled.  When
 + 		 * the thread wakes up, thread scheduling will again
 + 		 * be disabled.
 + 		 */
 + 		_thread_kern_sched_lock();
 + 
   		/* Lock the condition variable structure: */
   		_SPINLOCK(&(*cond)->lock);
   
 ***************
 *** 299,304 ****
 --- 324,332 ----
   
   		/* Unlock the condition variable structure: */
   		_SPINUNLOCK(&(*cond)->lock);
 + 
 + 		/* Reenable thread scheduling. */
 + 		_thread_kern_sched_unlock();
   	}
   
   	/* Return the completion status: */
 ***************
 *** 315,320 ****
 --- 343,356 ----
   	if (cond == NULL || *cond == NULL)
   		rval = EINVAL;
   	else {
 + 		/*
 + 		 * Disable thread scheduling.  If the thread blocks
 + 		 * in some way, thread scheduling is reenabled.  When
 + 		 * the thread wakes up, thread scheduling will again
 + 		 * be disabled.
 + 		 */
 + 		_thread_kern_sched_lock();
 + 
   		/* Lock the condition variable structure: */
   		_SPINLOCK(&(*cond)->lock);
   
 ***************
 *** 342,347 ****
 --- 378,386 ----
   
   		/* Unlock the condition variable structure: */
   		_SPINUNLOCK(&(*cond)->lock);
 + 
 + 		/* Reenable thread scheduling. */
 + 		_thread_kern_sched_unlock();
   	}
   
   	/* Return the completion status: */
 *** uthread_create.c.orig	Sat Oct 17 19:46:57 1998
 --- uthread_create.c	Thu Oct 22 11:44:46 1998
 ***************
 *** 90,95 ****
 --- 90,97 ----
   			new_thread->stack = stack;
   			new_thread->start_routine = start_routine;
   			new_thread->arg = arg;
 + 			new_thread->sched_lock = 0;
 + 			new_thread->yield_on_sched_unlock = 0;
   
   			/*
   			 * Write a magic value to the thread structure
 *** uthread_kern.c.orig	Thu Oct 22 12:20:14 1998
 --- uthread_kern.c	Thu Oct 22 23:44:55 1998
 ***************
 *** 196,201 ****
 --- 196,206 ----
   		/* Check if there is a current thread: */
   		if (_thread_run != &_thread_kern_thread) {
   			/*
 + 			 * This thread no longer needs to yield the CPU.
 + 			 */
 + 			_thread_run->yield_on_sched_unlock = 0;
 + 
 + 			/*
   			 * Save the current time as the time that the thread
   			 * became inactive: 
   			 */
 ***************
 *** 1303,1307 ****
 --- 1308,1333 ----
   		}
   	}
   	return;
 + }
 + 
 + void
 + _thread_kern_sched_lock(void)
 + {
 + 	/* Allow the scheduling lock to be recursively set. */
 + 	_thread_run->sched_lock++;
 + }
 + 
 + void
 + _thread_kern_sched_unlock(void)
 + {
 + 	/* Decrement the scheduling lock count. */
 + 	_thread_run->sched_lock--;
 + 
 + 	/* Check to see if we need to yield. */
 + 	if ((_thread_run->sched_lock == 0) &&
 + 	    (_thread_run->yield_on_sched_unlock != 0)) {
 + 		_thread_run->yield_on_sched_unlock = 0;
 + 		sched_yield();
 + 	}
   }
   #endif
 *** uthread_mutex.c.orig	Sat Oct 17 19:46:58 1998
 --- uthread_mutex.c	Thu Oct 22 11:44:46 1998
 ***************
 *** 168,173 ****
 --- 168,181 ----
   	 * initialization:
   	 */
   	else if (*mutex != NULL || (ret = init_static(mutex)) == 0) {
 + 		/*
 + 		 * Disable thread scheduling.  If the thread blocks
 + 		 * in some way, thread scheduling is reenabled.  When
 + 		 * the thread wakes up, thread scheduling will again
 + 		 * be disabled.
 + 		 */
 + 		_thread_kern_sched_lock();
 + 
   		/* Lock the mutex structure: */
   		_SPINLOCK(&(*mutex)->lock);
   
 ***************
 *** 215,220 ****
 --- 223,231 ----
   
   		/* Unlock the mutex structure: */
   		_SPINUNLOCK(&(*mutex)->lock);
 + 
 + 		/* Reenable thread scheduling. */
 + 		_thread_kern_sched_unlock();
   	}
   
   	/* Return the completion status: */
 ***************
 *** 234,239 ****
 --- 245,258 ----
   	 * initialization:
   	 */
   	else if (*mutex != NULL || (ret = init_static(mutex)) == 0) {
 + 		/*
 + 		 * Disable thread scheduling.  If the thread blocks
 + 		 * in some way, thread scheduling is reenabled.  When
 + 		 * the thread wakes up, thread scheduling will again
 + 		 * be disabled.
 + 		 */
 + 		_thread_kern_sched_lock();
 + 
   		/* Lock the mutex structure: */
   		_SPINLOCK(&(*mutex)->lock);
   
 ***************
 *** 241,270 ****
   		switch ((*mutex)->m_type) {
   		/* Fast mutexes do not check for any error conditions: */
   		case MUTEX_TYPE_FAST:
 ! 			/*
 ! 			 * Enter a loop to wait for the mutex to be locked by the
 ! 			 * current thread: 
 ! 			 */
 ! 			while ((*mutex)->m_owner != _thread_run) {
 ! 				/* Check if the mutex is not locked: */
 ! 				if ((*mutex)->m_owner == NULL) {
 ! 					/* Lock the mutex for this thread: */
 ! 					(*mutex)->m_owner = _thread_run;
 ! 				} else {
 ! 					/*
 ! 					 * Join the queue of threads waiting to lock
 ! 					 * the mutex: 
 ! 					 */
 ! 					_thread_queue_enq(&(*mutex)->m_queue, _thread_run);
 ! 
 ! 					/* Unlock the mutex structure: */
 ! 					_SPINUNLOCK(&(*mutex)->lock);
 ! 
 ! 					/* Block signals: */
 ! 					_thread_kern_sched_state(PS_MUTEX_WAIT, __FILE__, __LINE__);
 ! 
 ! 					/* Lock the mutex again: */
 ! 					_SPINLOCK(&(*mutex)->lock);
   				}
   			}
   			break;
 --- 260,295 ----
   		switch ((*mutex)->m_type) {
   		/* Fast mutexes do not check for any error conditions: */
   		case MUTEX_TYPE_FAST:
 ! 			if ((*mutex)->m_owner == _thread_run)
 ! 				ret = EDEADLK;
 ! 			else {
 ! 				/*
 ! 				 * Enter a loop to wait for the mutex to be
 ! 				 * locked by the current thread: 
 ! 				 */
 ! 				while ((*mutex)->m_owner != _thread_run) {
 ! 					/* Check if the mutex is not locked: */
 ! 					if ((*mutex)->m_owner == NULL) {
 ! 						/* Lock the mutex for this thread: */
 ! 						(*mutex)->m_owner = _thread_run;
 ! 					} else {
 ! 						/*
 ! 						 * Join the queue of threads
 ! 						 * waiting to lock the mutex: 
 ! 						 */
 ! 						_thread_queue_enq(&(*mutex)->m_queue,
 ! 						    _thread_run);
 ! 
 ! 						/* Unlock the mutex structure: */
 ! 						_SPINUNLOCK(&(*mutex)->lock);
 ! 	
 ! 						/* Schedule the next thread: */
 ! 						_thread_kern_sched_state(PS_MUTEX_WAIT,
 ! 						    __FILE__, __LINE__);
 ! 
 ! 						/* Lock the mutex structure again: */
 ! 						_SPINLOCK(&(*mutex)->lock);
 ! 					}
   				}
   			}
   			break;
 ***************
 *** 283,288 ****
 --- 308,316 ----
   
   					/* Reset the lock count for this mutex: */
   					(*mutex)->m_data.m_count = 0;
 + 
 + 					/* Unlock the mutex structure: */
 + 					_SPINUNLOCK(&(*mutex)->lock);
   				} else {
   					/*
   					 * Join the queue of threads waiting to lock
 ***************
 *** 293,302 ****
   					/* Unlock the mutex structure: */
   					_SPINUNLOCK(&(*mutex)->lock);
   
 ! 					/* Block signals: */
   					_thread_kern_sched_state(PS_MUTEX_WAIT, __FILE__, __LINE__);
   
 ! 					/* Lock the mutex again: */
   					_SPINLOCK(&(*mutex)->lock);
   				}
   			}
 --- 321,330 ----
   					/* Unlock the mutex structure: */
   					_SPINUNLOCK(&(*mutex)->lock);
   
 ! 					/* Schedule the next thread: */
   					_thread_kern_sched_state(PS_MUTEX_WAIT, __FILE__, __LINE__);
   
 ! 					/* Lock the mutex structure again: */
   					_SPINLOCK(&(*mutex)->lock);
   				}
   			}
 ***************
 *** 307,319 ****
   
   		/* Trap invalid mutex types: */
   		default:
   			/* Return an invalid argument error: */
   			ret = EINVAL;
   			break;
   		}
   
 ! 		/* Unlock the mutex structure: */
 ! 		_SPINUNLOCK(&(*mutex)->lock);
   	}
   
   	/* Return the completion status: */
 --- 335,350 ----
   
   		/* Trap invalid mutex types: */
   		default:
 + 			/* Unlock the mutex structure: */
 + 			_SPINUNLOCK(&(*mutex)->lock);
 + 
   			/* Return an invalid argument error: */
   			ret = EINVAL;
   			break;
   		}
   
 ! 		/* Reenable thread scheduling. */
 ! 		_thread_kern_sched_unlock();
   	}
   
   	/* Return the completion status: */
 ***************
 *** 328,333 ****
 --- 359,372 ----
   	if (mutex == NULL || *mutex == NULL) {
   		ret = EINVAL;
   	} else {
 + 		/*
 + 		 * Disable thread scheduling.  If the thread blocks
 + 		 * in some way, thread scheduling is reenabled.  When
 + 		 * the thread wakes up, thread scheduling will again
 + 		 * be disabled.
 + 		 */
 + 		_thread_kern_sched_lock();
 + 
   		/* Lock the mutex structure: */
   		_SPINLOCK(&(*mutex)->lock);
   
 ***************
 *** 381,386 ****
 --- 420,428 ----
   
   		/* Unlock the mutex structure: */
   		_SPINUNLOCK(&(*mutex)->lock);
 + 
 + 		/* Reenable thread scheduling. */
 + 		_thread_kern_sched_unlock();
   	}
   
   	/* Return the completion status: */
 *** uthread_sig.c.orig	Thu Oct 22 12:28:16 1998
 --- uthread_sig.c	Thu Oct 22 12:14:24 1998
 ***************
 *** 115,120 ****
 --- 115,128 ----
   			yield_on_unlock_thread = 1;
   
   		/*
 + 		 * Check if the scheduler interrupt has come when
 + 		 * the currently running thread has disabled thread
 + 		 * scheduling.
 + 		 */
 + 		else if (_thread_run->sched_lock)
 + 			_thread_run->yield_on_sched_unlock = 1;
 + 
 + 		/*
   		 * Check if the kernel has not been interrupted while
   		 * executing scheduler code:
   		 */
 
 --------------446B9B3D2781E494167EB0E7--
 

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199810231700.KAA21223>