]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libpthread/thread/thr_cond.c
add pthread_cancel, obtained from OpenBSD.
[FreeBSD/FreeBSD.git] / lib / libpthread / thread / thr_cond.c
1 /*
2  * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by John Birrell.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD$
33  */
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <string.h>
37 #ifdef _THREAD_SAFE
38 #include <pthread.h>
39 #include "pthread_private.h"
40
41 /*
42  * Prototypes
43  */
44 static inline pthread_t cond_queue_deq(pthread_cond_t);
45 static inline void      cond_queue_remove(pthread_cond_t, pthread_t);
46 static inline void      cond_queue_enq(pthread_cond_t, pthread_t);
47
48 /* Reinitialize a condition variable to defaults. */
49 int
50 _cond_reinit(pthread_cond_t * cond)
51 {
52         int ret = 0;
53
54         if (cond == NULL)
55                 ret = EINVAL;
56         else if (*cond == NULL)
57                 ret = pthread_cond_init(cond, NULL);
58         else {
59                 /*
60                  * Initialize the condition variable structure:
61                  */
62                 TAILQ_INIT(&(*cond)->c_queue);
63                 (*cond)->c_flags = COND_FLAGS_INITED;
64                 (*cond)->c_type = COND_TYPE_FAST;
65                 (*cond)->c_mutex = NULL;
66                 memset(&(*cond)->lock, 0, sizeof((*cond)->lock));
67         }
68         return (ret);
69 }
70
71 int
72 pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
73 {
74         enum pthread_cond_type type;
75         pthread_cond_t  pcond;
76         int             rval = 0;
77
78         if (cond == NULL)
79                 rval = EINVAL;
80         else {
81                 /*
82                  * Check if a pointer to a condition variable attribute
83                  * structure was passed by the caller: 
84                  */
85                 if (cond_attr != NULL && *cond_attr != NULL) {
86                         /* Default to a fast condition variable: */
87                         type = (*cond_attr)->c_type;
88                 } else {
89                         /* Default to a fast condition variable: */
90                         type = COND_TYPE_FAST;
91                 }
92
93                 /* Process according to condition variable type: */
94                 switch (type) {
95                 /* Fast condition variable: */
96                 case COND_TYPE_FAST:
97                         /* Nothing to do here. */
98                         break;
99
100                 /* Trap invalid condition variable types: */
101                 default:
102                         /* Return an invalid argument error: */
103                         rval = EINVAL;
104                         break;
105                 }
106
107                 /* Check for no errors: */
108                 if (rval == 0) {
109                         if ((pcond = (pthread_cond_t)
110                             malloc(sizeof(struct pthread_cond))) == NULL) {
111                                 rval = ENOMEM;
112                         } else {
113                                 /*
114                                  * Initialise the condition variable
115                                  * structure:
116                                  */
117                                 TAILQ_INIT(&pcond->c_queue);
118                                 pcond->c_flags |= COND_FLAGS_INITED;
119                                 pcond->c_type = type;
120                                 pcond->c_mutex = NULL;
121                                 memset(&pcond->lock,0,sizeof(pcond->lock));
122                                 *cond = pcond;
123                         }
124                 }
125         }
126         /* Return the completion status: */
127         return (rval);
128 }
129
130 int
131 pthread_cond_destroy(pthread_cond_t * cond)
132 {
133         int             rval = 0;
134
135         if (cond == NULL || *cond == NULL)
136                 rval = EINVAL;
137         else {
138                 /* Lock the condition variable structure: */
139                 _SPINLOCK(&(*cond)->lock);
140
141                 /*
142                  * Free the memory allocated for the condition
143                  * variable structure:
144                  */
145                 free(*cond);
146
147                 /*
148                  * NULL the caller's pointer now that the condition
149                  * variable has been destroyed:
150                  */
151                 *cond = NULL;
152         }
153         /* Return the completion status: */
154         return (rval);
155 }
156
157 int
158 pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
159 {
160         int rval = 0;
161
162         if (cond == NULL)
163                 rval = EINVAL;
164
165         /*
166          * If the condition variable is statically initialized,
167          * perform the dynamic initialization:
168          */
169         else if (*cond != NULL ||
170             (rval = pthread_cond_init(cond,NULL)) == 0) {
171
172                 _thread_enter_cancellation_point();
173         
174                 /* Lock the condition variable structure: */
175                 _SPINLOCK(&(*cond)->lock);
176
177                 /*
178                  * If the condvar was statically allocated, properly
179                  * initialize the tail queue.
180                  */
181                 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
182                         TAILQ_INIT(&(*cond)->c_queue);
183                         (*cond)->c_flags |= COND_FLAGS_INITED;
184                 }
185
186                 /* Process according to condition variable type: */
187                 switch ((*cond)->c_type) {
188                 /* Fast condition variable: */
189                 case COND_TYPE_FAST:
190                         if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
191                             ((*cond)->c_mutex != *mutex))) {
192                                 /* Unlock the condition variable structure: */
193                                 _SPINUNLOCK(&(*cond)->lock);
194
195                                 /* Return invalid argument error: */
196                                 rval = EINVAL;
197                         } else {
198                                 /* Reset the timeout and interrupted flags: */
199                                 _thread_run->timeout = 0;
200                                 _thread_run->interrupted = 0;
201
202                                 /*
203                                  * Queue the running thread for the condition
204                                  * variable:
205                                  */
206                                 cond_queue_enq(*cond, _thread_run);
207
208                                 /* Remember the mutex that is being used: */
209                                 (*cond)->c_mutex = *mutex;
210
211                                 /* Wait forever: */
212                                 _thread_run->wakeup_time.tv_sec = -1;
213
214                                 /* Unlock the mutex: */
215                                 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
216                                         /*
217                                          * Cannot unlock the mutex, so remove
218                                          * the running thread from the condition
219                                          * variable queue:
220                                          */
221                                         cond_queue_remove(*cond, _thread_run);
222
223                                         /* Check for no more waiters: */
224                                         if (TAILQ_FIRST(&(*cond)->c_queue) ==
225                                             NULL)
226                                                 (*cond)->c_mutex = NULL;
227
228                                         /* Unlock the condition variable structure: */
229                                         _SPINUNLOCK(&(*cond)->lock);
230                                 }
231                                 else {
232                                         /*
233                                          * Schedule the next thread and unlock
234                                          * the condition variable structure:
235                                          */
236                                         _thread_kern_sched_state_unlock(PS_COND_WAIT,
237                                             &(*cond)->lock, __FILE__, __LINE__);
238
239                                         if (_thread_run->interrupted != 0) {
240                                                 /*
241                                                  * Lock the condition variable
242                                                  * while removing the thread.
243                                                  */
244                                                 _SPINLOCK(&(*cond)->lock);
245
246                                                 cond_queue_remove(*cond,
247                                                     _thread_run);
248
249                                                 /* Check for no more waiters: */
250                                                 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
251                                                         (*cond)->c_mutex = NULL;
252
253                                                 _SPINUNLOCK(&(*cond)->lock);
254                                         }
255
256                                         /*
257                                          * Note that even though this thread may have
258                                          * been canceled, POSIX requires that the mutex
259                                          * be reaquired prior to cancellation.
260                                          */
261                                         rval = _mutex_cv_lock(mutex);
262                                 }
263                         }
264                         break;
265
266                 /* Trap invalid condition variable types: */
267                 default:
268                         /* Unlock the condition variable structure: */
269                         _SPINUNLOCK(&(*cond)->lock);
270
271                         /* Return an invalid argument error: */
272                         rval = EINVAL;
273                         break;
274                 }
275
276                 if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
277                         _thread_exit_cleanup();
278                         pthread_exit(PTHREAD_CANCELED);
279                 }
280
281                 _thread_leave_cancellation_point();
282         }
283
284         /* Return the completion status: */
285         return (rval);
286 }
287
288 int
289 pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
290                        const struct timespec * abstime)
291 {
292         int rval = 0;
293
294         if (cond == NULL || abstime == NULL)
295                 rval = EINVAL;
296
297         if (abstime->tv_sec < 0 || 
298                 abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
299                 errno = EINVAL;
300                 return (-1);
301         }
302
303         /*
304          * If the condition variable is statically initialized,
305          * perform the dynamic initialization:
306          */
307         if (*cond != NULL ||
308             (rval = pthread_cond_init(cond,NULL)) == 0) {
309
310                 _thread_enter_cancellation_point();
311
312                 /* Lock the condition variable structure: */
313                 _SPINLOCK(&(*cond)->lock);
314
315                 /*
316                  * If the condvar was statically allocated, properly
317                  * initialize the tail queue.
318                  */
319                 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
320                         TAILQ_INIT(&(*cond)->c_queue);
321                         (*cond)->c_flags |= COND_FLAGS_INITED;
322                 }
323
324                 /* Process according to condition variable type: */
325                 switch ((*cond)->c_type) {
326                 /* Fast condition variable: */
327                 case COND_TYPE_FAST:
328                         if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
329                             ((*cond)->c_mutex != *mutex))) {
330                                 /* Return invalid argument error: */
331                                 rval = EINVAL;
332
333                                 /* Unlock the condition variable structure: */
334                                 _SPINUNLOCK(&(*cond)->lock);
335                         } else {
336                                 /* Set the wakeup time: */
337                                 _thread_run->wakeup_time.tv_sec =
338                                     abstime->tv_sec;
339                                 _thread_run->wakeup_time.tv_nsec =
340                                     abstime->tv_nsec;
341
342                                 /* Reset the timeout and interrupted flags: */
343                                 _thread_run->timeout = 0;
344                                 _thread_run->interrupted = 0;
345
346                                 /*
347                                  * Queue the running thread for the condition
348                                  * variable:
349                                  */
350                                 cond_queue_enq(*cond, _thread_run);
351
352                                 /* Remember the mutex that is being used: */
353                                 (*cond)->c_mutex = *mutex;
354
355                                 /* Unlock the mutex: */
356                                 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
357                                         /*
358                                          * Cannot unlock the mutex, so remove
359                                          * the running thread from the condition
360                                          * variable queue: 
361                                          */
362                                         cond_queue_remove(*cond, _thread_run);
363
364                                         /* Check for no more waiters: */
365                                         if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
366                                                 (*cond)->c_mutex = NULL;
367
368                                         /* Unlock the condition variable structure: */
369                                         _SPINUNLOCK(&(*cond)->lock);
370                                 } else {
371                                         /*
372                                          * Schedule the next thread and unlock
373                                          * the condition variable structure:
374                                          */
375                                         _thread_kern_sched_state_unlock(PS_COND_WAIT,
376                                              &(*cond)->lock, __FILE__, __LINE__);
377
378                                         /*
379                                          * Check if the wait timedout or was
380                                          * interrupted (canceled):
381                                          */
382                                         if ((_thread_run->timeout == 0) &&
383                                             (_thread_run->interrupted == 0)) {
384                                                 /* Lock the mutex: */
385                                                 rval = _mutex_cv_lock(mutex);
386
387                                         } else {
388                                                 /* Lock the condition variable structure: */
389                                                 _SPINLOCK(&(*cond)->lock);
390
391                                                 /*
392                                                  * The wait timed out; remove
393                                                  * the thread from the condition
394                                                  * variable queue:
395                                                  */
396                                                 cond_queue_remove(*cond,
397                                                     _thread_run);
398
399                                                 /* Check for no more waiters: */
400                                                 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
401                                                         (*cond)->c_mutex = NULL;
402
403                                                 /* Unock the condition variable structure: */
404                                                 _SPINUNLOCK(&(*cond)->lock);
405
406                                                 /* Return a timeout error: */
407                                                 rval = ETIMEDOUT;
408
409                                                 /*
410                                                  * Lock the mutex and ignore any
411                                                  * errors.  Note that even though
412                                                  * this thread may have been
413                                                  * canceled, POSIX requires that
414                                                  * the mutex be reaquired prior
415                                                  * to cancellation.
416                                                  */
417                                                 (void)_mutex_cv_lock(mutex);
418                                         }
419                                 }
420                         }
421                         break;
422
423                 /* Trap invalid condition variable types: */
424                 default:
425                         /* Unlock the condition variable structure: */
426                         _SPINUNLOCK(&(*cond)->lock);
427
428                         /* Return an invalid argument error: */
429                         rval = EINVAL;
430                         break;
431                 }
432
433                 if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
434                         _thread_exit_cleanup();
435                         pthread_exit(PTHREAD_CANCELED);
436                 }
437
438                 _thread_leave_cancellation_point();
439         }
440
441         /* Return the completion status: */
442         return (rval);
443 }
444
445 int
446 pthread_cond_signal(pthread_cond_t * cond)
447 {
448         int             rval = 0;
449         pthread_t       pthread;
450
451         if (cond == NULL || *cond == NULL)
452                 rval = EINVAL;
453         else {
454                 /*
455                  * Defer signals to protect the scheduling queues
456                  * from access by the signal handler:
457                  */
458                 _thread_kern_sig_defer();
459
460                 /* Lock the condition variable structure: */
461                 _SPINLOCK(&(*cond)->lock);
462
463                 /* Process according to condition variable type: */
464                 switch ((*cond)->c_type) {
465                 /* Fast condition variable: */
466                 case COND_TYPE_FAST:
467                         if ((pthread = cond_queue_deq(*cond)) != NULL)
468                                 /* Allow the thread to run: */
469                                 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
470
471                         /* Check for no more waiters: */
472                         if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
473                                 (*cond)->c_mutex = NULL;
474                         break;
475
476                 /* Trap invalid condition variable types: */
477                 default:
478                         /* Return an invalid argument error: */
479                         rval = EINVAL;
480                         break;
481                 }
482
483                 /* Unlock the condition variable structure: */
484                 _SPINUNLOCK(&(*cond)->lock);
485
486                 /*
487                  * Undefer and handle pending signals, yielding if
488                  * necessary:
489                  */
490                 _thread_kern_sig_undefer();
491         }
492
493         /* Return the completion status: */
494         return (rval);
495 }
496
497 int
498 pthread_cond_broadcast(pthread_cond_t * cond)
499 {
500         int             rval = 0;
501         pthread_t       pthread;
502
503         if (cond == NULL || *cond == NULL)
504                 rval = EINVAL;
505         else {
506                 /*
507                  * Defer signals to protect the scheduling queues
508                  * from access by the signal handler:
509                  */
510                 _thread_kern_sig_defer();
511
512                 /* Lock the condition variable structure: */
513                 _SPINLOCK(&(*cond)->lock);
514
515                 /* Process according to condition variable type: */
516                 switch ((*cond)->c_type) {
517                 /* Fast condition variable: */
518                 case COND_TYPE_FAST:
519                         /*
520                          * Enter a loop to bring all threads off the
521                          * condition queue:
522                          */
523                         while ((pthread = cond_queue_deq(*cond)) != NULL) {
524                                 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
525                         }
526
527                         /* There are no more waiting threads: */
528                         (*cond)->c_mutex = NULL;
529                         break;
530         
531                 /* Trap invalid condition variable types: */
532                 default:
533                         /* Return an invalid argument error: */
534                         rval = EINVAL;
535                         break;
536                 }
537
538                 /* Unlock the condition variable structure: */
539                 _SPINUNLOCK(&(*cond)->lock);
540
541                 /*
542                  * Undefer and handle pending signals, yielding if
543                  * necessary:
544                  */
545                 _thread_kern_sig_undefer();
546         }
547
548         /* Return the completion status: */
549         return (rval);
550 }
551
552 /*
553  * Dequeue a waiting thread from the head of a condition queue in
554  * descending priority order.
555  */
556 static inline pthread_t
557 cond_queue_deq(pthread_cond_t cond)
558 {
559         pthread_t pthread;
560
561         while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
562                 TAILQ_REMOVE(&cond->c_queue, pthread, qe);
563                 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
564                 if ((pthread->timeout == 0) && (pthread->interrupted == 0))
565                         /*
566                          * Only exit the loop when we find a thread
567                          * that hasn't timed out or been canceled;
568                          * those threads are already running and don't
569                          * need their run state changed.
570                          */
571                         break;
572         }
573
574         return(pthread);
575 }
576
577 /*
578  * Remove a waiting thread from a condition queue in descending priority
579  * order.
580  */
581 static inline void
582 cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
583 {
584         /*
585          * Because pthread_cond_timedwait() can timeout as well
586          * as be signaled by another thread, it is necessary to
587          * guard against removing the thread from the queue if
588          * it isn't in the queue.
589          */
590         if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
591                 TAILQ_REMOVE(&cond->c_queue, pthread, qe);
592                 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
593         }
594 }
595
596 /*
597  * Enqueue a waiting thread to a condition queue in descending priority
598  * order.
599  */
600 static inline void
601 cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
602 {
603         pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
604
605         /*
606          * For the common case of all threads having equal priority,
607          * we perform a quick check against the priority of the thread
608          * at the tail of the queue.
609          */
610         if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
611                 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe);
612         else {
613                 tid = TAILQ_FIRST(&cond->c_queue);
614                 while (pthread->active_priority <= tid->active_priority)
615                         tid = TAILQ_NEXT(tid, qe);
616                 TAILQ_INSERT_BEFORE(tid, pthread, qe);
617         }
618         pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
619 }
620 #endif