Lots of magic
[kopensolaris-gnu/glibc.git] / nptl / sysdeps / unix / sysv / solaris2 / kopensolaris-gnu / mutex_reltimedlock.c
1 /* Copyright (C) 2008 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by David Bartley <dtbartle@csclub.uwaterloo.ca>, 2008.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <inline-syscall.h>
21 #include <pthreadP.h>
22 #include <synch.h>
23 #include <errno.h>
24 #include <time.h>
25 #include <synchP.h>
26 #include <abstime-to-reltime.h>
27
28 DECLARE_INLINE_SYSCALL (int, lwp_mutex_timedlock, mutex_t *lp,
29     struct timespec *tsp);
30
31 extern int __mutex_lock_fast (mutex_t *mutex, bool try);
32
33
34 int __mutex_timedlock (mutex, abstime)
35       mutex_t *mutex;
36       const struct timespec *abstime;
37 {
38   /* Handle inconsistent robust mutexes.  */
39   if ((mutex->mutex_type & LOCK_ROBUST) &&
40       (mutex->mutex_flag & LOCK_NOTRECOVERABLE))
41     return ENOTRECOVERABLE;
42
43   /* Always hit the kernel for priority inherit locks.  */
44   if ((mutex->mutex_type & LOCK_PRIO_INHERIT) == 0)
45     {
46       int res = __mutex_lock_fast (mutex, false);
47       if(res >= 0)
48         return res;
49     }
50   else
51     {
52       /* Except when we already hold a recursive lock.  */
53       if ((mutex->mutex_type & LOCK_RECURSIVE) && MUTEX_IS_OWNER (mutex))
54         {
55           /* XXX: Solaris mutexes have no overflow check and don't know about
56              EAGAIN; in practice overflow will not occur so we don't care.  */
57           if (mutex->mutex_rcount == RECURSION_MAX)
58             return EAGAIN;
59           ++mutex->mutex_rcount;
60           return 0;
61         }
62     }
63
64   /* Reject invalid timeouts.  */
65   if (INVALID_TIMESPEC (abstime))
66     return EINVAL;
67
68   struct timespec _reltime;
69   struct timespec *reltime = abstime_to_reltime (abstime, &_reltime);
70   if (reltime && reltime->tv_sec < 0)
71     return ETIME;
72
73   int errval;
74   do
75     errval = INLINE_SYSCALL (lwp_mutex_timedlock, 2, mutex, reltime);
76   while (errval == EINTR);
77
78   /* The kernel sets EDEADLK for priority inherit mutexes.  */
79   if (errval == EDEADLK && (mutex->mutex_type & LOCK_PRIO_INHERIT) &&
80         (mutex->mutex_type & LOCK_ERRORCHECK) == 0)
81     {
82       /* We aren't an error checking mutex so we need to block.  */
83       INTERNAL_SYSCALL_DECL (err);
84       if (abstime)
85         {
86           int result = INTERNAL_SYSCALL (nanosleep, err, 2, reltime, reltime);
87           do
88             errval = INTERNAL_SYSCALL_ERRNO (result, err) ? EINTR : ETIMEDOUT;
89           while (errval == EINTR);
90         }
91       else
92         {
93           do
94             INTERNAL_SYSCALL (pause, err, 1, 0);
95           while (1);
96         }
97     }
98   if (errval != 0 && errval != EOWNERDEAD)
99     return errval;
100
101   /* The kernel does not set mutex_owner so we set it here.  */
102   mutex->mutex_owner = THREAD_GETMEM (THREAD_SELF, tid);
103
104   /* The kernel does not set the lockbyte for priority inherit mutexes.  */
105   if (mutex->mutex_type & LOCK_PRIO_INHERIT)
106     mutex->mutex_lockbyte = 1;
107
108   return errval;
109 }